aboutsummaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb/ehci.c
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2013-02-21 13:41:40 -0800
committerIsaac Christensen <isaac.christensen@se-eng.com>2014-08-12 22:03:28 +0200
commite9738dbe2bb564f7be7930aa5b01e9ae3c1e2288 (patch)
tree83dc0c3c1463253ebbf76ebe81a69421744c26f7 /payloads/libpayload/drivers/usb/ehci.c
parent0f0c720621deacc3c51b409e10ea8100acc88c80 (diff)
libpayload: Make USB transfer functions return amount of bytes
The USB bulk and control transfer functions in libpayload currently always return 0 for success and 1 for all errors. This is sufficient for current use cases (essentially just mass storage), but other classes (like certain Ethernet adapters) need to be able to tell if a transfer reached the intended amount of bytes, or if it fell short. This patch slightly changes that USB API to return -1 on errors, and the amount of transferred bytes on successes. All drivers in the current libpayload mainline are modified to conform to the new error detection model. Any third party users of this API will need to adapt their if (...<controller>->bulk/control(...)) checks to if (...<controller>->bulk/control(...) < 0) as well. The host controller drivers for OHCI and EHCI correctly implement the new behavior. UHCI and the XHCI stub just comply with the new API by returning 0 or -1, but do not actually count the returned bytes. Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/48308 Reviewed-by: Gabe Black <gabeblack@chromium.org> Reviewed-by: Stefan Reinauer <reinauer@google.com> Tested-by: Gabe Black <gabeblack@chromium.org> Commit-Queue: Gabe Black <gabeblack@chromium.org> Updated the patch to support XHCI as well. Change-Id: Ic2ea2810c5edb992cbe185bc9711d2f8f557cae6 (cherry picked from commit e39e2d84762a3804653d950a228ed2269c651458) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6390 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com> Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'payloads/libpayload/drivers/usb/ehci.c')
-rw-r--r--payloads/libpayload/drivers/usb/ehci.c29
1 files changed, 19 insertions, 10 deletions
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
index b9837017e4..0764592f33 100644
--- a/payloads/libpayload/drivers/usb/ehci.c
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -270,6 +270,7 @@ static void free_qh_and_tds(ehci_qh_t *qh, qtd_t *cur)
static int wait_for_tds(qtd_t *head)
{
+ /* returns the amount of bytes *not* transmitted, or -1 for error */
int result = 0;
qtd_t *cur = head;
while (1) {
@@ -291,16 +292,18 @@ static int wait_for_tds(qtd_t *head)
if (timeout < 0) {
usb_debug("Error: ehci: queue transfer "
"processing timed out.\n");
- return 1;
+ return -1;
}
if (cur->token & QTD_HALTED) {
usb_debug("ERROR with packet\n");
dump_td(virt_to_phys(cur));
usb_debug("-----------------\n");
- return 1;
+ return -1;
}
+ result += (cur->token & QTD_TOTAL_LEN_MASK)
+ >> QTD_TOTAL_LEN_SHIFT;
if (cur->next_qtd & 1) {
- return 0;
+ break;
}
if (0) dump_td(virt_to_phys(cur));
/* helps debugging the TD chain */
@@ -338,13 +341,13 @@ static int ehci_process_async_schedule(
int result;
/* make sure async schedule is disabled */
- if (ehci_set_async_schedule(ehcic, 0)) return 1;
+ if (ehci_set_async_schedule(ehcic, 0)) return -1;
/* hook up QH */
ehcic->operation->asynclistaddr = virt_to_phys(qhead);
/* start async schedule */
- if (ehci_set_async_schedule(ehcic, 1)) return 1;
+ if (ehci_set_async_schedule(ehcic, 1)) return -1;
/* wait for result */
result = wait_for_tds(head);
@@ -358,6 +361,7 @@ static int ehci_process_async_schedule(
static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
{
int result = 0;
+ int remaining = size;
int endp = ep->endpoint & 0xf;
int pid = (ep->direction==IN)?EHCI_IN:EHCI_OUT;
@@ -365,7 +369,7 @@ static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
if (ep->dev->speed < 2) {
/* we need a split transaction */
if (closest_usb2_hub(ep->dev, &hubaddr, &hubport))
- return 1;
+ return -1;
}
qtd_t *head = memalign(64, sizeof(qtd_t));
@@ -375,12 +379,12 @@ static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
cur->token = QTD_ACTIVE |
(pid << QTD_PID_SHIFT) |
(0 << QTD_CERR_SHIFT);
- u32 chunk = fill_td(cur, data, size);
- size -= chunk;
+ u32 chunk = fill_td(cur, data, remaining);
+ remaining -= chunk;
data += chunk;
cur->alt_next_qtd = QTD_TERMINATE;
- if (size == 0) {
+ if (remaining == 0) {
cur->next_qtd = virt_to_phys(0) | QTD_TERMINATE;
break;
} else {
@@ -411,10 +415,13 @@ static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
result = ehci_process_async_schedule(
EHCI_INST(ep->dev->controller), qh, head);
+ if (result >= 0)
+ result = size - result;
ep->toggle = (cur->token & QTD_TOGGLE_MASK) >> QTD_TOGGLE_SHIFT;
free_qh_and_tds(qh, head);
+
return result;
}
@@ -432,7 +439,7 @@ static int ehci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq
if (dev->speed < 2) {
/* we need a split transaction */
if (closest_usb2_hub(dev, &hubaddr, &hubport))
- return 1;
+ return -1;
non_hs_ctrl_ep = 1;
}
@@ -499,6 +506,8 @@ static int ehci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq
result = ehci_process_async_schedule(
EHCI_INST(dev->controller), qh, head);
+ if (result >= 0)
+ result = dalen - result;
free_qh_and_tds(qh, head);
return result;