diff options
author | Marcus Meissner <marcus@jet.franken.de> | 2006-04-17 19:39:21 +0000 |
---|---|---|
committer | Marcus Meissner <marcus@jet.franken.de> | 2006-04-17 19:39:21 +0000 |
commit | ec6bbc7d0c34c76b53e57aec49c268f2de1bffce (patch) | |
tree | d2f483a46ae32b7a580f2412abe7c2cdd880f408 | |
parent | b80a50e4bb2cf10e2724c16c3a82299d0b55a3c9 (diff) | |
download | libgphoto2-ec6bbc7d0c34c76b53e57aec49c268f2de1bffce.tar.gz |
added MTP metadata retrieval and setting for MTP devices.
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@8748 67ed7778-7388-44ab-90cf-0a291f65f57c
-rw-r--r-- | camlibs/ptp2/ChangeLog | 2 | ||||
-rw-r--r-- | camlibs/ptp2/library.c | 202 | ||||
-rw-r--r-- | camlibs/ptp2/ptp-pack.c | 71 | ||||
-rw-r--r-- | camlibs/ptp2/ptp.c | 127 | ||||
-rw-r--r-- | camlibs/ptp2/ptp.h | 33 |
5 files changed, 430 insertions, 5 deletions
diff --git a/camlibs/ptp2/ChangeLog b/camlibs/ptp2/ChangeLog index 27601959e..c8e2bdbae 100644 --- a/camlibs/ptp2/ChangeLog +++ b/camlibs/ptp2/ChangeLog @@ -4,6 +4,8 @@ * library.c: Prepare handling Burst mode on the Nikon DSLRs, (I just need to know the Object ID sequence generated). + * library.c: Added METADATA retrieval and setting for MTP devices. + 2006-04-12 Marcus Meissner <marcus@jet.franken.de> * library.c: Added PowerShot A700 as reported. diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c index de0604b53..a5a2f5c0f 100644 --- a/camlibs/ptp2/library.c +++ b/camlibs/ptp2/library.c @@ -4012,6 +4012,20 @@ folder_list_func (CameraFilesystem *fs, const char *folder, CameraList *list, return (GP_OK); } +/* To avoid roundtrips for querying prop desc + * that are uninteresting for us we list all + * that are exposed by PTP anyway (and are r/o). + */ +static unsigned short uninteresting_props [] = { + PTP_OPC_StorageID, + PTP_OPC_ObjectFormat, + PTP_OPC_ProtectionStatus, + PTP_OPC_ObjectSize, + PTP_OPC_AssociationType, + PTP_OPC_AssociationDesc, + PTP_OPC_ParentObject +}; + static int ptp_mtp_render_metadata ( PTPParams *params, uint32_t object_id, uint16_t ofc, CameraFile *file @@ -4024,16 +4038,59 @@ ptp_mtp_render_metadata ( if (ret != PTP_RC_OK) return (GP_ERROR); for (j=0;j<propcnt;j++) { - char propname[256]; - int n; + char propname[256]; + char text[256]; + PTPObjectPropDesc opd; + int i, n; + + for (i=sizeof(uninteresting_props)/sizeof(uninteresting_props[0]);i--;) + if (uninteresting_props[i] == props[j]) + break; + if (i != -1) /* Is uninteresting. */ + continue; n = ptp_render_mtp_propname(props[j], sizeof(propname), propname); gp_file_append (file, "<", 1); gp_file_append (file, propname, n); gp_file_append (file, ">", 1); - /* TODO: Dump content of property into file here. */ - + ret = ptp_mtp_getobjectpropdesc (params, props[j], ofc, &opd); + if (ret != PTP_RC_OK) { + fprintf (stderr," getobjectpropdesc returns 0x%x\n", ret); + } else { + PTPPropertyValue pv; + ret = ptp_mtp_getobjectpropvalue (params, object_id, props[j], &pv, opd.DataType); + if (ret != PTP_RC_OK) { + sprintf (text, "failure to retrieve %x of oid %x, ret %x", props[j], object_id, ret); + } else { + switch (opd.DataType) { + default:sprintf (text, "Unknown type %d", opd.DataType); + break; + case PTP_DTC_STR: + snprintf (text, sizeof(text), "%s", pv.str); + break; + case PTP_DTC_INT32: + sprintf (text, "%d", pv.i32); + break; + case PTP_DTC_INT16: + sprintf (text, "%d", pv.i16); + break; + case PTP_DTC_INT8: + sprintf (text, "%d", pv.i8); + break; + case PTP_DTC_UINT32: + sprintf (text, "%u", pv.u32); + break; + case PTP_DTC_UINT16: + sprintf (text, "%u", pv.u16); + break; + case PTP_DTC_UINT8: + sprintf (text, "%u", pv.u8); + break; + } + } + gp_file_append (file, text, strlen(text)); + } gp_file_append (file, "</", 2); gp_file_append (file, propname, n); gp_file_append (file, ">\n", 2); @@ -4043,6 +4100,109 @@ ptp_mtp_render_metadata ( return (GP_OK); } +/* To avoid roundtrips for querying prop desc if it is R/O + * we list all that are by standard means R/O. + */ +static unsigned short readonly_props [] = { + PTP_OPC_StorageID, + PTP_OPC_ObjectFormat, + PTP_OPC_ProtectionStatus, + PTP_OPC_ObjectSize, + PTP_OPC_AssociationType, + PTP_OPC_AssociationDesc, + PTP_OPC_ParentObject, + PTP_OPC_PersistantUniqueObjectIdentifier, + PTP_OPC_DateAdded, + PTP_OPC_CorruptOrUnplayable, + PTP_OPC_RepresentativeSampleFormat, + PTP_OPC_RepresentativeSampleSize, + PTP_OPC_RepresentativeSampleHeight, + PTP_OPC_RepresentativeSampleWidth, + PTP_OPC_RepresentativeSampleDuration +}; + +static int +ptp_mtp_parse_metadata ( + PTPParams *params, uint32_t object_id, uint16_t ofc, CameraFile *file +) { + uint16_t ret, *props = NULL; + uint32_t propcnt = 0; + char *filedata = NULL; + unsigned long filesize = 0; + int j; + + if (gp_file_get_data_and_size (file, (const char**)&filedata, &filesize) < GP_OK) + return (GP_ERROR); + + ret = ptp_mtp_getobjectpropssupported (params, ofc, &propcnt, &props); + if (ret != PTP_RC_OK) return (GP_ERROR); + + for (j=0;j<propcnt;j++) { + char propname[256],propname2[256]; + char *begin, *end, *content; + PTPObjectPropDesc opd; + int i, n; + PTPPropertyValue pv; + + for (i=sizeof(readonly_props)/sizeof(readonly_props[0]);i--;) + if (readonly_props[i] == props[j]) + break; + if (i != -1) /* Is read/only */ + continue; + n = ptp_render_mtp_propname(props[j], sizeof(propname), propname); + sprintf (propname2, "<%s>", propname); + begin= strstr (filedata, propname2); + if (!begin) continue; + begin += strlen(propname2); + sprintf (propname2, "</%s>", propname); + end = strstr (begin, propname2); + if (!end) continue; + *end = '\0'; + content = strdup(begin); + *end = '<'; + fprintf (stderr, "found tag %s, content %s\n", propname, content); + ret = ptp_mtp_getobjectpropdesc (params, props[j], ofc, &opd); + if (ret != PTP_RC_OK) { + fprintf (stderr," getobjectpropdesc returns 0x%x\n", ret); + free (content); content = NULL; + continue; + } + if (opd.GetSet == 0) /* property is read/only */ + continue; + switch (opd.DataType) { + default:gp_log (GP_LOG_ERROR, "ptp2", "mtp parser: Unknown datatype %d, content %s", opd.DataType, content); + free (content); content = NULL; + continue; + break; + case PTP_DTC_STR: + pv.str = content; + break; + case PTP_DTC_INT32: + sscanf (content, "%d", &pv.i32); + break; + case PTP_DTC_INT16: + sscanf (content, "%hd", &pv.i16); + break; + case PTP_DTC_INT8: + sscanf (content, "%hhd", &pv.i8); + break; + case PTP_DTC_UINT32: + sscanf (content, "%u", &pv.u32); + break; + case PTP_DTC_UINT16: + sscanf (content, "%hu", &pv.u16); + break; + case PTP_DTC_UINT8: + sscanf (content, "%hhu", &pv.u8); + break; + } + ret = ptp_mtp_setobjectpropvalue (params, object_id, props[j], &pv, opd.DataType); + free (content); content = NULL; + } + free(props); + return (GP_OK); +} + static int get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, CameraFileType type, CameraFile *file, void *data, @@ -4151,7 +4311,7 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, if ((params->deviceinfo.VendorExtensionID == PTP_VENDOR_MICROSOFT) && ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported) ) - return ptp_mtp_render_metadata (params,object_id,oi->ObjectFormat,file); + return ptp_mtp_render_metadata (params,params->handles.Handler[object_id],oi->ObjectFormat,file); return (GP_ERROR_NOT_SUPPORTED); default: { unsigned char *ximage = NULL; @@ -4193,6 +4353,7 @@ put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file, unsigned long intsize; uint32_t size; PTPParams* params=&camera->pl->params; + CameraFileType type; ((PTPData *) camera->pl->params.data)->context = context; @@ -4207,6 +4368,37 @@ put_file_func (CameraFilesystem *fs, const char *folder, CameraFile *file, memset(&oi, 0, sizeof (PTPObjectInfo)); gp_file_get_name (file, &filename); + gp_file_get_type (file, &type); + + if (type == GP_FILE_TYPE_METADATA) { + if ((params->deviceinfo.VendorExtensionID==PTP_VENDOR_MICROSOFT) && + ptp_operation_issupported(params,PTP_OC_MTP_GetObjectPropsSupported) + ) { + uint32_t object_id; + int n; + PTPObjectInfo *poi; + + /* compute storage ID value from folder patch */ + folder_to_storage(folder,storage); + + /* Get file number omiting storage pseudofolder */ + find_folder_handle(folder, storage, object_id, data); + object_id = find_child(filename, storage, object_id, camera); + if (object_id ==PTP_HANDLER_SPECIAL) { + gp_context_error (context, _("File '%s/%s' does not exist."), folder, filename); + return (GP_ERROR_BAD_PARAMETERS); + } + if ((n = handle_to_n(object_id, camera))==PTP_HANDLER_SPECIAL) { + gp_context_error (context, _("File '%s/%s' does not exist."), folder, filename); + return (GP_ERROR_BAD_PARAMETERS); + } + poi=¶ms->objectinfo[n]; + return ptp_mtp_parse_metadata (params,object_id,poi->ObjectFormat,file); + } + gp_context_error (context, _("Metadata only supported for MTP devices.")); + return GP_ERROR; + } + gp_file_get_data_and_size (file, (const char **)&object, &intsize); size=(uint32_t)intsize; diff --git a/camlibs/ptp2/ptp-pack.c b/camlibs/ptp2/ptp-pack.c index d024399a9..cf3f636be 100644 --- a/camlibs/ptp2/ptp-pack.c +++ b/camlibs/ptp2/ptp-pack.c @@ -575,12 +575,83 @@ ptp_unpack_DPD (PTPParams *params, unsigned char* data, PTPDevicePropDesc *dpd, } } } +#undef N return 1; outofmemory: ptp_free_devicepropdesc(dpd); return 0; } +/* (MTP) Object Property pack/unpack */ +#define PTP_opd_ObjectPropertyCode 0 +#define PTP_opd_DataType 2 +#define PTP_opd_GetSet 4 +#define PTP_opd_FactoryDefaultValue 5 + +static inline int +ptp_unpack_OPD (PTPParams *params, unsigned char* data, PTPObjectPropDesc *opd, unsigned int opdlen) +{ + int offset=0, ret; + + memset (opd, 0, sizeof(*opd)); + opd->ObjectPropertyCode=dtoh16a(&data[PTP_opd_ObjectPropertyCode]); + opd->DataType=dtoh16a(&data[PTP_opd_DataType]); + opd->GetSet=dtoh8a(&data[PTP_opd_GetSet]); + + offset = PTP_opd_FactoryDefaultValue; + ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FactoryDefaultValue, opd->DataType); + if (!ret) goto outofmemory; + + opd->GroupCode=dtoh32a(&data[offset]); + offset+=sizeof(uint32_t); + + opd->FormFlag=dtoh8a(&data[offset]); + offset+=sizeof(uint8_t); + + switch (opd->FormFlag) { + case PTP_OPFF_Range: + ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.MinimumValue, opd->DataType); + if (!ret) goto outofmemory; + ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.MaximumValue, opd->DataType); + if (!ret) goto outofmemory; + ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Range.StepSize, opd->DataType); + if (!ret) goto outofmemory; + break; + case PTP_OPFF_Enumeration: { + int i; +#define N opd->FORM.Enum.NumberOfValues + N = dtoh16a(&data[offset]); + offset+=sizeof(uint16_t); + opd->FORM.Enum.SupportedValue = malloc(N*sizeof(opd->FORM.Enum.SupportedValue[0])); + if (!opd->FORM.Enum.SupportedValue) + goto outofmemory; + + memset (opd->FORM.Enum.SupportedValue,0 , N*sizeof(opd->FORM.Enum.SupportedValue[0])); + for (i=0;i<N;i++) { + ret = ptp_unpack_DPV (params, data, &offset, opdlen, &opd->FORM.Enum.SupportedValue[i], opd->DataType); + + /* Slightly different handling here. The HP PhotoSmart 120 + * specifies an enumeration with N in wrong endian + * 00 01 instead of 01 00, so we count the enum just until the + * the end of the packet. + */ + if (!ret) { + if (!i) + goto outofmemory; + opd->FORM.Enum.NumberOfValues = i; + break; + } + } +#undef N + } + } + return 1; +outofmemory: + ptp_free_objectpropdesc(opd); + return 0; +} + + static inline uint32_t ptp_pack_DPV (PTPParams *params, PTPPropertyValue* value, unsigned char** dpvptr, uint16_t datatype) { diff --git a/camlibs/ptp2/ptp.c b/camlibs/ptp2/ptp.c index e2f69661a..f5dacf774 100644 --- a/camlibs/ptp2/ptp.c +++ b/camlibs/ptp2/ptp.c @@ -1813,6 +1813,107 @@ ptp_mtp_getobjectpropssupported (PTPParams* params, uint16_t ofc, return ret; } +/** + * ptp_mtp_getobjectpropdesc: + * + * This command gets the object property description. + * + * params: PTPParams* + * uint16_t opc - object property code + * uint16_t ofc - object format code + * + * Return values: Some PTP_RC_* code. + * + **/ +uint16_t +ptp_mtp_getobjectpropdesc ( + PTPParams* params, uint16_t opc, uint16_t ofc, PTPObjectPropDesc *opd +) { + PTPContainer ptp; + uint16_t ret; + unsigned char *data = NULL; + unsigned int size = 0; + + PTP_CNT_INIT(ptp); + ptp.Code=PTP_OC_MTP_GetObjectPropDesc; + ptp.Nparam = 2; + ptp.Param1 = opc; + ptp.Param2 = ofc; + ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size); + if (ret == PTP_RC_OK) + ptp_unpack_OPD (params, data, opd, size); + free(data); + return ret; +} + +/** + * ptp_mtp_getobjectpropvalue: + * + * This command gets the object properties of an object handle. + * + * params: PTPParams* + * uint32_t objectid - object format code + * uint16_t opc - object prop code + * + * Return values: Some PTP_RC_* code. + * + **/ +uint16_t +ptp_mtp_getobjectpropvalue ( + PTPParams* params, uint32_t oid, uint16_t opc, + PTPPropertyValue *value, uint16_t datatype +) { + PTPContainer ptp; + uint16_t ret; + unsigned char *data = NULL; + unsigned int size = 0; + int offset = 0; + + PTP_CNT_INIT(ptp); + ptp.Code=PTP_OC_MTP_GetObjectPropValue; + ptp.Nparam = 2; + ptp.Param1 = oid; + ptp.Param2 = opc; + ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &data, &size); + if (ret == PTP_RC_OK) + ptp_unpack_DPV(params, data, &offset, size, value, datatype); + free(data); + return ret; +} + +/** + * ptp_mtp_setobjectpropvalue: + * + * This command gets the object properties of an object handle. + * + * params: PTPParams* + * uint32_t objectid - object format code + * uint16_t opc - object prop code + * + * Return values: Some PTP_RC_* code. + * + **/ +uint16_t +ptp_mtp_setobjectpropvalue ( + PTPParams* params, uint32_t oid, uint16_t opc, + PTPPropertyValue *value, uint16_t datatype +) { + PTPContainer ptp; + uint16_t ret; + unsigned char *data = NULL; + unsigned int size ; + + PTP_CNT_INIT(ptp); + ptp.Code=PTP_OC_MTP_SetObjectPropValue; + ptp.Nparam = 2; + ptp.Param1 = oid; + ptp.Param2 = opc; + size = ptp_pack_DPV(params, value, &data, datatype); + ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &data, NULL); + free(data); + return ret; +} + /* Non PTP protocol functions */ /* devinfo testing functions */ @@ -1901,6 +2002,32 @@ ptp_free_devicepropdesc(PTPDevicePropDesc* dpd) } } +void +ptp_free_objectpropdesc(PTPObjectPropDesc* opd) +{ + uint16_t i; + + ptp_free_devicepropvalue (opd->DataType, &opd->FactoryDefaultValue); + switch (opd->FormFlag) { + case PTP_OPFF_None: + break; + case PTP_OPFF_Range: + ptp_free_devicepropvalue (opd->DataType, &opd->FORM.Range.MinimumValue); + ptp_free_devicepropvalue (opd->DataType, &opd->FORM.Range.MaximumValue); + ptp_free_devicepropvalue (opd->DataType, &opd->FORM.Range.StepSize); + break; + case PTP_OPFF_Enumeration: + if (opd->FORM.Enum.SupportedValue) { + for (i=0;i<opd->FORM.Enum.NumberOfValues;i++) + ptp_free_devicepropvalue (opd->DataType, opd->FORM.Enum.SupportedValue+i); + free (opd->FORM.Enum.SupportedValue); + } + default: + fprintf (stderr, "Unknown OPFF type %d\n", opd->FormFlag); + break; + } +} + void ptp_perror(PTPParams* params, uint16_t error) { diff --git a/camlibs/ptp2/ptp.h b/camlibs/ptp2/ptp.h index d9497359f..b1e03497c 100644 --- a/camlibs/ptp2/ptp.h +++ b/camlibs/ptp2/ptp.h @@ -584,6 +584,22 @@ struct _PTPDevicePropDesc { }; typedef struct _PTPDevicePropDesc PTPDevicePropDesc; +/* Object Property Describing Dataset (DevicePropDesc) */ + +struct _PTPObjectPropDesc { + uint16_t ObjectPropertyCode; + uint16_t DataType; + uint8_t GetSet; + PTPPropertyValue FactoryDefaultValue; + uint32_t GroupCode; + uint8_t FormFlag; + union { + PTPPropDescEnumForm Enum; + PTPPropDescRangeForm Range; + } FORM; +}; +typedef struct _PTPObjectPropDesc PTPObjectPropDesc; + /* Canon filesystem's folder entry Dataset */ #define PTP_CANON_FilenameBufferLen 13 @@ -945,6 +961,16 @@ typedef struct _PTPEKTextParams PTPEKTextParams; #define PTP_DPFF_Range 0x01 #define PTP_DPFF_Enumeration 0x02 +/* Object Property Codes used by MTP (first 3 are same as DPFF codes) */ +#define PTP_OPFF_None 0x00 +#define PTP_OPFF_Range 0x01 +#define PTP_OPFF_Enumeration 0x02 +#define PTP_OPFF_DateTime 0x03 +#define PTP_OPFF_FixedLengthArray 0x04 +#define PTP_OPFF_RegularExpression 0x05 +#define PTP_OPFF_ByteArray 0x06 +#define PTP_OPFF_LongString 0xFF + /* Device Property GetSet type */ #define PTP_DPGS_Get 0x00 #define PTP_DPGS_GetSet 0x01 @@ -1093,6 +1119,12 @@ uint16_t ptp_getdevicepropvalue (PTPParams* params, uint16_t propcode, uint16_t ptp_setdevicepropvalue (PTPParams* params, uint16_t propcode, PTPPropertyValue* value, uint16_t datatype); +uint16_t ptp_mtp_getobjectpropdesc (PTPParams* params, uint16_t opc, uint16_t ofc, + PTPObjectPropDesc *objectpropertydesc); +uint16_t ptp_mtp_getobjectpropvalue (PTPParams* params, uint32_t oid, uint16_t opc, + PTPPropertyValue *value, uint16_t datatype); +uint16_t ptp_mtp_setobjectpropvalue (PTPParams* params, uint32_t oid, uint16_t opc, + PTPPropertyValue *value, uint16_t datatype); /* Eastman Kodak extensions */ uint16_t ptp_ek_9007 (PTPParams* params, unsigned char **serial, unsigned int *size); @@ -1160,6 +1192,7 @@ int ptp_property_issupported (PTPParams* params, uint16_t property); void ptp_free_devicepropdesc (PTPDevicePropDesc* dpd); void ptp_free_devicepropvalue (uint16_t dt, PTPPropertyValue* dpd); +void ptp_free_objectpropdesc (PTPObjectPropDesc* dpd); void ptp_perror (PTPParams* params, uint16_t error); const char* |