diff --git a/camlibs/sierra/sierra-usbwrap.c b/camlibs/sierra/sierra-usbwrap.c index 5b0affc8b..87526cf4a 100644 --- a/camlibs/sierra/sierra-usbwrap.c +++ b/camlibs/sierra/sierra-usbwrap.c @@ -1,6 +1,7 @@ /* sierra_usbwrap.c * - * Copyright © 2002 Lutz Müller + * Copyright 2002 Lutz Müller + * Copyright 2012-2013 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -123,7 +124,37 @@ cmdbyte (unsigned int type, unsigned char nr) { #define UW_PACKET_DATA ((uw4c_t){ 0x02, 0x00, 0xff, 0x9f }) #define UW_PACKET_STAT ((uw4c_t){ 0x03, 0x00, 0xff, 0x9f }) +#define UW_MAGIC_OUT ((uw4c_t){ 'U','S','B','C' }) +#define UW_MAGIC_IN ((uw4c_t){ 'U','S','B','S' }) + #pragma pack(1) +/* This is linux/include/linux/usb/storage.h, struct bulk_cb_wrap + * 31 byte length */ +typedef struct +{ + uw4c_t magic; /* The letters U S B C for packets sent to camera */ + uw32_t tag; /* The SCSI command tag */ + uw32_t rw_length; /* Length of data to be read or written next */ + unsigned char flags; /* in / out flag mostly */ + unsigned char lun; /* 0 here */ + unsigned char length; /* of the CDB... but 0x0c is used here in the traces */ + unsigned char cdb[16]; +} uw_header_t; + +/* + * This is the end response block from the camera looks like this: + * + * This is the generic bulk style USB Storage response block. + * + * linux/include/linux/usb/storage.h, struct bulk_cs_wrap */ +typedef struct +{ + uw4c_t magic; /* The letters U S B S for packets from camera */ + uw32_t tag; /* A copy of whatever value the host made up */ + uw32_t residue; /* residual read? */ + char status; /* status byte */ +} uw_response_t; + /* * Data packet sent along with a UW_REQUEST_RDY: */ @@ -165,16 +196,6 @@ typedef struct uw32_t size; /* The size of data waiting to be sent by camera */ } uw_size_t; -/* - * The end of the response from the camera looks like this: - */ -typedef struct -{ - uw4c_t magic; /* The letters U S B S for packets from camera */ - uw32_t sessionid; /* A copy of whatever value the host made up */ - char zero[5]; /* 00 00 00 00 00 */ -} uw_response_t; - typedef struct { unsigned char cmd; @@ -184,6 +205,99 @@ typedef struct } uw_scsicmd_t; #pragma pack() +static int ums_tag = 0x42424242; + +static int +scsi_wrap_cmd( + GPPort *dev, int todev, + char *cmd, unsigned int cmdlen, + char *sense, unsigned int senselen, + char *data, unsigned int size +) { + GPPortInfo info; + GPPortType type; + uw_header_t hdr; + uw_response_t rsp; + int ret; + + ret = gp_port_get_info (dev, &info); + if (ret != GP_OK) return ret; + + ret = gp_port_info_get_type (info, &type); + if (ret != GP_OK) return ret; + + if (type == GP_PORT_USB_SCSI) + /* just send it to the generic SCSI stack */ + return gp_port_send_scsi_cmd ( + dev, todev, + cmd, cmdlen, + sense, senselen, + data, size + ); + + /* Assuming plain USB, emulating SCSI ... */ + + memset(&hdr, 0, sizeof(hdr)); + hdr.magic = UW_MAGIC_OUT; + hdr.tag = uw_value(ums_tag); + ums_tag++; + hdr.rw_length = uw_value(size); + hdr.length = 12; /* seems to be always 12, even as we send 16 byte CDBs */ + hdr.flags = todev?0:(1<<7); + hdr.lun = 0; + + memcpy(hdr.cdb, cmd, cmdlen); + + /* WRITE scsi header / cdb */ + if ((ret=gp_port_write(dev, (char*)&hdr, sizeof(hdr))) < GP_OK) { + GP_DEBUG( "scsi_wrap_cmd *** FAILED to write scsi cmd" ); + return GP_ERROR_IO; + } + /* WRITE / READ data blob */ + if (todev) { + if ((ret=gp_port_write(dev, (char*)data, size)) < GP_OK) { + GP_DEBUG( "scsi_wrap_cmd *** FAILED to write scsi data" ); + return GP_ERROR_IO; + } + } else { + if ((ret=gp_port_read(dev, (char*)data, size)) < GP_OK) { + GP_DEBUG( "scsi_wrap_cmd *** FAILED to read scsi data" ); + return GP_ERROR_IO; + } + } + + /* READ RESPONSE */ + memset(&rsp, 0, sizeof(rsp)); + + GP_DEBUG( "usb_wrap_OK" ); + if ((ret = gp_port_read(dev, (char*)&rsp, sizeof(rsp))) != sizeof(rsp)) { + gp_log (GP_LOG_DEBUG, GP_MODULE, "scsi_wrap_cmd *** FAILED (%d vs %d bytes)", (int)sizeof(rsp), ret ); + if (ret < GP_OK) + return ret; + return GP_ERROR; + } + if ( !UW_EQUAL(rsp.magic, UW_MAGIC_IN) || + !UW_EQUAL(rsp.tag, hdr.tag)) + { + GP_DEBUG( "scsi_wrap_cmd wrong session *** FAILED" ); + return GP_ERROR; + } + /* + * 32bit residual length (0) and 8 bit status (0) are good. + */ + if ( rsp.residue.c1 != 0 || + rsp.residue.c2 != 0 || + rsp.residue.c3 != 0 || + rsp.residue.c4 != 0 || + rsp.status != 0) { + GP_DEBUG( "Error: scsi_wrap_cmd - residual non-0 or status %x", rsp.status); + return GP_ERROR; + } + return GP_OK; +} + +#define gp_port_send_scsi_cmd scsi_wrap_cmd + static int usb_wrap_RDY(gp_port* dev, unsigned int type) {