aboutsummaryrefslogtreecommitdiff
path: root/src/libmtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmtp.c')
-rw-r--r--src/libmtp.c163
1 files changed, 132 insertions, 31 deletions
diff --git a/src/libmtp.c b/src/libmtp.c
index 7a0ac13..c3d9c3a 100644
--- a/src/libmtp.c
+++ b/src/libmtp.c
@@ -44,6 +44,7 @@
#include "util.h"
#include "mtpz.h"
+int use_mtpz;
#include <stdarg.h>
#include <stdlib.h>
@@ -1876,8 +1877,8 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevi
current_params->error_func = LIBMTP_ptp_error;
/* TODO: Will this always be little endian? */
current_params->byteorder = PTP_DL_LE;
- current_params->cd_locale_to_ucs2 = iconv_open("UCS-2LE", "UTF-8");
- current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UCS-2LE");
+ current_params->cd_locale_to_ucs2 = iconv_open("UTF-16LE", "UTF-8");
+ current_params->cd_ucs2_to_locale = iconv_open("UTF-8", "UTF-16LE");
if(current_params->cd_locale_to_ucs2 == (iconv_t) -1 ||
current_params->cd_ucs2_to_locale == (iconv_t) -1) {
@@ -2221,6 +2222,8 @@ void LIBMTP_Handle_Event(PTPContainer *ptp_event,
case PTP_EC_DevicePropChanged:
LIBMTP_INFO("Received event PTP_EC_DevicePropChanged in session %u\n", session_id);
/* TODO: update device properties */
+ *event = LIBMTP_EVENT_DEVICE_PROPERTY_CHANGED;
+ *out1 = param1;
break;
case PTP_EC_ObjectInfoChanged:
LIBMTP_INFO("Received event PTP_EC_ObjectInfoChanged in session %u\n", session_id);
@@ -3197,7 +3200,7 @@ void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
printf(" None.\n");
} else {
for (i=0;i<params->deviceinfo.EventsSupported_len;i++) {
- printf(" 0x%04x (%s)\n", params->deviceinfo.EventsSupported[i], ptp_strerror(params->deviceinfo.EventsSupported[i], params->deviceinfo.VendorExtensionID));
+ printf(" 0x%04x: %s\n", params->deviceinfo.EventsSupported[i], ptp_get_event_code_name(params, params->deviceinfo.EventsSupported[i]));
}
}
printf("Device Properties Supported:\n");
@@ -4059,6 +4062,12 @@ int LIBMTP_Check_Capability(LIBMTP_mtpdevice_t *device, LIBMTP_devicecap_t cap)
PTP_OC_ANDROID_BeginEditObject) &&
ptp_operation_issupported(device->params,
PTP_OC_ANDROID_EndEditObject));
+ case LIBMTP_DEVICECAP_MoveObject:
+ return ptp_operation_issupported(device->params,
+ PTP_OC_MoveObject);
+ case LIBMTP_DEVICECAP_CopyObject:
+ return ptp_operation_issupported(device->params,
+ PTP_OC_CopyObject);
/*
* Handle other capabilities here, this is also a good place to
* blacklist some advanced operations on specific devices if need
@@ -4254,6 +4263,10 @@ static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
file->parent_id = ob->oi.ParentObject;
file->storage_id = ob->oi.StorageID;
+ if (ob->oi.Filename != NULL) {
+ file->filename = strdup(ob->oi.Filename);
+ }
+
// Set the filetype
file->filetype = map_ptp_type_to_libmtp_type(ob->oi.ObjectFormat);
@@ -4267,7 +4280,7 @@ static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
*/
if (file->filetype == LIBMTP_FILETYPE_UNKNOWN) {
if ((FLAG_IRIVER_OGG_ALZHEIMER(ptp_usb) ||
- FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
+ FLAG_OGG_IS_UNKNOWN(ptp_usb)) &&
has_ogg_extension(file->filename)) {
file->filetype = LIBMTP_FILETYPE_OGG;
}
@@ -4282,9 +4295,6 @@ static LIBMTP_file_t *obj2file(LIBMTP_mtpdevice_t *device, PTPObject *ob)
// 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;
@@ -4500,7 +4510,6 @@ LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device,
uint32_t const parent)
{
PTPParams *params = (PTPParams *) device->params;
- PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
LIBMTP_file_t *retfiles = NULL;
LIBMTP_file_t *curfile = NULL;
PTPObjectHandles currentHandles;
@@ -4516,17 +4525,6 @@ LIBMTP_file_t * LIBMTP_Get_Files_And_Folders(LIBMTP_mtpdevice_t *device,
return NULL;
}
- if (FLAG_BROKEN_GET_OBJECT_PROPVAL(ptp_usb)) {
- // These devices cannot handle the commands needed for
- // Uncached access!
- LIBMTP_ERROR("tried to use %s on an unsupported device, "
- "this command does not work on all devices "
- "due to missing low-level support to read "
- "information on individual tracks\n",
- __func__);
- return NULL;
- }
-
if (storage == 0)
storageid = PTP_GOH_ALL_STORAGE;
else
@@ -5270,26 +5268,28 @@ int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
uint16_t ret;
PTPParams *params = (PTPParams *) device->params;
PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
- PTPObject *ob;
- ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
- if (ret != PTP_RC_OK) {
+ LIBMTP_file_t *mtpfile = LIBMTP_Get_Filemetadata(device, id);
+ if (mtpfile == NULL) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
return -1;
}
- if (ob->oi.ObjectFormat == PTP_OFC_Association) {
+ if (mtpfile->filetype == LIBMTP_FILETYPE_FOLDER) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
return -1;
}
// Callbacks
ptp_usb->callback_active = 1;
- ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
+ ptp_usb->current_transfer_total = mtpfile->filesize +
PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
ptp_usb->current_transfer_complete = 0;
ptp_usb->current_transfer_callback = callback;
ptp_usb->current_transfer_callback_data = data;
+ // Don't need mtpfile anymore
+ LIBMTP_destroy_file_t(mtpfile);
+
ret = ptp_getobject_tofd(params, id, fd);
ptp_usb->callback_active = 0;
@@ -5332,29 +5332,31 @@ int LIBMTP_Get_File_To_Handler(LIBMTP_mtpdevice_t *device,
LIBMTP_progressfunc_t const callback,
void const * const data)
{
- PTPObject *ob;
uint16_t ret;
PTPParams *params = (PTPParams *) device->params;
PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
- ret = ptp_object_want (params, id, PTPOBJECT_OBJECTINFO_LOADED, &ob);
- if (ret != PTP_RC_OK) {
+ LIBMTP_file_t *mtpfile = LIBMTP_Get_Filemetadata(device, id);
+ if (mtpfile == NULL) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Could not get object info.");
return -1;
}
- if (ob->oi.ObjectFormat == PTP_OFC_Association) {
+ if (mtpfile->filetype == LIBMTP_FILETYPE_FOLDER) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Get_File_To_File_Descriptor(): Bad object format.");
return -1;
}
// Callbacks
ptp_usb->callback_active = 1;
- ptp_usb->current_transfer_total = ob->oi.ObjectCompressedSize+
+ ptp_usb->current_transfer_total = mtpfile->filesize +
PTP_USB_BULK_HDR_LEN+sizeof(uint32_t); // Request length, one parameter
ptp_usb->current_transfer_complete = 0;
ptp_usb->current_transfer_callback = callback;
ptp_usb->current_transfer_callback_data = data;
+ // Don't need mtpfile anymore
+ LIBMTP_destroy_file_t(mtpfile);
+
MTPDataHandler mtp_handler;
mtp_handler.getfunc = NULL;
mtp_handler.putfunc = put_func;
@@ -6807,6 +6809,79 @@ int LIBMTP_Delete_Object(LIBMTP_mtpdevice_t *device,
}
/**
+ * The function moves an object from one location on a device to another
+ * location.
+ *
+ * The semantics of moving a folder are not defined in the spec, but it
+ * appears to do the right thing when tested (but devices that implement
+ * this operation are rare).
+ *
+ * Note that moving an object may take a significant amount of time,
+ * particularly if being moved between storages. MTP does not provide
+ * any kind of progress mechanism, so the operation will simply block
+ * for the duration.
+ *
+ * @param device a pointer to the device where the object exists.
+ * @param object_id the object to move.
+ * @param storage_id the id of the destination storage.
+ * @param parent_id the id of the destination parent object (folder).
+ * If the destination is the root of the storage, pass '0'.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Move_Object(LIBMTP_mtpdevice_t *device,
+ uint32_t object_id,
+ uint32_t storage_id,
+ uint32_t parent_id)
+{
+ uint16_t ret;
+ PTPParams *params = (PTPParams *) device->params;
+
+ ret = ptp_moveobject(params, object_id, storage_id, parent_id);
+ if (ret != PTP_RC_OK) {
+ add_ptp_error_to_errorstack(device, ret, "LIBMTP_Move_Object(): could not move object.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * The function copies an object from one location on a device to another
+ * location.
+ *
+ * The semantics of copying a folder are not defined in the spec, but it
+ * appears to do the right thing when tested (but devices that implement
+ * this operation are rare).
+ *
+ * Note that copying an object may take a significant amount of time.
+ * MTP does not provide any kind of progress mechanism, so the operation
+ * will simply block for the duration.
+ *
+ * @param device a pointer to the device where the object exists.
+ * @param object_id the object to copy.
+ * @param storage_id the id of the destination storage.
+ * @param parent_id the id of the destination parent object (folder).
+ * If the destination is the root of the storage, pass '0'.
+ * @return 0 on success, any other value means failure.
+ */
+int LIBMTP_Copy_Object(LIBMTP_mtpdevice_t *device,
+ uint32_t object_id,
+ uint32_t storage_id,
+ uint32_t parent_id)
+{
+ uint16_t ret;
+ PTPParams *params = (PTPParams *) device->params;
+
+ ret = ptp_copyobject(params, object_id, storage_id, parent_id);
+ if (ret != PTP_RC_OK) {
+ add_ptp_error_to_errorstack(device, ret, "LIBMTP_Copy_Object(): could not copy object.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
* Internal function to update an object filename property.
*/
static int set_object_filename(LIBMTP_mtpdevice_t *device,
@@ -9008,8 +9083,34 @@ int LIBMTP_GetPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
uint64_t offset, uint32_t maxbytes,
unsigned char **data, unsigned int *size)
{
- PTPParams *params = (PTPParams *) device->params;
- uint16_t ret;
+ PTPParams *params = (PTPParams *) device->params;
+ uint16_t ret;
+ LIBMTP_file_t *mtpfile = LIBMTP_Get_Filemetadata(device, id);
+
+ /* Some devices do not like reading over the end and hang instead of progressing */
+ if (offset >= mtpfile->filesize) {
+ *size = 0;
+ LIBMTP_destroy_file_t (mtpfile);
+ return 0;
+ }
+ if (offset + maxbytes > mtpfile->filesize) {
+ maxbytes = mtpfile->filesize - offset;
+ }
+ /* The MTP stack of Samsung Galaxy devices has a mysterious bug in
+ * GetPartialObject. When GetPartialObject is invoked to read the
+ * last bytes of a file and the amount of data to read is such that
+ * the last USB packet sent in the reply matches exactly the USB 2.0
+ * packet size, then the Samsung Galaxy device hangs, resulting in a
+ * timeout error.
+ * As a workaround, we read one less byte instead of reaching the
+ * end of the file, forcing the caller to perform an additional read
+ * to get the last byte (i.e. the final read that would fail is
+ * replaced with two partial reads that succeed).
+ */
+ if ((params->device_flags & DEVICE_FLAG_SAMSUNG_OFFSET_BUG) &&
+ (maxbytes % PTP_USB_BULK_HS_MAX_PACKET_LEN_READ) == (PTP_USB_BULK_HS_MAX_PACKET_LEN_READ - PTP_USB_BULK_HDR_LEN)) {
+ maxbytes--;
+ }
if (!ptp_operation_issupported(params, PTP_OC_ANDROID_GetPartialObject64)) {
if (!ptp_operation_issupported(params, PTP_OC_GetPartialObject)) {