📄 ptp.c
字号:
/* ptp.c * * Copyright (C) 2001-2004 Mariusz Woloszyn <emsi@ipartners.pl> * Copyright (C) 2003-2006 Marcus Meissner <marcus@jet.franken.de> * Copyright (C) 2006 Linus Walleij <triad@df.lth.se> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#include <config.h>#include "ptp.h"#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#include <unistd.h>#ifdef ENABLE_NLS# include <libintl.h># undef _# define _(String) dgettext (PACKAGE, String)# ifdef gettext_noop# define N_(String) gettext_noop (String)# else# define N_(String) (String)# endif#else# define textdomain(String) (String)# define gettext(String) (String)# define dgettext(Domain,Message) (Message)# define dcgettext(Domain,Message,Type) (Message)# define bindtextdomain(Domain,Directory) (Domain)# define _(String) (String)# define N_(String) (String)#endif#define CHECK_PTP_RC(result) {uint16_t r=(result); if (r!=PTP_RC_OK) return r;}#define PTP_CNT_INIT(cnt) {memset(&cnt,0,sizeof(cnt));}static voidptp_debug (PTPParams *params, const char *format, ...){ va_list args; va_start (args, format); if (params->debug_func!=NULL) params->debug_func (params->data, format, args); else { vfprintf (stderr, format, args); fprintf (stderr,"\n"); fflush (stderr); } va_end (args);} static voidptp_error (PTPParams *params, const char *format, ...){ va_list args; va_start (args, format); if (params->error_func!=NULL) params->error_func (params->data, format, args); else { vfprintf (stderr, format, args); fprintf (stderr,"\n"); fflush (stderr); } va_end (args);}/* Pack / unpack functions */#include "ptp-pack.c"/* send / receive functions */uint16_tptp_usb_sendreq (PTPParams* params, PTPContainer* req){ uint16_t ret; PTPUSBBulkContainer usbreq; /* build appropriate USB container */ usbreq.length=htod32(PTP_USB_BULK_REQ_LEN- (sizeof(uint32_t)*(5-req->Nparam))); usbreq.type=htod16(PTP_USB_CONTAINER_COMMAND); usbreq.code=htod16(req->Code); usbreq.trans_id=htod32(req->Transaction_ID); usbreq.payload.params.param1=htod32(req->Param1); usbreq.payload.params.param2=htod32(req->Param2); usbreq.payload.params.param3=htod32(req->Param3); usbreq.payload.params.param4=htod32(req->Param4); usbreq.payload.params.param5=htod32(req->Param5); /* send it to responder */ ret=params->write_func((unsigned char *)&usbreq, PTP_USB_BULK_REQ_LEN-(sizeof(uint32_t)*(5-req->Nparam)), params->data); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO;/* ptp_error (params, "PTP: request code 0x%04x sending req error 0x%04x", req->Code,ret); */ } return ret;}/* Used for file transactions */#define FILE_BUFFER_SIZE 0x10000uint16_tptp_usb_senddata (PTPParams* params, PTPContainer* ptp, unsigned char *data, unsigned int size, int from_fd){ uint16_t ret; int wlen, datawlen; size_t written; PTPUSBBulkContainer usbdata; /* build appropriate USB container */ usbdata.length = htod32(PTP_USB_BULK_HDR_LEN+size); usbdata.type = htod16(PTP_USB_CONTAINER_DATA); usbdata.code = htod16(ptp->Code); usbdata.trans_id= htod32(ptp->Transaction_ID); if (params->split_header_data) { datawlen = 0; wlen = PTP_USB_BULK_HDR_LEN; } else { /* For all camera devices. */ datawlen = (size<PTP_USB_BULK_PAYLOAD_LEN)?size:PTP_USB_BULK_PAYLOAD_LEN; wlen = PTP_USB_BULK_HDR_LEN + datawlen; if (from_fd == -1) { memcpy(usbdata.payload.data, data, datawlen); } else { written = read(from_fd, usbdata.payload.data, datawlen); if (written != datawlen) return PTP_ERROR_IO; } } /* send first part of data */ ret = params->write_func((unsigned char *)&usbdata, wlen, params->data); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO;/* ptp_error (params, "PTP: request code 0x%04x sending data error 0x%04x", ptp->Code,ret);*/ return ret; } if (size <= datawlen) return ret; /* if everything OK send the rest */ if (from_fd == -1) { ret=params->write_func (data + datawlen, size - datawlen, params->data); } else { uint32_t bytes_to_transfer; uint32_t bytes_left_to_transfer; void *temp_buf; written = 0; bytes_left_to_transfer = size-datawlen; temp_buf = malloc(FILE_BUFFER_SIZE); if (temp_buf == NULL) return PTP_ERROR_IO; ret = PTP_RC_OK; while(bytes_left_to_transfer > 0) { if (bytes_left_to_transfer > FILE_BUFFER_SIZE) { bytes_to_transfer = FILE_BUFFER_SIZE; } else { bytes_to_transfer = bytes_left_to_transfer; } written = read(from_fd, temp_buf, bytes_to_transfer); if (written != bytes_to_transfer) { ret = PTP_ERROR_IO; break; } ret=params->write_func (temp_buf, bytes_to_transfer, params->data); bytes_left_to_transfer -= bytes_to_transfer; } free(temp_buf); } if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO;/* ptp_error (params, "PTP: request code 0x%04x sending data error 0x%04x", ptp->Code,ret); */ } return ret;}static uint16_t ptp_usb_getpacket(PTPParams *params, PTPUSBBulkContainer *packet, unsigned int *rlen){ /* read the header and potentially the first data */ if (params->response_packet_size > 0) { /* If there is a buffered packet, just use it. */ memcpy(packet, params->response_packet, params->response_packet_size); *rlen = params->response_packet_size; free(params->response_packet); params->response_packet = NULL; params->response_packet_size = 0; /* Here this signifies a "virtual read" */ return PTP_RC_OK; } else { return params->read_func((unsigned char *)packet, sizeof(*packet), params->data, rlen); }}uint16_tptp_usb_getdata (PTPParams* params, PTPContainer* ptp, unsigned char **data, unsigned int *readlen, int to_fd){ uint16_t ret; PTPUSBBulkContainer usbdata; PTP_CNT_INIT(usbdata); if (to_fd == -1 && *data != NULL) return PTP_ERROR_BADPARAM; do { unsigned int len, rlen; ret = ptp_usb_getpacket(params, &usbdata, &rlen); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO; break; } else if (dtoh16(usbdata.type)!=PTP_USB_CONTAINER_DATA) { ret = PTP_ERROR_DATA_EXPECTED; break; } else if (dtoh16(usbdata.code)!=ptp->Code) { ret = dtoh16(usbdata.code); break; } if (usbdata.length == 0xffffffffU) { /* This only happens for MTP_GetObjPropList */ uint32_t tsize = PTP_USB_BULK_HS_MAX_PACKET_LEN; unsigned char *tdata = malloc(tsize); uint32_t curoff = 0; while (1) { ret=params->read_func(tdata+curoff, PTP_USB_BULK_HS_MAX_PACKET_LEN, params->data, &rlen); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO; break; } if (rlen < PTP_USB_BULK_HS_MAX_PACKET_LEN) { tsize += rlen; break; } tsize += PTP_USB_BULK_HS_MAX_PACKET_LEN; curoff+= PTP_USB_BULK_HS_MAX_PACKET_LEN; tdata = realloc(tdata, tsize); } if (to_fd == -1) { *data = tdata; } else { write (to_fd, tdata, tsize); free (tdata); } return PTP_RC_OK; } if (rlen > dtoh32(usbdata.length)) { /* * Buffer the surplus response packet if it is >= * PTP_USB_BULK_HDR_LEN * (i.e. it is probably an entire package) * else discard it as erroneous surplus data. * This will even work if more than 2 packets appear * in the same transaction, they will just be handled * iteratively. * * Marcus observed stray bytes on iRiver devices; * these are still discarded. */ unsigned int packlen = dtoh32(usbdata.length); unsigned int surplen = rlen - packlen; if (surplen >= PTP_USB_BULK_HDR_LEN) { params->response_packet = malloc(surplen); memcpy(params->response_packet, (uint8_t *) &usbdata + packlen, surplen); params->response_packet_size = surplen; } else { ptp_debug (params, "ptp2/ptp_usb_getdata: read %d bytes too much, expect problems!", rlen - dtoh32(usbdata.length)); } rlen = packlen; } /* For most PTP devices rlen is 512 == sizeof(usbdata) * here. For MTP devices splitting header and data it might * be 12. */ /* Evaluate full data length. */ len=dtoh32(usbdata.length)-PTP_USB_BULK_HDR_LEN; /* autodetect split header/data MTP devices */ if (dtoh32(usbdata.length) > 12 && (rlen==12)) params->split_header_data = 1; if (to_fd == -1) { /* Allocate memory for data. */ *data=calloc(len,1); if (readlen) *readlen = len; /* Copy first part of data to 'data' */ memcpy(*data,usbdata.payload.data,rlen - PTP_USB_BULK_HDR_LEN); /* Is that all of data? */ if (len+PTP_USB_BULK_HDR_LEN<=rlen) break; /* If not read the rest of it. */ ret=params->read_func(((unsigned char *)(*data))+ rlen - PTP_USB_BULK_HDR_LEN, len-(rlen - PTP_USB_BULK_HDR_LEN), params->data, &rlen); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO; break; } } else { uint32_t bytes_to_write, written; uint32_t bytes_left_to_transfer; void *temp_buf; if (readlen) *readlen = len; bytes_to_write = rlen - PTP_USB_BULK_HDR_LEN; ret = write(to_fd, usbdata.payload.data, bytes_to_write); if (ret != bytes_to_write) { ret = PTP_ERROR_IO; break; } if (len + PTP_USB_BULK_HDR_LEN <= rlen) break; temp_buf = malloc(FILE_BUFFER_SIZE); if (temp_buf == NULL) { ret = PTP_ERROR_IO; break; } ret = PTP_RC_OK; bytes_left_to_transfer = len - (rlen - PTP_USB_BULK_HDR_LEN); while (bytes_left_to_transfer > 0) { bytes_to_write = ((bytes_left_to_transfer > FILE_BUFFER_SIZE) ? FILE_BUFFER_SIZE : bytes_left_to_transfer); ret = params->read_func(temp_buf, bytes_to_write, params->data, &rlen); if (ret != PTP_RC_OK) { ret = PTP_ERROR_IO; break; } written = write(to_fd, temp_buf, bytes_to_write); if (written != bytes_to_write) { ret = PTP_ERROR_IO; break; } else { ret = PTP_RC_OK; } bytes_left_to_transfer -= bytes_to_write; } free(temp_buf); if (ret != PTP_RC_OK) break; } } while (0);/* if (ret!=PTP_RC_OK) { ptp_error (params, "PTP: request code 0x%04x getting data error 0x%04x", ptp->Code, ret); }*/ return ret;}uint16_tptp_usb_getresp (PTPParams* params, PTPContainer* resp){ uint16_t ret; unsigned int rlen; PTPUSBBulkContainer usbresp; PTP_CNT_INIT(usbresp); /* read response, it should never be longer than sizeof(usbresp) */ ret = ptp_usb_getpacket(params, &usbresp, &rlen); if (ret!=PTP_RC_OK) { ret = PTP_ERROR_IO; } else if (dtoh16(usbresp.type)!=PTP_USB_CONTAINER_RESPONSE) { ret = PTP_ERROR_RESP_EXPECTED; } else if (dtoh16(usbresp.code)!=resp->Code) { ret = dtoh16(usbresp.code); } if (ret!=PTP_RC_OK) {/* ptp_error (params, "PTP: request code 0x%04x getting resp error 0x%04x", resp->Code, ret);*/ return ret; } /* build an appropriate PTPContainer */ resp->Code=dtoh16(usbresp.code); resp->SessionID=params->session_id; resp->Transaction_ID=dtoh32(usbresp.trans_id); resp->Param1=dtoh32(usbresp.payload.params.param1); resp->Param2=dtoh32(usbresp.payload.params.param2); resp->Param3=dtoh32(usbresp.payload.params.param3); resp->Param4=dtoh32(usbresp.payload.params.param4); resp->Param5=dtoh32(usbresp.payload.params.param5); return ret;}/* major PTP functions *//* 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_transaction: * params: PTPParams* * PTPContainer* ptp - general ptp container * uint16_t flags - lower 8 bits - data phase description * unsigned int sendlen - senddata phase data length * char** data - send or receive data buffer pointer * int* recvlen - receive data length * * Performs PTP transaction. ptp is a PTPContainer with appropriate fields * filled in (i.e. operation code and parameters). It's up to caller to do * so. * The flags decide thether the transaction has a data phase and what is its * direction (send or receive). * If transaction is sending data the sendlen should contain its length in * bytes, otherwise it's ignored. * The data should contain an address of a pointer to data going to be sent * or is filled with such a pointer address if data are received depending * od dataphase direction (send or received) or is beeing ignored (no * dataphase). * The memory for a pointer should be preserved by the caller, if data are * beeing retreived the appropriate amount of memory is beeing allocated * (the caller should handle that!). * * Return values: Some PTP_RC_* code. * Upon success PTPContainer* ptp contains PTP Response Phase container with * all fields filled in. **/static uint16_t_ptp_transaction (PTPParams* params, PTPContainer* ptp, uint16_t flags, unsigned int sendlen, unsigned char** data, int fd, unsigned int *recvlen){ if ((params==NULL) || (ptp==NULL)) return PTP_ERROR_BADPARAM; ptp->Transaction_ID=params->transaction_id++; ptp->SessionID=params->session_id; /* send request */ CHECK_PTP_RC(params->sendreq_func (params, ptp)); /* is there a dataphase? */ switch (flags&PTP_DP_DATA_MASK) { case PTP_DP_SENDDATA: CHECK_PTP_RC(params->senddata_func(params, ptp, *data, sendlen, fd)); break; case PTP_DP_GETDATA: CHECK_PTP_RC(params->getdata_func(params, ptp, (unsigned char**)data, recvlen, fd)); break; case PTP_DP_NODATA: break; default: return PTP_ERROR_BADPARAM; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -