我这里采用了1.1.7的libmtp。但其实计算机上实现这个协议的服务还有很多,而且还不一样。但理论上原理一样。
发送文件的流程
继续上一篇的逻辑。我在example里发现发送文件的协议逻辑。
int sendfile_function(char * from_path, char *to_path)
{
filesize = sb.st_size;
filename = basename(from_path);
parent_id = parse_path (to_path,files,folders);
LIBMTP_file_t *genfile;
genfile = LIBMTP_new_file_t();
genfile->filesize = filesize;
genfile->filename = strdup(filename);
genfile->filetype = find_filetype (filename);
genfile->parent_id = parent_id;
genfile->storage_id = 0;
printf("Sending file...\n");
ret = LIBMTP_Send_File_From_File(device, from_path, genfile, progress, NULL);
int LIBMTP_Send_File_From_File(LIBMTP_mtpdevice_t *device,
char const * const path, LIBMTP_file_t * const filedata,
LIBMTP_progressfunc_t const callback,
void const * const data)
{
int fd;
int ret;
if ( (fd = open(path, O_RDONLY)) == -1) {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL, "LIBMTP_Send_File_From_File(): Could not open source file.");
return -1;
}
ret = LIBMTP_Send_File_From_File_Descriptor(device, fd, filedata, callback, data);
int LIBMTP_Send_File_From_File_Descriptor(LIBMTP_mtpdevice_t *device,
int const fd, LIBMTP_file_t * const filedata,
LIBMTP_progressfunc_t const callback,
void const * const data)
{
uint16_t ret;
PTPParams *params = (PTPParams *) device->params;
PTP_USB *ptp_usb = (PTP_USB*) device->usbinfo;
LIBMTP_file_t *newfilemeta;
int oldtimeout;
int timeout;
if (send_file_object_info(device, filedata)) //发送文件信息
{
// no need to output an error since send_file_object_info will already have done so
return -1;
}
// Callbacks
ptp_usb->callback_active = 1;
// The callback will deactivate itself after this amount of data has been sent
// One BULK header for the request, one for the data phase. No parameters to the request.
ptp_usb->current_transfer_total = filedata->filesize+PTP_USB_BULK_HDR_LEN*2;
ptp_usb->current_transfer_complete = 0;
ptp_usb->current_transfer_callback = callback;
ptp_usb->current_transfer_callback_data = data;
/*
* We might need to increase the timeout here, files can be pretty
* large. Take the default timeout and add the calculated time for
* this transfer
*/
get_usb_device_timeout(ptp_usb, &oldtimeout);
timeout = oldtimeout +
(ptp_usb->current_transfer_total / guess_usb_speed(ptp_usb)) * 1000;
set_usb_device_timeout(ptp_usb, timeout);
ret = ptp_sendobject_fromfd(params, fd, filedata->filesize); //发送文件
ptp_usb->callback_active = 0;
ptp_usb->current_transfer_callback = NULL;
ptp_usb->current_transfer_callback_data = NULL;
set_usb_device_timeout(ptp_usb, oldtimeout);
if (ret == PTP_ERROR_CANCEL) {
add_error_to_errorstack(device, LIBMTP_ERROR_CANCELLED, "LIBMTP_Send_File_From_File_Descriptor(): Cancelled transfer.");
return -1;
}
if (ret != PTP_RC_OK) {
add_ptp_error_to_errorstack(device, ret, "LIBMTP_Send_File_From_File_Descriptor(): "
"Could not send object.");
return -1;
}
add_object_to_cache(device, filedata->item_id);
/*
* Get the device-assigned parent_id from the cache.
* The operation that adds it to the cache will
* look it up from the device, so we get the new
* parent_id from the cache.
*/
newfilemeta = LIBMTP_Get_Filemetadata(device, filedata->item_id);
if (newfilemeta != NULL) {
filedata->parent_id = newfilemeta->parent_id;
filedata->storage_id = newfilemeta->storage_id;
LIBMTP_destroy_file_t(newfilemeta);
} else {
add_error_to_errorstack(device, LIBMTP_ERROR_GENERAL,
"LIBMTP_Send_File_From_File_Descriptor(): "
"Could not retrieve updated metadata.");
return -1;
}
return 0;
}
再往下send_file_object_info就是发送的具体协议了。
/*
* MTP enhanched does it this way (from a sniff):
* -> PTP_OC_MTP_SendObjectPropList (0x9808):
* 20 00 00 00 01 00 08 98 1B 00 00 00 01 00 01 00
* FF FF FF FF 00 30 00 00 00 00 00 00 12 5E 00 00
* Length: 0x00000020
* Type: 0x0001 PTP_USB_CONTAINER_COMMAND
* Code: 0x9808
* Transaction ID: 0x0000001B
* Param1: 0x00010001 <- store
* Param2: 0xffffffff <- parent handle (-1 ?)
* Param3: 0x00003000 <- file type PTP_OFC_Undefined - we don't know about PDF files
* Param4: 0x00000000 <- file length MSB (-0x0c header len)
* Param5: 0x00005e12 <- file length LSB (-0x0c header len)
*
* -> PTP_OC_MTP_SendObjectPropList (0x9808):
* 46 00 00 00 02 00 08 98 1B 00 00 00 03 00 00 00
* 00 00 00 00 07 DC FF FF 0D 4B 00 53 00 30 00 36 - dc07 = file name
* 00 30 00 33 00 30 00 36 00 2E 00 70 00 64 00 66
* 00 00 00 00 00 00 00 03 DC 04 00 00 00 00 00 00 - dc03 = protection status
* 00 4F DC 02 00 01 - dc4f = non consumable
* Length: 0x00000046
* Type: 0x0002 PTP_USB_CONTAINER_DATA
* Code: 0x9808
* Transaction ID: 0x0000001B
* Metadata....
* 0x00000003 <- Number of metadata items
* 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
* 0xdc07 <- metadata type: file name
* 0xffff <- metadata type: string
* 0x0d <- number of (uint16_t) characters
* 4b 53 30 36 30 33 30 36 2e 50 64 66 00 "KS060306.pdf", null terminated
* 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
* 0xdc03 <- metadata type: protection status
* 0x0004 <- metadata type: uint16_t
* 0x0000 <- not protected
* 0x00000000 <- Object handle, set to 0x00000000 since it is unknown!
* 0xdc4f <- non consumable
* 0x0002 <- metadata type: uint8_t
* 0x01 <- non-consumable (this device cannot display PDF)
*
* <- Read 0x18 bytes back
* 18 00 00 00 03 00 01 20 1B 00 00 00 01 00 01 00
* 00 00 00 00 01 40 00 00
* Length: 0x000000018
* Type: 0x0003 PTP_USB_CONTAINER_RESPONSE
* Code: 0x2001 PTP_OK
* Transaction ID: 0x0000001B
* Param1: 0x00010001 <- store
* Param2: 0x00000000 <- parent handle
* Param3: 0x00004001 <- new file/object ID
*
* -> PTP_OC_SendObject (0x100d)
* 0C 00 00 00 01 00 0D 10 1C 00 00 00
* -> ... all the bytes ...
* <- Read 0x0c bytes back
* 0C 00 00 00 03 00 01 20 1C 00 00 00
* ... Then update metadata one-by one, actually (instead of sending it first!) ...
*/
/**
* ptp_sendobjectinfo:
* params: PTPParams*
* uint32_t* store - destination StorageID on Responder
* uint32_t* parenthandle - Parent ObjectHandle on responder
* uint32_t* handle - see Return values
* PTPObjectInfo* objectinfo- ObjectInfo that is to be sent
*
* Sends ObjectInfo of file that is to be sent via SendFileObject.
*
* Return values: Some PTP_RC_* code.
* Upon success : uint32_t* store - Responder StorageID in which
* object will be stored
* uint32_t* parenthandle- Responder Parent ObjectHandle
* in which the object will be stored
* uint32_t* handle - Responder's reserved ObjectHandle
* for the incoming object
**/
uint16_t
ptp_sendobjectinfo (PTPParams* params, uint32_t* store,
uint32_t* parenthandle, uint32_t* handle,
PTPObjectInfo* objectinfo)
{
uint16_t ret;
PTPContainer ptp;
unsigned char* oidata=NULL;
uint32_t size;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_SendObjectInfo;
ptp.Param1=*store;
ptp.Param2=*parenthandle;
ptp.Nparam=2;
size=ptp_pack_OI(params, objectinfo, &oidata);
ret = ptp_transaction(params, &ptp, PTP_DP_SENDDATA, size, &oidata, NULL);
free(oidata);
*store=ptp.Param1;
*parenthandle=ptp.Param2;
*handle=ptp.Param3;
return ret;
}
/* Transaction data phase description */
#define PTP_DP_NODATA 0x0000 /* no data phase */
#define PTP_DP_SENDDATA 0x0001 /* sending data */
#define PTP_DP_GETDATA 0x0002 /* receiving data */
#define PTP_DP_DATA_MASK 0x00ff /* data phase mask */
/**
* ptp_sendobject_fromfd:
* params: PTPParams*
* fd - File descriptor to read() object from
* uint64_t size - File/object size
*
* Sends object from file descriptor by consecutive reads from this
* descriptor.
*
* Return values: Some PTP_RC_* code.
**/
uint16_t
ptp_sendobject_fromfd (PTPParams* params, int fd, uint64_t size)
{
PTPContainer ptp;
PTPDataHandler handler;
uint16_t ret;
ptp_init_fd_handler (&handler, fd);
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_SendObject;
ptp.Nparam=0;
ret = ptp_transaction_new(params, &ptp, PTP_DP_SENDDATA, size, &handler);
ptp_exit_fd_handler (&handler);
return ret;
}
读取文件
进行下对比
int LIBMTP_Get_File_To_File_Descriptor(LIBMTP_mtpdevice_t *device,
uint32_t const id,
int const fd,
LIBMTP_progressfunc_t const callback,
void const * const data)
{
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) {
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) {
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_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;
ret = ptp_getobject_tofd(params, id, fd);
ptp_usb->callback_active = 0;
ptp_usb->current_transfer_callback = NULL;
ptp_usb->current_transfer_callback_data = NULL;
ptp_object_want (PTPParams *params, uint32_t handle, unsigned int want, PTPObject **retob) {
uint16_t ret;
PTPObject *ob;
/*Camera *camera = ((PTPData *)params->data)->camera;*/
/* If GetObjectInfo is broken, force GetPropList */
if (params->device_flags & DEVICE_FLAG_PROPLIST_OVERRIDES_OI)
want |= PTPOBJECT_MTPPROPLIST_LOADED;
*retob = NULL;
if (!handle) {
ptp_debug (params, "ptp_object_want: querying handle 0?\n");
return PTP_RC_GeneralError;
}
ret = ptp_object_find_or_insert (params, handle, &ob);
if (ret != PTP_RC_OK)
return PTP_RC_GeneralError;
*retob = ob;
/* Do we have all of it already? */
if ((ob->flags & want) == want)
return PTP_RC_OK;
#define X (PTPOBJECT_OBJECTINFO_LOADED|PTPOBJECT_STORAGEID_LOADED|PTPOBJECT_PARENTOBJECT_LOADED)
if ((want & X) && ((ob->flags & X) != X)) {
uint32_t saveparent = 0;
/* One EOS issue, where getobjecthandles(root) returns obs without root flag. */
if (ob->flags & PTPOBJECT_PARENTOBJECT_LOADED)
saveparent = ob->oi.ParentObject;
ret = ptp_getobjectinfo (params, handle, &ob->oi);
if (ret != PTP_RC_OK) {
/* kill it from the internal list ... */
ptp_remove_object_from_cache(params, handle);
return ret;
}
uint16_t
ptp_getobjectinfo (PTPParams* params, uint32_t handle,
PTPObjectInfo* objectinfo)
{
uint16_t ret;
PTPContainer ptp;
unsigned char* oi=NULL;
unsigned int len;
PTP_CNT_INIT(ptp);
ptp.Code=PTP_OC_GetObjectInfo;// 0x1008
ptp.Param1=handle;
ptp.Nparam=1;
len=0;
ret=ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &oi, &len); //0x0002
if (ret == PTP_RC_OK) ptp_unpack_OI(params, oi, objectinfo, len);
free(oi);
return ret;
}
三、协议公共 接口
/* Old style transaction, based on memory */
uint16_t
ptp_transaction (PTPParams* params, PTPContainer* ptp,
uint16_t flags, uint64_t sendlen,
unsigned char **data, unsigned int *recvlen
) {
PTPDataHandler handler;
uint16_t ret;
switch (flags & PTP_DP_DATA_MASK) {
case PTP_DP_SENDDATA:
ret = ptp_init_send_memory_handler (&handler, *data, sendlen);
if (ret != PTP_RC_OK) return ret;
break;
case PTP_DP_GETDATA:
ret = ptp_init_recv_memory_handler (&handler);
if (ret != PTP_RC_OK) return ret;
break;
default:break;
}
ret = ptp_transaction_new (params, ptp, flags, sendlen, &handler);
switch (flags & PTP_DP_DATA_MASK) {
case PTP_DP_SENDDATA:
ptp_exit_send_memory_handler (&handler);
break;
case PTP_DP_GETDATA: {
unsigned long len;
ptp_exit_recv_memory_handler (&handler, data, &len);
if (recvlen)
*recvlen = len;
break;
}
default:break;
}
return ret;
}
/* PTP v1.0 operation codes */
#define PTP_OC_Undefined 0x1000
#define PTP_OC_GetDeviceInfo 0x1001
#define PTP_OC_OpenSession 0x1002
#define PTP_OC_CloseSession 0x1003
#define PTP_OC_GetStorageIDs 0x1004
#define PTP_OC_GetStorageInfo 0x1005
#define PTP_OC_GetNumObjects 0x1006
#define PTP_OC_GetObjectHandles 0x1007
#define PTP_OC_GetObjectInfo 0x1008
#define PTP_OC_GetObject 0x1009
#define PTP_OC_GetThumb 0x100A
#define PTP_OC_DeleteObject 0x100B
#define PTP_OC_SendObjectInfo 0x100C
#define PTP_OC_SendObject 0x100D
这部分操作码和我在手机内核中看到的操作码虽然名称不一样,但功能基本是对应的。
struct _PTPContainer {
uint16_t Code;
uint32_t SessionID;
uint32_t Transaction_ID;
/* params may be of any type of size less or equal to uint32_t */
uint32_t Param1;
uint32_t Param2;
uint32_t Param3;
/* events can only have three parameters */
uint32_t Param4;
uint32_t Param5;
/* the number of meaningfull parameters */
uint8_t Nparam;
};
typedef struct _PTPContainer PTPContainer;
这部分PTPContainer->code就是具体操作码
//文件信息
struct _PTPObjectInfo {
uint32_t StorageID;
uint16_t ObjectFormat;
uint16_t ProtectionStatus;
/* In the regular objectinfo this is 32bit,
* but we keep the general object size here
* that also arrives via other methods and so
* use 64bit */
uint64_t ObjectCompressedSize;
uint16_t ThumbFormat;
uint32_t ThumbCompressedSize;
uint32_t ThumbPixWidth;
uint32_t ThumbPixHeight;
uint32_t ImagePixWidth;
uint32_t ImagePixHeight;
uint32_t ImageBitDepth;
uint32_t ParentObject;
uint16_t AssociationType;
uint32_t AssociationDesc;
uint32_t SequenceNumber;
char *Filename;
time_t CaptureDate;
time_t ModificationDate;
char *Keywords;
};
typedef struct _PTPObjectInfo PTPObjectInfo;
PTPParams* params属于设备信息