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; +}
On Sun, Oct 16, 2011 at 12:45:10PM +0200, Sven Schnelle wrote:
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.
Thanks - I'll take a look at these patches and run them with my usb devices on the e350m1.
BTW, it looks like you have some indentation changes in these patches - SeaBIOS uses spaces (not tabs) in all of its code.
-Kevin
On 10/16/2011 12:45 PM, Sven Schnelle wrote:
- /* 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
- ) {
In old versions of the SCSI standard all commands had the LUN in the CDB. In recent versions, no commands have the LUN in the CDB, but it doesn't surprise me that some devices make it a requirement. Where did you take the list from? It would make sense (also for simplicity and code size) to just include the LUN always.
- data[1]&= ~0x10;
Why should data[1] bit 4 be cleared?
Paolo