Better estimate the maximum USB transfer size.

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2013-03-19 08:17:51 +01:00
parent 2f1071d57e
commit 5dd6f58789
6 changed files with 85 additions and 44 deletions

View file

@ -1,3 +1,7 @@
2013-03-19 Aleš Nesrsta <starous@volny.cz>
Better estimate the maximum USB transfer size.
2013-03-17 Vladimir Serbinenko <phcoder@gmail.com>
Resend a packet if we got the wrong buffer in status.

View file

@ -1902,7 +1902,9 @@ static struct grub_usb_controller_dev usb_controller = {
.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)

View file

@ -1431,7 +1431,9 @@ static struct grub_usb_controller_dev usb_controller =
.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;

View file

@ -823,7 +823,9 @@ static struct grub_usb_controller_dev usb_controller =
.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)

View file

@ -25,6 +25,26 @@
#include <grub/usbtrans.h>
#include <grub/time.h>
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_device_t dev,
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_device_t dev,
return NULL;
}
max = grub_usb_bulk_maxpacket (dev, endpoint);
datablocks = ((size + max - 1) / max);
transfer->transcnt = datablocks;
transfer->size = size - 1;
@ -333,43 +341,59 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
return err;
}
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_bulk_transfer_len)
{
current_size = size - position;
if (current_size >= max_bulk_transfer_len)
current_size = max_bulk_transfer_len;
err = grub_usb_bulk_readwrite (dev, endpoint, current_size,
&data[position], type, 1000, &actual);
transferred += actual;
if (err || (current_size != actual)) break;
}
if (!err && transferred != size)
err = GRUB_USB_ERR_DATA;
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;
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)
{
grub_size_t actual, transferred;
grub_usb_err_t err;
grub_size_t current_size, position;
for (position = 0, transferred = 0;
position < size; position += MAX_USB_TRANSFER_LEN)
{
current_size = size - position;
if (current_size >= MAX_USB_TRANSFER_LEN)
current_size = MAX_USB_TRANSFER_LEN;
err = grub_usb_bulk_readwrite (dev, endpoint, current_size,
&data[position], GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual);
transferred += actual;
if (err || (current_size != actual) ) break;
}
if (!err && transferred != size)
err = GRUB_USB_ERR_DATA;
return err;
return grub_usb_bulk_readwrite_packetize (dev, endpoint,
GRUB_USB_TRANSFER_TYPE_IN,
size, data);
}
grub_usb_err_t

View file

@ -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;