summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Meissner <marcus@jet.franken.de>2006-04-17 19:39:21 +0000
committerMarcus Meissner <marcus@jet.franken.de>2006-04-17 19:39:21 +0000
commitec6bbc7d0c34c76b53e57aec49c268f2de1bffce (patch)
treed2f483a46ae32b7a580f2412abe7c2cdd880f408
parentb80a50e4bb2cf10e2724c16c3a82299d0b55a3c9 (diff)
downloadlibgphoto2-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/ChangeLog2
-rw-r--r--camlibs/ptp2/library.c202
-rw-r--r--camlibs/ptp2/ptp-pack.c71
-rw-r--r--camlibs/ptp2/ptp.c127
-rw-r--r--camlibs/ptp2/ptp.h33
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=&params->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*