Sync in Marcus changes in libgphoto2
diff --git a/src/libusb-glue.c b/src/libusb-glue.c
index 3541853..a063974 100644
--- a/src/libusb-glue.c
+++ b/src/libusb-glue.c
@@ -20,6 +20,8 @@
#include <string.h>
#include <usb.h>
+#include "ptp-pack.c"
+
/* To enable debug prints, switch on this */
//#define ENABLE_USB_BULK_DEBUG
@@ -226,7 +228,6 @@
static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev);
static short ptp_write_func (unsigned long,PTPDataHandler*,void *data,unsigned long*);
static short ptp_read_func (unsigned long,PTPDataHandler*,void *data,unsigned long*);
-static short ptp_check_int (unsigned long,PTPDataHandler*,void *, unsigned long *);
static int usb_clear_stall_feature(PTP_USB* ptp_usb, int ep);
static int usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status);
@@ -397,6 +398,40 @@
// TODO: add in string dumps for iManufacturer, iProduct, iSerialnumber...
}
+static void
+ptp_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 void
+ptp_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);
+}
+
/*
* ptp_read_func() and ptp_write_func() are
@@ -553,36 +588,463 @@
return PTP_RC_OK;
}
-// This is a bit hackish at the moment. I wonder if it even works.
-static short ptp_check_int (
- unsigned long size, PTPDataHandler *handler,void *data,
- unsigned long *readbytes
-) {
- PTP_USB *ptp_usb = (PTP_USB *)data;
- int result;
- unsigned char bytes[64];
+/* memory data get/put handler */
+typedef struct {
+ unsigned char *data;
+ unsigned long size, curoff;
+} PTPMemHandlerPrivate;
- if (size>64) return PTP_ERROR_IO;
-
- result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)bytes,size,ptpcam_usb_timeout);
- if (result==0)
- result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) bytes, size, ptpcam_usb_timeout);
- if (result >= 0) {
- handler->putfunc (NULL, handler->private, result, bytes, readbytes);
- return (PTP_RC_OK);
- } else {
- return PTP_ERROR_IO;
- }
+static uint16_t
+memory_getfunc(PTPParams* params, void* private,
+ unsigned long wantlen, unsigned char *data,
+ unsigned long *gotlen
+) {
+ PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;
+ unsigned long tocopy = wantlen;
+
+ if (priv->curoff + tocopy > priv->size)
+ tocopy = priv->size - priv->curoff;
+ memcpy (data, priv->data + priv->curoff, tocopy);
+ priv->curoff += tocopy;
+ *gotlen = tocopy;
+ return PTP_RC_OK;
}
+static uint16_t
+memory_putfunc(PTPParams* params, void* private,
+ unsigned long sendlen, unsigned char *data,
+ unsigned long *putlen
+) {
+ PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)private;
+
+ if (priv->curoff + sendlen > priv->size) {
+ priv->data = realloc (priv->data, priv->curoff+sendlen);
+ priv->size = priv->curoff + sendlen;
+ }
+ memcpy (priv->data + priv->curoff, data, sendlen);
+ priv->curoff += sendlen;
+ *putlen = sendlen;
+ return PTP_RC_OK;
+}
+
+/* init private struct for receiving data. */
+static uint16_t
+ptp_init_recv_memory_handler(PTPDataHandler *handler) {
+ PTPMemHandlerPrivate* priv;
+ priv = malloc (sizeof(PTPMemHandlerPrivate));
+ handler->private = priv;
+ handler->getfunc = memory_getfunc;
+ handler->putfunc = memory_putfunc;
+ priv->data = NULL;
+ priv->size = 0;
+ priv->curoff = 0;
+ return PTP_RC_OK;
+}
+
+/* init private struct and put data in for sending data.
+ * data is still owned by caller.
+ */
+static uint16_t
+ptp_init_send_memory_handler(PTPDataHandler *handler,
+ unsigned char *data, unsigned long len
+) {
+ PTPMemHandlerPrivate* priv;
+ priv = malloc (sizeof(PTPMemHandlerPrivate));
+ if (!priv)
+ return PTP_RC_GeneralError;
+ handler->private = priv;
+ handler->getfunc = memory_getfunc;
+ handler->putfunc = memory_putfunc;
+ priv->data = data;
+ priv->size = len;
+ priv->curoff = 0;
+ return PTP_RC_OK;
+}
+
+/* free private struct + data */
+static uint16_t
+ptp_exit_send_memory_handler (PTPDataHandler *handler) {
+ PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)handler->private;
+ /* data is owned by caller */
+ free (priv);
+ return PTP_RC_OK;
+}
+
+/* hand over our internal data to caller */
+static uint16_t
+ptp_exit_recv_memory_handler (PTPDataHandler *handler,
+ unsigned char **data, unsigned long *size
+) {
+ PTPMemHandlerPrivate* priv = (PTPMemHandlerPrivate*)handler->private;
+ *data = priv->data;
+ *size = priv->size;
+ free (priv);
+ return PTP_RC_OK;
+}
+
+/* send / receive functions */
+
+uint16_t
+ptp_usb_sendreq (PTPParams* params, PTPContainer* req)
+{
+ uint16_t ret;
+ PTPUSBBulkContainer usbreq;
+ PTPDataHandler memhandler;
+ unsigned long written, towrite;
+
+ /* 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 */
+ towrite = PTP_USB_BULK_REQ_LEN-(sizeof(uint32_t)*(5-req->Nparam));
+ ptp_init_send_memory_handler (&memhandler, (unsigned char*)&usbreq, towrite);
+ ret=ptp_write_func(
+ towrite,
+ &memhandler,
+ params->data,
+ &written
+ );
+ ptp_exit_send_memory_handler (&memhandler);
+ 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); */
+ }
+ if (written != towrite) {
+ ptp_error (params,
+ "PTP: request code 0x%04x sending req wrote only %ld bytes instead of %d",
+ written, towrite
+ );
+ ret = PTP_ERROR_IO;
+ }
+ return ret;
+}
+
+uint16_t
+ptp_usb_senddata (PTPParams* params, PTPContainer* ptp,
+ unsigned long size, PTPDataHandler *handler
+) {
+ uint16_t ret;
+ int wlen, datawlen;
+ unsigned long written;
+ PTPUSBBulkContainer usbdata;
+ uint32_t bytes_left_to_transfer;
+ PTPDataHandler memhandler;
+
+ /* 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);
+
+ ((PTP_USB*)params->data)->current_transfer_complete = 0;
+ ((PTP_USB*)params->data)->current_transfer_total = size;
+
+ if (params->split_header_data) {
+ datawlen = 0;
+ wlen = PTP_USB_BULK_HDR_LEN;
+ } else {
+ unsigned long gotlen;
+ /* For all camera devices. */
+ datawlen = (size<PTP_USB_BULK_PAYLOAD_LEN)?size:PTP_USB_BULK_PAYLOAD_LEN;
+ wlen = PTP_USB_BULK_HDR_LEN + datawlen;
+ ret = handler->getfunc(params, handler->private, datawlen, usbdata.payload.data, &gotlen);
+ if (ret != PTP_RC_OK)
+ return ret;
+ if (gotlen != datawlen)
+ return PTP_RC_GeneralError;
+ }
+ ptp_init_send_memory_handler (&memhandler, (unsigned char *)&usbdata, wlen);
+ /* send first part of data */
+ ret = ptp_write_func(wlen, &memhandler, params->data, &written);
+ ptp_exit_send_memory_handler (&memhandler);
+ 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 */
+ bytes_left_to_transfer = size-datawlen;
+ ret = PTP_RC_OK;
+ while(bytes_left_to_transfer > 0) {
+ ret = ptp_write_func (bytes_left_to_transfer, handler, params->data, &written);
+ if (ret != PTP_RC_OK)
+ break;
+ if (written == 0) {
+ ret = PTP_ERROR_IO;
+ break;
+ }
+ bytes_left_to_transfer -= written;
+ }
+ if (ret!=PTP_RC_OK)
+ ret = PTP_ERROR_IO;
+ return ret;
+}
+
+static uint16_t ptp_usb_getpacket(PTPParams *params,
+ PTPUSBBulkContainer *packet, unsigned long *rlen)
+{
+ PTPDataHandler memhandler;
+ uint16_t ret;
+ unsigned char *x = NULL;
+
+ /* 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;
+ }
+ ptp_init_recv_memory_handler (&memhandler);
+ ret = ptp_read_func( sizeof(*packet), &memhandler, params->data, rlen);
+ ptp_exit_recv_memory_handler (&memhandler, &x, rlen);
+ if (x) {
+ memcpy (packet, x, *rlen);
+ free (x);
+ }
+ return ret;
+}
+
+uint16_t
+ptp_usb_getdata (PTPParams* params, PTPContainer* ptp, PTPDataHandler *handler)
+{
+ uint16_t ret;
+ PTPUSBBulkContainer usbdata;
+ unsigned char *data;
+ unsigned long written;
+
+ memset(&usbdata,0,sizeof(usbdata));
+ do {
+ unsigned long 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) {
+ /* stuff data directly to passed data handler */
+ while (1) {
+ unsigned long readdata;
+ int xret;
+
+ xret = ptp_read_func(
+ PTP_USB_BULK_HS_MAX_PACKET_LEN,
+ handler,
+ params->data,
+ &readdata
+ );
+ if (xret == -1)
+ return PTP_ERROR_IO;
+ if (readdata < PTP_USB_BULK_HS_MAX_PACKET_LEN)
+ break;
+ }
+ 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;
+
+ data = malloc(PTP_USB_BULK_HS_MAX_PACKET_LEN);
+ /* Copy first part of data to 'data' */
+ handler->putfunc(
+ params, handler->private, rlen - PTP_USB_BULK_HDR_LEN, usbdata.payload.data,
+ &written
+ );
+
+ /* Is that all of data? */
+ if (len+PTP_USB_BULK_HDR_LEN<=rlen) break;
+
+ /* If not read the rest of it. */
+ ret=ptp_read_func(len - (rlen - PTP_USB_BULK_HDR_LEN),
+ handler,
+ params->data, &rlen);
+ if (ret!=PTP_RC_OK) {
+ ret = PTP_ERROR_IO;
+ 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_t
+ptp_usb_getresp (PTPParams* params, PTPContainer* resp)
+{
+ uint16_t ret;
+ unsigned long rlen;
+ PTPUSBBulkContainer usbresp;
+
+ memset(&usbresp,0,sizeof(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;
+}
+
+/* Event handling functions */
+
+/* PTP Events wait for or check mode */
+#define PTP_EVENT_CHECK 0x0000 /* waits for */
+#define PTP_EVENT_CHECK_FAST 0x0001 /* checks */
+
+static inline uint16_t
+ptp_usb_event (PTPParams* params, PTPContainer* event, int wait)
+{
+ uint16_t ret;
+ int result;
+ unsigned long rlen;
+ PTPUSBEventContainer usbevent;
+ PTP_USB *ptp_usb = (PTP_USB *)(params->data);
+
+ memset(&usbevent,0,sizeof(usbevent));
+
+ if ((params==NULL) || (event==NULL))
+ return PTP_ERROR_BADPARAM;
+ ret = PTP_RC_OK;
+ switch(wait) {
+ case PTP_EVENT_CHECK:
+ result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptpcam_usb_timeout);
+ if (result==0)
+ result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptpcam_usb_timeout);
+ if (result < 0) ret = PTP_ERROR_IO;
+ break;
+ case PTP_EVENT_CHECK_FAST:
+ result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)&usbevent,sizeof(usbevent),ptpcam_usb_timeout);
+ if (result==0)
+ result = USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *) &usbevent, sizeof(usbevent), ptpcam_usb_timeout);
+ if (result < 0) ret = PTP_ERROR_IO;
+ break;
+ default:
+ ret=PTP_ERROR_BADPARAM;
+ break;
+ }
+ if (ret!=PTP_RC_OK) {
+ ptp_error (params,
+ "PTP: reading event an error 0x%04x occurred", ret);
+ return PTP_ERROR_IO;
+ }
+ rlen = result;
+ if (rlen < 8) {
+ ptp_error (params,
+ "PTP: reading event an short read of %ld bytes occurred", rlen);
+ return PTP_ERROR_IO;
+ }
+ /* if we read anything over interrupt endpoint it must be an event */
+ /* build an appropriate PTPContainer */
+ event->Code=dtoh16(usbevent.code);
+ event->SessionID=params->session_id;
+ event->Transaction_ID=dtoh32(usbevent.trans_id);
+ event->Param1=dtoh32(usbevent.param1);
+ event->Param2=dtoh32(usbevent.param2);
+ event->Param3=dtoh32(usbevent.param3);
+ return ret;
+}
+
+uint16_t
+ptp_usb_event_check (PTPParams* params, PTPContainer* event) {
+
+ return ptp_usb_event (params, event, PTP_EVENT_CHECK_FAST);
+}
+
+uint16_t
+ptp_usb_event_wait (PTPParams* params, PTPContainer* event) {
+
+ return ptp_usb_event (params, event, PTP_EVENT_CHECK);
+}
+
+
static int init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev)
{
usb_dev_handle *device_handle;
- params->write_func=ptp_write_func;
- params->read_func=ptp_read_func;
- params->check_int_func=ptp_check_int;
- params->check_int_fast_func=ptp_check_int;
params->error_func=NULL;
params->debug_func=NULL;
params->sendreq_func=ptp_usb_sendreq;