Hi, there is the patch which adds support for USB Mass Storage type CBI (Control/Bulk/Interrupt).
I know, currently is probably not the best time to add some new features but it happened... :-) I don't know how it will be useful because CBI devices should be (according to specification) full speed only and they should not be used for future devices... I.e. CBI devices are probably only some (old) external FDD or (very) old digital cameras (my case) etc. I have unfortunately only one CBI device, so I cannot test this functionality too much deeply (but it looks working good with my device). Regards Ales
diff -urB ./grub/grub-core/disk/usbms.c ./grub_patched/grub-core/disk/usbms.c --- ./grub/grub-core/disk/usbms.c 2010-09-23 21:45:45.000000000 +0200 +++ ./grub_patched/grub-core/disk/usbms.c 2010-09-26 20:40:50.000000000 +0200 @@ -26,6 +26,8 @@ #define GRUB_USBMS_DIRECTION_BIT 7 +#define GRUB_USBMS_CBI_CMD_SIZE 12 + /* The USB Mass Storage Command Block Wrapper. */ struct grub_usbms_cbw { @@ -57,8 +59,9 @@ struct grub_usb_desc_endp *in; struct grub_usb_desc_endp *out; - int in_maxsz; - int out_maxsz; + int subclass; + int protocol; + struct grub_usb_desc_endp *intrpt; }; typedef struct grub_usbms_dev *grub_usbms_dev_t; @@ -68,11 +72,39 @@ static int first_available_slot = 0; static grub_err_t -grub_usbms_reset (grub_usb_device_t dev, int interface) +grub_usbms_cbi_cmd (grub_usb_device_t dev, int interface, + grub_uint8_t *cbicb) +{ + return grub_usb_control_msg (dev, 0x21, 0, 0, interface, + GRUB_USBMS_CBI_CMD_SIZE, (char*)cbicb); +} + +static grub_err_t +grub_usbms_cbi_reset (grub_usb_device_t dev, int interface) +{ + grub_uint8_t cbicb[GRUB_USBMS_CBI_CMD_SIZE] = + { 0x1d, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + }; + + return grub_usbms_cbi_cmd (dev, interface, cbicb); +} + +static grub_err_t +grub_usbms_bo_reset (grub_usb_device_t dev, int interface) { return grub_usb_control_msg (dev, 0x21, 255, 0, interface, 0, 0); } +static grub_err_t +grub_usbms_reset (grub_usbms_dev_t dev) +{ + if (dev->protocol == GRUB_USBMS_PROTOCOL_BULK) + return grub_usbms_bo_reset (dev->dev, dev->interface); + else + return grub_usbms_cbi_reset (dev->dev, dev->interface); +} + static void grub_usbms_detach (grub_usb_device_t usbdev, int config, int interface) { @@ -95,7 +127,7 @@ int j; grub_uint8_t luns = 0; unsigned curnum; - grub_usb_err_t err; + grub_usb_err_t err = GRUB_ERR_NONE; if (first_available_slot == ARRAY_SIZE (grub_usbms_devices)) return 0; @@ -111,7 +143,9 @@ && interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 && interf->subclass != GRUB_USBMS_SUBCLASS_UFI && interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 ) - || interf->protocol != GRUB_USBMS_PROTOCOL_BULK) + || (interf->protocol != GRUB_USBMS_PROTOCOL_BULK + && interf->protocol != GRUB_USBMS_PROTOCOL_CBI + && interf->protocol != GRUB_USBMS_PROTOCOL_CB)) return 0; grub_usbms_devices[curnum] = grub_zalloc (sizeof (struct grub_usbms_dev)); @@ -120,6 +154,8 @@ grub_usbms_devices[curnum]->dev = usbdev; grub_usbms_devices[curnum]->interface = interfno; + grub_usbms_devices[curnum]->subclass = interf->subclass; + grub_usbms_devices[curnum]->protocol = interf->protocol; grub_dprintf ("usbms", "alive\n"); @@ -131,24 +167,19 @@ endp = &usbdev->config[0].interf[interfno].descendp[j]; if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk IN endpoint. */ - grub_usbms_devices[curnum]->in = endp; - /* Clear Halt is not possible yet! */ - /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ - grub_usbms_devices[curnum]->in_maxsz = endp->maxpacket; - } + /* Bulk IN endpoint. */ + grub_usbms_devices[curnum]->in = endp; else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk OUT endpoint. */ - grub_usbms_devices[curnum]->out = endp; - /* Clear Halt is not possible yet! */ - /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ - grub_usbms_devices[curnum]->out_maxsz = endp->maxpacket; - } + /* Bulk OUT endpoint. */ + grub_usbms_devices[curnum]->out = endp; + else if ((endp->endp_addr & 128) && (endp->attrib & 3) == 3) + /* Interrupt (IN) endpoint. */ + grub_usbms_devices[curnum]->intrpt = endp; } - if (!grub_usbms_devices[curnum]->in || !grub_usbms_devices[curnum]->out) + if (!grub_usbms_devices[curnum]->in || !grub_usbms_devices[curnum]->out + || ((grub_usbms_devices[curnum]->protocol == GRUB_USBMS_PROTOCOL_CBI) + && !grub_usbms_devices[curnum]->intrpt)) { grub_free (grub_usbms_devices[curnum]); grub_usbms_devices[curnum] = 0; @@ -161,27 +192,34 @@ grub_usb_set_configuration (usbdev, 1); /* Query the amount of LUNs. */ - err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, interfno, 1, (char *) &luns); - - if (err) - { - /* In case of a stall, clear the stall. */ - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->in->endp_addr); - grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->out->endp_addr); - } - /* Just set the amount of LUNs to one. */ - grub_errno = GRUB_ERR_NONE; - grub_usbms_devices[curnum]->luns = 1; + if (grub_usbms_devices[curnum]->protocol == GRUB_USBMS_PROTOCOL_BULK) + { /* Only Bulk only devices support Get Max LUN command */ + err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, interfno, 1, (char *) &luns); + + if (err) + { + /* In case of a stall, clear the stall. */ + if (err == GRUB_USB_ERR_STALL) + { + grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->in->endp_addr); + grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->out->endp_addr); + } + /* Just set the amount of LUNs to one. */ + grub_errno = GRUB_ERR_NONE; + grub_usbms_devices[curnum]->luns = 1; + } + else + /* luns = 0 means one LUN with ID 0 present ! */ + /* We get from device not number of LUNs but highest + * LUN number. LUNs are numbered from 0, + * i.e. number of LUNs is luns+1 ! */ + grub_usbms_devices[curnum]->luns = luns + 1; } else - /* luns = 0 means one LUN with ID 0 present ! */ - /* We get from device not number of LUNs but highest - * LUN number. LUNs are numbered from 0, - * i.e. number of LUNs is luns+1 ! */ - grub_usbms_devices[curnum]->luns = luns + 1; - + /* XXX: Does CBI devices support multiple LUNs ? + * I.e., should we detect number of device's LUNs ? (How?) */ + grub_usbms_devices[curnum]->luns = 1; + grub_dprintf ("usbms", "alive\n"); usbdev->config[configno].interf[interfno].detach_hook = grub_usbms_detach; @@ -222,8 +260,8 @@ } static grub_err_t -grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, - grub_size_t size, char *buf, int read_write) +grub_usbms_transfer_bo (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) { struct grub_usbms_cbw cbw; grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; @@ -232,7 +270,7 @@ grub_usb_err_t err = GRUB_USB_ERR_NONE; grub_usb_err_t errCSW = GRUB_USB_ERR_NONE; int retrycnt = 3 + 1; - + retry: retrycnt--; if (retrycnt == 0) @@ -333,7 +371,7 @@ if (errCSW) { /* Bulk-only reset device. */ grub_dprintf ("usb", "Bulk-only reset device - errCSW\n"); - grub_usbms_reset (dev->dev, dev->interface); + grub_usbms_reset (dev); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); goto retry; @@ -350,7 +388,7 @@ (status.signature != grub_cpu_to_le32(0x53425355))) { /* Bulk-only reset device. */ grub_dprintf ("usb", "Bulk-only reset device - bad status\n"); - grub_usbms_reset (dev->dev, dev->interface); + grub_usbms_reset (dev); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); @@ -368,6 +406,158 @@ return GRUB_ERR_NONE; } +static grub_err_t +grub_usbms_transfer_cbi (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) +{ + grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; + int retrycnt = 3 + 1; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + grub_uint8_t cbicb[GRUB_USBMS_CBI_CMD_SIZE]; + grub_uint16_t status; + + retry: + retrycnt--; + if (retrycnt == 0) + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI failed"); + + /* Setup the request. */ + grub_memset (cbicb, 0, sizeof (cbicb)); + grub_memcpy (cbicb, cmd, + cmdsize >= GRUB_USBMS_CBI_CMD_SIZE + ? GRUB_USBMS_CBI_CMD_SIZE + : cmdsize); + + /* Debug print of CBIcb content. */ + grub_dprintf ("usb", "cbicb:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + cbicb[ 0], cbicb[ 1], cbicb[ 2], cbicb[ 3], + cbicb[ 4], cbicb[ 5], cbicb[ 6], cbicb[ 7], + cbicb[ 8], cbicb[ 9], cbicb[10], cbicb[11]); + + /* Write the request. + * XXX: Error recovery is maybe not correct. */ + err = grub_usbms_cbi_cmd (dev->dev, dev->interface, cbicb); + if (err) + { + grub_dprintf ("usb", "CBI cmdcb setup err=%d\n", err); + if (err == GRUB_USB_ERR_STALL) + { + /* Stall in this place probably means bad or unsupported + * command, so we will not try it again. */ + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI request failed"); + } + else if (dev->protocol == GRUB_USBMS_PROTOCOL_CBI) + { + /* Try to get status from interrupt pipe */ + err = grub_usb_bulk_read (dev->dev, dev->intrpt->endp_addr, + 2, (char*)&status); + grub_dprintf ("usb", "CBI cmdcb setup status: err=%d, status=0x%x\n", err, status); + } + /* Any other error could be transport problem, try it again */ + goto retry; + } + + /* Read/write the data, (maybe) according to specification. */ + if (size && (read_write == 0)) + { + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf); + grub_dprintf ("usb", "read: %d\n", err); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + goto retry; + } + } + else if (size) + { + err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf); + grub_dprintf ("usb", "write: %d\n", err); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto retry; + } + } + + /* XXX: It is not clear to me yet, how to check status of CBI + * data transfer on devices without interrupt pipe. + * AFAIK there is probably no status phase to indicate possibly + * bad transported data. + * Maybe we should do check on higher level, i.e. issue RequestSense + * command (we do it already in scsi.c) and check returned values + * (we do not it yet) - ? */ + if (dev->protocol == GRUB_USBMS_PROTOCOL_CBI) + { /* Check status in interrupt pipe */ + err = grub_usb_bulk_read (dev->dev, dev->intrpt->endp_addr, + 2, (char*)&status); + grub_dprintf ("usb", "read status: %d\n", err); + if (err) + { + /* Try to reset device, because it is probably not standard + * situation */ + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + grub_usb_clear_halt (dev->dev, dev->intrpt->endp_addr); + goto retry; + } + if (dev->subclass == GRUB_USBMS_SUBCLASS_UFI) + { + /* These devices should return bASC and bASCQ */ + if (status != 0) + /* Some error, currently we don't care what it is... */ + goto retry; + } + else if (dev->subclass == GRUB_USBMS_SUBCLASS_RBC) + { + /* XXX: I don't understand what returns RBC subclass devices, + * so I don't check it - maybe somebody helps ? */ + } + else + { + /* Any other device should return bType = 0 and some bValue */ + if (status & 0xff) + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI status type != 0"); + status = (status & 0x0300) >> 8; + switch (status) + { + case 0 : /* OK */ + break; + case 1 : /* Fail */ + goto retry; + break; + case 2 : /* Phase error */ + case 3 : /* Persistent Failure */ + grub_dprintf ("usb", "CBI reset device - phase error or persistent failure\n"); + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + grub_usb_clear_halt (dev->dev, dev->intrpt->endp_addr); + goto retry; + break; + } + } + } + + return err; +} + + +static grub_err_t +grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) +{ + grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; + + if (dev->protocol == GRUB_USBMS_PROTOCOL_BULK) + return grub_usbms_transfer_bo (scsi, cmdsize, cmd, size, buf, + read_write); + else + return grub_usbms_transfer_cbi (scsi, cmdsize, cmd, size, buf, + read_write); +} static grub_err_t grub_usbms_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, diff -urB ./grub/include/grub/usb.h ./grub_patched/include/grub/usb.h --- ./grub/include/grub/usb.h 2010-09-23 21:45:45.000000000 +0200 +++ ./grub_patched/include/grub/usb.h 2010-09-25 21:04:38.000000000 +0200 @@ -245,7 +245,10 @@ typedef enum { - GRUB_USBMS_PROTOCOL_BULK = 0x50 + GRUB_USBMS_PROTOCOL_BULK = 0x50, + /* Experimental support for Control/Bulk/Interrupt (CBI) devices */ + GRUB_USBMS_PROTOCOL_CBI = 0x00, /* CBI with interrupt */ + GRUB_USBMS_PROTOCOL_CB = 0x01 /* CBI wthout interrupt */ } grub_usbms_protocol_t; static inline struct grub_usb_desc_if *
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel