diff options
Diffstat (limited to 'payloads')
-rw-r--r-- | payloads/libpayload/drivers/usb/ehci.c | 29 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/ohci.c | 71 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/uhci.c | 4 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/usb.c | 8 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/usbmsc.c | 15 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci.c | 25 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_events.c | 6 |
7 files changed, 91 insertions, 67 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; diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c index 0683de3c5d..723b0dbadf 100644 --- a/payloads/libpayload/drivers/usb/ohci.c +++ b/payloads/libpayload/drivers/usb/ohci.c @@ -44,7 +44,7 @@ static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue); static u8* ohci_poll_intr_queue (void *queue); -static void ohci_process_done_queue(ohci_t *ohci, int spew_debug); +static int ohci_process_done_queue(ohci_t *ohci, int spew_debug); #ifdef USB_DEBUG static void @@ -321,13 +321,13 @@ wait_for_ed(usbdev_t *dev, ed_t *head, int pages) usb_debug("Error: ohci: endpoint " "descriptor processing timed out.\n"); /* Clear the done queue. */ - ohci_process_done_queue(OHCI_INST(dev->controller), 1); + int result = ohci_process_done_queue(OHCI_INST(dev->controller), 1); if (head->head_pointer & 1) { usb_debug("HALTED!\n"); - return 1; + return -1; } - return 0; + return result; } static void @@ -355,6 +355,7 @@ static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, unsigned char *data) { + int remaining = dalen; td_t *cur; // pages are specified as 4K in OHCI, so don't use getpagesize() @@ -392,21 +393,21 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen cur->current_buffer_pointer = virt_to_phys(data); pages--; int consumed = (4096 - ((unsigned long)data % 4096)); - if (consumed >= dalen) { + if (consumed >= remaining) { // end of data is within same page - cur->buffer_end = virt_to_phys(data + dalen - 1); - dalen = 0; + cur->buffer_end = virt_to_phys(data + remaining - 1); + remaining = 0; /* assert(pages == 0); */ } else { - dalen -= consumed; + remaining -= consumed; data += consumed; pages--; - int second_page_size = dalen; - if (dalen > 4096) { + int second_page_size = remaining; + if (remaining > 4096) { second_page_size = 4096; } cur->buffer_end = virt_to_phys(data + second_page_size - 1); - dalen -= second_page_size; + remaining -= second_page_size; data += second_page_size; } } @@ -454,7 +455,7 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen OHCI_INST(dev->controller)->opreg->HcControl |= ControlListEnable; OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled; - int failure = wait_for_ed(dev, head, + int result = wait_for_ed(dev, head, (dalen==0)?0:(last_page - first_page + 1)); /* Wait some frames before and one after disabling list access. */ mdelay(4); @@ -464,7 +465,10 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen /* free memory */ ohci_free_ed(head); - return failure; + if (result >= 0) + result = dalen - result; + + return result; } /* finalize == 1: if data is of packet aligned size, add a zero length packet */ @@ -472,6 +476,7 @@ static int ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) { int i; + int remaining = dalen; usb_debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize); td_t *cur, *next; @@ -501,28 +506,28 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) TD_CC_NOACCESS; cur->current_buffer_pointer = virt_to_phys(data); pages--; - if (dalen == 0) { + if (remaining == 0) { /* magic TD for empty packet transfer */ cur->current_buffer_pointer = 0; cur->buffer_end = 0; /* assert((pages == 0) && finalize); */ } int consumed = (4096 - ((unsigned long)data % 4096)); - if (consumed >= dalen) { + if (consumed >= remaining) { // end of data is within same page - cur->buffer_end = virt_to_phys(data + dalen - 1); - dalen = 0; + cur->buffer_end = virt_to_phys(data + remaining - 1); + remaining = 0; /* assert(pages == finalize); */ } else { - dalen -= consumed; + remaining -= consumed; data += consumed; pages--; - int second_page_size = dalen; - if (dalen > 4096) { + int second_page_size = remaining; + if (remaining > 4096) { second_page_size = 4096; } cur->buffer_end = virt_to_phys(data + second_page_size - 1); - dalen -= second_page_size; + remaining -= second_page_size; data += second_page_size; } /* One more TD. */ @@ -558,7 +563,7 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) OHCI_INST(ep->dev->controller)->opreg->HcControl |= BulkListEnable; OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled; - int failure = wait_for_ed(ep->dev, head, + int result = wait_for_ed(ep->dev, head, (dalen==0)?0:(last_page - first_page + 1)); /* Wait some frames before and one after disabling list access. */ mdelay(4); @@ -570,12 +575,12 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) /* free memory */ ohci_free_ed(head); - if (failure) { - /* try cleanup */ + if (result >= 0) + result = dalen - result; + else clear_stall(ep); - } - return failure; + return result; } @@ -780,9 +785,11 @@ ohci_poll_intr_queue(void *const q_) return data; } -static void +static int ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) { + /* returns the amount of bytes *not* transmitted for short packets */ + int result = 0; int i, j; /* Temporary queue of interrupt queue TDs (to reverse order). */ @@ -790,7 +797,7 @@ ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) /* Check if done head has been written. */ if (!(ohci->opreg->HcInterruptStatus & WritebackDoneHead)) - return; + return 0; /* Fetch current done head. Lsb is only interesting for hw interrupts. */ u32 phys_done_queue = ohci->hcca->HccaDoneHead & ~1; @@ -807,7 +814,11 @@ ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) switch (done_td->config & TD_QUEUETYPE_MASK) { case TD_QUEUETYPE_ASYNC: - /* Free processed async TDs. */ + /* Free processed async TDs and count short transfer. */ + if (done_td->current_buffer_pointer) + result += (done_td->buffer_end & 0xfff) - + (done_td->current_buffer_pointer + & 0xfff) + 1; free((void *)done_td); break; case TD_QUEUETYPE_INTR: { @@ -864,5 +875,7 @@ ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) } if (spew_debug) usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j); + + return result; } diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c index b81b911eef..3eff612129 100644 --- a/payloads/libpayload/drivers/usb/uhci.c +++ b/payloads/libpayload/drivers/usb/uhci.c @@ -386,7 +386,7 @@ uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen } else { usb_debug ("control packet, req %x\n", req); td_dump (td); - result = 1; + result = -1; } free (tds); return result; @@ -468,7 +468,7 @@ uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize) usb_debug("Stalled. Trying to clean up.\n"); clear_stall (ep); free (tds); - return 1; + return -1; } ep->toggle = toggle; free (tds); diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c index 6b54dbbada..d9727f01b0 100644 --- a/payloads/libpayload/drivers/usb/usb.c +++ b/payloads/libpayload/drivers/usb/usb.c @@ -157,7 +157,7 @@ get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType, dr.wValue = (descType << 8) | descIdx; dr.wIndex = langID; dr.wLength = 8; - if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) { + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf) < 0) { usb_debug ("getting descriptor size (type %x) failed\n", descType); } @@ -181,7 +181,7 @@ get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType, memset (result, 0, size); dr.wLength = size; if (dev->controller-> - control (dev, IN, sizeof (dr), &dr, size, result)) { + control (dev, IN, sizeof (dr), &dr, size, result) < 0) { usb_debug ("getting descriptor (type %x, size %x) failed\n", descType, size); } @@ -213,7 +213,7 @@ clear_feature (usbdev_t *dev, int endp, int feature, int rtype) dr.wValue = feature; dr.wIndex = endp; dr.wLength = 0; - return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); + return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0; } int @@ -270,7 +270,7 @@ generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr) dev->endpoints[0].toggle = 0; dev->endpoints[0].direction = SETUP; mdelay (50); - if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) { + if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0) { return -1; } mdelay (50); diff --git a/payloads/libpayload/drivers/usb/usbmsc.c b/payloads/libpayload/drivers/usb/usbmsc.c index f1de483170..2d28fbd12f 100644 --- a/payloads/libpayload/drivers/usb/usbmsc.c +++ b/payloads/libpayload/drivers/usb/usbmsc.c @@ -184,7 +184,7 @@ reset_transport (usbdev_t *dev) dr.wLength = 0; /* if any of these fails, detach device, as we are lost */ - if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) || + if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0 || clear_stall (MSC_INST (dev)->bulk_in) || clear_stall (MSC_INST (dev)->bulk_out)) { usb_debug ("Detaching unresponsive device.\n"); @@ -211,9 +211,8 @@ get_max_luns (usbdev_t *dev) dr.wValue = 0; dr.wIndex = 0; dr.wLength = 1; - if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns)) { + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns) < 0) luns = 0; // assume only 1 lun if req fails - } return luns; } @@ -239,10 +238,10 @@ wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd, static int get_csw (endpoint_t *ep, csw_t *csw) { - if (ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1)) { + if (ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) { clear_stall (ep); if (ep->dev->controller->bulk - (ep, sizeof (csw_t), (u8 *) csw, 1)) { + (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) { return reset_transport (ep->dev); } } @@ -265,17 +264,17 @@ execute_command (usbdev_t *dev, cbw_direction dir, const u8 *cb, int cblen, } wrap_cbw (&cbw, buflen, dir, cb, cblen); if (dev->controller-> - bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0)) { + bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0) < 0) { return reset_transport (dev); } if (buflen > 0) { if (dir == cbw_direction_data_in) { if (dev->controller-> - bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0)) + bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0) < 0) clear_stall (MSC_INST (dev)->bulk_in); } else { if (dev->controller-> - bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0)) + bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0) < 0) clear_stall (MSC_INST (dev)->bulk_out); } } diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index d31a8e2a8e..184a370936 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -588,14 +588,14 @@ xhci_control(usbdev_t *const dev, const direction_t dir, const size_t off = (size_t)data & 0xffff; if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) { xhci_debug("Unsupported transfer size\n"); - return 1; + return -1; } /* Reset endpoint if it's halted */ const unsigned ep_state = EC_GET(STATE, di->devctx.ep0); if (ep_state == 2 || ep_state == 4) { if (xhci_reset_endpoint(dev, NULL, 0)) - return 1; + return -1; } /* Fill and enqueue setup TRB */ @@ -631,11 +631,12 @@ xhci_control(usbdev_t *const dev, const direction_t dir, xhci->dbreg[dev->address] = 1; /* Wait for transfer events */ - int i; + int i, transferred = 0; const int n_stages = 2 + !!dalen; for (i = 0; i < n_stages; ++i) { const int ret = xhci_wait_for_transfer(xhci, dev->address, 1); - if (ret != CC_SUCCESS) { + transferred += ret; + if (ret < 0) { if (ret == TIMEOUT) { xhci_debug("Stopping ID %d EP 1\n", dev->address); @@ -651,11 +652,11 @@ xhci_control(usbdev_t *const dev, const direction_t dir, tr->ring, setup, status, ep_state, EC_GET(STATE, di->devctx.ep0), xhci->opreg->usbsts); - return 1; + return ret; } } - return 0; + return transferred; } /* finalize == 1: if data is of packet aligned size, add a zero length packet */ @@ -675,14 +676,14 @@ xhci_bulk(endpoint_t *const ep, const size_t off = (size_t)data & 0xffff; if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) { xhci_debug("Unsupported transfer size\n"); - return 1; + return -1; } /* Reset endpoint if it's halted */ const unsigned ep_state = EC_GET(STATE, di->devctx.eps[ep_id]); if (ep_state == 2 || ep_state == 4) { if (xhci_reset_endpoint(ep->dev, ep, 0)) - return 1; + return -1; } /* Enqueue transfer and ring doorbell */ @@ -693,12 +694,12 @@ xhci_bulk(endpoint_t *const ep, /* Wait for transfer event */ const int ret = xhci_wait_for_transfer(xhci, ep->dev->address, ep_id); - if (ret != CC_SUCCESS) { + if (ret < 0) { if (ret == TIMEOUT) { xhci_debug("Stopping ID %d EP %d\n", ep->dev->address, ep_id); xhci_cmd_stop_endpoint(xhci, ep->dev->address, ep_id); - } else if (ret == CC_STALL_ERROR) { + } else if (ret == -CC_STALL_ERROR) { xhci_reset_endpoint(ep->dev, ep, 1); } xhci_debug("Bulk transfer failed: %d\n" @@ -707,10 +708,10 @@ xhci_bulk(endpoint_t *const ep, ret, ep_state, EC_GET(STATE, di->devctx.eps[ep_id]), xhci->opreg->usbsts); - return 1; + return ret; } - return 0; + return ret; } static trb_t * diff --git a/payloads/libpayload/drivers/usb/xhci_events.c b/payloads/libpayload/drivers/usb/xhci_events.c index b04ecda267..ab90c59fe3 100644 --- a/payloads/libpayload/drivers/usb/xhci_events.c +++ b/payloads/libpayload/drivers/usb/xhci_events.c @@ -308,7 +308,7 @@ xhci_wait_for_command_done(xhci_t *const xhci, return cc; } -/* returns cc of transfer for given slot/endpoint pair */ +/* returns amount of bytes transferred on success, negative CC on error */ int xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id) { @@ -319,7 +319,9 @@ xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id) while (xhci_wait_for_event_type(xhci, TRB_EV_TRANSFER, &timeout_us)) { if (TRB_GET(ID, xhci->er.cur) == slot_id && TRB_GET(EP, xhci->er.cur) == ep_id) { - cc = TRB_GET(CC, xhci->er.cur); + cc = -TRB_GET(CC, xhci->er.cur); + if (cc == -CC_SUCCESS || cc == -CC_SHORT_PACKET) + cc = TRB_GET(EVTL, xhci->er.cur); xhci_advance_event_ring(xhci); break; } |