diff -purB ./grub/grub-core/bus/usb/ehci.c ./grub_patched/grub-core/bus/usb/ehci.c --- ./grub/grub-core/bus/usb/ehci.c 2013-01-27 22:12:17.000000000 +0100 +++ ./grub_patched/grub-core/bus/usb/ehci.c 2013-03-07 19:14:33.000000000 +0100 @@ -1902,7 +1902,9 @@ static struct grub_usb_controller_dev us .cancel_transfer = grub_ehci_cancel_transfer, .hubports = grub_ehci_hubports, .portstatus = grub_ehci_portstatus, - .detect_dev = grub_ehci_detect_dev + .detect_dev = grub_ehci_detect_dev, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = GRUB_EHCI_N_TD * 3 / 4 }; GRUB_MOD_INIT (ehci) diff -purB ./grub/grub-core/bus/usb/ohci.c ./grub_patched/grub-core/bus/usb/ohci.c --- ./grub/grub-core/bus/usb/ohci.c 2013-01-27 22:12:17.000000000 +0100 +++ ./grub_patched/grub-core/bus/usb/ohci.c 2013-03-07 19:14:33.000000000 +0100 @@ -1431,7 +1431,9 @@ static struct grub_usb_controller_dev us .cancel_transfer = grub_ohci_cancel_transfer, .hubports = grub_ohci_hubports, .portstatus = grub_ohci_portstatus, - .detect_dev = grub_ohci_detect_dev + .detect_dev = grub_ohci_detect_dev, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = GRUB_OHCI_TDS * 3 / 4 }; static struct grub_preboot *fini_hnd; diff -purB ./grub/grub-core/bus/usb/uhci.c ./grub_patched/grub-core/bus/usb/uhci.c --- ./grub/grub-core/bus/usb/uhci.c 2013-01-27 22:12:17.000000000 +0100 +++ ./grub_patched/grub-core/bus/usb/uhci.c 2013-03-07 19:14:33.000000000 +0100 @@ -823,7 +823,9 @@ static struct grub_usb_controller_dev us .cancel_transfer = grub_uhci_cancel_transfer, .hubports = grub_uhci_hubports, .portstatus = grub_uhci_portstatus, - .detect_dev = grub_uhci_detect_dev + .detect_dev = grub_uhci_detect_dev, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = N_TD * 3 / 4 }; GRUB_MOD_INIT(uhci) diff -purB ./grub/grub-core/bus/usb/usbtrans.c ./grub_patched/grub-core/bus/usb/usbtrans.c --- ./grub/grub-core/bus/usb/usbtrans.c 2013-01-27 22:12:17.000000000 +0100 +++ ./grub_patched/grub-core/bus/usb/usbtrans.c 2013-03-07 20:09:38.000000000 +0100 @@ -25,6 +25,26 @@ #include #include + +static inline unsigned int +grub_usb_bulk_maxpacket (grub_usb_device_t dev, int endpoint) +{ + unsigned int max = 64; + + /* Use the maximum packet size given in the endpoint descriptor. */ + if (dev->initialized) + { + struct grub_usb_desc_endp *endpdesc; + endpdesc = grub_usb_get_endpdescriptor (dev, endpoint); + + if (endpdesc) + max = endpdesc->maxpacket; + } + + return max; +} + + static grub_usb_err_t grub_usb_execute_and_wait_transfer (grub_usb_device_t dev, grub_usb_transfer_t transfer, @@ -224,20 +244,6 @@ grub_usb_bulk_setup_readwrite (grub_usb_ if (type == GRUB_USB_TRANSFER_TYPE_OUT) grub_memcpy ((char *) data, data_in, size); - /* Use the maximum packet size given in the endpoint descriptor. */ - if (dev->initialized) - { - struct grub_usb_desc_endp *endpdesc; - endpdesc = grub_usb_get_endpdescriptor (dev, endpoint); - - if (endpdesc) - max = endpdesc->maxpacket; - else - max = 64; - } - else - max = 64; - /* Create a transfer. */ transfer = grub_malloc (sizeof (struct grub_usb_transfer)); if (! transfer) @@ -246,6 +252,8 @@ grub_usb_bulk_setup_readwrite (grub_usb_ return NULL; } + max = grub_usb_bulk_maxpacket (dev, endpoint); + datablocks = ((size + max - 1) / max); transfer->transcnt = datablocks; transfer->size = size - 1; @@ -333,38 +341,36 @@ grub_usb_bulk_readwrite (grub_usb_device return err; } -grub_usb_err_t -grub_usb_bulk_write (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data) -{ - grub_size_t actual; - grub_usb_err_t err; - - err = grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_OUT, 1000, &actual); - if (!err && actual != size) - err = GRUB_USB_ERR_DATA; - return err; -} - -grub_usb_err_t -grub_usb_bulk_read (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data) +static grub_usb_err_t +grub_usb_bulk_readwrite_packetize (grub_usb_device_t dev, + int endpoint, + grub_transfer_type_t type, + grub_size_t size, char *data) { grub_size_t actual, transferred; grub_usb_err_t err; grub_size_t current_size, position; + grub_size_t max_bulk_transfer_len = MAX_USB_TRANSFER_LEN; + grub_size_t max; + + if (dev->controller.dev->max_bulk_tds) + { + max = grub_usb_bulk_maxpacket (dev, endpoint); + + /* Calculate max. possible length of bulk transfer */ + max_bulk_transfer_len = dev->controller.dev->max_bulk_tds * max; + } for (position = 0, transferred = 0; - position < size; position += MAX_USB_TRANSFER_LEN) + position < size; position += max_bulk_transfer_len) { current_size = size - position; - if (current_size >= MAX_USB_TRANSFER_LEN) - current_size = MAX_USB_TRANSFER_LEN; + if (current_size >= max_bulk_transfer_len) + current_size = max_bulk_transfer_len; err = grub_usb_bulk_readwrite (dev, endpoint, current_size, - &data[position], GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual); + &data[position], type, 1000, &actual); transferred += actual; - if (err || (current_size != actual) ) break; + if (err || (current_size != actual)) break; } if (!err && transferred != size) @@ -373,6 +379,24 @@ grub_usb_bulk_read (grub_usb_device_t de } grub_usb_err_t +grub_usb_bulk_write (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data) +{ + return grub_usb_bulk_readwrite_packetize (dev, endpoint, + GRUB_USB_TRANSFER_TYPE_OUT, + size, data); +} + +grub_usb_err_t +grub_usb_bulk_read (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data) +{ + return grub_usb_bulk_readwrite_packetize (dev, endpoint, + GRUB_USB_TRANSFER_TYPE_IN, + size, data); +} + +grub_usb_err_t grub_usb_check_transfer (grub_usb_transfer_t transfer, grub_size_t *actual) { grub_usb_err_t err; diff -purB ./grub/include/grub/usb.h ./grub_patched/include/grub/usb.h --- ./grub/include/grub/usb.h 2013-01-27 22:12:17.000000000 +0100 +++ ./grub_patched/include/grub/usb.h 2013-03-07 19:14:33.000000000 +0100 @@ -124,6 +124,13 @@ struct grub_usb_controller_dev /* Per controller flag - port reset pending, don't do another reset */ grub_uint64_t pending_reset; + + /* Max. number of transfer descriptors used per one bulk transfer */ + /* The reason is to prevent "exhausting" of TD by large bulk */ + /* transfer - number of TD is limited in USB host driver */ + /* Value is calculated/estimated in driver - some TDs should be */ + /* reserved for posible concurrent control or "interrupt" transfers */ + grub_size_t max_bulk_tds; /* The next host controller. */ struct grub_usb_controller_dev *next;