aboutsummaryrefslogtreecommitdiff
path: root/src/libmtp.c
diff options
context:
space:
mode:
authorLinus Walleij <triad@df.lth.se>2011-03-03 19:58:26 +0100
committerLinus Walleij <triad@df.lth.se>2011-03-03 19:58:26 +0100
commita8b8889ee3664b2c966a2816c25a9a536bedca64 (patch)
treef7e912d1764d4d1aa50e09b48be7333ee238731c /src/libmtp.c
parentf68f4c8e28e26e286c27a2d0adf7ee662e19d21f (diff)
downloadlibmtp-a8b8889ee3664b2c966a2816c25a9a536bedca64.tar.gz
Code revamps and new interfaces based on a large patch
from Yavor Goulishev <yavor@google.com> for use in an OS X MTP file transfer program for Android. Refactor: - Break out obj2file and reuse in all functions that want to fill in a LIBMTP_file_t from a PTP object. Introduce new interfaces: - LIBMTP_Open_Raw_Device_Uncached() to open an uncached device from a raw device. - LIBMTP_Get_Files_And_Folders() that will only work on uncached devices. Signed-off-by: Yavor Goulishev <yavor@google.com> Signed-off-by: Linus Walleij <triad@df.lth.se>
Diffstat (limited to 'src/libmtp.c')
-rw-r--r--src/libmtp.c417
1 files changed, 239 insertions, 178 deletions
diff --git a/src/libmtp.c b/src/libmtp.c
index 97e92a6..d8808d1 100644
--- a/src/libmtp.c
+++ b/src/libmtp.c
@@ -1,7 +1,7 @@
/**
* \file libmtp.c
*
- * Copyright (C) 2005-2009 Linus Walleij <triad@df.lth.se>
+ * Copyright (C) 2005-2011 Linus Walleij <triad@df.lth.se>
* Copyright (C) 2005-2008 Richard A. Low <richard@wentnet.com>
* Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
* Copyright (C) 2007 Tero Saarni <tero.saarni@gmail.com>
@@ -333,6 +333,7 @@ static int register_filetype(char const * const description, LIBMTP_filetype_t c
static void init_filemap()
{
+ register_filetype("Folder", LIBMTP_FILETYPE_FOLDER, PTP_OFC_Association);
register_filetype("MediaCard", LIBMTP_FILETYPE_MEDIACARD, PTP_OFC_MTP_MediaCard);
register_filetype("RIFF WAVE file", LIBMTP_FILETYPE_WAV, PTP_OFC_WAV);
register_filetype("ISO MPEG-1 Audio Layer 3", LIBMTP_FILETYPE_MP3, PTP_OFC_MP3);
@@ -1766,7 +1767,7 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
* @param rawdevice the raw device to open a "real" device for.
* @return an open device.
*/
-LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
+LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevice)
{
LIBMTP_mtpdevice_t *mtp_device;
uint8_t bs = 0;
@@ -1790,6 +1791,8 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
return NULL;
}
+ // Non-cached by default
+ mtp_device->cached = 0;
/* Create PTP params */
current_params = (PTPParams *) malloc(sizeof(PTPParams));
@@ -1876,7 +1879,7 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_OGG) {
/* This is not unknown anymore, unflag it */
- ptp_usb->rawdevice.device_entry.device_flags &=
+ ptp_usb->rawdevice.device_entry.device_flags &=
~DEVICE_FLAG_OGG_IS_UNKNOWN;
break;
}
@@ -1886,7 +1889,7 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
for (i=0;i<current_params->deviceinfo.ImageFormats_len;i++) {
if (current_params->deviceinfo.ImageFormats[i] == PTP_OFC_MTP_FLAC) {
/* This is not unknown anymore, unflag it */
- ptp_usb->rawdevice.device_entry.device_flags &=
+ ptp_usb->rawdevice.device_entry.device_flags &=
~DEVICE_FLAG_FLAC_IS_UNKNOWN;
break;
}
@@ -1987,13 +1990,25 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
mtp_device->storage = NULL;
}
+
+ return mtp_device;
+}
+
+LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device(LIBMTP_raw_device_t *rawdevice)
+{
+ LIBMTP_mtpdevice_t *mtp_device = LIBMTP_Open_Raw_Device_Uncached(rawdevice);
+
+ if (mtp_device == NULL)
+ return NULL;
+
+ // Set up this device as cached
+ mtp_device->cached = 1;
/*
* 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);
-
return mtp_device;
}
@@ -2463,6 +2478,10 @@ static void flush_handles(LIBMTP_mtpdevice_t *device)
int ret;
uint32_t i;
+ if (!device->cached) {
+ return;
+ }
+
if (params->objects != NULL) {
for (i=0;i<params->nrofobjects;i++)
ptp_free_object (&params->objects[i]);
@@ -2477,6 +2496,7 @@ static void flush_handles(LIBMTP_mtpdevice_t *device)
// 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->nrofobjects == 0) {
@@ -3782,6 +3802,146 @@ void LIBMTP_destroy_file_t(LIBMTP_file_t *file)
}
/**
+ * Helper function that takes one PTP object and creates a
+ * LIBMTP_file_t metadata entry.
+ */
+static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
+{
+ PTPParams *params = (PTPParams *) device->params;
+ PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
+ LIBMTP_file_t *file;
+ int i;
+
+ // Allocate a new file type
+ file = LIBMTP_new_file_t();
+
+ file->parent_id = ob->oi.ParentObject;
+ file->storage_id = ob->oi.StorageID;
+
+ // Set the filetype
+ file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+
+ /*
+ * A special quirk for devices that doesn't quite
+ * remember that some files marked as "unknown" type are
+ * actually OGG or FLAC files. We look at the filename extension
+ * and see if it happens that this was atleast named "ogg" or "flac"
+ * and fall back on this heuristic approach in that case,
+ * for these bugged devices only.
+ */
+ if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
+ if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
+ FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+ has_ogg_extension(file->filename)) {
+ file->filetype = LIBMTP_FILETYPE_OGG;
+ }
+
+ if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) && has_flac_extension(file->filename)) {
+ file->filetype = LIBMTP_FILETYPE_FLAC;
+ }
+ }
+
+ // Set the modification date
+ file->modificationdate = ob->oi.ModificationDate;
+
+ // We only have 32-bit file size here; later we use the PTP_OPC_ObjectSize property
+ file->filesize = ob->oi.ObjectCompressedSize;
+ if (ob->oi.Filename != NULL) {
+ file->filename = strdup(ob->oi.Filename);
+ }
+
+ // This is a unique ID so we can keep track of the file.
+ file->item_id = ob->oid;
+
+ /*
+ * If we have a cached, large set of metadata, then use it!
+ */
+ if (ob->mtpprops) {
+ MTPProperties *prop = ob->mtpprops;
+
+ for (i=0; i < ob->nrofmtpprops; 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.
+ if (device->object_bitsize == 64) {
+ file->filesize = prop->propval.u64;
+ } else {
+ file->filesize = prop->propval.u32;
+ }
+ break;
+ }
+ }
+ } else {
+ uint16_t *props = NULL;
+ uint32_t propcnt = 0;
+ int ret;
+
+ // 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);
+ if (ret != PTP_RC_OK) {
+ add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filemetadata(): call to ptp_mtp_getobjectpropssupported() failed.");
+ // Silently fall through.
+ } else {
+ for (i = 0; i < propcnt; i++) {
+ switch (props[i]) {
+ case PTP_OPC_ObjectSize:
+ 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;
+ }
+ }
+ free(props);
+ }
+ }
+
+ return file;
+}
+
+
+/**
+ * This function retrieves the metadata for a single file off
+ * the device.
+ *
+ * Do not call this function repeatedly! The file handles are linearly
+ * searched O(n) and the call may involve (slow) USB traffic, so use
+ * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
+ * as an efficient data structure such as a hash list.
+ *
+ * Incidentally this function will return metadata for
+ * a folder (association) as well, but this is not a proper use
+ * of it, it is intended for file manipulation, not folder manipulation.
+ *
+ * @param device a pointer to the device to get the file metadata from.
+ * @param fileid the object ID of the file that you want the metadata for.
+ * @return a metadata entry on success or NULL on failure.
+ * @see LIBMTP_Get_Filelisting()
+ */
+LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
+{
+ PTPParams *params = (PTPParams *) device->params;
+ uint16_t ret;
+ PTPObject *ob;
+
+ // Get all the handles if we haven't already done that
+ // (Only on cached devices.)
+ if (device->cached && params->nrofobjects == 0) {
+ flush_handles(device);
+ }
+
+ ret = ptp_object_want(params, fileid, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
+ if (ret != PTP_RC_OK)
+ return NULL;
+
+ return obj2file(device, ob);
+}
+
+/**
* THIS FUNCTION IS DEPRECATED. PLEASE UPDATE YOUR CODE IN ORDER
* NOT TO USE IT.
* @see LIBMTP_Get_Filelisting_With_Callback()
@@ -3842,8 +4002,6 @@ LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
LIBMTP_file_t *retfiles = NULL;
LIBMTP_file_t *curfile = NULL;
PTPParams *params = (PTPParams *) device->params;
- PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
- uint16_t ret;
// Get all the handles if we haven't already done that
if (params->nrofobjects == 0) {
@@ -3852,7 +4010,7 @@ LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
for (i = 0; i < params->nrofobjects; i++) {
LIBMTP_file_t *file;
- PTPObject *ob, *xob;
+ PTPObject *ob;
if (callback != NULL)
callback(i, params->nrofobjects, data);
@@ -3865,93 +4023,10 @@ LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
continue;
}
- // Allocate a new file type
- file = LIBMTP_new_file_t();
-
- file->parent_id = ob->oi.ParentObject;
- file->storage_id = ob->oi.StorageID;
-
- // This is some sort of unique ID so we can keep track of the track.
- file->item_id = ob->oid;
-
- // Set the filetype
- file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
-
- // Set the modification date
- file->modificationdate = ob->oi.ModificationDate;
-
- // Original file-specific properties
- // We only have 32-bit file size here; if we find it, we use the
- // PTP_OPC_ObjectSize property which has 64bit precision.
- file->filesize = ob->oi.ObjectCompressedSize;
- if (ob->oi.Filename != NULL) {
- file->filename = strdup(ob->oi.Filename);
- }
-
- /*
- * A special quirk for devices that doesn't quite
- * remember that some files marked as "unknown" type are
- * actually OGG or FLAC files. We look at the filename extension
- * and see if it happens that this was atleast named "ogg" or "flac"
- * and fall back on this heuristic approach in that case,
- * for these bugged devices only.
- */
- if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
- if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
- FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
- has_ogg_extension(file->filename))
- file->filetype = LIBMTP_FILETYPE_OGG;
- if (FLAG_FLAC_IS_UNKNOWN(ptp_usb) &&
- has_flac_extension(file->filename))
- file->filetype = LIBMTP_FILETYPE_FLAC;
- }
-
- /*
- * If we have a cached, large set of metadata, then use it!
- */
- ret = ptp_object_want (params, ob->oid, PTPOBJECT_MTPPROPLIST_LOADED, &xob);
- if (ob->mtpprops) {
- MTPProperties *prop = ob->mtpprops;
- int i;
-
- for (i=0;i<ob->nrofmtpprops;i++) {
- // 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++;
- }
- } 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, ob->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;i<propcnt;i++) {
- switch (props[i]) {
- case PTP_OPC_ObjectSize:
- 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;
- }
- }
- free(props);
- }
+ // Look up metadata
+ file = obj2file(device, ob);
+ if (file == NULL) {
+ continue;
}
// Add track to a list that will be returned afterwards.
@@ -3971,109 +4046,95 @@ LIBMTP_file_t *LIBMTP_Get_Filelisting_With_Callback(LIBMTP_mtpdevice_t *device,
}
/**
- * This function retrieves the metadata for a single file off
- * the device.
- *
- * Do not call this function repeatedly! The file handles are linearly
- * searched O(n) and the call may involve (slow) USB traffic, so use
- * <code>LIBMTP_Get_Filelisting()</code> and cache the file, preferably
- * as an efficient data structure such as a hash list.
- *
- * Incidentally this function will return metadata for
- * a folder (association) as well, but this is not a proper use
- * of it, it is intended for file manipulation, not folder manipulation.
- *
- * @param device a pointer to the device to get the file metadata from.
- * @param fileid the object ID of the file that you want the metadata for.
- * @return a metadata entry on success or NULL on failure.
- * @see LIBMTP_Get_Filelisting()
+ * This gets a file listing for a certain storage and parent ID
+ * without inspecting the object cache at all, used for uncached
+ * access.
*/
-LIBMTP_file_t *LIBMTP_Get_Filemetadata(LIBMTP_mtpdevice_t *device, uint32_t const fileid)
+static LIBMTP_file_t * get_files_uncached(LIBMTP_mtpdevice_t *device,
+ PTPParams *params,
+ uint32_t storageid,
+ uint32_t parentId,
+ LIBMTP_file_t *retfiles)
{
- uint32_t i = 0;
- PTPParams *params = (PTPParams *) device->params;
- uint16_t ret;
- PTPObject *ob;
- LIBMTP_file_t *file;
+ int i = 0;
+ LIBMTP_file_t *curfile = NULL;
+ PTPObjectHandles currentHandles;
- // Get all the handles if we haven't already done that
- if (params->nrofobjects == 0) {
- flush_handles(device);
+ uint16_t ret = ptp_getobjecthandles(params,
+ storageid,
+ PTP_GOH_ALL_FORMATS,
+ parentId,
+ &currentHandles);
+
+ if (ret != PTP_RC_OK) {
+ add_ptp_error_to_errorstack(device, ret, "get_files_uncached(): could not get object handles.");
+ return NULL;
}
- ret = ptp_object_want (params, fileid, PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_MTPPROPLIST_LOADED, &ob);
- if (ret != PTP_RC_OK)
+ if (currentHandles.Handler == NULL || currentHandles.n == 0)
return NULL;
- // Allocate a new file type
- file = LIBMTP_new_file_t();
+ for (i = 0; i < currentHandles.n; i++) {
+ LIBMTP_file_t *file;
- file->parent_id = ob->oi.ParentObject;
- file->storage_id = ob->oi.StorageID;
+ // Get metadata for one file, if it fails, try next file
+ file = LIBMTP_Get_Filemetadata(device, currentHandles.Handler[i]);
+ if (file == NULL)
+ continue;
- // Set the filetype
- file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
+ // Add track to a list that will be returned afterwards.
+ if (retfiles == NULL) {
+ retfiles = file;
+ curfile = file;
+ } else {
+ curfile->next = file;
+ curfile = file;
+ }
+ }
- // Original file-specific properties
+ free(currentHandles.Handler);
- // We only have 32-bit file size here; later we use the PTP_OPC_ObjectSize property
- file->filesize = ob->oi.ObjectCompressedSize;
- if (ob->oi.Filename != NULL) {
- file->filename = strdup(ob->oi.Filename);
- }
+ return retfiles;
+}
- // This is some sort of unique ID so we can keep track of the file.
- file->item_id = fileid;
+/**
+ * This function retrieves the content of a folder with id - parentId.
+ * The result contains both files and folders.
+ * The device used with this operations must have been opened with
+ * LIBMTP_Open_Raw_Device_Uncached() or it will fail.
+ *
+ * NOTE: the request will always perform I/O with the device.
+ * @param device a pointer to the MTP device to report info from.
+ * @param parent the parent folder id.
+ */
+LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device, uint32_t parent)
+{
+ PTPParams *params = (PTPParams *) device->params;
+ LIBMTP_file_t *retfiles = NULL;
- /*
- * If we have a cached, large set of metadata, then use it!
- */
- if (ob->mtpprops) {
- MTPProperties *prop = ob->mtpprops;
+ if (device->cached) {
+ // This function is only supposed to be used by devices
+ // opened as uncached!
+ LIBMTP_ERROR("tried to use %s on a cached device!\n",
+ __func__);
+ return NULL;
+ }
- for (i=0;i<ob->nrofmtpprops;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.
- if (device->object_bitsize == 64) {
- file->filesize = prop->propval.u64;
- } else {
- file->filesize = prop->propval.u32;
- }
- break;
- }
- }
+ if (device->storage == NULL) {
+ retfiles = get_files_uncached(device, params, PTP_GOH_ALL_STORAGE, parent, retfiles);
} 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);
- if (ret != PTP_RC_OK) {
- add_ptp_error_to_errorstack(device, ret, "LIBMTP_Get_Filemetadata(): call to ptp_mtp_getobjectpropssupported() failed.");
- // Silently fall through.
- } else {
- for (i=0;i<propcnt;i++) {
- switch (props[i]) {
- case PTP_OPC_ObjectSize:
- 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;
- }
- }
- free(props);
+ // Get handles for each storage in turn.
+ LIBMTP_devicestorage_t *storage = device->storage;
+ while(storage != NULL) {
+ retfiles = get_files_uncached(device, params, storage->id, parent, retfiles);
+ storage = storage->next;
}
}
- return file;
+ return retfiles;
}
+
/**
* This creates a new track metadata structure and allocates memory
* for it. Notice that if you add strings to this structure they