summaryrefslogtreecommitdiff
path: root/payloads
diff options
context:
space:
mode:
Diffstat (limited to 'payloads')
-rw-r--r--payloads/libpayload/drivers/usb/ehci.c29
-rw-r--r--payloads/libpayload/drivers/usb/ohci.c71
-rw-r--r--payloads/libpayload/drivers/usb/uhci.c4
-rw-r--r--payloads/libpayload/drivers/usb/usb.c8
-rw-r--r--payloads/libpayload/drivers/usb/usbmsc.c15
-rw-r--r--payloads/libpayload/drivers/usb/xhci.c25
-rw-r--r--payloads/libpayload/drivers/usb/xhci_events.c6
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;
}