Add suport for using USB storage devices with multiple LUNs. One example for such a device is the HAGIWARA UDRW stick, which has one LUN that simulates a cdrom device, and one that acts like a disk device.
Signed-off-by: Sven Schnelle svens@stackframe.org --- src/usb-msc.c | 146 +++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 101 insertions(+), 45 deletions(-)
diff --git a/src/usb-msc.c b/src/usb-msc.c index deccf35..bf5dce8 100644 --- a/src/usb-msc.c +++ b/src/usb-msc.c @@ -16,6 +16,7 @@ struct usbdrive_s { struct drive_s drive; struct usb_pipe *bulkin, *bulkout; + int lun; };
@@ -53,14 +54,32 @@ usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) if (!CONFIG_USB_MSC) return 0;
-restart: - dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" - , op->drive_g, 0, op->count, blocksize, op->buf_fl); struct usbdrive_s *udrive_g = container_of( op->drive_g, struct usbdrive_s, drive); struct usb_pipe *bulkin = GET_GLOBAL(udrive_g->bulkin); struct usb_pipe *bulkout = GET_GLOBAL(udrive_g->bulkout); struct cdbres_request_sense sense; + int lun = GET_GLOBAL(udrive_g->lun); + unsigned char *data = (unsigned char *)cdbcmd; + +restart: + dprintf(16, "usb_cmd_data lun=%d id=%p write=%d count=%d bs=%d buf=%p\n" + , lun, op->drive_g, 0, op->count, blocksize, op->buf_fl); + + /* some SCSI commands have the LUN in CDB */ + if (data[0] == 0x08 || // READ(6) + data[0] == 0x12 || // INQUIRY + data[0] == 0x15 || // MODE SELECT(6) + data[0] == 0x55 || // MODE SELECT(10) + data[0] == 0x1A || // MODE SENSE(6) + data[0] == 0x28 || // READ(10) + data[0] == 0x1B || // START STOP UNIT(6) + data[0] == 0x25 || // READ CAPACITY + data[0] == 0x03 // REQUEST SENSE + ) { + data[1] &= ~0x10; + data[1] |= (lun & 7) << 5; + }
// Setup command block wrapper. u32 bytes = blocksize * op->count; @@ -70,7 +89,7 @@ restart: cbw.dCBWTag = 999; // XXX cbw.dCBWDataTransferLength = bytes; cbw.bmCBWFlags = USB_DIR_IN; // XXX - cbw.bCBWLUN = 0; // XXX + cbw.bCBWLUN = lun; cbw.bCBWCBLength = USB_CDB_SIZE; memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE);
@@ -199,44 +218,36 @@ setup_drive_hd(struct disk_op_s *op, char *desc) return 0; }
-// Configure a usb msc device. -int -usb_msc_init(struct usb_pipe *pipe - , struct usb_interface_descriptor *iface, int imax) +static int +usb_msc_maxlun(struct usb_pipe *pipe, unsigned char *maxlun) { - if (!CONFIG_USB_MSC) - return -1; - - // Verify right kind of device - if ((iface->bInterfaceSubClass != US_SC_SCSI && - iface->bInterfaceSubClass != US_SC_ATAPI_8070 && - iface->bInterfaceSubClass != US_SC_ATAPI_8020) - || iface->bInterfaceProtocol != US_PR_BULK) { - dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" - , iface->bInterfaceSubClass, iface->bInterfaceProtocol); - return -1; - } + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = 0xfe; + req.wValue = 0; + req.wIndex = 0; + req.wLength = 1; + return send_default_control(pipe, &req, maxlun); +}
+static int usb_msc_init_lun(struct usb_pipe *inpipe, + struct usb_pipe *outpipe, + struct usb_interface_descriptor *iface, + int lun) +{ // Allocate drive structure. struct usbdrive_s *udrive_g = malloc_fseg(sizeof(*udrive_g)); if (!udrive_g) { - warn_noalloc(); - goto fail; + warn_noalloc(); + return -1; } + memset(udrive_g, 0, sizeof(*udrive_g)); udrive_g->drive.type = DTYPE_USB; + udrive_g->lun = lun;
- // Find bulk in and bulk out endpoints. - struct usb_endpoint_descriptor *indesc = findEndPointDesc( - iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); - struct usb_endpoint_descriptor *outdesc = findEndPointDesc( - iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); - if (!indesc || !outdesc) - goto fail; - udrive_g->bulkin = alloc_bulk_pipe(pipe, indesc); - udrive_g->bulkout = alloc_bulk_pipe(pipe, outdesc); - if (!udrive_g->bulkin || !udrive_g->bulkout) - goto fail; + udrive_g->bulkin = inpipe; + udrive_g->bulkout = outpipe;
// Validate drive and find block size and sector count. struct disk_op_s dop; @@ -245,10 +256,10 @@ usb_msc_init(struct usb_pipe *pipe struct cdbres_inquiry data; int ret = cdb_get_inquiry(&dop, &data); if (ret) - goto fail; + goto fail;
if (cdb_start_stop_unit(&dop, 0, 1)) - goto fail; + goto fail;
char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; char rev[sizeof(data.rev)+1]; @@ -261,24 +272,69 @@ usb_msc_init(struct usb_pipe *pipe int pdt = data.pdt & 0x1f; int removable = !!(data.removable & 0x80); dprintf(1, "USB MSC vendor='%s' product='%s' rev='%s' type=%d removable=%d\n" - , vendor, product, rev, pdt, removable); + , vendor, product, rev, pdt, removable); udrive_g->drive.removable = removable;
if (pdt == USB_MSC_TYPE_CDROM) { - char *desc = znprintf(MAXDESCSIZE, "DVD/CD [USB Drive %s %s %s]" - , vendor, product, rev); - ret = setup_drive_cdrom(&dop, desc); + char *desc = znprintf(MAXDESCSIZE, "DVD/CD [USB Drive %s %s %s]" + , vendor, product, rev); + ret = setup_drive_cdrom(&dop, desc); } else { - char *desc = znprintf(MAXDESCSIZE, "USB Drive %s %s %s" - , vendor, product, rev); - ret = setup_drive_hd(&dop, desc); + char *desc = znprintf(MAXDESCSIZE, "USB Drive %s %s %s" + , vendor, product, rev); + ret = setup_drive_hd(&dop, desc); } if (ret) - goto fail; + goto fail;
return 0; -fail: - dprintf(1, "Unable to configure USB MSC device.\n"); + fail: free(udrive_g); return -1; } + +// Configure a usb msc device. +int +usb_msc_init(struct usb_pipe *pipe + , struct usb_interface_descriptor *iface, int imax) +{ + unsigned char maxlun, lun; + + if (!CONFIG_USB_MSC) + return -1; + + // Verify right kind of device + if ((iface->bInterfaceSubClass != US_SC_SCSI && + iface->bInterfaceSubClass != US_SC_ATAPI_8070 && + iface->bInterfaceSubClass != US_SC_ATAPI_8020) + || iface->bInterfaceProtocol != US_PR_BULK) { + dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + // Find bulk in and bulk out endpoints. + struct usb_endpoint_descriptor *indesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); + struct usb_endpoint_descriptor *outdesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); + + if (!indesc || !outdesc) + return -1; + + struct usb_pipe *inpipe = alloc_bulk_pipe(pipe, indesc); + if (!inpipe) + return -1; + + struct usb_pipe *outpipe = alloc_bulk_pipe(pipe, outdesc); + if (!outpipe) + return -1; + + if (usb_msc_maxlun(pipe, &maxlun)) + maxlun = 0; + + for (lun = 0; lun < maxlun+1; lun++) + usb_msc_init_lun(inpipe, outpipe, iface, lun); + + return 0; +}