diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/ChangeLog /tmp/GW9hRDRi14/libmtp-0.2.2/ChangeLog --- /tmp/KyV3P4PTuG/libmtp-0.2.1/ChangeLog 2007-08-07 16:58:09.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/ChangeLog 2007-10-04 23:20:20.000000000 +0100 @@ -1,3 +1,204 @@ +2007-10-04 Linus Walleij + + * src/libusb-glue.c: rm some pointless confusing defines + inherited from libgphoto2. + * configure.ac: bump to 0.2.2 + * src/Makefile.am: interface to libmtp.so.6.0.2 (compatible) + * Release as 0.2.2. Now is as good time as ever. Release + early and release often. + +2007-10-03 Linus Walleij + + * src/libusb-glue.c: tag the OLD Creative devices with + DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL, after clear + indications that it was broken when retrieveing folders + on all those devices. The newer devices (those supporting + 32bit object size) presumably does not have this limitation. + +2007-10-02 Linus Walleij + + * src/libmtp.c: devices which represented file size with a + 32bit value (some Creative devices) would return a bananas + file size. Fixed it up by... + * src/libmtp.h.in: recycling the uint8_t "interface" + field in the device struct as a holder of the object size + for the device. This will make the new library binary + compatible with version 0.2.1 since no-one should *ever* + dereference that value (which used to be the USB interface + number and is now the object size). + * examples/files.c: display 64bit file sizes in hex correctly. + +2007-09-29 Richard Low + + * src/libmtp.c: avoid crash on failed connect + +2007-09-28 Linus Walleij + + * src/libusb-glue.h: introduce a new device flag for devices that + have broken PTP headers, first encountered on the Creative + ZEN 8GB. + * src/libusb-glue.c: attempt to begin to work around the broken + PTP headers. + +2007-09-25 Linus Walleij + + * src/libusb-glue.h: add a new device flag for devices that don't + like it if you release the interface (or try to clear endpoints). + * src/libusb-glue.c: dito, implement this flag, tag all SanDisk + Sansa devices with it except for the Linux-based Sansa Connect. + +2007-09-23 Richard Low + + * src/libusb-glue.c: updated some device flags, removed second + call to ptp_getdeviceinfo + * src/libmtp.c: property cache fixes + +2007-09-23 Linus Walleij + + * src/libmtp.c: wrapped updating of playlists and albums into an + abstract function so we do it consistently. Added support for + tagging on modification date to files, tracks, playlists and + albums. + +2007-09-21 Linus Walleij + + * src/libusb-glue.c: flag the Samsung YH-820 with + DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL after tests by Stephan + Fabel. + +2007-09-18 Linus Walleij + + * src/libusb-glue.c: stop the endpoint unhalting/unstalling when + closing the USB device. It was AYBABTU the Samsungs + (and others). + +2007-09-17 Linus Walleij + + * src/libusb-glue.c: silenced weirdo error message. + +2007-09-16 Linus Walleij + + * src/ptp.c: cancellation working *as it should* + * src/ptp.h: dito. + * src/libusb-glue.c: dito. + * src/libusb-glue.h: dito. + * src/libmtp.c: dito. + * src/libmtp.h.in: new error code for cancellation. + +2007-09-16 Richard Low + + * src/libusb-glue.c: only read descriptors for devices we + don't know since it breaks on some devices + * src/libmtp.c: a couple of bugs fixed in metadata retreival + +2007-09-15 Linus Walleij + + * src/ptp.c: get cancellation of xfers working. + * src/libusb-glue.c: dito. + * src/libusb-glue.h: dito. + * src/libmtp.c: dito. + +2007-09-14 Linus Walleij + + * src/ptp.h: sync in upstream to get cancellation prototypes. + * src/libusb-glue.c: first try to implement cancellation. + * src/libmtp.c: dito. Bugfix to one of Marcus' realloc():s. + +2007-09-12 Marcus Meissner + + * src/ptp-pack.c: sync to upstream, rewrote packing to use + a static array to be qsort():ed when reading in proplists. + * src/ptp.c: reflect changes. + * src/ptp.h: reflect changes. + * src/libmtp.c: reflect changes. + +2007-09-06 Linus Walleij + + * examples/hotplug.c: edit up into a udev ruleset that is + inexorably complicated but probably compatible with most + udev versions out there. Now please DON'T update udev + styles again! + +2007-09-05 Richard Low + + * src/libmtp.c: album fixups + +2007-09-05 Linus Walleij + + * examples/hotplug.c: use old udev style by default, use new + if requested explicitly by a -U switch. + +2007-09-04 Linus Walleij + + * src/libmtp.c: make sure we query recursively supplying each + storage ID in turn, so we spin over storages. Also check + what storage may be available to store a file, if the first + one fails, try the next! + +2007-09-01 Linus Walleij + + * src/ptp-pack.c: make the resulting MTP proplist sorted by + object ID. + * hotplug.sh.in: explicitly call BASH instead of just sh. Warn + if script is not run as root. + +2007-08-29 Linus Walleij + + * examples/Makefile.am: move include define from AM_CFLAGS to + AM_CPPFLAGS (as it should be) bug found by Petar Petrov + . + * src/libusb-glue.c: strange misleading message corrected. Fix + up the horrid interface passing back-and-forward and confusing. + * src/libusb-glue.h: dito. + * src/libmtp.c: rid interface references, that's a USB issue! + * src/libmtp.h.in: flag interface number in device struct as retired. + +2007-08-28 Linus Walleij + + * src/libmtp.c: retire the horrid, broken, stream send facility that + does not work on any device since they all want to know the file + size in advance. Add in some code to fall back on the recursive + metadata retrieveal if getting the whole long list fails. + * src/libusb-glue.c: tag all Sansas as having broken GetObjectPropList + when all tags for all objects are requested. Scan each INTERFACE + of the device for device descriptors. + +2007-08-24 Linus Walleij + + * src/libusb-glue.h: new device flag to strip all non-7bit chars from + filenames on some lame devices. + * src/libusb-glue.c: tag the Philips Shoqbox with this flag. + * src/libmtp.c: strip the non-7bit chars from filenames if that + flag is set. + * src/unicode.h: introduce a 7bit-strip helper function. + * src/unicode.c: dito. + +2007-08-22 Linus Walleij + + * src/libmtp.c: deal with setting of metadata sets for u16/u32:s that + are ranges or enums, rounding and twiddling if need be. This is + needed because some new devices (like the TrekStor Sweez, + has duration defined as a range: MIN 0, MAX 65535000, STEP 1 + and Sandisk Sansa c240 has duration as range: MIN 0, MAX 2147483000, + STEP 1000) whereas old devices would just accept any value. + +2007-08-16 Richard Low + + * src/ptp-pack.c: allow packing of NULL strings + +2007-08-15 Robert Reardon + + * src/libmtp.c: return device max values for representative + samples. + +2007-08-15 Richard Low + + * src/libusb-glue.c: added Philips Shoqbox + +2007-08-13 Linus Walleij + + * src/ptp.c: sync to upstream. + 2007-08-07 Linus Walleij * configure.ac: bump to 0.2.1. diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/config.guess /tmp/GW9hRDRi14/libmtp-0.2.2/config.guess --- /tmp/KyV3P4PTuG/libmtp-0.2.1/config.guess 2007-04-05 15:20:16.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/config.guess 2007-10-04 23:16:19.000000000 +0100 @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, # Inc. -timestamp='2006-07-02' +timestamp='2007-07-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -161,6 +161,7 @@ arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched @@ -329,7 +330,7 @@ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; - i86pc:SunOS:5.*:*) + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) @@ -780,7 +781,7 @@ i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; - i*:MINGW*:*) + *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) @@ -790,12 +791,15 @@ i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; - x86:Interix*:[3456]*) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - EM64T:Interix*:[3456]*) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; + *:Interix*:[3456]*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + EM64T | authenticamd) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; @@ -950,6 +954,9 @@ x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; + xtensa:Linux:*:*) + echo xtensa-unknown-linux-gnu + exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so # first see if it will tell us. cd to the root directory to prevent @@ -1208,6 +1215,15 @@ SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/config.sub /tmp/GW9hRDRi14/libmtp-0.2.2/config.sub --- /tmp/KyV3P4PTuG/libmtp-0.2.1/config.sub 2007-04-05 15:20:16.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/config.sub 2007-10-04 23:16:20.000000000 +0100 @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, # Inc. -timestamp='2006-09-20' +timestamp='2007-06-28' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -245,12 +245,12 @@ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ - | fr30 | frv \ + | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore \ + | maxq | mb | microblaze | mcore | mep \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ @@ -324,7 +324,7 @@ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ - | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ @@ -475,8 +475,8 @@ basic_machine=craynv-cray os=-unicosmp ;; - cr16c) - basic_machine=cr16c-unknown + cr16) + basic_machine=cr16-unknown os=-elf ;; crds | unos) @@ -683,6 +683,10 @@ basic_machine=i386-pc os=-mingw32 ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; miniframe) basic_machine=m68000-convergent ;; @@ -925,6 +929,9 @@ basic_machine=sh-hitachi os=-hms ;; + sh5el) + basic_machine=sh5le-unknown + ;; sh64) basic_machine=sh64-unknown ;; @@ -1219,7 +1226,7 @@ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1414,6 +1421,9 @@ m68*-cisco) os=-aout ;; + mep-*) + os=-elf + ;; mips*-cisco) os=-elf ;; diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/configure /tmp/GW9hRDRi14/libmtp-0.2.2/configure --- /tmp/KyV3P4PTuG/libmtp-0.2.1/configure 2007-08-07 16:58:24.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/configure 2007-10-04 23:16:15.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for libmtp 0.2.1. +# Generated by GNU Autoconf 2.61 for libmtp 0.2.2. # # Report bugs to . # @@ -728,8 +728,8 @@ # Identity of this package. PACKAGE_NAME='libmtp' PACKAGE_TARNAME='libmtp' -PACKAGE_VERSION='0.2.1' -PACKAGE_STRING='libmtp 0.2.1' +PACKAGE_VERSION='0.2.2' +PACKAGE_STRING='libmtp 0.2.2' PACKAGE_BUGREPORT='libmtp-users@lists.sourceforge.net' ac_unique_file="src/libmtp.c" @@ -1401,7 +1401,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libmtp 0.2.1 to adapt to many kinds of systems. +\`configure' configures libmtp 0.2.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1471,7 +1471,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libmtp 0.2.1:";; + short | recursive ) echo "Configuration of libmtp 0.2.2:";; esac cat <<\_ACEOF @@ -1573,7 +1573,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libmtp configure 0.2.1 +libmtp configure 0.2.2 generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1587,7 +1587,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libmtp $as_me 0.2.1, which was +It was created by libmtp $as_me 0.2.2, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ @@ -2277,7 +2277,7 @@ # Define the identity of the package. PACKAGE='libmtp' - VERSION='0.2.1' + VERSION='0.2.2' cat >>confdefs.h <<_ACEOF @@ -22770,7 +22770,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libmtp $as_me 0.2.1, which was +This file was extended by libmtp $as_me 0.2.2, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22823,7 +22823,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -libmtp config.status 0.2.1 +libmtp config.status 0.2.2 configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/configure.ac /tmp/GW9hRDRi14/libmtp-0.2.2/configure.ac --- /tmp/KyV3P4PTuG/libmtp-0.2.1/configure.ac 2007-08-07 16:57:10.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/configure.ac 2007-10-04 23:15:03.000000000 +0100 @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.52) -AC_INIT([libmtp], [0.2.1], [libmtp-users@lists.sourceforge.net]) +AC_INIT([libmtp], [0.2.2], [libmtp-users@lists.sourceforge.net]) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_SRCDIR([src/libmtp.c]) AM_CONFIG_HEADER(config.h) diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/changelog /tmp/GW9hRDRi14/libmtp-0.2.2/debian/changelog --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/changelog 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/changelog 2007-11-22 20:41:03.000000000 +0000 @@ -1,9 +1,60 @@ -libmtp (0.2.1-0ubuntu3) gutsy; urgency=low +libmtp (0.2.2-2ubuntu1) hardy; urgency=low + + * Merge from debian unstable, remaining changes: + - Munge Maintainer field as per spec. + - Remove hotplug from the Depends of lib. + * Remove bashism in debian/rules to rename udev files. + + -- Flávio Martins Thu, 22 Nov 2007 20:37:15 +0000 + +libmtp (0.2.2-2) unstable; urgency=low + + * Releasing to unstable + + -- Rafael Laboissiere Fri, 12 Oct 2007 14:05:54 +0200 + +libmtp (0.2.2-1) experimental; urgency=low + + * New upstream release. Uploading to experimental to avoid delaying the + libmtp6/gnomad2/amarok transition into testing + * debian/control.in: Added Homepage field + * debian/rules: Remove extra doc/man/man3/LIBMTP* files generate by + doxygen + * Fixed problems with manpages generated by doxygen: + * debian/sanitize-manpage.pl: Added script + * debian/rules: Run sanitize-manpage.pl on doc/man/man3/*.3 files + + -- Rafael Laboissiere Wed, 10 Oct 2007 14:20:25 +0200 + +libmtp (0.2.1-3) unstable; urgency=low + + * debian/rules: Fix rules for libmtp-doc to avoid extraneous manpages to + be included in the package + + -- Rafael Laboissiere Wed, 19 Sep 2007 11:43:07 +0200 + +libmtp (0.2.1-2) unstable; urgency=low + + * debian/rules, debian/libmtp6.install, debian/libmtp6.links: Install + all udev and hotplug files with names libmtp6.*, such that the clashes + with libmtp5 are avoided (closes: #439550) + + -- Rafael Laboissiere Sun, 26 Aug 2007 12:24:02 +0200 + +libmtp (0.2.1-1) unstable; urgency=low + + * New upstream release, uploaded to unstable (closes: #436461) + * The IDs for the Samsung YP-U3 players have been already included in + the src/libusb-glue.c upstream source (closes: #437829) + + -- Rafael Laboissiere Fri, 24 Aug 2007 11:50:56 +0200 + +libmtp (0.2.1-0ubuntu3) gutsy; urgency=low [ Flávio Martins ] * debian/control: Add Conflicts on libmtp5 to fix upgrades. (LP: #133165) - - [ Sarah Hobbs ] + + [ Sarah Hobbs ] * Removed libmtp2 conflicts - no longer needed. this is only in edgy, and we dont support edgy --> gutsy upgrades @@ -25,6 +76,17 @@ -- Mario Danic Tue, 14 Aug 2007 19:40:57 +0200 +libmtp (0.2.0-1) experimental; urgency=low + + * New upstream release (closes: #436060) + * debian/rules, debian/control.in, debian/mtp-tools.lintian.in: + Define variable SOVERSION in rules and use it to create files in the + debian/ directory + * debian/mtp-tools.links: Added link for mtp-reset man page + * debian/libmtp6.*: Renamed files from debian/libmtp5.* + + -- Rafael Laboissiere Sun, 5 Aug 2007 10:38:03 +0200 + libmtp (0.1.5-2ubuntu1) gutsy; urgency=low * Manually merge from Debian unstable. diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/control /tmp/GW9hRDRi14/libmtp-0.2.2/debian/control --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/control 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/control 2007-11-22 20:41:03.000000000 +0000 @@ -6,15 +6,14 @@ Build-Depends: libusb-dev, doxygen, debhelper (>= 5), cdbs, dpkg-dev (>= 1.13.19), xsltproc, docbook-xsl Standards-Version: 3.7.2 +Homepage: http://libmtp.sourceforge.net/ XS-Vcs-Svn: svn://svn.debian.org/svn/private/rafael/deb-pkg/libmtp/ XS-Vcs-Browser: http://svn.debian.org/wsvn/private/rafael/deb-pkg/libmtp/ Package: libmtp6 Section: libs Architecture: any -Depends: udev, ${shlibs:Depends} -Replaces: libmtp-dev (<< 0.1.3-0ubuntu1) -Conflicts: libmtp-dev (<< 0.1.3-0ubuntu1), libmtp5 +Depends: udev | hotplug, ${shlibs:Depends} Description: Media Transfer Protocol (MTP) library A library for communicating with MTP aware devices. MTP (Media Transfer Protocol) is necessary to communicate with some USB portable diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/control.in /tmp/GW9hRDRi14/libmtp-0.2.2/debian/control.in --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/control.in 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/control.in 2007-11-22 20:41:03.000000000 +0000 @@ -0,0 +1,76 @@ +Source: libmtp +Section: libs +Priority: optional +Maintainer: Ubuntu Core Developers +XSBC-Original-Maintainer: Rafael Laboissiere +Build-Depends: libusb-dev, doxygen, debhelper (>= 5), cdbs, + dpkg-dev (>= 1.13.19), xsltproc, docbook-xsl +Standards-Version: 3.7.2 +Homepage: http://libmtp.sourceforge.net/ +XS-Vcs-Svn: svn://svn.debian.org/svn/private/rafael/deb-pkg/libmtp/ +XS-Vcs-Browser: http://svn.debian.org/wsvn/private/rafael/deb-pkg/libmtp/ + +Package: libmtp@SOVERSION@ +Section: libs +Architecture: any +Depends: udev | hotplug, ${shlibs:Depends} +Description: Media Transfer Protocol (MTP) library + A library for communicating with MTP aware devices. MTP (Media + Transfer Protocol) is necessary to communicate with some USB portable + devices like mp3 players, video players or digital camera. + . + While some portable device will use USB mass storage protocol or PTP + (picture transfer protocol), some device can only communicate through + MTP [ see http://en.wikipedia.org/wiki/Media_Transfer_Protocol ] + . + Homepage: http://libmtp.sourceforge.net/ + +Package: libmtp-dev +Section: libdevel +Architecture: any +Depends: libmtp@SOVERSION@ (= ${binary:Version}), libusb-dev (>> 0.1.7) +Description: Media Transfer Protocol (MTP) development files + A library for communicating with MTP aware devices. MTP (Media + Transfer Protocol) is necessary to communicate with some USB portable + devices like mp3 players, video players or digital camera. + . + While some portable device will use USB mass storage protocol or PTP + (picture transfer protocol), some device can only communicate through + MTP [ see http://en.wikipedia.org/wiki/Media_Transfer_Protocol ] + . + This package contains the headers and development libraries. + . + Homepage: http://libmtp.sourceforge.net/ + +Package: libmtp-doc +Section: doc +Architecture: all +Description: Media Transfer Protocol (MTP) library documentation + A library for communicating with MTP aware devices. MTP (Media + Transfer Protocol) is necessary to communicate with some USB portable + devices like mp3 players, video players or digital camera. + . + While some portable device will use USB mass storage protocol or PTP + (picture transfer protocol), some device can only communicate through + MTP [ see http://en.wikipedia.org/wiki/Media_Transfer_Protocol ] + . + This package contains the development documentation. + . + Homepage: http://libmtp.sourceforge.net/ + +Package: mtp-tools +Section: utils +Architecture: any +Depends: libmtp@SOVERSION@ (= ${binary:Version}), ${shlibs:Depends} +Description: Media Transfer Protocol (MTP) library tools + A library for communicating with MTP aware devices. MTP (Media + Transfer Protocol) is necessary to communicate with some USB portable + devices like mp3 players, video players or digital camera. + . + While some portable device will use USB mass storage protocol or PTP + (picture transfer protocol), some device can only communicate through + MTP [ see http://en.wikipedia.org/wiki/Media_Transfer_Protocol ] + . + This package contains tools for communicating with MTP devices. + . + Homepage: http://libmtp.sourceforge.net/ diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp6.install /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp6.install --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp6.install 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp6.install 2007-11-22 20:41:03.000000000 +0000 @@ -1,4 +1,4 @@ usr/lib/*.so.* -../../libmtp.rules etc/udev -../../libmtp.usermap etc/hotplug/usb -../../libmtp.sh etc/hotplug/usb +../../libmtp6.rules etc/udev +../../libmtp6.usermap etc/hotplug/usb +../../libmtp6.sh etc/hotplug/usb diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp6.links /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp6.links --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp6.links 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp6.links 2007-11-22 20:41:03.000000000 +0000 @@ -1 +1 @@ -etc/udev/libmtp.rules etc/udev/rules.d/libmtp.rules +etc/udev/libmtp6.rules etc/udev/rules.d/libmtp6.rules diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp-doc.manpages /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp-doc.manpages --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/libmtp-doc.manpages 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/libmtp-doc.manpages 2007-11-22 20:41:03.000000000 +0000 @@ -1 +1 @@ -doc/man/man3/*.3 +doc/man/man3/mtp_*.3 diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/mtp-tools.lintian /tmp/GW9hRDRi14/libmtp-0.2.2/debian/mtp-tools.lintian --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/mtp-tools.lintian 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/mtp-tools.lintian 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -mtp-tools: package-has-a-duplicate-relation depends: libmtp6 (= @DEBVERSION@), libmtp6 diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/rules /tmp/GW9hRDRi14/libmtp-0.2.2/debian/rules --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/rules 2007-11-22 20:41:03.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/rules 2007-11-22 20:41:03.000000000 +0000 @@ -4,37 +4,54 @@ include /usr/share/cdbs/1/class/autotools.mk DEB_DH_INSTALL_SOURCEDIR = debian/tmp -DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl -XP=xsltproc -''-nonet +DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl +XP = xsltproc -''-nonet +SOVERSION = 6 +SUBSTFILES = debian/control debian/mtp-tools.lintian +UDEVFILES = libmtp$(SOVERSION).rules \ + libmtp$(SOVERSION).usermap \ + libmtp$(SOVERSION).sh -makebuilddir/libmtp6:: +debdir = $(CURDIR)/debian +deblin = $(debdir)/mtp-tools/usr/share/lintian/overrides +debver = $(shell perl -ne '/([\d.-]+)/;print "$$1"; last' \ + debian/changelog) + +%: %.in + # Replace SOVERSION string in debian/ files + sed "s/@SOVERSION@/$(SOVERSION)/g" < $< > $@ + +makebuilddir/libmtp$(SOVERSION):: $(SUBSTFILES) # Save file modified by configure test -e src/gphoto2-endian.h-orig \ || cp src/gphoto2-endian.h src/gphoto2-endian.h-orig -build/libmtp6:: +install/libmtp-doc:: # Rename some man files and remove others - cd doc/man/man3 && \ - rm -f _* *.[ch].3 && \ - for i in $(ls *.3 | grep -v '^n'); do mv $$i mtp_$$i; done + chmod +x debian/sanitize-manpage.pl + ( cd doc/man/man3/ ; \ + rm -f _* ; \ + for i in $$(ls *.3 | grep -v ^mtp_) ; do \ + ../../../debian/sanitize-manpage.pl < $$i > mtp_$$i ; \ + done ) + +build/libmtp$(SOVERSION):: + # Fix mode of udev lifting script chmod +x libmtp.sh # Adapt upstream udev rules file to Debian standards perl -pi -e 's/MODE.*/MODE="660", GROUP="audio"/ if /MODE/' \ libmtp.rules - # Adapt upstream udev rules file to our kernel - perl -pi -e 's/ATTR{/ATTRS{/g' libmtp.rules - + # Rename udev and hotplug files + for f in $(UDEVFILES) ; do \ + cp "$$(printf %s "$$f" | sed 's/$(SOVERSION)//')" "$$f" ; \ + done + mtp-tools.1: debian/mtp-tools.dbk # Build and install the man page for mtp-tools $(XP) $(DB2MAN) $< build/mtp-tools:: mtp-tools.1 -debdir = $(CURDIR)/debian -deblin = $(debdir)/mtp-tools/usr/share/lintian/overrides -debver = $(shell perl -ne '/([\d.-]+)/;print "$$1"; last' \ - debian/changelog) - install/mtp-tools:: mkdir -p $(deblin) sed "s/@DEBVERSION@/$(debver)/" \ @@ -42,7 +59,7 @@ > $(deblin)/mtp-tools clean:: - rm -f mtp-tools.1 + rm -f mtp-tools.1 debian/mtp-tools.lintian $(UDEVFILES) # Restore original file test ! -e src/gphoto2-endian.h-orig \ || mv src/gphoto2-endian.h-orig src/gphoto2-endian.h diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/sanitize-manpage.pl /tmp/GW9hRDRi14/libmtp-0.2.2/debian/sanitize-manpage.pl --- /tmp/KyV3P4PTuG/libmtp-0.2.1/debian/sanitize-manpage.pl 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/debian/sanitize-manpage.pl 2007-11-22 20:41:03.000000000 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/perl -w + +my $flag = 0; + +while (<>) { + s {"/.*/}{"} if /^\.TH/; + if (/^\.SH NAME/) { + $flag = 1; + } elsif ($flag) { + m {^(.*) \\-} if not m {^/.*/([^/\\]+) }; + $_ = "libmtp \\- $1\n"; + $flag = 0; + } + print; +} diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/files.c /tmp/GW9hRDRi14/libmtp-0.2.2/examples/files.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/files.c 2007-04-02 09:51:35.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/examples/files.c 2007-10-02 21:30:19.000000000 +0100 @@ -33,9 +33,9 @@ printf(" None. (abstract file, size = -1)\n"); } else { #ifdef __WIN32__ - printf(" File size %llu (0x%08I64X) bytes\n", file->filesize, file->filesize); + printf(" File size %llu (0x%016I64X) bytes\n", file->filesize, file->filesize); #else - printf(" File size %llu (0x%08llX) bytes\n", file->filesize, file->filesize); + printf(" File size %llu (0x%016llX) bytes\n", file->filesize, file->filesize); #endif } printf(" Parent ID: %u\n", file->parent_id); diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/hotplug.c /tmp/GW9hRDRi14/libmtp-0.2.2/examples/hotplug.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/hotplug.c 2007-08-04 20:58:51.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/examples/hotplug.c 2007-09-06 10:07:32.000000000 +0100 @@ -55,9 +55,10 @@ extern char *optarg; char *udev_action = NULL; char default_udev_action[] = "SYMLINK+=\"libmtp-%k\", MODE=\"666\""; - uint16_t last_vendor = 0x0000U; + char *action; // To hold the action actually used. + uint16_t last_vendor = 0x0000U; - while ( (opt = getopt(argc, argv, "uiHa:")) != -1 ) { + while ( (opt = getopt(argc, argv, "uUiHa:")) != -1 ) { switch (opt) { case 'a': udev_action = strdup(optarg); @@ -75,6 +76,12 @@ } } + if (udev_action != NULL) { + action = udev_action; + } else { + action = default_udev_action; + } + LIBMTP_Init(); ret = LIBMTP_Get_Supported_Devices_List(&entries, &numentries); if (ret == 0) { @@ -82,12 +89,13 @@ case style_udev: printf("# UDEV-style hotplug map for libmtp\n"); printf("# Put this file in /etc/udev/rules.d\n\n"); - printf("ACTION!=\"add\", GOTO=\"libmtp_rules_end\"\n" - "SUBSYSTEM==\"usb\", GOTO=\"libmtp_rules\"\n" - "# The following line will be deprecated when older kernels are phased out.\n" - "SUBSYSTEM==\"usb_device\", GOTO=\"libmtp_rules\"\n\n" + printf("ACTION!=\"add\", GOTO=\"libmtp_rules_end\"\n"); + printf("ATTR{dev}!=\"?*\", GOTO=\"libmtp_rules_end\"\n"); + printf("SUBSYSTEM==\"usb\", GOTO=\"libmtp_usb_rules\"\n" + "# The following thing will be deprecated when older kernels are phased out.\n" + "SUBSYSTEM==\"usb_device\", GOTO=\"libmtp_usb_device_rules\"\n" "GOTO=\"libmtp_rules_end\"\n\n" - "LABEL=\"libmtp_rules\"\n\n"); + "LABEL=\"libmtp_usb_rules\"\n\n"); break; case style_usbmap: printf("# This usermap will call the script \"libmtp.sh\" whenever a known MTP device is attached.\n\n"); @@ -109,21 +117,16 @@ LIBMTP_device_entry_t * entry = &entries[i]; switch (style) { - case style_udev: { - char *action; + case style_udev: + { printf("# %s\n", entry->name); - if (udev_action != NULL) { - action = udev_action; - } else { - action = default_udev_action; - } // Old style directly SYSFS named. - // printf("SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action); + // printf("SYSFS{idVendor}==\"%04x\", SYSFS{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action); // Newer style printf("ATTR{idVendor}==\"%04x\", ATTR{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action); - break; + break; } - case style_usbmap: + case style_usbmap: printf("# %s\n", entry->name); printf("libmtp.sh 0x0003 0x%04x 0x%04x 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000\n", entry->vendor_id, entry->product_id); break; @@ -174,11 +177,26 @@ exit(1); } + // For backward comparibility with the #$!+@! ever changing + // udev rule style... + if (style == style_udev) { + printf("GOTO=\"libmtp_rules_end\"\n\n"); + printf("LABEL=\"libmtp_usb_device_rules\"\n"); + for (i = 0; i < numentries; i++) { + LIBMTP_device_entry_t * entry = &entries[i]; + + printf("# %s\n", entry->name); + printf("ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", %s\n", entry->vendor_id, entry->product_id, action); + } + printf("GOTO=\"libmtp_rules_end\"\n\n"); + } + + // Then the footer. switch (style) { case style_usbmap: break; case style_udev: - printf("\nLABEL=\"libmtp_rules_end\"\n"); + printf("LABEL=\"libmtp_rules_end\"\n"); break; case style_hal: printf(" \n"); diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/Makefile.am /tmp/GW9hRDRi14/libmtp-0.2.2/examples/Makefile.am --- /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/Makefile.am 2007-08-03 10:29:10.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/examples/Makefile.am 2007-08-29 11:15:10.000000000 +0100 @@ -21,7 +21,7 @@ thumb_SOURCES=thumb.c common.h reset_SOURCES=reset.c common.h -AM_CFLAGS=-I$(top_builddir)/src +AM_CPPFLAGS=-I$(top_builddir)/src LDADD=../src/libmtp.la EXTRA_DIST=evolution-sync.sh diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/Makefile.in /tmp/GW9hRDRi14/libmtp-0.2.2/examples/Makefile.in --- /tmp/KyV3P4PTuG/libmtp-0.2.1/examples/Makefile.in 2007-08-07 16:58:21.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/examples/Makefile.in 2007-10-04 23:16:13.000000000 +0100 @@ -274,7 +274,7 @@ emptyfolders_SOURCES = emptyfolders.c common.h thumb_SOURCES = thumb.c common.h reset_SOURCES = reset.c common.h -AM_CFLAGS = -I$(top_builddir)/src +AM_CPPFLAGS = -I$(top_builddir)/src LDADD = ../src/libmtp.la EXTRA_DIST = evolution-sync.sh all: all-am diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/hotplug.sh.in /tmp/GW9hRDRi14/libmtp-0.2.2/hotplug.sh.in --- /tmp/KyV3P4PTuG/libmtp-0.2.1/hotplug.sh.in 2007-05-25 00:27:13.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/hotplug.sh.in 2007-09-01 19:57:54.000000000 +0100 @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash INSTALL="@INSTALL@" HOTPLUGPATH=/etc/hotplug @@ -114,6 +114,10 @@ echo "Missing awk program. Fatal error." exit 1 fi +if [ "x${USER}" != "xroot" ]; + echo "WARNING: this program should be run as root!" +fi + # This script locates the hotplug distribution on a certain host # and sets up userland hotplugging scripts according to rules. diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/INSTALL /tmp/GW9hRDRi14/libmtp-0.2.2/INSTALL --- /tmp/KyV3P4PTuG/libmtp-0.2.1/INSTALL 2006-06-03 21:36:35.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/INSTALL 2007-08-24 21:45:03.000000000 +0100 @@ -76,8 +76,10 @@ By default, libmtp will add the program-prefix "mtp-" to all the example programs prior to installation. The program-prefix option makes libmtp sample programs avoid collision with other programs like -sox' "play" program. On Linux you should then typically type (see -below for details): +sox' "play" program. If the default prefix for some reason fail, +try to tag on "--program-prefix=mtp-" to the "configure" command. + +On Linux you should then typically type (see below for details): % ./hotplug.sh @@ -166,3 +168,20 @@ You can find the Linux hotplug project at: http://linux-hotplug.sourceforge.net/ + + +Compilation for embedded devices +-------------------------------- + +Problems with Autoconf complaining about a missing malloc() function +during cross-compilation can be solved with this hack if you're using +glibc: + + % export ac_cv_func_malloc_0_nonnull=yes + % ./configure + +If you're using uclibc you may have to smack in a custom rpl_malloc() +function in your program, see the Autoconf texinfo documentation. + +See further: +http://wiki.buici.com/wiki/Autoconf_and_RPL_MALLOC \ No newline at end of file diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/libmtp.pc /tmp/GW9hRDRi14/libmtp-0.2.2/libmtp.pc --- /tmp/KyV3P4PTuG/libmtp-0.2.1/libmtp.pc 2007-08-07 16:58:35.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/libmtp.pc 2007-10-04 23:16:34.000000000 +0100 @@ -7,7 +7,7 @@ Name: libmtp Description: libmtp is a library for accessing Media Transfer Protocol devices -Version: 0.2.1 +Version: 0.2.2 Requires: libusb Conflicts: Libs: -L${libdir} -lmtp diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/README /tmp/GW9hRDRi14/libmtp-0.2.2/README --- /tmp/KyV3P4PTuG/libmtp-0.2.1/README 2007-08-04 22:26:43.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/README 2007-11-22 20:41:03.000000000 +0000 @@ -114,28 +114,117 @@ if you really need to get your dual-mode device to work with MTP. +If you are a device vendor, please consider assigning one +of your employees as a contact person for libmtp, have them +sign up to the libmtp development list and answer questions +and post new device ID:s as they are released to our +mailing list. By the way: do you have spare devices you +can give us? Send them to Richard (Mac support) or Linus +(Linux support). (So far nobody did that except for Microsoft +who sent us a Zune by proxy!) + If your device is very problematic we are curious of how it works under Windows, so we enjoy reading USB packet sniffs that reveal the low-level traffic carried out between Windows Media Player and your device. This can be done -using the trial version of HHD Softwares software-only -USB monitor. You need to get a copy of version 2.37 since -the newer trial versions won't let you carry out the -needed packet sniffs. (As of 2007-03-10 a copy can be found -at: http://www.cobbleware.com/files/usb-monitor-237.exe) +using e.g.: + +* USBsnoop: + http://benoit.papillault.free.fr/usbsnoop/ + +* The trial version of HHD Softwares software-only + USB monitor. You need to get a copy of version 2.37 since + the newer trial versions won't let you carry out the + needed packet sniffs. (As of 2007-03-10 a copy can be found + at: http://www.cobbleware.com/files/usb-monitor-237.exe) + There are other USB monitors as well, some more expensive alternatives use hardware and even measure electronic characteristics of the traffic (which is far too much detail for us). -If you are a device vendor, please consider assigning one -of your employees as a contact person for libmtp, have them -sign up to the libmtp development list and answer questions -and post new device ID:s as they are released to our -mailing list. By the way: do you have spare devices you -can give us? Send them to Richard (Mac support) or Linus -(Linux support). (So far nobody did that except for Microsoft -who sent us a Zune by proxy!) +Device sniffs are an easy read since the PTP/MTP protocol +is nicely structured. All commands will have a structure such +as this in the log, we examplify with a object list request: + +PTP REQEUST: +000120: Bulk or Interrupt Transfer (UP), 03.09.2007 12:49:25.9843750 +0.0 +Pipe Handle: 0x863ce234 (Endpoint Address: 0x2) +Send 0x20 bytes to the device: + 20 00 00 00 01 00 05 98 23 00 00 00 27 03 00 10 ......?#...'... + Length TYPE CMD Trans# Param1 + + 00 00 00 00 02 DC 00 00 00 00 00 00 00 00 00 00 .....Ü.......... + Param2 Param3 Param4 Param5 + +[OPTIONAL] DATA PHASE: +000121: Bulk or Interrupt Transfer (UP), 03.09.2007 12:49:26.0 +0.0156250 +Pipe Handle: 0x863ce214 (Endpoint Address: 0x81) +Get 0x1a bytes from the device: + 1A 00 00 00 02 00 05 98 23 00 00 00 01 00 00 00 .......?#....... + Length TYPE CMD Trans# DATA + + 27 03 00 10 02 DC 04 00 00 30 '....Ü...0 + +RESPONSE: +000122: Bulk or Interrupt Transfer (UP), 03.09.2007 12:49:26.0 +0.0 +Pipe Handle: 0x863ce214 (Endpoint Address: 0x81) +Get 0xc bytes from the device: + 0C 00 00 00 03 00 01 20 23 00 00 00 ....... #... + Length TYPE CODE Trans# + +* One send (OUT to the device), two reads (IN from the device). + +* All three byte chunks commands are + sent/recieved/recieeved by the function ptp_transaction() + in the file ptp.c. + +* It boils down to ptp_usb_sendreq(), optionally ptp_usb_senddata() + or ptp_usb_getdata() and finally ptp_usb_getresp() in the file + libusb-glue.c. Notice ptp_usb_sendreq() and ptp_usb_getresp() + are ALWAYS called. The TYPE field correspond to this, so the + TYPES in this case are "COMMAND" (0x0001), "DATA" (0x0002), + and "RESPONSE" (0x0003). + +* Notice that the byte order is little endian, so you need to read + each field from right to left. + +* This COMMAND has: + CMD 0x99805, we see in ptp.h that this is PTP_OC_MTP_GetObjPropList. + Transaction# 0x00000023. + REQUEST parameters 0x10000327, 0x00000000, 0x0000DC02, 0x00000000 + 0x00000000, in this case it means "get props for object 0x10000327", + "any format", "property 0xDC02" (PTP_OPC_ObjectFormat), then two + parameters that are always zero (no idea what they mean or their + use). + +* The DATA has: + CMD 0x99805, we see in ptp.h that this is PTP_OC_MTP_GetObjPropList. + Transaction# 0x00000023. + Then comes data 0x00000001, 0x10000327, 0xDC02, 0x0004, 0x3000 + Which means in this case, (and this is the tricky part) "here + you have 1 property", "for object 0x10000327", "it is property + 0xDC02" (PTP_OPC_ObjectFormat), "which is of type 0x0004" + (PTP_DTC_UINT16), "and set to 0x3000" (PTP_OFC_Undefined, it + is perfectly valid to have undefined object formats, since it + is a legal value defining this). + +* This RESPONSE has: + CMD 0x99805, we see in ptp.h that this is PTP_OC_MTP_GetObjPropList. + Return Code ("RC") = 0x2001, PTP_RC_OK, all went fine. + Transaction# 0x00000023. + +If you want to compare the Windows behaviour with a similar +operation using libmtp you can go into the libusb-glue.c file +and uncomment the row that reads: + +//#define ENABLE_USB_BULK_DEBUG + +(I.e. remove the two //.) + +This will make libmtp print out a hex dump of every bulk USB +transaction. The bulk transactions contain all the PTP/MTP layer +data, which is usually where the problems appear. Devices does not work - last resort: @@ -149,7 +238,14 @@ at the USB mass storage interface AGAIN because it enumerates.) -Try this, if you have a recent 2.6.x Linux kernel: +Try this, if you have a recent 2.6.x Linux kernel, +run (as root) something like: + +> rmmod usb_storage ; mtp-detect + +You can run most any command or a client like gnomad2 or +Amarok immediately after the rmmod command. This works +sometimes. Another way: * Edit /etc/modprobe.d/blacklist @@ -269,10 +365,21 @@ problematic. Please upgrade to as new firmware as you can get. (Yes this requires some kind of Windows Installation I think.) -* Very few devices that implement GetObjectPropList (0x9805) will - return the entire object list if you request a list for object - 0xffffffffu. (But they should.) So we're currently not using - that feature. +* Some devices that implement GetObjectPropList (0x9805) will + not return the entire object list if you request a list for object + 0xffffffffu. (But they should.) So they may need the special + DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL. + +* Some (smaller) subset of devices cannot even get all the + properties for a single object in one go, these need the + DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST. Currently only the + iriver devices seem to have this bug. + +* The Toshiba Gigabeat S (and probably its sibling the + Microsoft Zune and other Toshiba devices) will only display + album information tags for a song in case there is also + an abstract album (created with the album interface) with + the exact same name. Lost symbols diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.c /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.c 2007-08-06 21:42:08.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.c 2007-10-02 22:12:35.000000000 +0100 @@ -45,6 +45,7 @@ #include #include #include +#include #ifdef _MSC_VER // For MSVC++ #define USE_WINDOWS_IO_H #include @@ -81,16 +82,29 @@ uint16_t ptp_error, char const * const error_text); static void flush_handles(LIBMTP_mtpdevice_t *device); -static void get_handles_recursively(LIBMTP_mtpdevice_t *device, PTPParams *params, PTPObjectHandles *handles, uint32_t parent); +static void get_handles_recursively(LIBMTP_mtpdevice_t *device, + PTPParams *params, + PTPObjectHandles *handles, + uint32_t storageid, + uint32_t parent); static void free_storage_list(LIBMTP_mtpdevice_t *device); static int sort_storage_by(LIBMTP_mtpdevice_t *device, int const sortby); static uint32_t get_first_storageid(LIBMTP_mtpdevice_t *device); -static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device,uint64_t *freespace); -static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize); +static int get_storage_freespace(LIBMTP_mtpdevice_t *device, + LIBMTP_devicestorage_t *storage, + uint64_t *freespace); +static int check_if_file_fits(LIBMTP_mtpdevice_t *device, + LIBMTP_devicestorage_t *storage, + uint64_t const filesize); static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype); static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype); static int get_device_unicode_property(LIBMTP_mtpdevice_t *device, char **unicstring, uint16_t property); +static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd); +static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd); +static MTPProperties *find_propvalue (PTPParams *params, + uint32_t const oid, uint32_t const attribute_id); +static char *get_iso8601_stamp(void); static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const object_id, uint16_t const attribute_id); static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const object_id, @@ -121,9 +135,17 @@ uint32_t * const newid, uint32_t const * const tracks, uint32_t const no_tracks); -static MTPPropList *new_mtp_prop_entry(); -static MTPPropList *add_mtp_prop_to_proplist(MTPPropList *proplist, MTPPropList *prop); -static void destroy_mtp_prop_list(MTPPropList *proplist); +static int update_abstract_list(LIBMTP_mtpdevice_t *device, + char const * const name, + char const * const artist, + char const * const genre, + uint32_t const objecthandle, + uint16_t const objectformat, + uint32_t const * const tracks, + uint32_t const no_tracks); +static MTPProperties *get_new_mtp_prop_entry(MTPProperties**, int*); +static void destroy_mtp_prop_list(MTPProperties *proplist, int nrofprops); +static void destroy_mtp_prop(MTPProperties *prop); static void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t handle); static void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t handle); @@ -334,6 +356,133 @@ return "Unknown filetype"; } +/** + * This function will do its best to fit a 16bit + * value into a PTP object property if the property + * is limited in range or step sizes. + */ +static uint16_t adjust_u16(uint16_t val, PTPObjectPropDesc *opd) +{ + switch (opd->FormFlag) { + case PTP_DPFF_Range: + if (val < opd->FORM.Range.MinimumValue.u16) { + return opd->FORM.Range.MinimumValue.u16; + } + if (val > opd->FORM.Range.MaximumValue.u16) { + return opd->FORM.Range.MaximumValue.u16; + } + // Round down to last step. + if (val % opd->FORM.Range.StepSize.u16 != 0) { + return val - (val % opd->FORM.Range.StepSize.u16); + } + return val; + break; + case PTP_DPFF_Enumeration: + { + int i; + uint16_t bestfit = opd->FORM.Enum.SupportedValue[0].u16; + + for (i=0; iFORM.Enum.NumberOfValues; i++) { + if (val == opd->FORM.Enum.SupportedValue[i].u16) { + return val; + } + // Rough guess of best fit + if (opd->FORM.Enum.SupportedValue[i].u16 < val) { + bestfit = opd->FORM.Enum.SupportedValue[i].u16; + } + } + // Just some default that'll work. + return bestfit; + } + default: + // Will accept any value + break; + } + return val; +} + +/** + * This function will do its best to fit a 32bit + * value into a PTP object property if the property + * is limited in range or step sizes. + */ +static uint32_t adjust_u32(uint32_t val, PTPObjectPropDesc *opd) +{ + switch (opd->FormFlag) { + case PTP_DPFF_Range: + if (val < opd->FORM.Range.MinimumValue.u32) { + return opd->FORM.Range.MinimumValue.u32; + } + if (val > opd->FORM.Range.MaximumValue.u32) { + return opd->FORM.Range.MaximumValue.u32; + } + // Round down to last step. + if (val % opd->FORM.Range.StepSize.u32 != 0) { + return val - (val % opd->FORM.Range.StepSize.u32); + } + return val; + break; + case PTP_DPFF_Enumeration: + { + int i; + uint32_t bestfit = opd->FORM.Enum.SupportedValue[0].u32; + + for (i=0; iFORM.Enum.NumberOfValues; i++) { + if (val == opd->FORM.Enum.SupportedValue[i].u32) { + return val; + } + // Rough guess of best fit + if (opd->FORM.Enum.SupportedValue[i].u32 < val) { + bestfit = opd->FORM.Enum.SupportedValue[i].u32; + } + } + // Just some default that'll work. + return bestfit; + } + default: + // Will accept any value + break; + } + return val; +} + +/** + * This function tries its bewst to locate an attribute for a + * certain object ID in the cached properties stored in params. + */ +static MTPProperties * +find_propvalue (PTPParams *params, uint32_t const oid, uint32_t const attribute_id) +{ + int i; + MTPProperties *prop = params->props; + + if (!prop) + return NULL; + + for (i=0;inrofprops;i++) { + if (oid == prop->ObjectHandle && attribute_id == prop->property) + return prop; + prop ++; + } + return NULL; +} + +/** + * This function returns a newly created ISO 8601 timestamp with the + * current time in as high precision as possible. It even adds + * the time zone if it can. + */ +static char *get_iso8601_stamp(void) +{ + time_t curtime; + struct tm *loctime; + char tmp[64]; + + curtime = time(NULL); + loctime = localtime(&curtime); + strftime (tmp, sizeof(tmp), "%Y%m%dT%H%M%S.0%z", loctime); + return strdup(tmp); +} /** * Retrieves a string from an object @@ -359,17 +508,13 @@ // This O(n) search should not be used so often, since code // using the cached properties don't usually call this function. - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop) { - if (object_id == prop->ObjectHandle && attribute_id == prop->property) { + if (params->props) { + MTPProperties *prop = find_propvalue (params, object_id, attribute_id); + if (prop) { if (prop->propval.str != NULL) return strdup(prop->propval.str); else return NULL; - } - prop = prop->next; } } @@ -409,17 +554,12 @@ // This O(n) search should not be used so often, since code // using the cached properties don't usually call this function. - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop) { - if (object_id == prop->ObjectHandle && attribute_id == prop->property) { - return prop->propval.u64; - } - prop = prop->next; - } + if (params->props) { + MTPProperties *prop = find_propvalue (params, object_id, attribute_id); + if (prop) + return prop->propval.u64; } - + ret = ptp_mtp_getobjectpropvalue(params, object_id, attribute_id, &propval, @@ -456,15 +596,10 @@ // This O(n) search should not be used so often, since code // using the cached properties don't usually call this function. - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop) { - if (object_id == prop->ObjectHandle && attribute_id == prop->property) { - return prop->propval.u32; - } - prop = prop->next; - } + if (params->props) { + MTPProperties *prop = find_propvalue (params, object_id, attribute_id); + if (prop) + return prop->propval.u32; } ret = ptp_mtp_getobjectpropvalue(params, object_id, @@ -503,15 +638,10 @@ // This O(n) search should not be used so often, since code // using the cached properties don't usually call this function. - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop) { - if (object_id == prop->ObjectHandle && attribute_id == prop->property) { - return prop->propval.u16; - } - prop = prop->next; - } + if (params->props) { + MTPProperties *prop = find_propvalue (params, object_id, attribute_id); + if (prop) + return prop->propval.u16; } ret = ptp_mtp_getobjectpropvalue(params, object_id, @@ -550,15 +680,10 @@ // This O(n) search should not be used so often, since code // using the cached properties don't usually call this function. - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop) { - if (object_id == prop->ObjectHandle && attribute_id == prop->property) { - return prop->propval.u8; - } - prop = prop->next; - } + if (params->props) { + MTPProperties *prop = find_propvalue (params, object_id, attribute_id); + if (prop) + return prop->propval.u8; } ret = ptp_mtp_getobjectpropvalue(params, object_id, @@ -609,6 +734,7 @@ return 0; } + /** * Sets an object attribute from an unsigned 32-bit integer * @@ -634,6 +760,7 @@ "PTP_OC_MTP_SetObjectPropValue not supported."); return -1; } + propval.u32 = value; ret = ptp_mtp_setobjectpropvalue(params, object_id, attribute_id, &propval, PTP_DTC_UINT32); if (ret != PTP_RC_OK) { @@ -761,12 +888,9 @@ /** * Recursive function that adds MTP devices to a linked list - * @param numdevices The number of detected USB devices - * @param interface_number Dynamic array of interface numbers - * @param params Dynamic array of PTP parameters - * @param ptp_usb Dynamic array of USB PTP devices + * @param devices a list of devices to be created. * @return a device pointer to a newly created mtpdevice (used in linked - * list creation + * list creation). */ static LIBMTP_mtpdevice_t * create_usb_mtp_devices(mtpdevice_list_t *devices) { @@ -778,11 +902,12 @@ while (tmplist != NULL) { LIBMTP_mtpdevice_t *mtp_device; + uint8_t bs = 0; /* Clear any handlers */ tmplist->params->handles.Handler = NULL; tmplist->params->objectinfo = NULL; - tmplist->params->proplist = NULL; + tmplist->params->props = NULL; /* Allocate dynamic space for our device */ mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t)); @@ -805,11 +930,12 @@ tmplist->params = NULL; /* We have freed a bit of memory so try again with the next device */ + tmplist = tmplist->next; + i++; continue; } /* Copy device information to mtp_device structure */ - mtp_device->interface_number = tmplist->interface_number; mtp_device->params = tmplist->params; mtp_device->usbinfo = tmplist->ptp_usb; current_params = tmplist->params; @@ -829,21 +955,58 @@ free(mtp_device); /* try again with the next device */ + tmplist = tmplist->next; + i++; continue; } + + /* Determine if the object size supported is 32 or 64 bit wide */ + for (i=0;ideviceinfo.ImageFormats_len;i++) { + PTPObjectPropDesc opd; + + if (ptp_mtp_getobjectpropdesc(current_params, + PTP_OPC_ObjectSize, + current_params->deviceinfo.ImageFormats[i], + &opd) != PTP_RC_OK) { + printf("LIBMTP PANIC: create_usb_mtp_devices(): " + "could not inspect object property descriptions!\n"); + } else { + if (opd.DataType == PTP_DTC_UINT32) { + if (bs == 0) { + bs = 32; + } else if (bs != 32) { + printf("LIBMTP PANIC: create_usb_mtp_devices(): " + "different objects support different object sizes!\n"); + bs = 0; + break; + } + } else if (opd.DataType == PTP_DTC_UINT64) { + if (bs == 0) { + bs = 64; + } else if (bs != 64) { + printf("LIBMTP PANIC: create_usb_mtp_devices(): " + "different objects support different object sizes!\n"); + bs = 0; + break; + } + } else { + // Ignore if other size. + printf("LIBMTP PANIC: create_usb_mtp_devices(): " + "awkward object size data type: %04x\n", opd.DataType); + bs = 0; + break; + } + } + } + if (bs == 0) { + // Could not detect object bitsize, assume 32 bits + bs = 32; + } + mtp_device->object_bitsize = bs; /* No Errors yet for this device */ mtp_device->errorstack = NULL; - /* Cache the device information */ - if (ptp_getdeviceinfo(current_params, - ¤t_params->deviceinfo) != PTP_RC_OK) { - add_error_to_errorstack(mtp_device, - LIBMTP_ERROR_CONNECTING, - "Unable to read device information. Recommend " - "disconnecting this device\n"); - } - /* Default Max Battery Level, we will adjust this if possible */ mtp_device->maximum_battery_level = 100; @@ -882,20 +1045,21 @@ mtp_device->default_album_folder = 0; mtp_device->default_text_folder = 0; - /* - * Then get the handles and try to locate the default folders. - * This has the desired side effect of caching all handles from - * the device which speeds up later operations. - */ - flush_handles(mtp_device); - /* Set initial storage information */ mtp_device->storage = NULL; if (LIBMTP_Get_Storage(mtp_device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == -1) { add_error_to_errorstack(mtp_device, LIBMTP_ERROR_GENERAL, "Get Storage information failed."); + mtp_device->storage = NULL; } + + /* + * Then get the handles and try to locate the default folders. + * This has the desired side effect of caching all handles from + * the device which speeds up later operations. + */ + flush_handles(mtp_device); /* Add the device to the list */ mtp_device->next = NULL; @@ -952,6 +1116,10 @@ /* TODO: Add wifi device access here */ free_mtpdevice_list(devices); + + /* We have found some devices but create failed */ + if (*device_list == NULL) + return LIBMTP_ERROR_CONNECTING; return LIBMTP_ERROR_NONE; } @@ -982,7 +1150,7 @@ PTPParams *params = (PTPParams *) device->params; PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo; - close_device(ptp_usb, params, device->interface_number); + close_device(ptp_usb, params); // Clear error stack LIBMTP_Clear_Errorstack(device); // Free iconv() converters... @@ -1128,36 +1296,49 @@ * This works on the vast majority of MTP devices (there ARE exceptions!) * and is quite quick. Check the error stack to see if there were * problems getting the metadata. + * @return 0 if all was OK, -1 on failure. */ -static void get_all_metadata_fast(LIBMTP_mtpdevice_t *device) +static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device, + uint32_t storage) { PTPParams *params = (PTPParams *) device->params; int cnt = 0; - int i; + int i, j, nrofprops; uint32_t lasthandle = 0xffffffff; - MTPPropList *proplist = NULL; - MTPPropList *prop; + MTPProperties *props = NULL; + MTPProperties *prop; uint16_t ret; - ret = ptp_mtp_getobjectproplist (params, 0xffffffff, &proplist); + ret = ptp_mtp_getobjectproplist (params, 0xffffffff, &props, &nrofprops); + + if (ret == PTP_RC_MTP_Specification_By_Group_Unsupported) { + // What's the point in the device implementing this command if + // you cannot use it to get all props for AT LEAST one object? + // Well, whatever... + add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): " + "cannot retrieve all metadata for an object on this device."); + return -1; + } if (ret != PTP_RC_OK) { - add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): could not get all object proplist."); - return; + add_ptp_error_to_errorstack(device, ret, "get_all_metadata_fast(): " + "could not get proplist of all objects."); + return -1; } - params->proplist = proplist; /* cache it */ + params->props = props; /* cache it */ + params->nrofprops = nrofprops; /* cache it */ /* * We count the number of objects by counting the ObjectHandle * references, whenever it changes we get a new objects, when it's * the same, it is just different properties of the same object. */ - prop = proplist; - while (prop) { + prop = props; + for (i=0;iObjectHandle) { cnt++; lasthandle = prop->ObjectHandle; } - prop = prop->next; + prop++; } lasthandle = 0xffffffff; params->objectinfo = malloc (sizeof (PTPObjectInfo) * cnt); @@ -1165,13 +1346,13 @@ params->handles.Handler = malloc (sizeof (uint32_t) * cnt); params->handles.n = cnt; - prop = proplist; + prop = props; i = -1; - while (prop) { + for (j=0;jObjectHandle) { if (i >= 0) { if (!params->objectinfo[i].Filename) { - /* I have one such file on my Creative */ + /* I have one such file on my Creative (Marcus) */ params->objectinfo[i].Filename = strdup(""); } } @@ -1190,7 +1371,11 @@ // We loose precision here, up to 32 bits! However the commands that // retrieve metadata for files and tracks will make sure that the // PTP_OPC_ObjectSize is read in and duplicated again. - params->objectinfo[i].ObjectCompressedSize = (uint32_t) prop->propval.u64; + if (device->object_bitsize == 64) { + params->objectinfo[i].ObjectCompressedSize = (uint32_t) prop->propval.u64; + } else { + params->objectinfo[i].ObjectCompressedSize = prop->propval.u32; + } break; case PTP_OPC_StorageID: params->objectinfo[i].StorageID = prop->propval.u32; @@ -1208,8 +1393,9 @@ break; // FIXME: the rest of the metadata is readily available right here! } - prop = prop->next; + prop++; } + return 0; } /** @@ -1219,14 +1405,18 @@ * certain directory and does not respect the option to get all metadata * for all objects. */ -static void get_handles_recursively(LIBMTP_mtpdevice_t *device, PTPParams *params, PTPObjectHandles *handles, uint32_t parent) +static void get_handles_recursively(LIBMTP_mtpdevice_t *device, + PTPParams *params, + PTPObjectHandles *handles, + uint32_t storageid, + uint32_t parent) { PTPObjectHandles currentHandles; int i = 0; uint32_t old_handles; uint16_t ret = ptp_getobjecthandles(params, - PTP_GOH_ALL_STORAGE, + storageid, PTP_GOH_ALL_FORMATS, parent, ¤tHandles); @@ -1264,7 +1454,7 @@ oi = ¶ms->objectinfo[old_handles + i]; if (oi->ObjectFormat == PTP_OFC_Association) { - get_handles_recursively(device, params, handles, currentHandles.Handler[i]); + get_handles_recursively(device, params, handles, storageid, currentHandles.Handler[i]); } } else { add_error_to_errorstack(device, @@ -1287,6 +1477,7 @@ { PTPParams *params = (PTPParams *) device->params; PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo; + int ret; uint32_t i; if (params->handles.Handler != NULL) { @@ -1297,25 +1488,42 @@ ptp_free_objectinfo (¶ms->objectinfo[i]); free(params->objectinfo); } - if (params->proplist != NULL) { - destroy_mtp_prop_list(params->proplist); + if (params->props != NULL) { + destroy_mtp_prop_list(params->props, params->nrofprops); } params->handles.n = 0; params->handles.Handler = NULL; params->objectinfo = NULL; - params->proplist = NULL; + params->props = NULL; + params->nrofprops = 0; if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList) && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST) && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL)) { - // Use the fast method. - get_all_metadata_fast(device); - } else { + // Use the fast method. Ignore return value for now. + ret = get_all_metadata_fast(device, PTP_GOH_ALL_STORAGE); + } + // If the previous failed or returned no objects, use classic + // methods instead. + if (params->props == NULL) { // Get all the handles using just standard commands. - get_handles_recursively(device, params, - ¶ms->handles, - PTP_GOH_ROOT_PARENT); + if (device->storage == NULL) { + get_handles_recursively(device, params, + ¶ms->handles, + PTP_GOH_ALL_STORAGE, + PTP_GOH_ROOT_PARENT); + } else { + // Get handles for each storage in turn. + LIBMTP_devicestorage_t *storage = device->storage; + while(storage != NULL) { + get_handles_recursively(device, params, + ¶ms->handles, + storage->id, + PTP_GOH_ROOT_PARENT); + storage = storage->next; + } + } } for(i = 0; i < params->handles.n; i++) { @@ -1492,21 +1700,23 @@ } /** - * This function grabs the freespace from the first storage in + * This function grabs the freespace from a certain storage in * device storage list. * @param device a pointer to the MTP device to free the storage * list for. - */ -static int get_first_storage_freespace(LIBMTP_mtpdevice_t *device, uint64_t *freespace) + * @param storageid the storage ID for the storage to flush and + * get free space for. + * @param freespace the free space on this storage will be returned + * in this variable. + */ +static int get_storage_freespace(LIBMTP_mtpdevice_t *device, + LIBMTP_devicestorage_t *storage, + uint64_t *freespace) { - LIBMTP_devicestorage_t *storage = device->storage; PTPParams *params = (PTPParams *) device->params; - if(storage == NULL) { - return -1; - } // Always query the device about this, since some models explicitly - // needs that. + // needs that. We flush all data on queries storage here. if (ptp_operation_issupported(params,PTP_OC_GetStorageInfo)) { PTPStorageInfo storageInfo; uint16_t ret; @@ -1560,6 +1770,7 @@ printf(" Serial number: %s\n", params->deviceinfo.SerialNumber); printf(" Vendor extension ID: 0x%08x\n", params->deviceinfo.VendorExtensionID); printf(" Vendor extension description: %s\n", params->deviceinfo.VendorExtensionDesc); + printf(" Detected object size: %d bits\n", device->object_bitsize); printf("Supported operations:\n"); for (i=0;ideviceinfo.OperationsSupported_len;i++) { char txt[256]; @@ -2036,9 +2247,12 @@ * if it's too big. * @param device a pointer to the device. * @param filesize the size of the file to check whether it will fit. + * @param storageid the ID of the storage to try to fit the file on. * @return 0 if the file fits, any other value means failure. */ -static int check_if_file_fits(LIBMTP_mtpdevice_t *device, uint64_t const filesize) { +static int check_if_file_fits(LIBMTP_mtpdevice_t *device, + LIBMTP_devicestorage_t *storage, + uint64_t const filesize) { PTPParams *params = (PTPParams *) device->params; uint64_t freebytes; int ret; @@ -2048,13 +2262,13 @@ return 0; } - ret = get_first_storage_freespace(device,&freebytes); + ret = get_storage_freespace(device, storage, &freebytes); if (ret != 0) { - add_ptp_error_to_errorstack(device, ret, "check_if_file_fits(): error checking free storage."); + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, + "check_if_file_fits(): error checking free storage."); return -1; } else { if (filesize > freebytes) { - add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL, "check_if_file_fits(): device storage is full."); return -1; } } @@ -2498,63 +2712,82 @@ /* * If we have a cached, large set of metadata, then use it! */ - if (params->proplist) { - MTPPropList *prop = params->proplist; + if (params->props) { + MTPProperties *prop = params->props; + int i; - while (prop != NULL && prop->ObjectHandle != file->item_id) { - prop = prop->next; - } - while (prop != NULL && prop->ObjectHandle == file->item_id) { + for (i=0;(inrofprops) && (prop->ObjectHandle != file->item_id);i++,prop++) + /*empty*/; + for (;inrofprops;i++) { + if (prop->ObjectHandle != file->item_id) + break; + // Pick ObjectSize here... if (prop->property == PTP_OPC_ObjectSize) { - // This may already be set, but this 64bit precision value - // is better than the PTP 32bit value, so let it override. - file->filesize = prop->propval.u64; + if (device->object_bitsize == 64) { + file->filesize = prop->propval.u64; + } else { + file->filesize = prop->propval.u32; + } break; } - prop = prop->next; + prop ++; } } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList) && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)) { - MTPPropList *proplist = NULL; - MTPPropList *prop; + MTPProperties *props = NULL; + MTPProperties *prop; + + int nrofprops; /* * This should retrieve all properties for an object, but on devices * which are inherently broken it will not, so these need the * special flag DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST. */ - ret = ptp_mtp_getobjectproplist(params, file->item_id, &proplist); + ret = ptp_mtp_getobjectproplist(params, file->item_id, &props, &nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filelisting_With_Callback(): call to ptp_mtp_getobjectproplist() failed."); // Silently fall through. } - prop = proplist; - while (prop != NULL && prop->ObjectHandle == file->item_id) { - // Pick ObjectSize here... - if (prop->property == PTP_OPC_ObjectSize) { - // This may already be set, but this 64bit precision value - // is better than the PTP 32bit value, so let it override. - file->filesize = prop->propval.u64; - break; - } - prop = prop->next; + if (props != NULL) { + int i; + prop = props; + for (i=0;iObjectHandle != file->item_id) + break; + // Pick ObjectSize here... + if (prop->property == PTP_OPC_ObjectSize) { + if (device->object_bitsize == 64) { + file->filesize = prop->propval.u64; + } else { + file->filesize = prop->propval.u32; + } + break; + } + prop ++; + } + destroy_mtp_prop_list(props, nrofprops); } - destroy_mtp_prop_list(proplist); } else { uint16_t *props = NULL; uint32_t propcnt = 0; // First see which properties can be retrieved for this object format - ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(file->filetype), &propcnt, &props); + ret = ptp_mtp_getobjectpropssupported(params, oi->ObjectFormat, &propcnt, &props); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filelisting_With_Callback(): call to ptp_mtp_getobjectpropssupported() failed."); // Silently fall through. } else { + int i; for (i=0;ifilesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + if (device->object_bitsize == 64) { + file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + } else { + file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + } break; default: break; @@ -2645,49 +2878,55 @@ /* * If we have a cached, large set of metadata, then use it! */ - if (params->proplist) { - MTPPropList *prop = params->proplist; + if (params->props) { + MTPProperties *prop = params->props; - while (prop != NULL && prop->ObjectHandle != file->item_id) { - prop = prop->next; - } - while (prop != NULL && prop->ObjectHandle == file->item_id) { + for (i=0;(inrofprops) && (prop->ObjectHandle != file->item_id);i++,prop++) + /*empty*/; + for (;(inrofprops) && (prop->ObjectHandle == file->item_id);i++,prop++) { // Pick ObjectSize here... if (prop->property == PTP_OPC_ObjectSize) { // This may already be set, but this 64bit precision value // is better than the PTP 32bit value, so let it override. - file->filesize = prop->propval.u64; + if (device->object_bitsize == 64) { + file->filesize = prop->propval.u64; + } else { + file->filesize = prop->propval.u32; + } break; } - prop = prop->next; } } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList) && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)) { - MTPPropList *proplist = NULL; - MTPPropList *prop; + MTPProperties *props = NULL; + MTPProperties *prop; + int nrofprops; /* * This should retrieve all properties for an object, but on devices * which are inherently broken it will not, so these need the * special flag DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST. */ - ret = ptp_mtp_getobjectproplist(params, file->item_id, &proplist); + ret = ptp_mtp_getobjectproplist(params, file->item_id, &props, &nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filelisting_With_Callback(): call to ptp_mtp_getobjectproplist() failed."); // Silently fall through. } - prop = proplist; - while (prop != NULL && prop->ObjectHandle == file->item_id) { - // Pick ObjectSize here... - if (prop->property == PTP_OPC_ObjectSize) { + prop = props; + for (i=0;iObjectHandle == file->item_id) && (prop->property == PTP_OPC_ObjectSize)) { // This may already be set, but this 64bit precision value // is better than the PTP 32bit value, so let it override. - file->filesize = prop->propval.u64; + if (device->object_bitsize == 64) { + file->filesize = prop->propval.u64; + } else { + file->filesize = prop->propval.u32; + } break; } - prop = prop->next; + prop ++; } - destroy_mtp_prop_list(proplist); + destroy_mtp_prop_list(props, nrofprops); } else { uint16_t *props = NULL; uint32_t propcnt = 0; @@ -2701,7 +2940,11 @@ for (i=0;ifilesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + if (device->object_bitsize == 64) { + file->filesize = get_u64_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + } else { + file->filesize = get_u32_from_object(device, file->item_id, PTP_OPC_ObjectSize, 0); + } break; default: break; @@ -2792,7 +3035,7 @@ /** * This function maps and copies a property onto the track metadata if applicable. */ -static void pick_property_to_track_metadata(MTPPropList *prop, LIBMTP_track_t *track) +static void pick_property_to_track_metadata(LIBMTP_mtpdevice_t *device, MTPProperties *prop, LIBMTP_track_t *track) { switch (prop->property) { case PTP_OPC_Name: @@ -2854,9 +3097,11 @@ track->usecount = prop->propval.u32; break; case PTP_OPC_ObjectSize: - // This may already be set, but this 64bit precision value - // is better than the PTP 32bit value, so let it override. - track->filesize = prop->propval.u64; + if (device->object_bitsize == 64) { + track->filesize = prop->propval.u64; + } else { + track->filesize = prop->propval.u32; + } break; default: break; @@ -2882,37 +3127,36 @@ /* * If we have a cached, large set of metadata, then use it! */ - if (params->proplist) { - MTPPropList *prop = params->proplist; - - while (prop != NULL && prop->ObjectHandle != track->item_id) { - prop = prop->next; - } - while (prop != NULL && prop->ObjectHandle == track->item_id) { - pick_property_to_track_metadata(prop, track); - prop = prop->next; + if (params->props) { + MTPProperties *prop = params->props; + + for (i=0;(inrofprops) && (prop->ObjectHandle != track->item_id);i++,prop++) + /*empty*/; + for (i=0;(inrofprops) && (prop->ObjectHandle == track->item_id);i++,prop++) { + pick_property_to_track_metadata(device, prop, track); } } else if (ptp_operation_issupported(params,PTP_OC_MTP_GetObjPropList) && !(ptp_usb->device_flags & DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST)) { - MTPPropList *proplist = NULL; - MTPPropList *prop; + MTPProperties *props = NULL; + MTPProperties *prop; + int nrofprops; /* * This should retrieve all properties for an object, but on devices * which are inherently broken it will not, so these need the * special flag DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST. */ - ret = ptp_mtp_getobjectproplist(params, track->item_id, &proplist); + ret = ptp_mtp_getobjectproplist(params, track->item_id, &props, &nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "get_track_metadata(): call to ptp_mtp_getobjectproplist() failed."); return; } - prop = proplist; - while (prop != NULL && prop->ObjectHandle == track->item_id) { - pick_property_to_track_metadata(prop, track); - prop = prop->next; + prop = props; + for (i=0;iObjectHandle == track->item_id) + pick_property_to_track_metadata(device, prop, track); } - destroy_mtp_prop_list(proplist); + destroy_mtp_prop_list(props, nrofprops); } else { uint16_t *props = NULL; uint32_t propcnt = 0; @@ -2970,9 +3214,11 @@ track->usecount = get_u32_from_object(device, track->item_id, PTP_OPC_UseCount, 0); break; case PTP_OPC_ObjectSize: - // This may already be set, but this 64bit precision value - // is better than the PTP 32bit value, so let it override. - track->filesize = get_u64_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0); + if (device->object_bitsize == 64) { + track->filesize = get_u64_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0); + } else { + track->filesize = (uint64_t) get_u32_from_object(device, track->item_id, PTP_OPC_ObjectSize, 0); + } break; } } @@ -3253,6 +3499,11 @@ // Close file close(fd); + // Delete partial file. + if (ret == -1) { + unlink(path); + } + return ret; } @@ -3312,13 +3563,16 @@ ptp_usb->current_transfer_callback = callback; ptp_usb->current_transfer_callback_data = data; - // This now exist in upstream ret = ptp_getobject_tofd(params, id, fd); ptp_usb->callback_active = 0; ptp_usb->current_transfer_callback = NULL; ptp_usb->current_transfer_callback_data = NULL; + if (ret == PTP_ERROR_CANCEL) { + add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer."); + return -1; + } if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_File_To_File_Descriptor(): Could not get file from device."); return -1; @@ -3398,6 +3652,7 @@ * @return 0 if the transfer was successful, any other value means * failure. * @see LIBMTP_Send_Track_From_File_Descriptor() + * @see LIBMTP_Send_File_From_File() * @see LIBMTP_Delete_Object() */ int LIBMTP_Send_Track_From_File(LIBMTP_mtpdevice_t *device, @@ -3444,44 +3699,52 @@ return ret; } +static MTPProperties* +get_new_mtp_prop_entry(MTPProperties **props, int *nrofprops) { + MTPProperties *newprops; + MTPProperties *prop; -static MTPPropList *new_mtp_prop_entry() -{ - MTPPropList *prop; - prop = (MTPPropList *) malloc(sizeof(MTPPropList)); + if (*props == NULL) { + newprops = malloc(sizeof(MTPProperties)*(*nrofprops+1)); + } else { + newprops = realloc(*props,sizeof(MTPProperties)*(*nrofprops+1)); + } + if (newprops == NULL) + return NULL; + prop = &newprops[*nrofprops]; prop->property = PTP_OPC_StorageID; /* Should be "unknown" */ prop->datatype = PTP_DTC_UNDEF; prop->ObjectHandle = 0x00000000U; - prop->next = NULL; + prop->propval.str = NULL; + + (*props) = newprops; + (*nrofprops)++; return prop; } -static MTPPropList *add_mtp_prop_to_proplist(MTPPropList *proplist, MTPPropList *prop) +static void destroy_mtp_prop_list(MTPProperties *props, int nrofprops) { - if (proplist == NULL) { - return prop; - } else { - MTPPropList *tmp = proplist; - while (tmp->next != NULL) { - tmp = tmp->next; - } - tmp->next = prop; - } - return proplist; + int i; + MTPProperties *prop = props; + + for (i=0;inext; - if (prop->datatype == PTP_DTC_STR) { - free(prop->propval.str); - } - free(prop); - } + if (!prop) + return; + + if (prop->datatype == PTP_DTC_STR && prop->propval.str != NULL) + free(prop->propval.str); + else if ((prop->datatype == PTP_DTC_AINT8 || prop->datatype == PTP_DTC_AINT16 || + prop->datatype == PTP_DTC_AINT32 || prop->datatype == PTP_DTC_AINT64 || prop->datatype == PTP_DTC_AINT128 || + prop->datatype == PTP_DTC_AUINT8 || prop->datatype == PTP_DTC_AUINT16 || + prop->datatype == PTP_DTC_AUINT32 || prop->datatype == PTP_DTC_AUINT64 || prop->datatype == PTP_DTC_AUINT128) + && prop->propval.a.v != NULL) + free(prop->propval.a.v); } /** @@ -3568,7 +3831,8 @@ return -1; } - add_object_to_cache(device, metadata->item_id); + // note we don't need to update the cache here because LIBMTP_Send_File_From_File_Descriptor + // has added the object handle and LIBMTP_Update_Track_Metadata has added the metadata. return 0; } @@ -3648,13 +3912,8 @@ * given as input. * * This can potentially be used for sending in a stream of unknown - * length. Set filedata->filesize = (uint64_t) -1 to - * make libmtp send some dummy length to the device and just - * accept a stream up to some device-determined max length. There - * is not guarantee this will work on all devices... Remember to - * set correct metadata for the track with - * LIBMTP_Update_Track_Metadata() afterwards if it's - * a music file. (This doesn't seem to work very well right now.) + * length. Send music files with + * LIBMTP_Send_Track_From_File_Descriptor() * * @param device a pointer to the device to send the file to. * @param fd the filedescriptor for a local file which will be sent. @@ -3675,6 +3934,7 @@ * @return 0 if the transfer was successful, any other value means * failure. * @see LIBMTP_Send_File_From_File() + * @see LIBMTP_Send_Track_From_File_Descriptor() * @see LIBMTP_Delete_Object() */ int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device, @@ -3683,38 +3943,37 @@ void const * const data, uint32_t const parenthandle) { uint16_t ret; - uint32_t store = get_first_storageid(device); + LIBMTP_devicestorage_t *storage; + uint32_t store; uint32_t localph = parenthandle; PTPParams *params = (PTPParams *) device->params; PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo; int i; int subcall_ret; uint16_t of = map_libmtp_type_to_ptp_type(filedata->filetype); - uint8_t nonconsumable = 0x01U; /* By default it is non-consumable */ - - subcall_ret = check_if_file_fits(device, filedata->filesize); - if (subcall_ret != 0) { - return -1; - } - // Sanity check: no zerolength files + // Sanity check: no zerolength files. if (filedata->filesize == 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File_Descriptor(): " "File of zero size."); return -1; } - - /* - * If this file is among the supported filetypes for this device, - * then it is indeed consumable. - */ - for (i=0;ideviceinfo.ImageFormats_len;i++) { - if (params->deviceinfo.ImageFormats[i] == of) { - nonconsumable = 0x00U; - break; + // See if there is some storage we can fit this file on. + storage = device->storage; + while(storage != NULL) { + subcall_ret = check_if_file_fits(device, storage, filedata->filesize); + if (subcall_ret != 0) { + storage = storage->next; } + break; } + if (storage == NULL) { + add_error_to_errorstack(device, LIBMTP_ERROR_STORAGE_FULL, "LIBMTP_Send_File_From_File_Descriptor(): " + "all device storage is full or corrupt."); + return -1; + } + store = storage->id; /* * If no destination folder was given, look up a default @@ -3834,9 +4093,10 @@ * 0C 00 00 00 03 00 01 20 1C 00 00 00 * ... Then update metadata one-by one, actually (instead of sending it first!) ... */ - MTPPropList *proplist = NULL; - MTPPropList *prop = NULL; - uint16_t *props = NULL; + MTPProperties *props = NULL; + int nrofprops = 0; + MTPProperties *prop = NULL; + uint16_t *properties = NULL; uint32_t propcnt = 0; // default handle @@ -3846,62 +4106,70 @@ // Must be 0x00000000U for new objects filedata->item_id = 0x00000000U; - ret = ptp_mtp_getobjectpropssupported(params, of, &propcnt, &props); + ret = ptp_mtp_getobjectpropssupported(params, of, &propcnt, &properties); for (i=0;iObjectHandle = filedata->item_id; prop->property = PTP_OPC_ObjectFileName; prop->datatype = PTP_DTC_STR; - if (filedata->filename != NULL) + if (filedata->filename != NULL) { prop->propval.str = strdup(filedata->filename); - proplist = add_mtp_prop_to_proplist(proplist, prop); + if (ptp_usb->device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES) { + strip_7bit_from_utf8(prop->propval.str); + } + } break; case PTP_OPC_ProtectionStatus: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = filedata->item_id; prop->property = PTP_OPC_ProtectionStatus; prop->datatype = PTP_DTC_UINT16; prop->propval.u16 = 0x0000U; /* Not protected */ - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_NonConsumable: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = filedata->item_id; prop->property = PTP_OPC_NonConsumable; prop->datatype = PTP_DTC_UINT8; - prop->propval.u8 = nonconsumable; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u8 = 0x00; /* It is supported, then it is consumable */ break; case PTP_OPC_Name: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = filedata->item_id; prop->property = PTP_OPC_Name; prop->datatype = PTP_DTC_STR; if (filedata->filename != NULL) prop->propval.str = strdup(filedata->filename); - proplist = add_mtp_prop_to_proplist(proplist, prop); + break; + case PTP_OPC_DateModified: + // Tag with current time if that is supported + prop = get_new_mtp_prop_entry(&props,&nrofprops); + prop->ObjectHandle = filedata->item_id; + prop->property = PTP_OPC_DateModified; + prop->datatype = PTP_DTC_STR; + prop->propval.str = get_iso8601_stamp(); break; } } ptp_free_objectpropdesc(&opd); } - free(props); + free(properties); ret = ptp_mtp_sendobjectproplist(params, &store, &localph, &filedata->item_id, - of, filedata->filesize, proplist); + of, filedata->filesize, props, nrofprops); /* Free property list */ - destroy_mtp_prop_list(proplist); + destroy_mtp_prop_list(props, nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor():" @@ -3917,13 +4185,11 @@ memset(&new_file, 0, sizeof(PTPObjectInfo)); new_file.Filename = filedata->filename; - if (filedata->filesize == (uint64_t) -1) { - // This is a stream. Set a dummy length... - new_file.ObjectCompressedSize = 1; - } else { - // We loose precision here. - new_file.ObjectCompressedSize = (uint32_t) filedata->filesize; + if (ptp_usb->device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES) { + strip_7bit_from_utf8(new_file.Filename); } + // We loose precision here. + new_file.ObjectCompressedSize = (uint32_t) filedata->filesize; new_file.ObjectFormat = of; new_file.StorageID = store; new_file.ParentObject = localph; @@ -3946,49 +4212,31 @@ // Now there IS an object with this parent handle. filedata->parent_id = localph; - if (filedata->filesize != (uint64_t) -1) { - // Callbacks - ptp_usb->callback_active = 1; - // The callback will deactivate itself after this amount of data has been sent - // One BULK header for the request, one for the data phase. No parameters to the request. - ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2; - ptp_usb->current_transfer_complete = 0; - ptp_usb->current_transfer_callback = callback; - ptp_usb->current_transfer_callback_data = data; - - ret = ptp_sendobject_fromfd(params, fd, filedata->filesize); - - ptp_usb->callback_active = 0; - ptp_usb->current_transfer_callback = NULL; - ptp_usb->current_transfer_callback_data = NULL; - } else { - // This is a stream.. - ret = ptp_sendobject_fromfd(params, fd, 0xFFFFFFFFU-PTP_USB_BULK_HDR_LEN); - if (ret == PTP_ERROR_IO) { - // That's expected. The stream ends, simply... - ret = PTP_RC_OK; - } else { - add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): " - "Error while sending stream."); - } - } + // Callbacks + ptp_usb->callback_active = 1; + // The callback will deactivate itself after this amount of data has been sent + // One BULK header for the request, one for the data phase. No parameters to the request. + ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2; + ptp_usb->current_transfer_complete = 0; + ptp_usb->current_transfer_callback = callback; + ptp_usb->current_transfer_callback_data = data; + + ret = ptp_sendobject_fromfd(params, fd, filedata->filesize); + + ptp_usb->callback_active = 0; + ptp_usb->current_transfer_callback = NULL; + ptp_usb->current_transfer_callback_data = NULL; + if (ret == PTP_ERROR_CANCEL) { + add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer."); + return -1; + } if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): " "Could not send object."); return -1; } - if (!ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList) && nonconsumable != 0x00U) { - // Flag it as non-consumable if it is - subcall_ret = set_object_u8(device, filedata->item_id, PTP_OPC_NonConsumable, nonconsumable); - if (subcall_ret != 0) { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File_Descriptor(): " - "could not set non-consumable status."); - return -1; - } - } - add_object_to_cache(device, filedata->item_id); return 0; @@ -4017,12 +4265,12 @@ uint16_t ret; PTPParams *params = (PTPParams *) device->params; uint32_t i; - uint16_t *props = NULL; + uint16_t *properties = NULL; uint32_t propcnt = 0; // First see which properties can be set on this file format and apply accordingly // i.e only try to update this metadata for object tags that exist on the current player. - ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &props); + ret = ptp_mtp_getobjectpropssupported(params, map_libmtp_type_to_ptp_type(metadata->filetype), &propcnt, &properties); if (ret != PTP_RC_OK) { // Just bail out for now, nothing is ever set. add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " @@ -4030,142 +4278,138 @@ return -1; } if (ptp_operation_issupported(params, PTP_OC_MTP_SetObjPropList)) { - MTPPropList *proplist = NULL; - MTPPropList *prop = NULL; + MTPProperties *props = NULL; + MTPProperties *prop = NULL; + int nrofprops = 0; for (i=0;ifiletype), &opd); + ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd); if (ret != PTP_RC_OK) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not get property description."); } else if (opd.GetSet) { - switch (props[i]) { + switch (properties[i]) { case PTP_OPC_Name: if (metadata->title == NULL) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Name; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(metadata->title); - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_AlbumName: if (metadata->album == NULL) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_AlbumName; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(metadata->album); - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_Artist: if (metadata->artist == NULL) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Artist; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(metadata->artist); - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_Genre: if (metadata->genre == NULL) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Genre; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(metadata->genre); - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_Duration: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Duration; prop->datatype = PTP_DTC_UINT32; - prop->propval.u32 = metadata->duration; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u32 = adjust_u32(metadata->duration, &opd); break; case PTP_OPC_Track: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Track; prop->datatype = PTP_DTC_UINT16; - prop->propval.u16 = metadata->tracknumber; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u16 = adjust_u16(metadata->tracknumber, &opd); break; case PTP_OPC_OriginalReleaseDate: if (metadata->date == NULL) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_OriginalReleaseDate; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(metadata->date); - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_SampleRate: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_SampleRate; prop->datatype = PTP_DTC_UINT32; - prop->propval.u32 = metadata->samplerate; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u32 = adjust_u32(metadata->samplerate, &opd); break; case PTP_OPC_NumberOfChannels: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_NumberOfChannels; prop->datatype = PTP_DTC_UINT16; - prop->propval.u16 = metadata->nochannels; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u16 = adjust_u16(metadata->nochannels, &opd); break; case PTP_OPC_AudioWAVECodec: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_AudioWAVECodec; prop->datatype = PTP_DTC_UINT32; - prop->propval.u32 = metadata->wavecodec; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u32 = adjust_u32(metadata->wavecodec, &opd); break; case PTP_OPC_AudioBitRate: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_AudioBitRate; prop->datatype = PTP_DTC_UINT32; - prop->propval.u32 = metadata->bitrate; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u32 = adjust_u32(metadata->bitrate, &opd); break; case PTP_OPC_BitRateType: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_BitRateType; prop->datatype = PTP_DTC_UINT16; - prop->propval.u16 = metadata->bitratetype; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u16 = adjust_u16(metadata->bitratetype, &opd); break; case PTP_OPC_Rating: // TODO: shall this be set for rating 0? if (metadata->rating == 0) break; - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_Rating; prop->datatype = PTP_DTC_UINT16; - prop->propval.u16 = metadata->rating; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u16 = adjust_u16(metadata->rating, &opd); break; case PTP_OPC_UseCount: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props, &nrofprops); prop->ObjectHandle = metadata->item_id; prop->property = PTP_OPC_UseCount; prop->datatype = PTP_DTC_UINT32; - prop->propval.u32 = metadata->usecount; - proplist = add_mtp_prop_to_proplist(proplist, prop); + prop->propval.u32 = adjust_u32(metadata->usecount, &opd); + break; + case PTP_OPC_DateModified: + // Tag with current time if that is supported + prop = get_new_mtp_prop_entry(&props, &nrofprops); + prop->ObjectHandle = metadata->item_id; + prop->property = PTP_OPC_DateModified; + prop->datatype = PTP_DTC_STR; + prop->propval.str = get_iso8601_stamp(); + break; } } ptp_free_objectpropdesc(&opd); @@ -4174,14 +4418,15 @@ // NOTE: File size is not updated, this should not change anyway. // neither will we change the filename. - ret = ptp_mtp_setobjectproplist(params, proplist); + ret = ptp_mtp_setobjectproplist(params, props, nrofprops); - destroy_mtp_prop_list(proplist); + destroy_mtp_prop_list(props, nrofprops); if (ret != PTP_RC_OK) { // TODO: return error of which property we couldn't set add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set object property list."); + free(properties); return -1; } @@ -4189,12 +4434,12 @@ for (i=0;ifiletype), &opd); + ret = ptp_mtp_getobjectpropdesc(params, properties[i], map_libmtp_type_to_ptp_type(metadata->filetype), &opd); if (ret != PTP_RC_OK) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not get property description."); } else if (opd.GetSet) { - switch (props[i]) { + switch (properties[i]) { case PTP_OPC_Name: // Update title ret = set_object_string(device, metadata->item_id, PTP_OPC_Name, metadata->title); @@ -4230,7 +4475,7 @@ case PTP_OPC_Duration: // Update duration if (metadata->duration != 0) { - ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, metadata->duration); + ret = set_object_u32(device, metadata->item_id, PTP_OPC_Duration, adjust_u32(metadata->duration, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set track duration."); @@ -4240,7 +4485,7 @@ case PTP_OPC_Track: // Update track number. if (metadata->tracknumber != 0) { - ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, metadata->tracknumber); + ret = set_object_u16(device, metadata->item_id, PTP_OPC_Track, adjust_u16(metadata->tracknumber, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set track tracknumber."); @@ -4259,7 +4504,7 @@ case PTP_OPC_SampleRate: // Update sample rate if (metadata->samplerate != 0) { - ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, metadata->samplerate); + ret = set_object_u32(device, metadata->item_id, PTP_OPC_SampleRate, adjust_u32(metadata->samplerate, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set samplerate."); @@ -4269,7 +4514,7 @@ case PTP_OPC_NumberOfChannels: // Update number of channels if (metadata->nochannels != 0) { - ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, metadata->nochannels); + ret = set_object_u16(device, metadata->item_id, PTP_OPC_NumberOfChannels, adjust_u16(metadata->nochannels, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set number of channels."); @@ -4279,7 +4524,7 @@ case PTP_OPC_AudioWAVECodec: // Update WAVE codec if (metadata->wavecodec != 0) { - ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, metadata->wavecodec); + ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioWAVECodec, adjust_u32(metadata->wavecodec, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set WAVE codec."); @@ -4289,7 +4534,7 @@ case PTP_OPC_AudioBitRate: // Update bitrate if (metadata->bitrate != 0) { - ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, metadata->bitrate); + ret = set_object_u32(device, metadata->item_id, PTP_OPC_AudioBitRate, adjust_u32(metadata->bitrate, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set bitrate."); @@ -4299,7 +4544,7 @@ case PTP_OPC_BitRateType: // Update bitrate type if (metadata->bitratetype != 0) { - ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, metadata->bitratetype); + ret = set_object_u16(device, metadata->item_id, PTP_OPC_BitRateType, adjust_u16(metadata->bitratetype, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set bitratetype."); @@ -4310,7 +4555,7 @@ // Update user rating // TODO: shall this be set for rating 0? if (metadata->rating != 0) { - ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, metadata->rating); + ret = set_object_u16(device, metadata->item_id, PTP_OPC_Rating, adjust_u16(metadata->rating, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set user rating."); @@ -4319,12 +4564,24 @@ break; case PTP_OPC_UseCount: // Update use count, set even to zero if desired. - ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, metadata->usecount); + ret = set_object_u32(device, metadata->item_id, PTP_OPC_UseCount, adjust_u32(metadata->usecount, &opd)); if (ret != 0) { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "could not set use count."); } break; + case PTP_OPC_DateModified: + { + // Update modification time if supported + char *tmpstamp = get_iso8601_stamp(); + ret = set_object_string(device, metadata->item_id, PTP_OPC_DateModified, tmpstamp); + if (ret != 0) { + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " + "could not set modification date."); + } + free(tmpstamp); + } + break; // NOTE: File size is not updated, this should not change anyway. // neither will we change the filename. @@ -4334,18 +4591,20 @@ } ptp_free_objectpropdesc(&opd); } - free(props); } else { add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Track_Metadata(): " "Your device doesn't seem to support any known way of setting metadata."); + free(properties); return -1; } // update cached object properties if metadata cache exists - if (params->proplist != NULL) { + if (params->props != NULL) { update_metadata_cache(device, metadata->item_id); } + free(properties); + return 0; } @@ -4385,35 +4644,22 @@ } // delete cached object properties if metadata cache exists - if (params->proplist) { - MTPPropList *prop = params->proplist; - MTPPropList *prev = NULL; - MTPPropList *last_deleted = NULL; - MTPPropList *deleted_props = NULL; - - // find the place where properties for this object begin - while (prop != NULL && prop->ObjectHandle != object_id) { - prev = prop; - prop = prop->next; - } - - // safeguard in case object is not found - if (!prop) return 0; + if (params->props) { + int i, nrofoldprops = 0; - // head of properties for the deleted object - deleted_props = prop; - - // find the end of properties for this object - while (prop != NULL && prop->ObjectHandle == object_id) { - last_deleted = prop; - prop = prop->next; + // not the most efficient way, if we assume objecthandle grouping + for (i=0;inrofprops;i++) { + MTPProperties *prop = ¶ms->props[i]; + if (prop->ObjectHandle == object_id) + { + destroy_mtp_prop(prop); + memcpy (prop,prop+1,(params->nrofprops-i-1)*sizeof(*prop)); + nrofoldprops++; + } } - - // the properties to be deleted are split into separate list - prev->next = prop; - last_deleted->next = NULL; - - destroy_mtp_prop_list(deleted_props); + + params->props = realloc(params->props, params->nrofprops - nrofoldprops); + params->nrofprops -= nrofoldprops; } return 0; @@ -4607,6 +4853,7 @@ uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name, uint32_t parent_id) { PTPParams *params = (PTPParams *) device->params; + PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo; uint32_t parenthandle = 0; uint32_t store = get_first_storageid(device); PTPObjectInfo new_folder; @@ -4615,6 +4862,9 @@ memset(&new_folder, 0, sizeof(new_folder)); new_folder.Filename = name; + if (ptp_usb->device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES) { + strip_7bit_from_utf8(new_folder.Filename); + } new_folder.ObjectCompressedSize = 1; new_folder.ObjectFormat = PTP_OFC_Association; new_folder.ParentObject = parent_id; @@ -4819,6 +5069,8 @@ * @param device a pointer to the device to create the new abstract list * on. * @param name the name of the new abstract list. + * @param artist the artist of the new abstract list or NULL. + * @param genre the genre of the new abstract list or NULL. * @param parenthandle the handle of the parent or 0 for no parent * i.e. the root folder. * @param objectformat the abstract list type to create. @@ -4845,12 +5097,13 @@ int i; int supported = 0; uint16_t ret; - uint16_t *props = NULL; + uint16_t *properties = NULL; uint32_t propcnt = 0; uint32_t store = get_first_storageid(device); uint32_t localph = parenthandle; uint8_t nonconsumable = 0x00U; /* By default it is consumable */ PTPParams *params = (PTPParams *) device->params; + PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo; char fname[256]; uint8_t data[2]; @@ -4885,87 +5138,93 @@ if (ptp_operation_issupported(params,PTP_OC_MTP_SendObjectPropList)) { - MTPPropList *proplist = NULL; - MTPPropList *prop = NULL; + MTPProperties *props = NULL; + MTPProperties *prop = NULL; + int nrofprops = 0; *newid = 0x00000000U; - ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &props); + ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties); for (i=0;iObjectHandle = *newid; prop->property = PTP_OPC_ObjectFileName; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(fname); - proplist = add_mtp_prop_to_proplist(proplist, prop); + if (ptp_usb->device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES) { + strip_7bit_from_utf8(prop->propval.str); + } break; case PTP_OPC_ProtectionStatus: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = *newid; prop->property = PTP_OPC_ProtectionStatus; prop->datatype = PTP_DTC_UINT16; prop->propval.u16 = 0x0000U; /* Not protected */ - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_NonConsumable: - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = *newid; prop->property = PTP_OPC_NonConsumable; prop->datatype = PTP_DTC_UINT8; prop->propval.u8 = nonconsumable; - proplist = add_mtp_prop_to_proplist(proplist, prop); break; case PTP_OPC_Name: if (name != NULL) { - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = *newid; prop->property = PTP_OPC_Name; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(name); - proplist = add_mtp_prop_to_proplist(proplist, prop); } break; case PTP_OPC_Artist: if (artist != NULL) { - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = *newid; prop->property = PTP_OPC_Artist; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(artist); - proplist = add_mtp_prop_to_proplist(proplist, prop); } break; case PTP_OPC_Genre: if (genre != NULL) { - prop = new_mtp_prop_entry(); + prop = get_new_mtp_prop_entry(&props,&nrofprops); prop->ObjectHandle = *newid; prop->property = PTP_OPC_Genre; prop->datatype = PTP_DTC_STR; prop->propval.str = strdup(genre); - proplist = add_mtp_prop_to_proplist(proplist, prop); } break; + case PTP_OPC_DateModified: + // Tag with current time if that is supported + prop = get_new_mtp_prop_entry(&props,&nrofprops); + prop->ObjectHandle = *newid; + prop->property = PTP_OPC_DateModified; + prop->datatype = PTP_DTC_STR; + prop->propval.str = get_iso8601_stamp(); + break; } } ptp_free_objectpropdesc(&opd); } - free(props); + free(properties); ret = ptp_mtp_sendobjectproplist(params, &store, &localph, newid, - objectformat, 0, proplist); + objectformat, 0, props, nrofprops); /* Free property list */ - destroy_mtp_prop_list(proplist); + destroy_mtp_prop_list(props, nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "create_new_abstract_list(): Could not send object property list."); @@ -4986,6 +5245,9 @@ PTPObjectInfo new_object; new_object.Filename = fname; + if (ptp_usb->device_flags & DEVICE_FLAG_ONLY_7BIT_FILENAMES) { + strip_7bit_from_utf8(new_object.Filename); + } new_object.ObjectCompressedSize = 1; new_object.ObjectFormat = objectformat; @@ -5014,6 +5276,7 @@ } // Update title + // FIXME: check if supported if (name != NULL) { ret = set_object_string(device, *newid, PTP_OPC_Name, name); if (ret != 0) { @@ -5023,6 +5286,7 @@ } // Update artist + // FIXME: check if supported if (artist != NULL) { ret = set_object_string(device, *newid, PTP_OPC_Artist, artist); if (ret != 0) { @@ -5032,6 +5296,7 @@ } // Update genre + // FIXME: check if supported if (genre != NULL) { ret = set_object_string(device, *newid, PTP_OPC_Genre, genre); if (ret != 0) { @@ -5040,6 +5305,8 @@ } } + // FIXME: Update date modified + } if (no_tracks > 0) { @@ -5056,6 +5323,183 @@ return 0; } +/** + * This updates the metadata and track listing + * for an abstract list. + * @param device a pointer to the device that the abstract list + * resides on. + * @param name the name of the abstract list. + * @param artist the artist of the abstract list or NULL. + * @param genre the genre of the abstract list or NULL. + * @param objecthandle the object to be updated. + * @param objectformat the abstract list type to update. + * @param tracks an array of tracks to associate with this list. + * @param no_tracks the number of tracks in the list. + * @return 0 on success, any other value means failure. + */ +static int update_abstract_list(LIBMTP_mtpdevice_t *device, + char const * const name, + char const * const artist, + char const * const genre, + uint32_t const objecthandle, + uint16_t const objectformat, + uint32_t const * const tracks, + uint32_t const no_tracks) +{ + uint16_t ret; + PTPParams *params = (PTPParams *) device->params; + uint16_t *properties = NULL; + uint32_t propcnt = 0; + int i; + + // First see which properties can be set + // i.e only try to update this metadata for object tags that exist on the current player. + ret = ptp_mtp_getobjectpropssupported(params, objectformat, &propcnt, &properties); + if (ret != PTP_RC_OK) { + // Just bail out for now, nothing is ever set. + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): " + "could not retrieve supported object properties."); + return -1; + } + if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList)) { + MTPProperties *props = NULL; + MTPProperties *prop = NULL; + int nrofprops = 0; + + for (i=0;iObjectHandle = objecthandle; + prop->property = PTP_OPC_Name; + prop->datatype = PTP_DTC_STR; + if (name != NULL) + prop->propval.str = strdup(name); + break; + case PTP_OPC_Artist: + if (artist != NULL) { + prop = get_new_mtp_prop_entry(&props, &nrofprops); + prop->ObjectHandle = objecthandle; + prop->property = PTP_OPC_Artist; + prop->datatype = PTP_DTC_STR; + prop->propval.str = strdup(artist); + } + break; + case PTP_OPC_Genre: + if (genre != NULL) { + prop = get_new_mtp_prop_entry(&props, &nrofprops); + prop->ObjectHandle = objecthandle; + prop->property = PTP_OPC_Genre; + prop->datatype = PTP_DTC_STR; + prop->propval.str = strdup(genre); + } + break; + case PTP_OPC_DateModified: + // Tag with current time if that is supported + prop = get_new_mtp_prop_entry(&props, &nrofprops); + prop->ObjectHandle = objecthandle; + prop->property = PTP_OPC_DateModified; + prop->datatype = PTP_DTC_STR; + prop->propval.str = get_iso8601_stamp(); + break; + default: + break; + } + } + ptp_free_objectpropdesc(&opd); + } + + // proplist could be NULL if we can't write any properties + if (props != NULL) { + ret = ptp_mtp_setobjectproplist(params, props, nrofprops); + + destroy_mtp_prop_list(props, nrofprops); + + if (ret != PTP_RC_OK) { + // TODO: return error of which property we couldn't set + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): " + "could not set object property list."); + free(properties); + return -1; + } + } + + } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) { + for (i=0;i 0) { + // Add tracks to the new album as object references. + ret = ptp_mtp_setobjectreferences (params, objecthandle, (uint32_t *) tracks, no_tracks); + if (ret != PTP_RC_OK) { + add_ptp_error_to_errorstack(device, ret, "update_abstract_list(): could not add tracks as object references."); + free(properties); + return -1; + } + } + + if (params->props != NULL) { + update_metadata_cache(device, objecthandle); + } + + free(properties); + + return 0; +} + /** * This routine creates a new playlist based on the metadata @@ -5113,25 +5557,14 @@ int LIBMTP_Update_Playlist(LIBMTP_mtpdevice_t *device, LIBMTP_playlist_t const * const metadata) { - uint16_t ret; - PTPParams *params = (PTPParams *) device->params; - - // Update title - ret = set_object_string(device, metadata->playlist_id, PTP_OPC_Name, metadata->name); - if (ret != 0) { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Playlist(): could not set playlist name."); - return -1; - } - - if (metadata->no_tracks > 0) { - // Add tracks to the new playlist as object references. - ret = ptp_mtp_setobjectreferences (params, metadata->playlist_id, metadata->tracks, metadata->no_tracks); - if (ret != PTP_RC_OK) { - add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Playlist(): could not add tracks as object references."); - return -1; - } - } - return 0; + return update_abstract_list(device, + metadata->name, + NULL, + NULL, + metadata->playlist_id, + PTP_OFC_MTP_AbstractAudioVideoPlaylist, + metadata->tracks, + metadata->no_tracks); } /** @@ -5413,6 +5846,11 @@ int support_width = 0; int support_duration = 0; + PTPObjectPropDesc opd_height; + PTPObjectPropDesc opd_width; + PTPObjectPropDesc opd_format; + PTPObjectPropDesc opd_duration; + // Default to no type supported. *sample = NULL; @@ -5451,23 +5889,39 @@ } } free(props); - - /* - * TODO: figure out what format, max height and width, or duration is actually - * supported on this device. - */ + if (support_data && support_format && support_height && support_width && !support_duration) { // Something that supports height and width and not duration is likely to be JPEG LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t(); - retsam->filetype = LIBMTP_FILETYPE_JPEG; - retsam->width = 100; - retsam->height = 100; + /* + * Populate the sample format with the first supported format + * + * TODO: figure out how to pass back more than one format if more are + * supported by the device. + */ + ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format); + retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16); + /* Populate the maximum image height */ + ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleWidth, map_libmtp_type_to_ptp_type(filetype), &opd_width); + retsam->width = opd_width.FORM.Range.MaximumValue.u32; + /* Populate the maximum image width */ + ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleHeight, map_libmtp_type_to_ptp_type(filetype), &opd_height); + retsam->height = opd_height.FORM.Range.MaximumValue.u32; *sample = retsam; } else if (support_data && support_format && !support_height && !support_width && support_duration) { // Another qualified guess LIBMTP_filesampledata_t *retsam = LIBMTP_new_filesampledata_t(); - retsam->filetype = LIBMTP_FILETYPE_MP3; - retsam->duration = 2000; // 2 seconds + /* + * Populate the sample format with the first supported format + * + * TODO: figure out how to pass back more than one format if more are + * supported by the device. + */ + ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleFormat, map_libmtp_type_to_ptp_type(filetype), &opd_format); + retsam->filetype = map_ptp_type_to_libmtp_type(opd_format.FORM.Enum.SupportedValue[0].u16); + /* Populate the maximum duration */ + ptp_mtp_getobjectpropdesc (params, PTP_OPC_RepresentativeSampleDuration, map_libmtp_type_to_ptp_type(filetype), &opd_duration); + retsam->duration = opd_duration.FORM.Range.MaximumValue.u32; *sample = retsam; } return 0; @@ -5599,131 +6053,14 @@ int LIBMTP_Update_Album(LIBMTP_mtpdevice_t *device, LIBMTP_album_t const * const metadata) { - uint16_t ret; - PTPParams *params = (PTPParams *) device->params; - uint16_t *props = NULL; - uint32_t propcnt = 0; - int i; - - // First see which properties can be set - // i.e only try to update this metadata for object tags that exist on the current player. - ret = ptp_mtp_getobjectpropssupported(params, PTP_OFC_MTP_AbstractAudioAlbum, &propcnt, &props); - if (ret != PTP_RC_OK) { - // Just bail out for now, nothing is ever set. - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "could not retrieve supported object properties."); - return -1; - } - if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjPropList)) { - MTPPropList *proplist = NULL; - MTPPropList *prop = NULL; - - for (i=0;iObjectHandle = metadata->album_id; - prop->property = PTP_OPC_Name; - prop->datatype = PTP_DTC_STR; - if (metadata->name != NULL) - prop->propval.str = strdup(metadata->name); - proplist = add_mtp_prop_to_proplist(proplist, prop); - break; - case PTP_OPC_Artist: - prop = new_mtp_prop_entry(); - prop->ObjectHandle = metadata->album_id; - prop->property = PTP_OPC_Artist; - prop->datatype = PTP_DTC_STR; - if (metadata->artist != NULL) - prop->propval.str = strdup(metadata->artist); - proplist = add_mtp_prop_to_proplist(proplist, prop); - break; - case PTP_OPC_Genre: - prop = new_mtp_prop_entry(); - prop->ObjectHandle = metadata->album_id; - prop->property = PTP_OPC_Genre; - prop->datatype = PTP_DTC_STR; - if (metadata->genre != NULL) - prop->propval.str = strdup(metadata->genre); - proplist = add_mtp_prop_to_proplist(proplist, prop); - break; - default: - break; - } - } - ptp_free_objectpropdesc(&opd); - } - - ret = ptp_mtp_setobjectproplist(params, proplist); - - destroy_mtp_prop_list(proplist); - - if (ret != PTP_RC_OK) { - // TODO: return error of which property we couldn't set - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "could not set object property list."); - return -1; - } - - } else if (ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) { - for (i=0;ialbum_id, PTP_OPC_Name, metadata->name); - if (ret != 0) { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "could not set album title."); - } - break; - case PTP_OPC_Artist: - // Update artist - ret = set_object_string(device, metadata->album_id, PTP_OPC_Artist, metadata->artist); - if (ret != 0) { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "could not set album artist name."); - } - break; - case PTP_OPC_Genre: - // Update genre - ret = set_object_string(device, metadata->album_id, PTP_OPC_Genre, metadata->genre); - if (ret != 0) { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "could not set album genre."); - } - break; - default: - break; - } - } - free(props); - } else { - add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Update_Album(): " - "Your device doesn't seem to support any known way of setting metadata."); - return -1; - } - - // Then the object references... - if (metadata->no_tracks > 0) { - // Add tracks to the new album as object references. - ret = ptp_mtp_setobjectreferences (params, metadata->album_id, metadata->tracks, metadata->no_tracks); - if (ret != PTP_RC_OK) { - add_ptp_error_to_errorstack(device, ret, "LIBMTP_Update_Album(): could not add tracks as object references."); - return -1; - } - } - - if (params->proplist != NULL) { - update_metadata_cache(device, metadata->album_id); - } - return 0; + return update_abstract_list(device, + metadata->name, + metadata->artist, + metadata->genre, + metadata->album_id, + PTP_OFC_MTP_AbstractAudioAlbum, + metadata->tracks, + metadata->no_tracks); } /** @@ -5740,7 +6077,6 @@ void add_object_to_cache(LIBMTP_mtpdevice_t *device, uint32_t handle) { PTPParams *params = (PTPParams *)device->params; - MTPPropList *prop = NULL; uint16_t ret; int n; @@ -5756,14 +6092,25 @@ ptp_getobjectinfo(params, handle, ¶ms->objectinfo[n-1]); - if (params->proplist) { + if (params->props) { + MTPProperties *props = NULL; + MTPProperties *xprops; + int nrofprops = 0; - ret = ptp_mtp_getobjectproplist(params, handle, &prop); + ret = ptp_mtp_getobjectproplist(params, handle, &props, &nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): call to ptp_mtp_getobjectproplist() failed."); return; } - add_mtp_prop_to_proplist(params->proplist, prop); + xprops = realloc(params->props, (params->nrofprops+nrofprops)*sizeof(*props)); + if (!xprops) { + add_ptp_error_to_errorstack(device, ret, "add_object_to_cache(): call to realloc() failed."); + return; + } + params->props = xprops; + memcpy(xprops+params->nrofprops,props,nrofprops*sizeof(*props)); + free (props); /* do not free sub strings, we copied them above */ + params->nrofprops += nrofprops; } } @@ -5774,48 +6121,39 @@ void update_metadata_cache(LIBMTP_mtpdevice_t *device, uint32_t handle) { PTPParams *params = (PTPParams *)device->params; - MTPPropList *prop = params->proplist; - MTPPropList *prev = NULL; - MTPPropList *deleted_props = NULL; - MTPPropList *updated_props; + MTPProperties *xprops; + MTPProperties *props; + int i, nrofprops, nrofoldprops = 0; uint16_t ret; - // find the place where the properties for this object begin - while (prop != NULL && prop->ObjectHandle != handle) { - prev = prop; - prop = prop->next; + // remove the current properties for this object + // not the most efficient way, if we assume objecthandle grouping + for (i=0;inrofprops;i++) { + MTPProperties *prop = ¶ms->props[i]; + if (prop->ObjectHandle == handle) + { + destroy_mtp_prop(prop); + memcpy (prop,prop+1,(params->nrofprops-i-1)*sizeof(*prop)); + nrofoldprops++; + } } - // safeguard in case object is not found - if (!prop) return; - // fetch updated properties and add them to the metadata cache in the same // place where outdated properties were found - ret = ptp_mtp_getobjectproplist(params, handle, &updated_props); + ret = ptp_mtp_getobjectproplist(params, handle, &props, &nrofprops); if (ret != PTP_RC_OK) { add_ptp_error_to_errorstack(device, ret, "update_metadata_cache(): call to ptp_mtp_getobjectproplist() failed."); return; } - prev->next = updated_props; - - // head of outdated object properties that will be deleted - deleted_props = prop; - - // find the end of properties for this object - while (prop != NULL && prop->ObjectHandle == handle) { - prev = prop; - prop = prop->next; - } - // find the end of the fetched properties and link rest of the list to it - while (updated_props->next != NULL) { - updated_props = updated_props->next; + xprops = realloc(params->props, (params->nrofprops+nrofprops-nrofoldprops)*sizeof(*props)); + if (!xprops) { + add_ptp_error_to_errorstack(device, ret, "update_metadata_cache(): call to realloc() failed."); + return; } - updated_props->next = prop; - - // split outdated properties into separate list and delete it - prev->next = NULL; - destroy_mtp_prop_list(deleted_props); - + params->props = xprops; + memcpy(xprops+params->nrofprops-nrofoldprops,props,nrofprops*sizeof(*props)); + free (props); /* do not free sub strings, we copied them above */ + params->nrofprops += nrofprops-nrofoldprops; return; } diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.h /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.h --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.h 2007-08-07 16:58:35.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.h 2007-10-04 23:16:33.000000000 +0100 @@ -28,8 +28,8 @@ #ifndef LIBMTP_H_INCLUSION_GUARD #define LIBMTP_H_INCLUSION_GUARD -#define LIBMTP_VERSION 0.2.1 -#define LIBMTP_VERSION_STRING "0.2.1" +#define LIBMTP_VERSION 0.2.2 +#define LIBMTP_VERSION_STRING "0.2.2" /* This handles MSVC pecularities */ #ifdef _MSC_VER @@ -120,7 +120,8 @@ LIBMTP_ERROR_MEMORY_ALLOCATION, LIBMTP_ERROR_NO_DEVICE_ATTACHED, LIBMTP_ERROR_STORAGE_FULL, - LIBMTP_ERROR_CONNECTING + LIBMTP_ERROR_CONNECTING, + LIBMTP_ERROR_CANCELLED } LIBMTP_error_number_t; typedef struct LIBMTP_device_entry_struct LIBMTP_device_entry_t; /**< @see LIBMTP_device_entry_struct */ typedef struct LIBMTP_error_struct LIBMTP_error_t; /**< @see LIBMTP_error_struct */ @@ -176,8 +177,10 @@ * Main MTP device object struct */ struct LIBMTP_mtpdevice_struct { - /** Interface number of this device */ - uint8_t interface_number; + /** + * Object bitsize, typically 32 or 64. + */ + uint8_t object_bitsize; /** * Parameters for this device, must be cast into * \c (PTPParams*) before internal use. diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.h.in /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.h.in --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libmtp.h.in 2007-08-03 10:29:11.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/libmtp.h.in 2007-10-02 22:00:14.000000000 +0100 @@ -120,7 +120,8 @@ LIBMTP_ERROR_MEMORY_ALLOCATION, LIBMTP_ERROR_NO_DEVICE_ATTACHED, LIBMTP_ERROR_STORAGE_FULL, - LIBMTP_ERROR_CONNECTING + LIBMTP_ERROR_CONNECTING, + LIBMTP_ERROR_CANCELLED } LIBMTP_error_number_t; typedef struct LIBMTP_device_entry_struct LIBMTP_device_entry_t; /**< @see LIBMTP_device_entry_struct */ typedef struct LIBMTP_error_struct LIBMTP_error_t; /**< @see LIBMTP_error_struct */ @@ -176,8 +177,10 @@ * Main MTP device object struct */ struct LIBMTP_mtpdevice_struct { - /** Interface number of this device */ - uint8_t interface_number; + /** + * Object bitsize, typically 32 or 64. + */ + uint8_t object_bitsize; /** * Parameters for this device, must be cast into * \c (PTPParams*) before internal use. diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libusb-glue.c /tmp/GW9hRDRi14/libmtp-0.2.2/src/libusb-glue.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libusb-glue.c 2007-08-07 08:41:24.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/libusb-glue.c 2007-10-04 22:23:48.000000000 +0100 @@ -44,15 +44,11 @@ /* To enable debug prints, switch on this */ //#define ENABLE_USB_BULK_DEBUG -/* OUR APPLICATION USB URB (2MB) ;) */ -#define PTPCAM_USB_URB 2097152 - /* this must not be too short - the original 4000 was not long enough for big file transfers. I imagine the player spends a bit of time gearing up to receiving lots of data. This also makes connecting/disconnecting more reliable */ #define USB_TIMEOUT 10000 -#define USB_CAPTURE_TIMEOUT 20000 /* USB control message data phase direction */ #ifndef USB_DP_HTD @@ -76,26 +72,34 @@ /* * Creative Technology * Initially the Creative devices was all we supported so these are - * the most thoroughly tested devices. - */ - { "Creative Zen Vision", 0x041e, 0x411f, DEVICE_FLAG_NONE }, - { "Creative Portable Media Center", 0x041e, 0x4123, DEVICE_FLAG_NONE }, - { "Creative Zen Xtra (MTP mode)", 0x041e, 0x4128, DEVICE_FLAG_NONE }, - { "Second generation Dell DJ", 0x041e, 0x412f, DEVICE_FLAG_NONE }, - { "Creative Zen Micro (MTP mode)", 0x041e, 0x4130, DEVICE_FLAG_NONE }, - { "Creative Zen Touch (MTP mode)", 0x041e, 0x4131, DEVICE_FLAG_NONE }, - { "Dell Pocket DJ (MTP mode)", 0x041e, 0x4132, DEVICE_FLAG_NONE }, - { "Creative Zen Sleek (MTP mode)", 0x041e, 0x4137, DEVICE_FLAG_NONE }, - { "Creative Zen MicroPhoto", 0x041e, 0x413c, DEVICE_FLAG_NONE }, - { "Creative Zen Sleek Photo", 0x041e, 0x413d, DEVICE_FLAG_NONE }, - { "Creative Zen Vision:M", 0x041e, 0x413e, DEVICE_FLAG_NONE }, + * the most thoroughly tested devices. Presumably only the devices + * with older firmware (the ones that have 32bit object size) will + * need the DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL flag. + */ + { "Creative ZEN Vision", 0x041e, 0x411f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative Portable Media Center", 0x041e, 0x4123, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative ZEN Xtra (MTP mode)", 0x041e, 0x4128, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Second generation Dell DJ", 0x041e, 0x412f, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative ZEN Micro (MTP mode)", 0x041e, 0x4130, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative ZEN Touch (MTP mode)", 0x041e, 0x4131, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Dell Pocket DJ (MTP mode)", 0x041e, 0x4132, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative ZEN Sleek (MTP mode)", 0x041e, 0x4137, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, + { "Creative ZEN MicroPhoto", 0x041e, 0x413c, DEVICE_FLAG_NONE }, + { "Creative ZEN Sleek Photo", 0x041e, 0x413d, DEVICE_FLAG_NONE }, + { "Creative ZEN Vision:M", 0x041e, 0x413e, DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by marazm@o2.pl - { "Creative Zen V", 0x041e, 0x4150, DEVICE_FLAG_NONE }, + { "Creative ZEN V", 0x041e, 0x4150, DEVICE_FLAG_NONE }, // Reported by danielw@iinet.net.au - { "Creative Zen Vision:M (DVP-HD0004)", 0x041e, 0x4151, DEVICE_FLAG_NONE }, + // This version of the Vision:M needs the no release interface flag, + // unclear whether the other version above need it too or not. + { "Creative ZEN Vision:M (DVP-HD0004)", 0x041e, 0x4151, DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by Darel on the XNJB forums - { "Creative Zen V Plus", 0x041e, 0x4152, DEVICE_FLAG_NONE }, - { "Creative Zen Vision W", 0x041e, 0x4153, DEVICE_FLAG_NONE }, + { "Creative ZEN V Plus", 0x041e, 0x4152, DEVICE_FLAG_NONE }, + { "Creative ZEN Vision W", 0x041e, 0x4153, DEVICE_FLAG_NONE }, + // Reported by Paul Kurczaba + { "Creative ZEN 8GB", 0x041e, 0x4157, DEVICE_FLAG_IGNORE_HEADER_ERRORS }, + // Reported by Ringofan + { "Creative ZEN V 2GB", 0x041e, 0x4158, DEVICE_FLAG_NONE }, /* * Samsung @@ -105,14 +109,15 @@ { "Samsung YH-920", 0x04e8, 0x5022, DEVICE_FLAG_UNLOAD_DRIVER }, // Contributed by aronvanammers on SourceForge { "Samsung YH-925GS", 0x04e8, 0x5024, DEVICE_FLAG_NONE }, - // From libgphoto2 - { "Samsung YH-820", 0x04e8, 0x502e, DEVICE_FLAG_NONE }, + // From libgphoto2, according to tests by Stephan Fabel it cannot + // get all objects with the getobjectproplist command.. + { "Samsung YH-820", 0x04e8, 0x502e, DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, // Contributed by polux2001@users.sourceforge.net - { "Samsung YH-925(-GS)", 0x04e8, 0x502f, DEVICE_FLAG_NONE }, + { "Samsung YH-925(-GS)", 0x04e8, 0x502f, DEVICE_FLAG_UNLOAD_DRIVER }, // Contributed by anonymous person on SourceForge { "Samsung YH-J70J", 0x04e8, 0x5033, DEVICE_FLAG_UNLOAD_DRIVER }, // From XNJB user - { "Samsung YP-Z5", 0x04e8, 0x503c, DEVICE_FLAG_NONE }, + { "Samsung YP-Z5", 0x04e8, 0x503c, DEVICE_FLAG_UNLOAD_DRIVER }, // From XNJB user { "Samsung YP-Z5 2GB", 0x04e8, 0x5041, DEVICE_FLAG_NONE }, // Contributed by anonymous person on SourceForge @@ -123,9 +128,7 @@ { "Samsung YP-F2J", 0x04e8, 0x5057, DEVICE_FLAG_UNLOAD_DRIVER }, // Reported by Patrick { "Samsung YP-K5", 0x04e8, 0x505a, DEVICE_FLAG_NO_ZERO_READS }, - // From dev.local@gmail.com - // A bit suspicious about this, is this really the MTP mode? - { "Samsung YP-U3QB/XER", 0x04e8, 0x507c, DEVICE_FLAG_NONE }, + // From dev.local@gmail.com - 0x4e8/0x507c is the UMS mode, don't add this. // From m.eik michalke { "Samsung YP-U3", 0x04e8, 0x507d, DEVICE_FLAG_NONE }, // Reported by Matthew Wilcox @@ -161,8 +164,12 @@ { "Philips HDD085/00 and HDD082/17", 0x0471, 0x014d, DEVICE_FLAG_NONE }, // from XNJB forum { "Philips GoGear SA9200", 0x0471, 0x014f, DEVICE_FLAG_NONE }, + // From John Coppens + { "Philips SA1115/55", 0x0471, 0x0164, DEVICE_FLAG_NONE }, // From Gerhard Mekenkamp { "Philips GoGear Audio", 0x0471, 0x0165, DEVICE_FLAG_NONE }, + // from David Holm + { "Philips Shoqbox", 0x0471, 0x0172, DEVICE_FLAG_ONLY_7BIT_FILENAMES }, // from npedrosa { "Philips PSA610", 0x0471, 0x0181, DEVICE_FLAG_NONE }, // From libgphoto2 source @@ -175,28 +182,44 @@ * SanDisk * several devices (c150 for sure) are definately dual-mode and must * have the USB mass storage driver that hooks them unloaded first. + * They all have problematic dual-mode making the device unload effect + * uncertain on these devices. All except for the Linux based ones seem + * to need DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL. */ // Reported by Brian Robison - { "SanDisk Sansa m230/m240", 0x0781, 0x7400, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa m230/m240", 0x0781, 0x7400, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by tangent_@users.sourceforge.net - { "SanDisk Sansa c150", 0x0781, 0x7410, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa c150", 0x0781, 0x7410, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // From libgphoto2 source // Reported by // Reported by Mike Owen - { "SanDisk Sansa e200/e250/e260/e270/e280", 0x0781, 0x7420, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa e200/e250/e260/e270/e280", 0x0781, 0x7420, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by XNJB user - { "SanDisk Sansa e280", 0x0781, 0x7421, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa e280", 0x0781, 0x7421, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by anonymous user at sourceforge.net - { "SanDisk Sansa c250", 0x0781, 0x7450, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa c240/c250", 0x0781, 0x7450, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by XNJB user, and Miguel de Icaza // This has no dual-mode so no need to unload any driver. // This is a Linux based device! { "SanDisk Sansa Connect", 0x0781, 0x7480, DEVICE_FLAG_NONE }, // Reported by Troy Curtis Jr. - { "SanDisk Sansa Express", 0x0781, 0x7460, DEVICE_FLAG_UNLOAD_DRIVER | - DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST }, + { "SanDisk Sansa Express", 0x0781, 0x7460, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, // Reported by XNJB user - { "SanDisk Sansa m240", 0x0781, 0x7430, DEVICE_FLAG_UNLOAD_DRIVER }, + { "SanDisk Sansa m240", 0x0781, 0x7430, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL | + DEVICE_FLAG_NO_RELEASE_INTERFACE }, /* @@ -293,16 +316,20 @@ /* * Archos + * These devices have some dual-mode interfaces which will really + * respect the driver unloading, so DEVICE_FLAG_UNLOAD_DRIVER + * really work on these devices! */ - // Reported by gudul1@users.sourceforge.net - { "Archos 104 (MTP mode)", 0x0e79, 0x120a, DEVICE_FLAG_NONE }, + // Reported by Alexander Haertig + { "Archos Gmini XS100", 0x0e79, 0x1207, DEVICE_FLAG_UNLOAD_DRIVER }, // Added by Jan Binder { "Archos XS202 (MTP mode)", 0x0e79, 0x1208, DEVICE_FLAG_NONE }, + // Reported by gudul1@users.sourceforge.net + { "Archos 104 (MTP mode)", 0x0e79, 0x120a, DEVICE_FLAG_NONE }, // Reported by Etienne Chauchot - // This seems to be dual-mode and thus need to have the unload flag. { "Archos 504 (MTP mode)", 0x0e79, 0x1307, DEVICE_FLAG_UNLOAD_DRIVER }, - // Reported by Alexander Haertig - { "Archos Gmini XS100", 0x0e79, 0x1207, DEVICE_FLAG_UNLOAD_DRIVER }, + // Reported by Kay McCormick + { "Archos 704 mobile dvr", 0x0e79, 0x130d, DEVICE_FLAG_UNLOAD_DRIVER }, /* * Dunlop (OEM of EGOMAN ltd?) reported by Nanomad @@ -337,6 +364,8 @@ */ // From: Mitchell Hicks {"Nokia 5300 Mobile Phone", 0x0421, 0x04ba, DEVICE_FLAG_NONE }, + // From Christian Arnold + {"Nokia N73 Mobile Phone", 0x0421, 0x04d1, DEVICE_FLAG_UNLOAD_DRIVER }, // From Swapan {"Nokia N75 Mobile Phone", 0x0421, 0x04e1, DEVICE_FLAG_NONE }, // From: Pat Nicholls @@ -351,7 +380,12 @@ /* * RCA / Thomson */ + // From kiki + {"Thomson EM28 Series", 0x069b, 0x0774, DEVICE_FLAG_NONE }, {"Thomson Opal / RCA Lyra MC4002", 0x069b, 0x0777, DEVICE_FLAG_NONE }, + // From Svenna + // Not confirmed to be MTP. + {"Thomson scenium E308", 0x069b, 0x3028, DEVICE_FLAG_NONE }, /* * NTT DoCoMo @@ -363,6 +397,8 @@ * Reported by Peter Gyongyosi */ {"Palm / Handspring Pocket Tunes", 0x1703, 0x0001, DEVICE_FLAG_NONE }, + // Reported by anonymous submission + {"Palm / Handspring Pocket Tunes 4", 0x1703, 0x0002, DEVICE_FLAG_NONE }, /* * TrekStor devices @@ -384,24 +420,44 @@ * Cowon Systems, Inc. * The iAudio audiophile devices don't encourage the use of MTP. */ + // Reported by Patrik Johansson + {"Cowon iAudio U3 (MTP mode)", 0x0e21, 0x0701, DEVICE_FLAG_NONE }, // Reported by Roberth Karman {"Cowon iAudio 7 (MTP mode)", 0x0e21, 0x0751, DEVICE_FLAG_NONE }, // Reported by TJ Something - {"Cowon D2 (MTP mode)", 0x0e21, 0x0801, DEVICE_FLAG_NONE }, + {"Cowon iAudio D2 (MTP mode)", 0x0e21, 0x0801, + DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL }, /* + * Insignia, dual-mode. + */ + {"Insignia NS-DV45", 0x19ff, 0x0303, DEVICE_FLAG_UNLOAD_DRIVER }, + // Reported by "brad" (anonymous, sourceforge) + {"Insignia Pilot 4GB", 0x19ff, 0x0309, DEVICE_FLAG_UNLOAD_DRIVER }, + + /* + * LG Electronics + */ + // Not verified - anonymous submission + { "LG UP3", 0x043e, 0x70b1, DEVICE_FLAG_NONE }, + + /* * Other strange stuff. */ {"Isabella's prototype", 0x0b20, 0xddee, DEVICE_FLAG_NONE } }; static const int mtp_device_table_size = sizeof(mtp_device_table) / sizeof(LIBMTP_device_entry_t); -int ptpcam_usb_timeout = USB_TIMEOUT; - // Local functions static struct usb_bus* init_usb(); -static void close_usb(PTP_USB* ptp_usb, uint8_t interfaceNumber); -static void find_endpoints(struct usb_device *dev, int* inep, int* inep_maxpacket, int* outep, int* outep_maxpacket, int* intep); +static void close_usb(PTP_USB* ptp_usb); +static void find_interface_and_endpoints(struct usb_device *dev, + uint8_t *interface, + int* inep, + int* inep_maxpacket, + int* outep, + int* outep_maxpacket, + int* intep); static void clear_stall(PTP_USB* ptp_usb); static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev); static short ptp_write_func (unsigned long,PTPDataHandler*,void *data,unsigned long*); @@ -492,7 +548,7 @@ while (tmplist != NULL) { mtpdevice_list_t *tmp = tmplist; tmplist = tmplist->next; - // Do not free() the fields (ptp_usb, parms)! These are used elsewhere. + // Do not free() the fields (ptp_usb, params)! These are used elsewhere. free(tmp); } return; @@ -558,7 +614,7 @@ 4, (char *) buf, sizeof(buf), - 1000); + USB_TIMEOUT); // Dump it, if requested if (dumpfile != NULL && ret > 0) { @@ -591,7 +647,7 @@ 5, (char *) buf, sizeof(buf), - 1000); + USB_TIMEOUT); // Dump it, if requested if (dumpfile != NULL && ret > 0) { @@ -641,32 +697,38 @@ * properties (if this list is not empty, new entries will be appended * to the list). * @return LIBMTP_ERROR_NONE implies that devices have been found, scan the list - * appropriately. LIBMTP_ERROR_NO_DEVICE_ATTACHED implies that no devices have - * been found. + * appropriately. LIBMTP_ERROR_NO_DEVICE_ATTACHED implies that no + * devices have been found. */ static LIBMTP_error_number_t get_mtp_usb_device_list(mtpdevice_list_t ** mtp_device_list) { struct usb_bus *bus = init_usb(); - for (; bus != NULL; bus = bus->next) { struct usb_device *dev = bus->devices; for (; dev != NULL; dev = dev->next) { - if (probe_device_descriptor(dev, NULL)) { - /* Append this usb device to the MTP USB Device List */ - *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev); - } else { - /* Check if it's in the known devices list then */ + if (dev->descriptor.bDeviceClass != USB_CLASS_HUB) { int i; - - for(i = 0; i < mtp_device_table_size; i++) { - if(dev->descriptor.bDeviceClass != USB_CLASS_HUB && - dev->descriptor.idVendor == mtp_device_table[i].vendor_id && - dev->descriptor.idProduct == mtp_device_table[i].product_id) { - /* Append this usb device to the MTP device list */ - *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev); - break; - } - } + int found = 0; + + // First check if we know about the device already. + // Devices well known to us will not have their descriptors + // probed, it caused problems with some devices. + for(i = 0; i < mtp_device_table_size; i++) { + if(dev->descriptor.idVendor == mtp_device_table[i].vendor_id && + dev->descriptor.idProduct == mtp_device_table[i].product_id) { + /* Append this usb device to the MTP device list */ + *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev); + found = 1; + break; + } + } + // If we didn't know it, try probing the "OS Descriptor". + if (!found) { + if (probe_device_descriptor(dev, NULL)) { + /* Append this usb device to the MTP USB Device List */ + *mtp_device_list = append_to_mtpdevice_list(*mtp_device_list, dev); + } + } } } } @@ -720,7 +782,7 @@ char devname[0x10]; devname[0] = '\0'; - res = usb_get_driver_np(ptp_usb->handle, ptp_usb->interface, devname, sizeof(devname)); + res = usb_get_driver_np(ptp_usb->handle, (int) ptp_usb->interface, devname, sizeof(devname)); if (devname[0] != '\0') { printf(" Using kernel interface \"%s\"\n", devname); } @@ -844,7 +906,7 @@ #ifdef ENABLE_USB_BULK_DEBUG printf("Reading in 0x%04x bytes\n", toread); #endif - result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, (char*)bytes, toread, ptpcam_usb_timeout); + result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, (char*)bytes, toread, USB_TIMEOUT); #ifdef ENABLE_USB_BULK_DEBUG printf("Result of read: 0x%04x\n", result); #endif @@ -882,9 +944,13 @@ ptp_usb->callback_active = 0; } if (ptp_usb->current_transfer_callback != NULL) { - (void) ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete, - ptp_usb->current_transfer_total, - ptp_usb->current_transfer_callback_data); + int ret; + ret = ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete, + ptp_usb->current_transfer_total, + ptp_usb->current_transfer_callback_data); + if (ret != 0) { + return PTP_ERROR_CANCEL; + } } } @@ -905,7 +971,7 @@ printf("<==USB IN\n"); printf("Zero Read\n"); #endif - zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &temp, 0, ptpcam_usb_timeout); + zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &temp, 0, USB_TIMEOUT); if (zeroresult != 0) printf("LIBMTP panic: unable to read in zero packet, response 0x%04x", zeroresult); } @@ -946,7 +1012,7 @@ } } handler->getfunc(NULL, handler->private,towrite,bytes,&towrite); - result = USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char*)bytes,towrite,ptpcam_usb_timeout); + result = USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char*)bytes,towrite,USB_TIMEOUT); #ifdef ENABLE_USB_BULK_DEBUG printf("USB OUT==>\n"); data_dump_ascii (stdout,bytes,towrite,16); @@ -966,9 +1032,13 @@ ptp_usb->callback_active = 0; } if (ptp_usb->current_transfer_callback != NULL) { - (void) ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete, - ptp_usb->current_transfer_total, - ptp_usb->current_transfer_callback_data); + int ret; + ret = ptp_usb->current_transfer_callback(ptp_usb->current_transfer_complete, + ptp_usb->current_transfer_total, + ptp_usb->current_transfer_callback_data); + if (ret != 0) { + return PTP_ERROR_CANCEL; + } } } if (result < towrite) /* short writes happen */ @@ -987,7 +1057,7 @@ printf("USB OUT==>\n"); printf("Zero Write\n"); #endif - result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)"x",0,ptpcam_usb_timeout); + result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)"x",0,USB_TIMEOUT); } } @@ -1121,13 +1191,10 @@ &written ); ptp_exit_send_memory_handler (&memhandler); - if (ret!=PTP_RC_OK) { + if (ret!=PTP_RC_OK && ret!=PTP_ERROR_CANCEL) { ret = PTP_ERROR_IO; -/* ptp_error (params, - "PTP: request code 0x%04x sending req error 0x%04x", - req->Code,ret); */ } - if (written != towrite) { + if (written != towrite && ret != PTP_ERROR_CANCEL && ret != PTP_ERROR_IO) { ptp_error (params, "PTP: request code 0x%04x sending req wrote only %ld bytes instead of %d", req->Code, written, towrite @@ -1176,10 +1243,6 @@ ret = ptp_write_func(wlen, &memhandler, params->data, &written); ptp_exit_send_memory_handler (&memhandler); if (ret!=PTP_RC_OK) { - ret = PTP_ERROR_IO; -/* ptp_error (params, - "PTP: request code 0x%04x sending data error 0x%04x", - ptp->Code,ret);*/ return ret; } if (size <= datawlen) return ret; @@ -1196,7 +1259,7 @@ } bytes_left_to_transfer -= written; } - if (ret!=PTP_RC_OK) + if (ret!=PTP_RC_OK && ret!=PTP_ERROR_CANCEL) ret = PTP_ERROR_IO; return ret; } @@ -1235,6 +1298,7 @@ uint16_t ret; PTPUSBBulkContainer usbdata; unsigned long written; + PTP_USB *ptp_usb = (PTP_USB *) params->data; memset(&usbdata,0,sizeof(usbdata)); do { @@ -1244,14 +1308,29 @@ if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO; break; - } else + } if (dtoh16(usbdata.type)!=PTP_USB_CONTAINER_DATA) { ret = PTP_ERROR_DATA_EXPECTED; break; - } else + } if (dtoh16(usbdata.code)!=ptp->Code) { - ret = dtoh16(usbdata.code); - break; + if (ptp_usb->device_flags & DEVICE_FLAG_IGNORE_HEADER_ERRORS) { + ptp_debug (params, "ptp2/ptp_usb_getdata: detected a broken " + "PTP header, code field insane, expect problems! (But continuing)"); + ret = PTP_RC_OK; + } else { + ret = dtoh16(usbdata.code); + // This filters entirely insane garbage return codes, but still + // makes it possible to return error codes in the code field when + // getting data. It appears Windows ignores the contents of this + // field entirely. + if (ret < PTP_RC_Undefined || ret > PTP_RC_SpecificationOfDestinationUnsupported) { + ptp_debug (params, "ptp2/ptp_usb_getdata: detected a broken " + "PTP header, code field insane."); + ret = PTP_ERROR_IO; + } + break; + } } if (usbdata.length == 0xffffffffU) { /* stuff data directly to passed data handler */ @@ -1266,8 +1345,8 @@ &readdata, 0 ); - if (xret == -1) - return PTP_ERROR_IO; + if (xret != PTP_RC_OK) + return ret; if (readdata < PTP_USB_BULK_HS_MAX_PACKET_LEN_READ) break; } @@ -1295,12 +1374,11 @@ (uint8_t *) &usbdata + packlen, surplen); params->response_packet_size = surplen; /* Ignore reading one extra byte if device flags have been set */ - } else if( !(((PTP_USB *)params->data)->device_flags & - DEVICE_FLAG_NO_ZERO_READS && - rlen - dtoh32(usbdata.length) == 1)) { - ptp_debug (params, "ptp2/ptp_usb_getdata: read %d bytes " - "too much, expect problems!", - rlen - dtoh32(usbdata.length)); + } else if(( !(ptp_usb->device_flags & DEVICE_FLAG_NO_ZERO_READS) && + rlen - dtoh32(usbdata.length) == 1)) { + ptp_debug (params, "ptp2/ptp_usb_getdata: read %d bytes " + "too much, expect problems!", + rlen - dtoh32(usbdata.length)); } rlen = packlen; } @@ -1322,7 +1400,7 @@ &written ); - if (((PTP_USB *)params->data)->device_flags & DEVICE_FLAG_NO_ZERO_READS && + if (ptp_usb->device_flags & DEVICE_FLAG_NO_ZERO_READS && len+PTP_USB_BULK_HDR_LEN == PTP_USB_BULK_HS_MAX_PACKET_LEN_READ) { #ifdef ENABLE_USB_BULK_DEBUG printf("Reading in extra terminating byte\n"); @@ -1330,42 +1408,36 @@ // need to read in extra byte and discard it int result = 0; char byte = 0; - PTP_USB *ptp_usb = (PTP_USB *)params->data; - result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &byte, 1, ptpcam_usb_timeout); + result = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &byte, 1, USB_TIMEOUT); if (result != 1) printf("Could not read in extra byte for PTP_USB_BULK_HS_MAX_PACKET_LEN_READ long file, return value 0x%04x\n", result); } else if (len+PTP_USB_BULK_HDR_LEN == PTP_USB_BULK_HS_MAX_PACKET_LEN_READ && params->split_header_data == 0) { + int zeroresult = 0; + char zerobyte = 0; + #ifdef ENABLE_USB_BULK_DEBUG - printf("Reading in zero packet after header\n"); + printf("Reading in zero packet after header\n"); #endif - int zeroresult = 0; - char zerobyte = 0; - PTP_USB *ptp_usb = (PTP_USB *)params->data; - zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &zerobyte, 0, ptpcam_usb_timeout); - - if (zeroresult != 0) - printf("LIBMTP panic: unable to read in zero packet, response 0x%04x", zeroresult); - } - + zeroresult = USB_BULK_READ(ptp_usb->handle, ptp_usb->inep, &zerobyte, 0, USB_TIMEOUT); + + if (zeroresult != 0) + printf("LIBMTP panic: unable to read in zero packet, response 0x%04x", zeroresult); + } + /* Is that all of data? */ - if (len+PTP_USB_BULK_HDR_LEN<=rlen) break; + if (len+PTP_USB_BULK_HDR_LEN<=rlen) { + break; + } - ret=ptp_read_func(len - (rlen - PTP_USB_BULK_HDR_LEN), - handler, - params->data, &rlen, 1); + ret = ptp_read_func(len - (rlen - PTP_USB_BULK_HDR_LEN), + handler, + params->data, &rlen, 1); if (ret!=PTP_RC_OK) { - ret = PTP_ERROR_IO; break; } } while (0); - /* - if (ret!=PTP_RC_OK) { - ptp_error (params, - "PTP: request code 0x%04x getting data error 0x%04x", - ptp->Code, ret); - }*/ return ret; } @@ -1429,15 +1501,15 @@ ret = PTP_RC_OK; switch(wait) { case PTP_EVENT_CHECK: - result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptpcam_usb_timeout); + result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),USB_TIMEOUT); if (result==0) - result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptpcam_usb_timeout); + result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), USB_TIMEOUT); if (result < 0) ret = PTP_ERROR_IO; break; case PTP_EVENT_CHECK_FAST: - result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptpcam_usb_timeout); + result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),USB_TIMEOUT); if (result==0) - result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptpcam_usb_timeout); + result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), USB_TIMEOUT); if (result < 0) ret = PTP_ERROR_IO; break; default: @@ -1478,6 +1550,21 @@ return ptp_usb_event (params, event, PTP_EVENT_CHECK); } +uint16_t +ptp_usb_control_cancel_request (PTPParams *params, uint32_t transactionid) { + PTP_USB *ptp_usb = (PTP_USB *)(params->data); + int ret; + unsigned char buffer[6]; + + htod16a(&buffer[0],PTP_EC_CancelTransaction); + htod32a(&buffer[2],transactionid); + ret = usb_control_msg(ptp_usb->handle, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x64, 0x0000, 0x0000, (char *) buffer, sizeof(buffer), USB_TIMEOUT); + if (ret < sizeof(buffer)) + return PTP_ERROR_IO; + return PTP_RC_OK; +} static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev) { @@ -1489,6 +1576,7 @@ params->senddata_func=ptp_usb_senddata; params->getresp_func=ptp_usb_getresp; params->getdata_func=ptp_usb_getdata; + params->cancelreq_func=ptp_usb_control_cancel_request; params->data=ptp_usb; params->transaction_id=0; /* @@ -1510,7 +1598,7 @@ * accessible from user space. */ if (ptp_usb->device_flags & DEVICE_FLAG_UNLOAD_DRIVER) { - if (usb_detach_kernel_driver_np(device_handle, dev->config->interface->altsetting->bInterfaceNumber)) { + if (usb_detach_kernel_driver_np(device_handle, (int) ptp_usb->interface)) { // Totally ignore this error! // perror("usb_detach_kernel_driver_np()"); } @@ -1523,11 +1611,10 @@ return -1; } #endif - if (usb_claim_interface(device_handle, dev->config->interface->altsetting->bInterfaceNumber)) { + if (usb_claim_interface(device_handle, (int) ptp_usb->interface)) { perror("usb_claim_interface()"); return -1; } - ptp_usb->interface = dev->config->interface->altsetting->bInterfaceNumber; } return 0; } @@ -1584,16 +1671,21 @@ } } -static void close_usb(PTP_USB* ptp_usb, uint8_t interfaceNumber) +static void close_usb(PTP_USB* ptp_usb) { - // Clear any stalled endpoints - clear_stall(ptp_usb); - // Clear halts on any endpoints - clear_halt(ptp_usb); - // Added to clear some stuff on the OUT endpoint - // TODO: is this good on the Mac too? - usb_resetep(ptp_usb->handle, ptp_usb->outep); - usb_release_interface(ptp_usb->handle, interfaceNumber); + // Commented out since it was confusing some + // devices to do these things. + if (!(ptp_usb->device_flags & DEVICE_FLAG_NO_RELEASE_INTERFACE)) { + // Clear any stalled endpoints + clear_stall(ptp_usb); + // Clear halts on any endpoints + clear_halt(ptp_usb); + // Added to clear some stuff on the OUT endpoint + // TODO: is this good on the Mac too? + // HINT: some devices may need that you comment these two out too. + usb_resetep(ptp_usb->handle, ptp_usb->outep); + usb_release_interface(ptp_usb->handle, (int) ptp_usb->interface); + } // Brutally reset device // TODO: is this good on the Mac too? usb_reset(ptp_usb->handle); @@ -1687,6 +1779,9 @@ return LIBMTP_ERROR_MEMORY_ALLOCATION; } + /* Pointer back to params */ + tmplist->ptp_usb->params = tmplist->params; + /* TODO: Will this always be little endian? */ tmplist->params->byteorder = PTP_DL_LE; tmplist->params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8"); @@ -1703,7 +1798,8 @@ // no_of_ep = device->config->interface->altsetting->bNumEndpoints; /* Assign endpoints to usbinfo... */ - find_endpoints(tmplist->libusb_device, + find_interface_and_endpoints(tmplist->libusb_device, + &tmplist->ptp_usb->interface, &tmplist->ptp_usb->inep, &tmplist->ptp_usb->inep_maxpacket, &tmplist->ptp_usb->outep, @@ -1722,8 +1818,8 @@ * have not used LIBMTP_Release_Device on exit */ if ((ret = ptp_opensession(tmplist->params, 1)) == PTP_ERROR_IO) { - fprintf(stderr, "PTP_ERROR_IO: Trying again after resetting USB\n"); - close_usb(tmplist->ptp_usb, tmplist->libusb_device->config->interface->altsetting->bInterfaceNumber); + fprintf(stderr, "PTP_ERROR_IO: Trying again after re-initializing USB interface\n"); + close_usb(tmplist->ptp_usb); if(init_ptp_usb(tmplist->params, tmplist->ptp_usb, tmplist->libusb_device) <0) { fprintf(stderr, "LIBMTP PANIC: Could not open session on device %d\n", current_device+1); @@ -1746,16 +1842,7 @@ "(Return code %d)\n Try to reset the device.\n", ret); usb_release_interface(tmplist->ptp_usb->handle, - tmplist->libusb_device->config->interface->altsetting->bInterfaceNumber); - return LIBMTP_ERROR_CONNECTING; - } - - /* It is permissible to call this before opening the session */ - if (ptp_getdeviceinfo(tmplist->params, - &tmplist->params->deviceinfo) != PTP_RC_OK) { - fprintf(stderr, "LIBMTP PANIC: Could not get device info!\n"); - usb_release_interface(tmplist->ptp_usb->handle, - tmplist->libusb_device->config->interface->altsetting->bInterfaceNumber); + (int) tmplist->ptp_usb->interface); return LIBMTP_ERROR_CONNECTING; } @@ -1807,16 +1894,6 @@ goto find_usb_devices_error_exit; } - /* Configure interface numbers */ - { - mtpdevice_list_t *tmplist = mtp_device_list; - - while (tmplist != NULL) { - tmplist->interface_number = tmplist->libusb_device->config->interface->altsetting->bInterfaceNumber; - tmplist = tmplist->next; - } - } - /* we're connected to all devices, return the list and OK */ *devlist = mtp_device_list; return LIBMTP_ERROR_NONE; @@ -1830,42 +1907,68 @@ return ret; } -static void find_endpoints(struct usb_device *dev, int* inep, int* inep_maxpacket, int* outep, int *outep_maxpacket, int* intep) +static void find_interface_and_endpoints(struct usb_device *dev, + uint8_t *interface, + int* inep, + int* inep_maxpacket, + int* outep, + int *outep_maxpacket, + int* intep) { - int i,n; - struct usb_endpoint_descriptor *ep; - - ep = dev->config->interface->altsetting->endpoint; - n=dev->config->interface->altsetting->bNumEndpoints; - - for (i=0;idescriptor.bNumConfigurations; i++) { + uint8_t j; + + for (j = 0; j < dev->config[i].bNumInterfaces; j++) { + uint8_t k; + uint8_t no_ep; + struct usb_endpoint_descriptor *ep; + + if (dev->descriptor.bNumConfigurations > 1 || dev->config[i].bNumInterfaces > 1) { + // OK This device has more than one interface, so we have to find out + // which one to use! + // FIXME: Probe the interface. + // FIXME: Release modules attached to all other interfaces in Linux...? + } + + *interface = dev->config[i].interface[j].altsetting->bInterfaceNumber; + ep = dev->config[i].interface[j].altsetting->endpoint; + no_ep = dev->config[i].interface[j].altsetting->bNumEndpoints; + + for (k = 0; k < no_ep; k++) { + if (ep[k].bmAttributes==USB_ENDPOINT_TYPE_BULK) { + if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)== + USB_ENDPOINT_DIR_MASK) + { + *inep=ep[k].bEndpointAddress; + *inep_maxpacket=ep[k].wMaxPacketSize; + } + if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==0) + { + *outep=ep[k].bEndpointAddress; + *outep_maxpacket=ep[k].wMaxPacketSize; + } + } else if (ep[k].bmAttributes==USB_ENDPOINT_TYPE_INTERRUPT){ + if ((ep[k].bEndpointAddress&USB_ENDPOINT_DIR_MASK)== + USB_ENDPOINT_DIR_MASK) + { + *intep=ep[k].bEndpointAddress; + } } + } + // We assigned the endpoints so return here. + return; } } } -void close_device (PTP_USB *ptp_usb, PTPParams *params, uint8_t interfaceNumber) +void close_device (PTP_USB *ptp_usb, PTPParams *params) { if (ptp_closesession(params)!=PTP_RC_OK) fprintf(stderr,"ERROR: Could not close session!\n"); - close_usb(ptp_usb, interfaceNumber); + close_usb(ptp_usb); } static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep) @@ -1873,12 +1976,12 @@ return (usb_control_msg(ptp_usb->handle, USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE, USB_FEATURE_HALT, - ep, NULL, 0, 3000)); + ep, NULL, 0, USB_TIMEOUT)); } static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status) { return (usb_control_msg(ptp_usb->handle, USB_DP_DTH|USB_RECIP_ENDPOINT, USB_REQ_GET_STATUS, - USB_FEATURE_HALT, ep, (char *)status, 2, 3000)); + USB_FEATURE_HALT, ep, (char *)status, 2, USB_TIMEOUT)); } diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libusb-glue.h /tmp/GW9hRDRi14/libmtp-0.2.2/src/libusb-glue.h --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/libusb-glue.h 2007-08-03 10:29:11.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/libusb-glue.h 2007-09-28 19:43:48.000000000 +0100 @@ -49,6 +49,8 @@ * set to 0xFFFFFFFF. Compare to * DEVICE_FLAG_BROKEN_MTPGETOBJECTPROPLIST which only signify * that it's broken when getting metadata for a SINGLE object. + * A typical way the implementation may be broken is that it + * may not return a proper count of the objects. */ #define DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL 0x00000001 /** @@ -98,14 +100,44 @@ * filename extension will be done for files of "unknown" type. */ #define DEVICE_FLAG_IRIVER_OGG_ALZHEIMER 0x00000010 +/** + * This flag indicates a limitation in the filenames a device + * can accept - they must be 7 bit (all chars <= 127/0x7F). + * It was found first on the Philips Shoqbox, and is a deviation + * from the PTP standard which mandates that any unicode chars + * may be used for filenames. I guess this is caused by a 7bit-only + * filesystem being used intrinsically on the device. + */ +#define DEVICE_FLAG_ONLY_7BIT_FILENAMES 0x00000020 +/** + * This flag indicates that the device will lock up if you + * try to get status of endpoints and/or release the interface + * when closing the device. This fixes problems with SanDisk + * Sansa devices especially. It may be a side-effect of a + * Windows behaviour of never releasing interfaces. + */ +#define DEVICE_FLAG_NO_RELEASE_INTERFACE 0x00000040 +/** + * This falg was introduced with the advent of Creative ZEN + * 8GB. The device sometimes return a broken PTP header + * like this: < 1502 0000 0200 01d1 02d1 01d2 > + * the latter 6 bytes (representing "code" and "transaction ID") + * contain junk. This is breaking the PTP/MTP spec but works + * on Windows anyway, probably because the Windows implementation + * does not check that these bytes are valid. To interoperate + * with devices like this, we need this flag to emulate the + * Windows bug. + */ +#define DEVICE_FLAG_IGNORE_HEADER_ERRORS 0x00000080 /** * Internal USB struct. */ typedef struct _PTP_USB PTP_USB; struct _PTP_USB { + PTPParams *params; usb_dev_handle* handle; - int interface; + uint8_t interface; int inep; int inep_maxpacket; int outep; @@ -125,14 +157,13 @@ struct usb_device *libusb_device; PTPParams *params; PTP_USB *ptp_usb; - uint8_t interface_number; struct mtpdevice_list_struct *next; }; typedef struct mtpdevice_list_struct mtpdevice_list_t; int open_device (int busn, int devn, short force, PTP_USB *ptp_usb, PTPParams *params, struct usb_device **dev); void dump_usbinfo(PTP_USB *ptp_usb); -void close_device (PTP_USB *ptp_usb, PTPParams *params, uint8_t interfaceNumber); +void close_device (PTP_USB *ptp_usb, PTPParams *params); LIBMTP_error_number_t find_usb_devices(mtpdevice_list_t **devlist); void free_mtpdevice_list(mtpdevice_list_t *devlist); diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/Makefile.am /tmp/GW9hRDRi14/libmtp-0.2.2/src/Makefile.am --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/Makefile.am 2007-08-07 16:56:04.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/Makefile.am 2007-10-04 23:15:37.000000000 +0100 @@ -31,7 +31,7 @@ # --------------------------------------------------------------------------- CURRENT=6 AGE=0 -REVISION=1 +REVISION=2 SOVERSION=$(CURRENT):$(REVISION):$(AGE) if COMPILE_MINGW32 diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/Makefile.in /tmp/GW9hRDRi14/libmtp-0.2.2/src/Makefile.in --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/Makefile.in 2007-08-07 16:58:21.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/Makefile.in 2007-10-04 23:16:13.000000000 +0100 @@ -227,7 +227,7 @@ # --------------------------------------------------------------------------- CURRENT = 6 AGE = 0 -REVISION = 1 +REVISION = 2 SOVERSION = $(CURRENT):$(REVISION):$(AGE) @COMPILE_MINGW32_TRUE@noinst_DATA = libmtp.lib @COMPILE_MINGW32_TRUE@W32LF = -export-dynamic -no-undefined -export-symbols libmtp.sym diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp.c /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp.c 2007-08-05 22:50:40.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp.c 2007-09-16 22:31:12.000000000 +0100 @@ -2,7 +2,7 @@ * * Copyright (C) 2001-2004 Mariusz Woloszyn * Copyright (C) 2003-2007 Marcus Meissner - * Copyright (C) 2006 Linus Walleij + * Copyright (C) 2006-2007 Linus Walleij * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -147,11 +147,33 @@ /* is there a dataphase? */ switch (flags&PTP_DP_DATA_MASK) { case PTP_DP_SENDDATA: - CHECK_PTP_RC(params->senddata_func(params, ptp, - sendlen, handler)); + { + uint16_t ret; + ret = params->senddata_func(params, ptp, + sendlen, handler); + if (ret == PTP_ERROR_CANCEL) { + ret = params->cancelreq_func(params, + params->transaction_id-1); + if (ret == PTP_RC_OK) + ret = PTP_ERROR_CANCEL; + } + if (ret != PTP_RC_OK) + return ret; + } break; case PTP_DP_GETDATA: - CHECK_PTP_RC(params->getdata_func(params, ptp, handler)); + { + uint16_t ret; + ret = params->getdata_func(params, ptp, handler); + if (ret == PTP_ERROR_CANCEL) { + ret = params->cancelreq_func(params, + params->transaction_id-1); + if (ret == PTP_RC_OK) + ret = PTP_ERROR_CANCEL; + } + if (ret != PTP_RC_OK) + return ret; + } break; case PTP_DP_NODATA: break; @@ -471,14 +493,13 @@ ptp_free_params (PTPParams *params) { int i; - while (params->proplist) { - MTPPropList *xpl = params->proplist; + for (i=0;inrofprops;i++) { + MTPProperties *xpl = ¶ms->props[i]; if ((xpl->datatype == PTP_DTC_STR) && (xpl->propval.str)) free (xpl->propval.str); - params->proplist = xpl->next; - free (xpl); } + if (params->props) free (params->props); if (params->canon_flags) free (params->canon_flags); if (params->cameraname) free (params->cameraname); if (params->wifi_profiles) free (params->wifi_profiles); @@ -2805,7 +2826,7 @@ } uint16_t -ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPPropList **proplist) +ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPProperties **props, int *nrofprops) { uint16_t ret; PTPContainer ptp; @@ -2821,7 +2842,7 @@ ptp.Param5 = 0x00000000U; ptp.Nparam = 5; ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &opldata, &oplsize); - if (ret == PTP_RC_OK) ptp_unpack_OPL(params, opldata, proplist, oplsize); + if (ret == PTP_RC_OK) *nrofprops = ptp_unpack_OPL(params, opldata, props, oplsize); if (opldata != NULL) free(opldata); return ret; @@ -2829,7 +2850,7 @@ uint16_t ptp_mtp_sendobjectproplist (PTPParams* params, uint32_t* store, uint32_t* parenthandle, uint32_t* handle, - uint16_t objecttype, uint64_t objectsize, MTPPropList *proplist) + uint16_t objecttype, uint64_t objectsize, MTPProperties *props, int nrofprops) { uint16_t ret; PTPContainer ptp; @@ -2846,7 +2867,7 @@ ptp.Nparam = 5; /* Set object handle to 0 for a new object */ - oplsize = ptp_pack_OPL(params,proplist,&opldata); + oplsize = ptp_pack_OPL(params,props,nrofprops,&opldata); ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, oplsize, &opldata, NULL); free(opldata); *store = ptp.Param1; @@ -2857,7 +2878,7 @@ } uint16_t -ptp_mtp_setobjectproplist (PTPParams* params, MTPPropList *proplist) +ptp_mtp_setobjectproplist (PTPParams* params, MTPProperties *props, int nrofprops) { uint16_t ret; PTPContainer ptp; @@ -2868,10 +2889,10 @@ ptp.Code = PTP_OC_MTP_SetObjPropList; ptp.Nparam = 0; - oplsize = ptp_pack_OPL(params,proplist,&opldata); + oplsize = ptp_pack_OPL(params,props,nrofprops,&opldata); ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, oplsize, &opldata, NULL); free(opldata); - + return ret; } @@ -3087,7 +3108,7 @@ N_("Exposure Index (film speed ISO)")}, {PTP_DPC_ExposureBiasCompensation, N_("Exposure Bias Compensation")}, - {PTP_DPC_DateTime, N_("Date Time")}, + {PTP_DPC_DateTime, N_("Date & Time")}, {PTP_DPC_CaptureDelay, N_("Pre-Capture Delay")}, {PTP_DPC_StillCaptureMode, N_("Still Capture Mode")}, {PTP_DPC_Contrast, N_("Contrast")}, @@ -3123,7 +3144,7 @@ const char *txt; } ptp_device_properties_Canon[] = { {PTP_DPC_CANON_BeepMode, N_("Beep Mode")}, - {PTP_DPC_CANON_BatteryKind, N_("Battery Kind")}, + {PTP_DPC_CANON_BatteryKind, N_("Battery Type")}, {PTP_DPC_CANON_BatteryStatus, N_("Battery Mode")}, {PTP_DPC_CANON_UILockType, N_("UILockType")}, {PTP_DPC_CANON_CameraMode, N_("Camera Mode")}, @@ -3151,7 +3172,7 @@ {PTP_DPC_CANON_ParameterSet, N_("Parameter Set")}, {PTP_DPC_CANON_ISOSpeed, N_("ISO Speed")}, {PTP_DPC_CANON_Aperture, N_("Aperture")}, - {PTP_DPC_CANON_ShutterSpeed, N_("ShutterSpeed")}, + {PTP_DPC_CANON_ShutterSpeed, N_("Shutter Speed")}, {PTP_DPC_CANON_ExpCompensation, N_("Exposure Compensation")}, {PTP_DPC_CANON_FlashCompensation, N_("Flash Compensation")}, {PTP_DPC_CANON_AEBExposureCompensation, N_("AEB Exposure Compensation")}, @@ -3182,7 +3203,7 @@ {PTP_DPC_CANON_DispAvMax, N_("Disp Av Max")}, {PTP_DPC_CANON_AvMaxApex, N_("Av Max Apex")}, {PTP_DPC_CANON_EZoomStartPosition, N_("EZoom Start Position")}, - {PTP_DPC_CANON_FocalLengthOfTele, N_("Focal Length of Tele")}, + {PTP_DPC_CANON_FocalLengthOfTele, N_("Focal Length Tele")}, {PTP_DPC_CANON_EZoomSizeOfTele, N_("EZoom Size of Tele")}, {PTP_DPC_CANON_PhotoEffect, N_("Photo Effect")}, {PTP_DPC_CANON_AssistLight, N_("Assist Light")}, @@ -3283,7 +3304,7 @@ {PTP_DPC_NIKON_D1ShootingSpeed, /* 0xD068 */ N_("Shooting Speed")}, {PTP_DPC_NIKON_D2MaximumShots, /* 0xD069 */ - N_("Max. Shots")}, + N_("Maximum Shots")}, {PTP_DPC_NIKON_D3ExpDelayMode, /* 0xD06a */ "PTP_DPC_NIKON_D3ExpDelayMode"}, {PTP_DPC_NIKON_LongExposureNoiseReduction, /* 0xD06b */ @@ -3415,7 +3436,7 @@ {PTP_DPC_MTP_SynchronizationPartner, N_("Synchronization Partner")}, {PTP_DPC_MTP_DeviceFriendlyName, - N_("Device Friendly Name")}, + N_("Friendly Device Name")}, {PTP_DPC_MTP_VolumeLevel, N_("Volume Level")}, {PTP_DPC_MTP_DeviceIcon, N_("Device Icon")}, {PTP_DPC_MTP_PlaybackRate, N_("Playback Rate")}, @@ -3423,8 +3444,8 @@ {PTP_DPC_MTP_PlaybackContainerIndex, N_("Playback Container Index")}, {PTP_DPC_MTP_PlaybackPosition, N_("Playback Position")}, - {PTP_DPC_MTP_RevocationInfo, N_("RevocationInfo")}, - {PTP_DPC_MTP_PlaysForSureID, N_("PlaysForSureID")}, + {PTP_DPC_MTP_RevocationInfo, N_("Revocation Info")}, + {PTP_DPC_MTP_PlaysForSureID, N_("PlaysForSure ID")}, {0,NULL} }; @@ -3652,8 +3673,8 @@ {PTP_DPC_NIKON_ToneCompensation, 0, N_("Auto")}, {PTP_DPC_NIKON_ToneCompensation, 1, N_("Normal")}, {PTP_DPC_NIKON_ToneCompensation, 2, N_("Low contrast")}, - {PTP_DPC_NIKON_ToneCompensation, 3, N_("Medium low")}, - {PTP_DPC_NIKON_ToneCompensation, 4, N_("Medium high")}, + {PTP_DPC_NIKON_ToneCompensation, 3, N_("Medium Low")}, + {PTP_DPC_NIKON_ToneCompensation, 4, N_("Medium High")}, {PTP_DPC_NIKON_ToneCompensation, 5, N_("High control")}, {PTP_DPC_NIKON_ToneCompensation, 6, N_("Custom")}, @@ -3769,13 +3790,13 @@ uint16_t ofc; const char *format; } ptp_ofc_mtp_trans[] = { - {PTP_OFC_MTP_MediaCard,N_("MediaCard")}, - {PTP_OFC_MTP_MediaCardGroup,N_("MediaCardGroup")}, + {PTP_OFC_MTP_MediaCard,N_("Media Card")}, + {PTP_OFC_MTP_MediaCardGroup,N_("Media Card Group")}, {PTP_OFC_MTP_Encounter,N_("Encounter")}, - {PTP_OFC_MTP_EncounterBox,N_("EncounterBox")}, + {PTP_OFC_MTP_EncounterBox,N_("Encounter Box")}, {PTP_OFC_MTP_M4A,N_("M4A")}, {PTP_OFC_MTP_Firmware,N_("Firmware")}, - {PTP_OFC_MTP_WindowsImageFormat,N_("WindowsImageFormat")}, + {PTP_OFC_MTP_WindowsImageFormat,N_("Windows Image Format")}, {PTP_OFC_MTP_UndefinedAudio,N_("Undefined Audio")}, {PTP_OFC_MTP_WMA,"WMA"}, {PTP_OFC_MTP_OGG,"OGG"}, @@ -3804,21 +3825,21 @@ {PTP_OFC_MTP_MPLPlaylist,N_("MPL Playlist")}, {PTP_OFC_MTP_ASXPlaylist,N_("ASX Playlist")}, {PTP_OFC_MTP_PLSPlaylist,N_("PLS Playlist")}, - {PTP_OFC_MTP_UndefinedDocument,N_("UndefinedDocument")}, - {PTP_OFC_MTP_AbstractDocument,N_("AbstractDocument")}, + {PTP_OFC_MTP_UndefinedDocument,N_("Undefined Document")}, + {PTP_OFC_MTP_AbstractDocument,N_("Abstract Document")}, {PTP_OFC_MTP_XMLDocument,N_("XMLDocument")}, {PTP_OFC_MTP_MSWordDocument,N_("Microsoft Word Document")}, {PTP_OFC_MTP_MHTCompiledHTMLDocument,N_("MHT Compiled HTML Document")}, {PTP_OFC_MTP_MSExcelSpreadsheetXLS,N_("Microsoft Excel Spreadsheet (.xls)")}, {PTP_OFC_MTP_MSPowerpointPresentationPPT,N_("Microsoft Powerpoint (.ppt)")}, - {PTP_OFC_MTP_UndefinedMessage,N_("UndefinedMessage")}, - {PTP_OFC_MTP_AbstractMessage,N_("AbstractMessage")}, - {PTP_OFC_MTP_UndefinedContact,N_("UndefinedContact")}, - {PTP_OFC_MTP_AbstractContact,N_("AbstractContact")}, + {PTP_OFC_MTP_UndefinedMessage,N_("Undefined Message")}, + {PTP_OFC_MTP_AbstractMessage,N_("Abstract Message")}, + {PTP_OFC_MTP_UndefinedContact,N_("Undefined Contact")}, + {PTP_OFC_MTP_AbstractContact,N_("Abstract Contact")}, {PTP_OFC_MTP_vCard2,N_("vCard2")}, {PTP_OFC_MTP_vCard3,N_("vCard3")}, - {PTP_OFC_MTP_UndefinedCalendarItem,N_("UndefinedCalendarItem")}, - {PTP_OFC_MTP_AbstractCalendarItem,N_("AbstractCalendarItem")}, + {PTP_OFC_MTP_UndefinedCalendarItem,N_("Undefined Calendar Item")}, + {PTP_OFC_MTP_AbstractCalendarItem,N_("Abstract Calendar Item")}, {PTP_OFC_MTP_vCalendar1,N_("vCalendar1")}, {PTP_OFC_MTP_vCalendar2,N_("vCalendar2")}, {PTP_OFC_MTP_UndefinedWindowsExecutable,N_("Undefined Windows Executable")}, @@ -3930,7 +3951,7 @@ /* WMPPD Extensions */ {PTP_OC_MTP_WMPPD_ReportAddedDeletedItems,N_("Report Added/Deleted Items")}, {PTP_OC_MTP_WMPPD_ReportAcquiredItems,N_("Report Acquired Items")}, - {PTP_OC_MTP_WMPPD_PlaylistObjectPref,N_("Get type of playlists that are allowed to be transfered")}, + {PTP_OC_MTP_WMPPD_PlaylistObjectPref,N_("Get transferable playlist types")}, /* WMDRMPD Extensions... these have no identifiers associated with them */ {PTP_OC_MTP_WMDRMPD_SendWMDRMPDAppRequest,N_("Send WMDRM-PD Application Request")}, @@ -3976,7 +3997,7 @@ default:break; } } - return snprintf (txt, spaceleft,_("Unknown(%04x)"), opcode); + return snprintf (txt, spaceleft,_("Unknown (%04x)"), opcode); } diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp.h /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp.h --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp.h 2007-08-03 10:43:31.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp.h 2007-09-15 23:12:15.000000000 +0100 @@ -492,6 +492,7 @@ #define PTP_ERROR_DATA_EXPECTED 0x02FE #define PTP_ERROR_RESP_EXPECTED 0x02FD #define PTP_ERROR_BADPARAM 0x02FC +#define PTP_ERROR_CANCEL 0x02FB /* PTP Event Codes */ @@ -773,14 +774,13 @@ typedef union _PTPPropertyValue PTPPropertyValue; /* Metadata lists for MTP operations */ -struct _MTPPropList { +struct _MTPProperties { uint16_t property; uint16_t datatype; uint32_t ObjectHandle; PTPPropertyValue propval; - struct _MTPPropList *next; }; -typedef struct _MTPPropList MTPPropList; +typedef struct _MTPProperties MTPProperties; struct _PTPPropDescRangeForm { PTPPropertyValue MinimumValue; @@ -1473,6 +1473,8 @@ typedef uint16_t (* PTPIOGetResp) (PTPParams* params, PTPContainer* resp); typedef uint16_t (* PTPIOGetData) (PTPParams* params, PTPContainer* ptp, PTPDataHandler *putter); +typedef uint16_t (* PTPIOCancelReq) (PTPParams* params, uint32_t transaction_id); + /* debug functions */ typedef void (* PTPErrorFunc) (void *data, const char *format, va_list args) #if (__GNUC__ >= 3) @@ -1497,6 +1499,7 @@ PTPIOGetData getdata_func; PTPIOGetResp event_check; PTPIOGetResp event_wait; + PTPIOCancelReq cancelreq_func; /* Custom error and debug function */ PTPErrorFunc error_func; @@ -1514,7 +1517,8 @@ int split_header_data; /* PTP: MTP specific structure. */ - MTPPropList *proplist; + MTPProperties *props; + int nrofprops; /* PTP: internal structures used by ptp driver */ PTPObjectHandles handles; @@ -1561,6 +1565,7 @@ uint16_t ptp_usb_control_get_extended_event_data (PTPParams *params, char *buffer, int *size); uint16_t ptp_usb_control_device_reset_request (PTPParams *params); uint16_t ptp_usb_control_get_device_status (PTPParams *params, char *buffer, int *size); +uint16_t ptp_usb_control_cancel_request (PTPParams *params, uint32_t transid); int ptp_ptpip_connect (PTPParams* params, const char *port); @@ -1636,10 +1641,10 @@ PTPPropertyValue *value, uint16_t datatype); uint16_t ptp_mtp_getobjectreferences (PTPParams* params, uint32_t handle, uint32_t** ohArray, uint32_t* arraylen); uint16_t ptp_mtp_setobjectreferences (PTPParams* params, uint32_t handle, uint32_t* ohArray, uint32_t arraylen); -uint16_t ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPPropList **proplist); +uint16_t ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPProperties **props, int *nrofprops); uint16_t ptp_mtp_sendobjectproplist (PTPParams* params, uint32_t* store, uint32_t* parenthandle, uint32_t* handle, - uint16_t objecttype, uint64_t objectsize, MTPPropList *proplist); -uint16_t ptp_mtp_setobjectproplist (PTPParams* params, MTPPropList *proplist); + uint16_t objecttype, uint64_t objectsize, MTPProperties *props, int nrofprops); +uint16_t ptp_mtp_setobjectproplist (PTPParams* params, MTPProperties *props, int nrofprops); /* Eastman Kodak extensions */ uint16_t ptp_ek_9007 (PTPParams* params, unsigned char **serial, unsigned int *size); diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp-pack.c /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp-pack.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/ptp-pack.c 2007-08-03 10:38:42.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/ptp-pack.c 2007-09-12 20:31:20.000000000 +0100 @@ -190,8 +190,12 @@ uint8_t packed[PTP_MAXSTRLEN*2+3], len; size_t plen; unsigned char *retcopy = NULL; - - ptp_pack_string(params, string, (unsigned char*) packed, 0, &len); + + if (string == NULL) + ptp_pack_string(params, "", (unsigned char*) packed, 0, &len); + else + ptp_pack_string(params, string, (unsigned char*) packed, 0, &len); + /* returned length is in characters, then one byte for string length */ plen = len*2 + 1; @@ -918,10 +922,10 @@ #define MAX_MTP_PROPS 127 static inline uint32_t -ptp_pack_OPL (PTPParams *params, MTPPropList *proplist, unsigned char** opldataptr) +ptp_pack_OPL (PTPParams *params, MTPProperties *props, int nrofprops, unsigned char** opldataptr) { unsigned char* opldata; - MTPPropList *propitr; + MTPProperties *propitr; unsigned char *packedprops[MAX_MTP_PROPS]; uint32_t packedpropslens[MAX_MTP_PROPS]; uint32_t packedobjecthandles[MAX_MTP_PROPS]; @@ -933,8 +937,8 @@ uint32_t i; totalsize = sizeof(uint32_t); /* 4 bytes to store the number of elements */ - propitr = proplist; - while (propitr != NULL && noitems < MAX_MTP_PROPS) { + propitr = props; + while (nrofprops-- && noitems < MAX_MTP_PROPS) { /* Object Handle */ packedobjecthandles[noitems]=propitr->ObjectHandle; totalsize += sizeof(uint32_t); /* Object ID */ @@ -948,7 +952,7 @@ packedpropslens[noitems] = ptp_pack_DPV (params, &propitr->propval, &packedprops[noitems], propitr->datatype); totalsize += packedpropslens[noitems]; noitems ++; - propitr = propitr->next; + propitr ++; } /* Allocate memory for the packed property list */ @@ -975,44 +979,48 @@ return totalsize; } +static int +_compare_func(const void* x, const void *y) { + const MTPProperties *px = x; + const MTPProperties *py = y; + + return px->ObjectHandle - py->ObjectHandle; +} + static inline int -ptp_unpack_OPL (PTPParams *params, unsigned char* data, MTPPropList **proplist, unsigned int len) +ptp_unpack_OPL (PTPParams *params, unsigned char* data, MTPProperties **pprops, unsigned int len) { uint32_t prop_count = dtoh32a(data); - MTPPropList *prop = NULL; + MTPProperties *props = NULL; int offset = 0, i; if (prop_count == 0) { - *proplist = NULL; + *pprops = NULL; return 0; } data += sizeof(uint32_t); - *proplist = malloc(sizeof(MTPPropList)); - prop = *proplist; + props = malloc(prop_count * sizeof(MTPProperties)); + if (!props) return 0; for (i = 0; i < prop_count; i++) { - prop->ObjectHandle = dtoh32a(data); + props[i].ObjectHandle = dtoh32a(data); data += sizeof(uint32_t); len -= sizeof(uint32_t); - prop->property = dtoh16a(data); + props[i].property = dtoh16a(data); data += sizeof(uint16_t); len -= sizeof(uint16_t); - prop->datatype = dtoh16a(data); + props[i].datatype = dtoh16a(data); data += sizeof(uint16_t); len -= sizeof(uint16_t); offset = 0; - ptp_unpack_DPV(params, data, &offset, len, &prop->propval, prop->datatype); + ptp_unpack_DPV(params, data, &offset, len, &props[i].propval, props[i].datatype); data += offset; len -= offset; - - if (i != prop_count - 1) { - prop->next = malloc(sizeof(MTPPropList)); - prop = prop->next; - } else - prop->next = NULL; } + qsort (props, prop_count, sizeof(MTPProperties),_compare_func); + *pprops = props; return prop_count; } diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/unicode.c /tmp/GW9hRDRi14/libmtp-0.2.2/src/unicode.c --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/unicode.c 2007-02-02 22:47:39.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/unicode.c 2007-08-25 19:20:34.000000000 +0100 @@ -94,3 +94,34 @@ } return strdup(loclstr); } + +/** + * This helper function simply removes any consecutive chars + * > 0x7F and replace then with an underscore. In UTF-8 + * consequtive chars > 0x7F represent one single character so + * it has to be done like this (and it's elegant). It will only + * shrink the string in size so no copying is needed. + */ +void strip_7bit_from_utf8(char *str) +{ + int i,j,k; + i = 0; + j = 0; + k = strlen(str); + while (i < k) { + if ((uint8_t) str[i] > 0x7FU) { + str[j] = '_'; + i++; + // Skip over any consequtive > 0x7F chars. + while((uint8_t) str[i] > 0x7FU) { + i++; + } + } else { + str[j] = str[i]; + i++; + } + j++; + } + // Terminate stripped string... + str[j] = '\0'; +} diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/src/unicode.h /tmp/GW9hRDRi14/libmtp-0.2.2/src/unicode.h --- /tmp/KyV3P4PTuG/libmtp-0.2.1/src/unicode.h 2007-02-05 18:22:27.000000000 +0000 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/src/unicode.h 2007-08-24 22:19:27.000000000 +0100 @@ -36,5 +36,6 @@ int ucs2_strlen(uint16_t const * const); char *utf16_to_utf8(LIBMTP_mtpdevice_t*,const uint16_t*); +void strip_7bit_from_utf8(char *str); #endif /* __MTP__UNICODE__H */ diff -Nru /tmp/KyV3P4PTuG/libmtp-0.2.1/TODO /tmp/GW9hRDRi14/libmtp-0.2.2/TODO --- /tmp/KyV3P4PTuG/libmtp-0.2.1/TODO 2007-08-04 19:11:04.000000000 +0100 +++ /tmp/GW9hRDRi14/libmtp-0.2.2/TODO 2007-08-16 08:33:58.000000000 +0100 @@ -18,6 +18,9 @@ We don't know why, it may be related to low-level USB behaviour that is not exposed in the logs we read. +3. COMPATIBILITY: account for different step sizes and intervals on some + numeric properties we set, make the functions round off when possible. + SPEEDUP fixes: 1. SPEED: Cache the supported object properties at first read/startup.