| Index: src/libmtp.c
|
| diff --git a/src/libmtp.c b/src/libmtp.c
|
| index b8f856b7bc393a4cc8870162cfdf19bcb44b5c1e..c3df3f53ce3f852c908823ba808ff6dd0961ecee 100644
|
| --- a/src/libmtp.c
|
| +++ b/src/libmtp.c
|
| @@ -116,11 +116,20 @@ typedef struct propertymap_struct {
|
| struct propertymap_struct *next;
|
| } propertymap_t;
|
|
|
| +/*
|
| + * This is a simple container for holding our callback and user_data
|
| + * for parsing onwards to the usb_event_async function.
|
| + */
|
| +typedef struct event_cb_data_struct {
|
| + LIBMTP_event_cb_fn cb;
|
| + void *user_data;
|
| +} event_cb_data_t;
|
| +
|
| // Global variables
|
| // This holds the global filetype mapping table
|
| -static filemap_t *filemap = NULL;
|
| +static filemap_t *g_filemap = NULL;
|
| // This holds the global property mapping table
|
| -static propertymap_t *propertymap = NULL;
|
| +static propertymap_t *g_propertymap = NULL;
|
|
|
| /*
|
| * Forward declarations of local (static) functions.
|
| @@ -144,7 +153,8 @@ static void get_handles_recursively(LIBMTP_mtpdevice_t *device,
|
| 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_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fitsize);
|
| +static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
|
| + uint64_t fitsize);
|
| static int get_storage_freespace(LIBMTP_mtpdevice_t *device,
|
| LIBMTP_devicestorage_t *storage,
|
| uint64_t *freespace);
|
| @@ -211,6 +221,8 @@ static int set_object_filename(LIBMTP_mtpdevice_t *device,
|
| const char **newname);
|
| static char *generate_unique_filename(PTPParams* params, char const * const filename);
|
| static int check_filename_exists(PTPParams* params, char const * const filename);
|
| +static void LIBMTP_Handle_Event(PTPContainer *ptp_event,
|
| + LIBMTP_event_t *event, uint32_t *out1);
|
|
|
| /**
|
| * These are to wrap the get/put handlers to convert from the MTP types to PTP types
|
| @@ -223,7 +235,7 @@ typedef struct _MTPDataHandler {
|
| } MTPDataHandler;
|
|
|
| static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wantlen, unsigned char *data, unsigned long *gotlen);
|
| -static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen);
|
| +static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data);
|
|
|
| /**
|
| * Checks if a filename ends with ".ogg". Used in various
|
| @@ -305,7 +317,7 @@ static int register_filetype(char const * const description, LIBMTP_filetype_t c
|
| filemap_t *new = NULL, *current;
|
|
|
| // Has this LIBMTP filetype been registered before ?
|
| - current = filemap;
|
| + current = g_filemap;
|
| while (current != NULL) {
|
| if(current->id == id) {
|
| break;
|
| @@ -327,10 +339,10 @@ static int register_filetype(char const * const description, LIBMTP_filetype_t c
|
| new->ptp_id = ptp_id;
|
|
|
| // Add the entry to the list
|
| - if(filemap == NULL) {
|
| - filemap = new;
|
| + if(g_filemap == NULL) {
|
| + g_filemap = new;
|
| } else {
|
| - current = filemap;
|
| + current = g_filemap;
|
| while (current->next != NULL ) current=current->next;
|
| current->next = new;
|
| }
|
| @@ -407,7 +419,7 @@ static uint16_t map_libmtp_type_to_ptp_type(LIBMTP_filetype_t intype)
|
| {
|
| filemap_t *current;
|
|
|
| - current = filemap;
|
| + current = g_filemap;
|
|
|
| while (current != NULL) {
|
| if(current->id == intype) {
|
| @@ -430,7 +442,7 @@ static LIBMTP_filetype_t map_ptp_type_to_libmtp_type(uint16_t intype)
|
| {
|
| filemap_t *current;
|
|
|
| - current = filemap;
|
| + current = g_filemap;
|
|
|
| while (current != NULL) {
|
| if(current->ptp_id == intype) {
|
| @@ -476,7 +488,7 @@ static int register_property(char const * const description, LIBMTP_property_t c
|
| propertymap_t *new = NULL, *current;
|
|
|
| // Has this LIBMTP propety been registered before ?
|
| - current = propertymap;
|
| + current = g_propertymap;
|
| while (current != NULL) {
|
| if(current->id == id) {
|
| break;
|
| @@ -498,10 +510,10 @@ static int register_property(char const * const description, LIBMTP_property_t c
|
| new->ptp_id = ptp_id;
|
|
|
| // Add the entry to the list
|
| - if(propertymap == NULL) {
|
| - propertymap = new;
|
| + if(g_propertymap == NULL) {
|
| + g_propertymap = new;
|
| } else {
|
| - current = propertymap;
|
| + current = g_propertymap;
|
| while (current->next != NULL ) current=current->next;
|
| current->next = new;
|
| }
|
| @@ -701,7 +713,7 @@ static uint16_t map_libmtp_property_to_ptp_property(LIBMTP_property_t inproperty
|
| {
|
| propertymap_t *current;
|
|
|
| - current = propertymap;
|
| + current = g_propertymap;
|
|
|
| while (current != NULL) {
|
| if(current->id == inproperty) {
|
| @@ -723,7 +735,7 @@ static LIBMTP_property_t map_ptp_property_to_libmtp_property(uint16_t inproperty
|
| {
|
| propertymap_t *current;
|
|
|
| - current = propertymap;
|
| + current = g_propertymap;
|
|
|
| while (current != NULL) {
|
| if(current->ptp_id == inproperty) {
|
| @@ -796,7 +808,7 @@ char const * LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t intype)
|
| {
|
| filemap_t *current;
|
|
|
| - current = filemap;
|
| + current = g_filemap;
|
|
|
| while (current != NULL) {
|
| if(current->id == intype) {
|
| @@ -819,7 +831,7 @@ char const * LIBMTP_Get_Property_Description(LIBMTP_property_t inproperty)
|
| {
|
| propertymap_t *current;
|
|
|
| - current = propertymap;
|
| + current = g_propertymap;
|
|
|
| while (current != NULL) {
|
| if(current->id == inproperty) {
|
| @@ -1328,13 +1340,14 @@ static char *get_string_from_object(LIBMTP_mtpdevice_t *device, uint32_t const o
|
| {
|
| PTPPropertyValue propval;
|
| char *retstring = NULL;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
| MTPProperties *prop;
|
|
|
| - if ( device == NULL || object_id == 0) {
|
| + if (!device || !object_id)
|
| return NULL;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
|
| if (prop) {
|
| @@ -1371,13 +1384,14 @@ static uint64_t get_u64_from_object(LIBMTP_mtpdevice_t *device,uint32_t const ob
|
| {
|
| PTPPropertyValue propval;
|
| uint64_t retval = value_default;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
| MTPProperties *prop;
|
|
|
| - if ( device == NULL ) {
|
| + if (!device)
|
| return value_default;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
|
| if (prop)
|
| @@ -1410,13 +1424,14 @@ static uint32_t get_u32_from_object(LIBMTP_mtpdevice_t *device,uint32_t const ob
|
| {
|
| PTPPropertyValue propval;
|
| uint32_t retval = value_default;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
| MTPProperties *prop;
|
|
|
| - if ( device == NULL ) {
|
| + if (!device)
|
| return value_default;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| prop = ptp_find_object_prop_in_cache(params, object_id, attribute_id);
|
| if (prop)
|
| @@ -1448,13 +1463,14 @@ static uint16_t get_u16_from_object(LIBMTP_mtpdevice_t *device, uint32_t const o
|
| {
|
| PTPPropertyValue propval;
|
| uint16_t retval = value_default;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
| MTPProperties *prop;
|
|
|
| - if ( device == NULL ) {
|
| + if (!device)
|
| return value_default;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| // This O(n) search should not be used so often, since code
|
| // using the cached properties don't usually call this function.
|
| @@ -1489,13 +1505,14 @@ static uint8_t get_u8_from_object(LIBMTP_mtpdevice_t *device, uint32_t const obj
|
| {
|
| PTPPropertyValue propval;
|
| uint8_t retval = value_default;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
| MTPProperties *prop;
|
|
|
| - if ( device == NULL ) {
|
| + if (!device)
|
| return value_default;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| // This O(n) search should not be used so often, since code
|
| // using the cached properties don't usually call this function.
|
| @@ -1529,12 +1546,13 @@ static int set_object_string(LIBMTP_mtpdevice_t *device, uint32_t const object_i
|
| uint16_t const attribute_id, char const * const string)
|
| {
|
| PTPPropertyValue propval;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
|
|
| - if (device == NULL || string == NULL) {
|
| + if (!device || !string)
|
| return -1;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
|
| add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_string(): could not set object string: "
|
| @@ -1565,12 +1583,13 @@ static int set_object_u32(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
|
| uint16_t const attribute_id, uint32_t const value)
|
| {
|
| PTPPropertyValue propval;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
|
|
| - if (device == NULL) {
|
| + if (!device)
|
| return -1;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
|
| add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u32(): could not set unsigned 32bit integer property: "
|
| @@ -1601,12 +1620,13 @@ static int set_object_u16(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
|
| uint16_t const attribute_id, uint16_t const value)
|
| {
|
| PTPPropertyValue propval;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
|
|
| - if (device == NULL) {
|
| + if (!device)
|
| return 1;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
|
| add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u16(): could not set unsigned 16bit integer property: "
|
| @@ -1636,12 +1656,13 @@ static int set_object_u8(LIBMTP_mtpdevice_t *device, uint32_t const object_id,
|
| uint16_t const attribute_id, uint8_t const value)
|
| {
|
| PTPPropertyValue propval;
|
| - PTPParams *params = (PTPParams *) device->params;
|
| + PTPParams *params;
|
| uint16_t ret;
|
|
|
| - if (device == NULL) {
|
| + if (!device)
|
| return 1;
|
| - }
|
| +
|
| + params = (PTPParams *) device->params;
|
|
|
| if (!ptp_operation_issupported(params,PTP_OC_MTP_SetObjectPropValue)) {
|
| add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "set_object_u8(): could not set unsigned 8bit integer property: "
|
| @@ -1676,6 +1697,7 @@ LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device(void)
|
| }
|
|
|
| if (devices == NULL || numdevs == 0) {
|
| + free(devices);
|
| return NULL;
|
| }
|
|
|
| @@ -1746,12 +1768,12 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
|
| /* descriptors are divided by semicolons */
|
| while (end < strlen(desc)) {
|
| /* Skip past initial whitespace */
|
| - while (desc[start] == ' ' && end < strlen(desc)) {
|
| + while ((end < strlen(desc)) && (desc[start] == ' ' )) {
|
| start++;
|
| end++;
|
| }
|
| /* Detect extension */
|
| - while (desc[end] != ';' && end < strlen(desc))
|
| + while ((end < strlen(desc)) && (desc[end] != ';'))
|
| end++;
|
| if (end < strlen(desc)) {
|
| char *element = strndup(desc + start, end-start);
|
| @@ -1760,7 +1782,7 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
|
| // printf(" Element: \"%s\"\n", element);
|
|
|
| /* Parse for an extension */
|
| - while (element[i] != ':' && i < strlen(element))
|
| + while ((i < strlen(element)) && (element[i] != ':'))
|
| i++;
|
| if (i < strlen(element)) {
|
| char *name = strndup(element, i);
|
| @@ -1768,7 +1790,7 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
|
| // printf(" Extension: \"%s\"\n", name);
|
|
|
| /* Parse for minor/major punctuation mark for this extension */
|
| - while (element[i] != '.' && i < strlen(element))
|
| + while ((i < strlen(element)) && (element[i] != '.'))
|
| i++;
|
| if (i > majstart && i < strlen(element)) {
|
| LIBMTP_device_extension_t *extension;
|
| @@ -1778,6 +1800,8 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
|
| char *minorstr = strndup(element + i + 1, strlen(element) - i - 1);
|
| major = atoi(majorstr);
|
| minor = atoi(minorstr);
|
| + // printf(" Major: \"%s\" (parsed %d) Minor: \"%s\" (parsed %d)\n",
|
| + // majorstr, major, minorstr, minor);
|
| free(majorstr);
|
| free(minorstr);
|
| extension = malloc(sizeof(LIBMTP_device_extension_t));
|
| @@ -1793,8 +1817,6 @@ static void parse_extension_descriptor(LIBMTP_mtpdevice_t *mtpdevice,
|
| tmp = tmp->next;
|
| tmp->next = extension;
|
| }
|
| - // printf(" Major: \"%s\" (parsed %d) Minor: \"%s\" (parsed %d)\n",
|
| - // majorstr, major, minorstr, minor);
|
| } else {
|
| LIBMTP_ERROR("LIBMTP ERROR: couldnt parse extension %s\n",
|
| element);
|
| @@ -1827,7 +1849,6 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevi
|
|
|
| /* Allocate dynamic space for our device */
|
| mtp_device = (LIBMTP_mtpdevice_t *) malloc(sizeof(LIBMTP_mtpdevice_t));
|
| - memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
|
| /* Check if there was a memory allocation error */
|
| if(mtp_device == NULL) {
|
| /* There has been an memory allocation error. We are going to ignore this
|
| @@ -1840,6 +1861,7 @@ LIBMTP_mtpdevice_t *LIBMTP_Open_Raw_Device_Uncached(LIBMTP_raw_device_t *rawdevi
|
|
|
| return NULL;
|
| }
|
| + memset(mtp_device, 0, sizeof(LIBMTP_mtpdevice_t));
|
| // Non-cached by default
|
| mtp_device->cached = 0;
|
|
|
| @@ -2153,27 +2175,27 @@ int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_
|
| PTPParams *params = (PTPParams *) device->params;
|
| PTPContainer ptp_event;
|
| uint16_t ret = ptp_usb_event_wait(params, &ptp_event);
|
| - uint16_t code;
|
| - uint32_t session_id;
|
| - uint32_t transaction_id;
|
| - uint32_t param1;
|
| - uint32_t param2;
|
| - uint32_t param3;
|
|
|
| if (ret != PTP_RC_OK) {
|
| /* Device is closing down or other fatal stuff, exit thread */
|
| return -1;
|
| }
|
| + LIBMTP_Handle_Event(&ptp_event, event, out1);
|
| + return 0;
|
| +}
|
| +
|
| +void LIBMTP_Handle_Event(PTPContainer *ptp_event,
|
| + LIBMTP_event_t *event, uint32_t *out1) {
|
| + uint16_t code;
|
| + uint32_t session_id;
|
| + uint32_t param1;
|
|
|
| *event = LIBMTP_EVENT_NONE;
|
|
|
| /* Process the event */
|
| - code = ptp_event.Code;
|
| - session_id = ptp_event.SessionID;
|
| - transaction_id = ptp_event.Transaction_ID;
|
| - param1 = ptp_event.Param1;
|
| - param2 = ptp_event.Param2;
|
| - param3 = ptp_event.Param3;
|
| + code = ptp_event->Code;
|
| + session_id = ptp_event->SessionID;
|
| + param1 = ptp_event->Param1;
|
|
|
| switch(code) {
|
| case PTP_EC_Undefined:
|
| @@ -2184,9 +2206,13 @@ int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_
|
| break;
|
| case PTP_EC_ObjectAdded:
|
| LIBMTP_INFO("Received event PTP_EC_ObjectAdded in session %u\n", session_id);
|
| + *event = LIBMTP_EVENT_OBJECT_ADDED;
|
| + *out1 = param1;
|
| break;
|
| case PTP_EC_ObjectRemoved:
|
| LIBMTP_INFO("Received event PTP_EC_ObjectRemoved in session %u\n", session_id);
|
| + *event = LIBMTP_EVENT_OBJECT_REMOVED;
|
| + *out1 = param1;
|
| break;
|
| case PTP_EC_StoreAdded:
|
| LIBMTP_INFO("Received event PTP_EC_StoreAdded in session %u\n", session_id);
|
| @@ -2197,6 +2223,8 @@ int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_
|
| case PTP_EC_StoreRemoved:
|
| LIBMTP_INFO("Received event PTP_EC_StoreRemoved in session %u\n", session_id);
|
| /* TODO: rescan storages */
|
| + *event = LIBMTP_EVENT_STORE_REMOVED;
|
| + *out1 = param1;
|
| break;
|
| case PTP_EC_DevicePropChanged:
|
| LIBMTP_INFO("Received event PTP_EC_DevicePropChanged in session %u\n", session_id);
|
| @@ -2233,8 +2261,60 @@ int LIBMTP_Read_Event(LIBMTP_mtpdevice_t *device, LIBMTP_event_t *event, uint32_
|
| LIBMTP_INFO( "Received unknown event in session %u\n", session_id);
|
| break;
|
| }
|
| +}
|
|
|
| - return 0;
|
| +static void LIBMTP_Read_Event_Cb(PTPParams *params, uint16_t ret_code,
|
| + PTPContainer *ptp_event, void *user_data) {
|
| + event_cb_data_t *data = user_data;
|
| + LIBMTP_event_t event = LIBMTP_EVENT_NONE;
|
| + uint32_t param1 = 0;
|
| + int handler_ret;
|
| +
|
| + switch (ret_code) {
|
| + case PTP_RC_OK:
|
| + handler_ret = LIBMTP_HANDLER_RETURN_OK;
|
| + LIBMTP_Handle_Event(ptp_event, &event, ¶m1);
|
| + break;
|
| + case PTP_ERROR_CANCEL:
|
| + handler_ret = LIBMTP_HANDLER_RETURN_CANCEL;
|
| + break;
|
| + default:
|
| + handler_ret = LIBMTP_HANDLER_RETURN_ERROR;
|
| + break;
|
| + }
|
| +
|
| + data->cb(handler_ret, event, param1, data->user_data);
|
| + free(data);
|
| +}
|
| +
|
| +/**
|
| + * This function reads events sent by the device, in a non-blocking manner.
|
| + * The callback function will be called when an event is received, but for the function
|
| + * to make progress, polling must take place, using LIBMTP_Handle_Events_Timeout_Completed.
|
| + *
|
| + * After an event is received, this function should be called again to listen for the next
|
| + * event.
|
| + *
|
| + * For now, this non-blocking mechanism only works with libusb-1.0, and not any of the
|
| + * other usb library backends. Attempting to call this method with another backend will
|
| + * always return an error.
|
| + *
|
| + * @param device a pointer to the MTP device to poll for events.
|
| + * @param cb a callback to be invoked when an event is received.
|
| + * @param user_data arbitrary user data passed to the callback.
|
| + * @return 0 on success, any other value means that the callback was not registered and
|
| + * no event notification will take place.
|
| + */
|
| +int LIBMTP_Read_Event_Async(LIBMTP_mtpdevice_t *device, LIBMTP_event_cb_fn cb, void *user_data) {
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + event_cb_data_t *data = malloc(sizeof(event_cb_data_t));
|
| + uint16_t ret;
|
| +
|
| + data->cb = cb;
|
| + data->user_data = user_data;
|
| +
|
| + ret = ptp_usb_event_async(params, LIBMTP_Read_Event_Cb, data);
|
| + return ret == PTP_RC_OK ? 0 : -1;
|
| }
|
|
|
| /**
|
| @@ -2308,6 +2388,7 @@ LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **device_l
|
| /* Assign linked list of devices */
|
| if (devices == NULL || numdevs == 0) {
|
| *device_list = NULL;
|
| + free(devices);
|
| return LIBMTP_ERROR_NO_DEVICE_ATTACHED;
|
| }
|
|
|
| @@ -2415,6 +2496,8 @@ static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
|
| uint16_t ptp_error,
|
| char const * const error_text)
|
| {
|
| + PTPParams *params = (PTPParams *) device->params;
|
| +
|
| if (device == NULL) {
|
| LIBMTP_ERROR("LIBMTP PANIC: Trying to add PTP error to a NULL device!\n");
|
| return;
|
| @@ -2424,7 +2507,7 @@ static void add_ptp_error_to_errorstack(LIBMTP_mtpdevice_t *device,
|
| outstr[sizeof(outstr)-1] = '\0';
|
| add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
|
|
|
| - snprintf(outstr, sizeof(outstr), "Error %04x: %s", ptp_error, ptp_strerror(ptp_error));
|
| + snprintf(outstr, sizeof(outstr), "Error %04x: %s", ptp_error, ptp_strerror(ptp_error, params->deviceinfo.VendorExtensionID));
|
| outstr[sizeof(outstr)-1] = '\0';
|
| add_error_to_errorstack(device, LIBMTP_ERROR_PTP_LAYER, outstr);
|
| }
|
| @@ -2580,7 +2663,7 @@ static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
|
| prop++;
|
| }
|
| lasthandle = 0xffffffff;
|
| - params->objects = calloc (sizeof(PTPObject),cnt);
|
| + params->objects = calloc (cnt, sizeof(PTPObject));
|
| prop = props;
|
| i = -1;
|
| for (j=0;j<nrofprops;j++) {
|
| @@ -2630,7 +2713,7 @@ static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
|
| newprops = realloc(params->objects[i].mtpprops,
|
| (params->objects[i].nrofmtpprops+1)*sizeof(MTPProperties));
|
| } else {
|
| - newprops = calloc(sizeof(MTPProperties),1);
|
| + newprops = calloc(1,sizeof(MTPProperties));
|
| }
|
| if (!newprops) return 0; /* FIXME: error handling? */
|
| params->objects[i].mtpprops = newprops;
|
| @@ -2646,6 +2729,7 @@ static int get_all_metadata_fast(LIBMTP_mtpdevice_t *device)
|
| /* mark last entry also */
|
| params->objects[i].flags |= PTPOBJECT_OBJECTINFO_LOADED;
|
| params->nrofobjects = i+1;
|
| + free (props);
|
| /* The device might not give the list in linear ascending order */
|
| ptp_objects_sort (params);
|
| return 0;
|
| @@ -2929,7 +3013,8 @@ static int sort_storage_by(LIBMTP_mtpdevice_t *device,int const sortby)
|
| * storage for.
|
| * @param fitsize a file of this file must fit on the device.
|
| */
|
| -static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fitsize)
|
| +static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device,
|
| + uint64_t fitsize)
|
| {
|
| LIBMTP_devicestorage_t *storage;
|
| uint32_t store = 0x00000000; //Should this be 0xffffffffu instead?
|
| @@ -2982,6 +3067,32 @@ static uint32_t get_writeable_storageid(LIBMTP_mtpdevice_t *device, uint64_t fit
|
| }
|
|
|
| /**
|
| + * Tries to suggest a storage_id of a given ID when we have a parent
|
| + * @param device a pointer to the device where to search for the storage ID
|
| + * @param fitsize a file of this file must fit on the device.
|
| + * @param parent_id look for this ID
|
| + * @ret storageID
|
| + */
|
| +static int get_suggested_storage_id(LIBMTP_mtpdevice_t *device,
|
| + uint64_t fitsize,
|
| + uint32_t parent_id)
|
| +{
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + PTPObject *ob;
|
| + uint16_t ret;
|
| +
|
| + ret = ptp_object_want(params, parent_id, PTPOBJECT_MTPPROPLIST_LOADED, &ob);
|
| + if ((ret != PTP_RC_OK) || (ob->oi.StorageID == 0)) {
|
| + add_ptp_error_to_errorstack(device, ret, "get_suggested_storage_id(): "
|
| + "could not get storage id from parent id.");
|
| + return get_writeable_storageid(device, fitsize);
|
| + } else {
|
| + /* OK we know the parent storage, then use that */
|
| + return ob->oi.StorageID;
|
| + }
|
| +}
|
| +
|
| +/**
|
| * 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
|
| @@ -3070,7 +3181,7 @@ void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t *device)
|
| for (i=0;i<params->deviceinfo.OperationsSupported_len;i++) {
|
| char txt[256];
|
|
|
| - (void) ptp_render_opcode(params, params->deviceinfo.OperationsSupported[i],
|
| + (void) ptp_render_ofc(params, params->deviceinfo.OperationsSupported[i],
|
| sizeof(txt), txt);
|
| printf(" %04x: %s\n", params->deviceinfo.OperationsSupported[i], txt);
|
| }
|
| @@ -3915,6 +4026,45 @@ int LIBMTP_Get_Supported_Filetypes(LIBMTP_mtpdevice_t *device, uint16_t ** const
|
| }
|
|
|
| /**
|
| + * This function checks if the device has some specific capabilities, in
|
| + * order to avoid calling APIs that may disturb the device.
|
| + *
|
| + * @param device a pointer to the device to check the capability on.
|
| + * @param cap the capability to check.
|
| + * @return 0 if not supported, any other value means the device has the
|
| + * requested capability.
|
| + */
|
| +int LIBMTP_Check_Capability(LIBMTP_mtpdevice_t *device, LIBMTP_devicecap_t cap)
|
| +{
|
| + switch (cap) {
|
| + case LIBMTP_DEVICECAP_GetPartialObject:
|
| + return (ptp_operation_issupported(device->params,
|
| + PTP_OC_GetPartialObject) ||
|
| + ptp_operation_issupported(device->params,
|
| + PTP_OC_ANDROID_GetPartialObject64));
|
| + case LIBMTP_DEVICECAP_SendPartialObject:
|
| + return ptp_operation_issupported(device->params,
|
| + PTP_OC_ANDROID_SendPartialObject);
|
| + case LIBMTP_DEVICECAP_EditObjects:
|
| + return (ptp_operation_issupported(device->params,
|
| + PTP_OC_ANDROID_TruncateObject) &&
|
| + ptp_operation_issupported(device->params,
|
| + PTP_OC_ANDROID_BeginEditObject) &&
|
| + ptp_operation_issupported(device->params,
|
| + PTP_OC_ANDROID_EndEditObject));
|
| + /*
|
| + * Handle other capabilities here, this is also a good place to
|
| + * blacklist some advanced operations on specific devices if need
|
| + * be.
|
| + */
|
| +
|
| + default:
|
| + break;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/**
|
| * This function updates all the storage id's of a device and their
|
| * properties, then creates a linked list and puts the list head into
|
| * the device struct. It also optionally sorts this list. If you want
|
| @@ -5124,16 +5274,19 @@ static uint16_t get_func_wrapper(PTPParams* params, void* priv, unsigned long wa
|
| * This is a manual conversion from MTPDataPutFunc to PTPDataPutFunc
|
| * to isolate the internal type.
|
| */
|
| -static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data, unsigned long *putlen)
|
| +static uint16_t put_func_wrapper(PTPParams* params, void* priv, unsigned long sendlen, unsigned char *data)
|
| {
|
| MTPDataHandler *handler = (MTPDataHandler *)priv;
|
| uint16_t ret;
|
| uint32_t local_putlen = 0;
|
| +
|
| ret = handler->putfunc(params, handler->priv, sendlen, data, &local_putlen);
|
| - *putlen = local_putlen;
|
| +
|
| switch (ret)
|
| {
|
| case LIBMTP_HANDLER_RETURN_OK:
|
| + if (local_putlen != sendlen)
|
| + return PTP_ERROR_IO;
|
| return PTP_RC_OK;
|
| case LIBMTP_HANDLER_RETURN_ERROR:
|
| return PTP_ERROR_IO;
|
| @@ -6089,12 +6242,12 @@ static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *file
|
| return -1;
|
| }
|
| #endif
|
| -
|
| if (filedata->storage_id != 0) {
|
| store = filedata->storage_id;
|
| } else {
|
| - store = get_writeable_storageid(device, filedata->filesize);
|
| + store = get_suggested_storage_id(device, filedata->filesize, localph);
|
| }
|
| +
|
| // Detect if something non-primary is in use.
|
| storage = device->storage;
|
| if (storage != NULL && store != storage->id) {
|
| @@ -6309,8 +6462,12 @@ static int send_file_object_info(LIBMTP_mtpdevice_t *device, LIBMTP_file_t *file
|
| if (FLAG_ONLY_7BIT_FILENAMES(ptp_usb)) {
|
| strip_7bit_from_utf8(new_file.Filename);
|
| }
|
| - // We lose precision here.
|
| - new_file.ObjectCompressedSize = (uint32_t) filedata->filesize;
|
| + if (filedata->filesize > 0xFFFFFFFFL) {
|
| + // This is a kludge in the MTP standard for large files.
|
| + new_file.ObjectCompressedSize = (uint32_t) 0xFFFFFFFF;
|
| + } else {
|
| + new_file.ObjectCompressedSize = (uint32_t) filedata->filesize;
|
| + }
|
| new_file.ObjectFormat = of;
|
| new_file.StorageID = store;
|
| new_file.ParentObject = localph;
|
| @@ -7314,7 +7471,7 @@ LIBMTP_folder_t *LIBMTP_Get_Folder_List(LIBMTP_mtpdevice_t *device)
|
| * if the device does not support all the characters in the
|
| * name.
|
| * @param parent_id id of parent folder to add the new folder to,
|
| - * or 0 to put it in the root directory.
|
| + * or 0xFFFFFFFF to put it in the root directory.
|
| * @param storage_id id of the storage to add this new folder to.
|
| * notice that you cannot mismatch storage id and parent id:
|
| * they must both be on the same storage! Pass in 0 if you
|
| @@ -7334,7 +7491,7 @@ uint32_t LIBMTP_Create_Folder(LIBMTP_mtpdevice_t *device, char *name,
|
|
|
| if (storage_id == 0) {
|
| // I'm just guessing that a folder may require 512 bytes
|
| - store = get_writeable_storageid(device, 512);
|
| + store = get_suggested_storage_id(device, 512, parent_id);
|
| } else {
|
| store = storage_id;
|
| }
|
| @@ -7641,7 +7798,7 @@ static int create_new_abstract_list(LIBMTP_mtpdevice_t *device,
|
|
|
| if (storageid == 0) {
|
| // I'm just guessing that an abstract list may require 512 bytes
|
| - store = get_writeable_storageid(device, 512);
|
| + store = get_suggested_storage_id(device, 512, localph);
|
| } else {
|
| store = storageid;
|
| }
|
| @@ -8083,6 +8240,7 @@ static int update_abstract_list(LIBMTP_mtpdevice_t *device,
|
| add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "update_abstract_list(): "
|
| "could not set artist name.");
|
| }
|
| + break;
|
| case PTP_OPC_Composer:
|
| // Update composer
|
| ret = set_object_string(device, objecthandle, PTP_OPC_Composer, composer);
|
| @@ -8112,6 +8270,7 @@ static int update_abstract_list(LIBMTP_mtpdevice_t *device,
|
| }
|
| free(tmpdate);
|
| }
|
| + break;
|
| default:
|
| break;
|
| }
|
| @@ -8961,6 +9120,114 @@ int LIBMTP_Get_Thumbnail(LIBMTP_mtpdevice_t *device, uint32_t const id,
|
| return -1;
|
| }
|
|
|
| +
|
| +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;
|
| +
|
| + if (!ptp_operation_issupported(params, PTP_OC_ANDROID_GetPartialObject64)) {
|
| + if (!ptp_operation_issupported(params, PTP_OC_GetPartialObject)) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_GetPartialObject: PTP_OC_GetPartialObject not supported");
|
| + return -1;
|
| + }
|
| +
|
| + if (offset >> 32 != 0) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_GetPartialObject: PTP_OC_GetPartialObject only supports 32bit offsets");
|
| + return -1;
|
| + }
|
| +
|
| + ret = ptp_getpartialobject(params, id, (uint32_t)offset, maxbytes, data, size);
|
| + } else {
|
| + ret = ptp_android_getpartialobject64(params, id, offset, maxbytes, data, size);
|
| + }
|
| + if (ret == PTP_RC_OK)
|
| + return 0;
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +int LIBMTP_SendPartialObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
|
| + uint64_t offset, unsigned char *data, unsigned int size)
|
| +{
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + uint16_t ret;
|
| +
|
| + if (!ptp_operation_issupported(params, PTP_OC_ANDROID_SendPartialObject)) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_SendPartialObject: PTP_OC_ANDROID_SendPartialObject not supported");
|
| + return -1;
|
| + }
|
| +
|
| + ret = ptp_android_sendpartialobject(params, id, offset, data, size);
|
| + if (ret == PTP_RC_OK)
|
| + return 0;
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +int LIBMTP_BeginEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
|
| +{
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + uint16_t ret;
|
| +
|
| + if (!ptp_operation_issupported(params, PTP_OC_ANDROID_BeginEditObject)) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_BeginEditObject: PTP_OC_ANDROID_BeginEditObject not supported");
|
| + return -1;
|
| + }
|
| +
|
| + ret = ptp_android_begineditobject(params, id);
|
| + if (ret == PTP_RC_OK)
|
| + return 0;
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +int LIBMTP_EndEditObject(LIBMTP_mtpdevice_t *device, uint32_t const id)
|
| +{
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + uint16_t ret;
|
| +
|
| + if (!ptp_operation_issupported(params, PTP_OC_ANDROID_EndEditObject)) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_EndEditObject: PTP_OC_ANDROID_EndEditObject not supported");
|
| + return -1;
|
| + }
|
| +
|
| + ret = ptp_android_endeditobject(params, id);
|
| + if (ret == PTP_RC_OK) {
|
| + // update cached object properties if metadata cache exists
|
| + update_metadata_cache(device, id);
|
| + return 0;
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +
|
| +int LIBMTP_TruncateObject(LIBMTP_mtpdevice_t *device, uint32_t const id,
|
| + uint64_t offset)
|
| +{
|
| + PTPParams *params = (PTPParams *) device->params;
|
| + uint16_t ret;
|
| +
|
| + if (!ptp_operation_issupported(params, PTP_OC_ANDROID_TruncateObject)) {
|
| + add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
|
| + "LIBMTP_TruncateObject: PTP_OC_ANDROID_TruncateObject not supported");
|
| + return -1;
|
| + }
|
| +
|
| + ret = ptp_android_truncate(params, id, offset);
|
| + if (ret == PTP_RC_OK)
|
| + return 0;
|
| + return -1;
|
| +}
|
| +
|
| +
|
| /**
|
| * Get thumbnail format of a file.
|
| * @param device a pointer to the device to get thumbnail format of.
|
|
|