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>
This commit is contained in:
parent
0f0c720621
commit
e9738dbe2b
|
@ -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)
|
static int wait_for_tds(qtd_t *head)
|
||||||
{
|
{
|
||||||
|
/* returns the amount of bytes *not* transmitted, or -1 for error */
|
||||||
int result = 0;
|
int result = 0;
|
||||||
qtd_t *cur = head;
|
qtd_t *cur = head;
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -291,16 +292,18 @@ static int wait_for_tds(qtd_t *head)
|
||||||
if (timeout < 0) {
|
if (timeout < 0) {
|
||||||
usb_debug("Error: ehci: queue transfer "
|
usb_debug("Error: ehci: queue transfer "
|
||||||
"processing timed out.\n");
|
"processing timed out.\n");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (cur->token & QTD_HALTED) {
|
if (cur->token & QTD_HALTED) {
|
||||||
usb_debug("ERROR with packet\n");
|
usb_debug("ERROR with packet\n");
|
||||||
dump_td(virt_to_phys(cur));
|
dump_td(virt_to_phys(cur));
|
||||||
usb_debug("-----------------\n");
|
usb_debug("-----------------\n");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
result += (cur->token & QTD_TOTAL_LEN_MASK)
|
||||||
|
>> QTD_TOTAL_LEN_SHIFT;
|
||||||
if (cur->next_qtd & 1) {
|
if (cur->next_qtd & 1) {
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
if (0) dump_td(virt_to_phys(cur));
|
if (0) dump_td(virt_to_phys(cur));
|
||||||
/* helps debugging the TD chain */
|
/* helps debugging the TD chain */
|
||||||
|
@ -338,13 +341,13 @@ static int ehci_process_async_schedule(
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
/* make sure async schedule is disabled */
|
/* 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 */
|
/* hook up QH */
|
||||||
ehcic->operation->asynclistaddr = virt_to_phys(qhead);
|
ehcic->operation->asynclistaddr = virt_to_phys(qhead);
|
||||||
|
|
||||||
/* start async schedule */
|
/* start async schedule */
|
||||||
if (ehci_set_async_schedule(ehcic, 1)) return 1;
|
if (ehci_set_async_schedule(ehcic, 1)) return -1;
|
||||||
|
|
||||||
/* wait for result */
|
/* wait for result */
|
||||||
result = wait_for_tds(head);
|
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)
|
static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
int remaining = size;
|
||||||
int endp = ep->endpoint & 0xf;
|
int endp = ep->endpoint & 0xf;
|
||||||
int pid = (ep->direction==IN)?EHCI_IN:EHCI_OUT;
|
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) {
|
if (ep->dev->speed < 2) {
|
||||||
/* we need a split transaction */
|
/* we need a split transaction */
|
||||||
if (closest_usb2_hub(ep->dev, &hubaddr, &hubport))
|
if (closest_usb2_hub(ep->dev, &hubaddr, &hubport))
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
qtd_t *head = memalign(64, sizeof(qtd_t));
|
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 |
|
cur->token = QTD_ACTIVE |
|
||||||
(pid << QTD_PID_SHIFT) |
|
(pid << QTD_PID_SHIFT) |
|
||||||
(0 << QTD_CERR_SHIFT);
|
(0 << QTD_CERR_SHIFT);
|
||||||
u32 chunk = fill_td(cur, data, size);
|
u32 chunk = fill_td(cur, data, remaining);
|
||||||
size -= chunk;
|
remaining -= chunk;
|
||||||
data += chunk;
|
data += chunk;
|
||||||
|
|
||||||
cur->alt_next_qtd = QTD_TERMINATE;
|
cur->alt_next_qtd = QTD_TERMINATE;
|
||||||
if (size == 0) {
|
if (remaining == 0) {
|
||||||
cur->next_qtd = virt_to_phys(0) | QTD_TERMINATE;
|
cur->next_qtd = virt_to_phys(0) | QTD_TERMINATE;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -411,10 +415,13 @@ static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
|
||||||
|
|
||||||
result = ehci_process_async_schedule(
|
result = ehci_process_async_schedule(
|
||||||
EHCI_INST(ep->dev->controller), qh, head);
|
EHCI_INST(ep->dev->controller), qh, head);
|
||||||
|
if (result >= 0)
|
||||||
|
result = size - result;
|
||||||
|
|
||||||
ep->toggle = (cur->token & QTD_TOGGLE_MASK) >> QTD_TOGGLE_SHIFT;
|
ep->toggle = (cur->token & QTD_TOGGLE_MASK) >> QTD_TOGGLE_SHIFT;
|
||||||
|
|
||||||
free_qh_and_tds(qh, head);
|
free_qh_and_tds(qh, head);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +439,7 @@ static int ehci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq
|
||||||
if (dev->speed < 2) {
|
if (dev->speed < 2) {
|
||||||
/* we need a split transaction */
|
/* we need a split transaction */
|
||||||
if (closest_usb2_hub(dev, &hubaddr, &hubport))
|
if (closest_usb2_hub(dev, &hubaddr, &hubport))
|
||||||
return 1;
|
return -1;
|
||||||
non_hs_ctrl_ep = 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(
|
result = ehci_process_async_schedule(
|
||||||
EHCI_INST(dev->controller), qh, head);
|
EHCI_INST(dev->controller), qh, head);
|
||||||
|
if (result >= 0)
|
||||||
|
result = dalen - result;
|
||||||
|
|
||||||
free_qh_and_tds(qh, head);
|
free_qh_and_tds(qh, head);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -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_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
|
||||||
static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue);
|
static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue);
|
||||||
static u8* ohci_poll_intr_queue (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
|
#ifdef USB_DEBUG
|
||||||
static void
|
static void
|
||||||
|
@ -321,13 +321,13 @@ wait_for_ed(usbdev_t *dev, ed_t *head, int pages)
|
||||||
usb_debug("Error: ohci: endpoint "
|
usb_debug("Error: ohci: endpoint "
|
||||||
"descriptor processing timed out.\n");
|
"descriptor processing timed out.\n");
|
||||||
/* Clear the done queue. */
|
/* 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) {
|
if (head->head_pointer & 1) {
|
||||||
usb_debug("HALTED!\n");
|
usb_debug("HALTED!\n");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -355,6 +355,7 @@ static int
|
||||||
ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
|
ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
|
||||||
unsigned char *data)
|
unsigned char *data)
|
||||||
{
|
{
|
||||||
|
int remaining = dalen;
|
||||||
td_t *cur;
|
td_t *cur;
|
||||||
|
|
||||||
// pages are specified as 4K in OHCI, so don't use getpagesize()
|
// 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);
|
cur->current_buffer_pointer = virt_to_phys(data);
|
||||||
pages--;
|
pages--;
|
||||||
int consumed = (4096 - ((unsigned long)data % 4096));
|
int consumed = (4096 - ((unsigned long)data % 4096));
|
||||||
if (consumed >= dalen) {
|
if (consumed >= remaining) {
|
||||||
// end of data is within same page
|
// end of data is within same page
|
||||||
cur->buffer_end = virt_to_phys(data + dalen - 1);
|
cur->buffer_end = virt_to_phys(data + remaining - 1);
|
||||||
dalen = 0;
|
remaining = 0;
|
||||||
/* assert(pages == 0); */
|
/* assert(pages == 0); */
|
||||||
} else {
|
} else {
|
||||||
dalen -= consumed;
|
remaining -= consumed;
|
||||||
data += consumed;
|
data += consumed;
|
||||||
pages--;
|
pages--;
|
||||||
int second_page_size = dalen;
|
int second_page_size = remaining;
|
||||||
if (dalen > 4096) {
|
if (remaining > 4096) {
|
||||||
second_page_size = 4096;
|
second_page_size = 4096;
|
||||||
}
|
}
|
||||||
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
||||||
dalen -= second_page_size;
|
remaining -= second_page_size;
|
||||||
data += 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->HcControl |= ControlListEnable;
|
||||||
OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled;
|
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));
|
(dalen==0)?0:(last_page - first_page + 1));
|
||||||
/* Wait some frames before and one after disabling list access. */
|
/* Wait some frames before and one after disabling list access. */
|
||||||
mdelay(4);
|
mdelay(4);
|
||||||
|
@ -464,7 +465,10 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
|
||||||
/* free memory */
|
/* free memory */
|
||||||
ohci_free_ed(head);
|
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 */
|
/* 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)
|
ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int remaining = dalen;
|
||||||
usb_debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
|
usb_debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
|
||||||
|
|
||||||
td_t *cur, *next;
|
td_t *cur, *next;
|
||||||
|
@ -501,28 +506,28 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
|
||||||
TD_CC_NOACCESS;
|
TD_CC_NOACCESS;
|
||||||
cur->current_buffer_pointer = virt_to_phys(data);
|
cur->current_buffer_pointer = virt_to_phys(data);
|
||||||
pages--;
|
pages--;
|
||||||
if (dalen == 0) {
|
if (remaining == 0) {
|
||||||
/* magic TD for empty packet transfer */
|
/* magic TD for empty packet transfer */
|
||||||
cur->current_buffer_pointer = 0;
|
cur->current_buffer_pointer = 0;
|
||||||
cur->buffer_end = 0;
|
cur->buffer_end = 0;
|
||||||
/* assert((pages == 0) && finalize); */
|
/* assert((pages == 0) && finalize); */
|
||||||
}
|
}
|
||||||
int consumed = (4096 - ((unsigned long)data % 4096));
|
int consumed = (4096 - ((unsigned long)data % 4096));
|
||||||
if (consumed >= dalen) {
|
if (consumed >= remaining) {
|
||||||
// end of data is within same page
|
// end of data is within same page
|
||||||
cur->buffer_end = virt_to_phys(data + dalen - 1);
|
cur->buffer_end = virt_to_phys(data + remaining - 1);
|
||||||
dalen = 0;
|
remaining = 0;
|
||||||
/* assert(pages == finalize); */
|
/* assert(pages == finalize); */
|
||||||
} else {
|
} else {
|
||||||
dalen -= consumed;
|
remaining -= consumed;
|
||||||
data += consumed;
|
data += consumed;
|
||||||
pages--;
|
pages--;
|
||||||
int second_page_size = dalen;
|
int second_page_size = remaining;
|
||||||
if (dalen > 4096) {
|
if (remaining > 4096) {
|
||||||
second_page_size = 4096;
|
second_page_size = 4096;
|
||||||
}
|
}
|
||||||
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
cur->buffer_end = virt_to_phys(data + second_page_size - 1);
|
||||||
dalen -= second_page_size;
|
remaining -= second_page_size;
|
||||||
data += second_page_size;
|
data += second_page_size;
|
||||||
}
|
}
|
||||||
/* One more TD. */
|
/* 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->HcControl |= BulkListEnable;
|
||||||
OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled;
|
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));
|
(dalen==0)?0:(last_page - first_page + 1));
|
||||||
/* Wait some frames before and one after disabling list access. */
|
/* Wait some frames before and one after disabling list access. */
|
||||||
mdelay(4);
|
mdelay(4);
|
||||||
|
@ -570,12 +575,12 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
|
||||||
/* free memory */
|
/* free memory */
|
||||||
ohci_free_ed(head);
|
ohci_free_ed(head);
|
||||||
|
|
||||||
if (failure) {
|
if (result >= 0)
|
||||||
/* try cleanup */
|
result = dalen - result;
|
||||||
|
else
|
||||||
clear_stall(ep);
|
clear_stall(ep);
|
||||||
}
|
|
||||||
|
|
||||||
return failure;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -780,9 +785,11 @@ ohci_poll_intr_queue(void *const q_)
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
ohci_process_done_queue(ohci_t *const ohci, const int spew_debug)
|
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;
|
int i, j;
|
||||||
|
|
||||||
/* Temporary queue of interrupt queue TDs (to reverse order). */
|
/* 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. */
|
/* Check if done head has been written. */
|
||||||
if (!(ohci->opreg->HcInterruptStatus & WritebackDoneHead))
|
if (!(ohci->opreg->HcInterruptStatus & WritebackDoneHead))
|
||||||
return;
|
return 0;
|
||||||
/* Fetch current done head.
|
/* Fetch current done head.
|
||||||
Lsb is only interesting for hw interrupts. */
|
Lsb is only interesting for hw interrupts. */
|
||||||
u32 phys_done_queue = ohci->hcca->HccaDoneHead & ~1;
|
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) {
|
switch (done_td->config & TD_QUEUETYPE_MASK) {
|
||||||
case TD_QUEUETYPE_ASYNC:
|
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);
|
free((void *)done_td);
|
||||||
break;
|
break;
|
||||||
case TD_QUEUETYPE_INTR: {
|
case TD_QUEUETYPE_INTR: {
|
||||||
|
@ -864,5 +875,7 @@ ohci_process_done_queue(ohci_t *const ohci, const int spew_debug)
|
||||||
}
|
}
|
||||||
if (spew_debug)
|
if (spew_debug)
|
||||||
usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j);
|
usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -386,7 +386,7 @@ uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
|
||||||
} else {
|
} else {
|
||||||
usb_debug ("control packet, req %x\n", req);
|
usb_debug ("control packet, req %x\n", req);
|
||||||
td_dump (td);
|
td_dump (td);
|
||||||
result = 1;
|
result = -1;
|
||||||
}
|
}
|
||||||
free (tds);
|
free (tds);
|
||||||
return result;
|
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");
|
usb_debug("Stalled. Trying to clean up.\n");
|
||||||
clear_stall (ep);
|
clear_stall (ep);
|
||||||
free (tds);
|
free (tds);
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
ep->toggle = toggle;
|
ep->toggle = toggle;
|
||||||
free (tds);
|
free (tds);
|
||||||
|
|
|
@ -157,7 +157,7 @@ get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
|
||||||
dr.wValue = (descType << 8) | descIdx;
|
dr.wValue = (descType << 8) | descIdx;
|
||||||
dr.wIndex = langID;
|
dr.wIndex = langID;
|
||||||
dr.wLength = 8;
|
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",
|
usb_debug ("getting descriptor size (type %x) failed\n",
|
||||||
descType);
|
descType);
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
|
||||||
memset (result, 0, size);
|
memset (result, 0, size);
|
||||||
dr.wLength = size;
|
dr.wLength = size;
|
||||||
if (dev->controller->
|
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",
|
usb_debug ("getting descriptor (type %x, size %x) failed\n",
|
||||||
descType, size);
|
descType, size);
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ clear_feature (usbdev_t *dev, int endp, int feature, int rtype)
|
||||||
dr.wValue = feature;
|
dr.wValue = feature;
|
||||||
dr.wIndex = endp;
|
dr.wIndex = endp;
|
||||||
dr.wLength = 0;
|
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
|
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].toggle = 0;
|
||||||
dev->endpoints[0].direction = SETUP;
|
dev->endpoints[0].direction = SETUP;
|
||||||
mdelay (50);
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
mdelay (50);
|
mdelay (50);
|
||||||
|
|
|
@ -184,7 +184,7 @@ reset_transport (usbdev_t *dev)
|
||||||
dr.wLength = 0;
|
dr.wLength = 0;
|
||||||
|
|
||||||
/* if any of these fails, detach device, as we are lost */
|
/* 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_in) ||
|
||||||
clear_stall (MSC_INST (dev)->bulk_out)) {
|
clear_stall (MSC_INST (dev)->bulk_out)) {
|
||||||
usb_debug ("Detaching unresponsive device.\n");
|
usb_debug ("Detaching unresponsive device.\n");
|
||||||
|
@ -211,9 +211,8 @@ get_max_luns (usbdev_t *dev)
|
||||||
dr.wValue = 0;
|
dr.wValue = 0;
|
||||||
dr.wIndex = 0;
|
dr.wIndex = 0;
|
||||||
dr.wLength = 1;
|
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
|
luns = 0; // assume only 1 lun if req fails
|
||||||
}
|
|
||||||
return luns;
|
return luns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,10 +238,10 @@ wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
|
||||||
static int
|
static int
|
||||||
get_csw (endpoint_t *ep, csw_t *csw)
|
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);
|
clear_stall (ep);
|
||||||
if (ep->dev->controller->bulk
|
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);
|
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);
|
wrap_cbw (&cbw, buflen, dir, cb, cblen);
|
||||||
if (dev->controller->
|
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);
|
return reset_transport (dev);
|
||||||
}
|
}
|
||||||
if (buflen > 0) {
|
if (buflen > 0) {
|
||||||
if (dir == cbw_direction_data_in) {
|
if (dir == cbw_direction_data_in) {
|
||||||
if (dev->controller->
|
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);
|
clear_stall (MSC_INST (dev)->bulk_in);
|
||||||
} else {
|
} else {
|
||||||
if (dev->controller->
|
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);
|
clear_stall (MSC_INST (dev)->bulk_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -588,14 +588,14 @@ xhci_control(usbdev_t *const dev, const direction_t dir,
|
||||||
const size_t off = (size_t)data & 0xffff;
|
const size_t off = (size_t)data & 0xffff;
|
||||||
if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) {
|
if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) {
|
||||||
xhci_debug("Unsupported transfer size\n");
|
xhci_debug("Unsupported transfer size\n");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset endpoint if it's halted */
|
/* Reset endpoint if it's halted */
|
||||||
const unsigned ep_state = EC_GET(STATE, di->devctx.ep0);
|
const unsigned ep_state = EC_GET(STATE, di->devctx.ep0);
|
||||||
if (ep_state == 2 || ep_state == 4) {
|
if (ep_state == 2 || ep_state == 4) {
|
||||||
if (xhci_reset_endpoint(dev, NULL, 0))
|
if (xhci_reset_endpoint(dev, NULL, 0))
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill and enqueue setup TRB */
|
/* Fill and enqueue setup TRB */
|
||||||
|
@ -631,11 +631,12 @@ xhci_control(usbdev_t *const dev, const direction_t dir,
|
||||||
xhci->dbreg[dev->address] = 1;
|
xhci->dbreg[dev->address] = 1;
|
||||||
|
|
||||||
/* Wait for transfer events */
|
/* Wait for transfer events */
|
||||||
int i;
|
int i, transferred = 0;
|
||||||
const int n_stages = 2 + !!dalen;
|
const int n_stages = 2 + !!dalen;
|
||||||
for (i = 0; i < n_stages; ++i) {
|
for (i = 0; i < n_stages; ++i) {
|
||||||
const int ret = xhci_wait_for_transfer(xhci, dev->address, 1);
|
const int ret = xhci_wait_for_transfer(xhci, dev->address, 1);
|
||||||
if (ret != CC_SUCCESS) {
|
transferred += ret;
|
||||||
|
if (ret < 0) {
|
||||||
if (ret == TIMEOUT) {
|
if (ret == TIMEOUT) {
|
||||||
xhci_debug("Stopping ID %d EP 1\n",
|
xhci_debug("Stopping ID %d EP 1\n",
|
||||||
dev->address);
|
dev->address);
|
||||||
|
@ -651,11 +652,11 @@ xhci_control(usbdev_t *const dev, const direction_t dir,
|
||||||
tr->ring, setup, status,
|
tr->ring, setup, status,
|
||||||
ep_state, EC_GET(STATE, di->devctx.ep0),
|
ep_state, EC_GET(STATE, di->devctx.ep0),
|
||||||
xhci->opreg->usbsts);
|
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 */
|
/* 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;
|
const size_t off = (size_t)data & 0xffff;
|
||||||
if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) {
|
if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) {
|
||||||
xhci_debug("Unsupported transfer size\n");
|
xhci_debug("Unsupported transfer size\n");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset endpoint if it's halted */
|
/* Reset endpoint if it's halted */
|
||||||
const unsigned ep_state = EC_GET(STATE, di->devctx.eps[ep_id]);
|
const unsigned ep_state = EC_GET(STATE, di->devctx.eps[ep_id]);
|
||||||
if (ep_state == 2 || ep_state == 4) {
|
if (ep_state == 2 || ep_state == 4) {
|
||||||
if (xhci_reset_endpoint(ep->dev, ep, 0))
|
if (xhci_reset_endpoint(ep->dev, ep, 0))
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enqueue transfer and ring doorbell */
|
/* Enqueue transfer and ring doorbell */
|
||||||
|
@ -693,12 +694,12 @@ xhci_bulk(endpoint_t *const ep,
|
||||||
|
|
||||||
/* Wait for transfer event */
|
/* Wait for transfer event */
|
||||||
const int ret = xhci_wait_for_transfer(xhci, ep->dev->address, ep_id);
|
const int ret = xhci_wait_for_transfer(xhci, ep->dev->address, ep_id);
|
||||||
if (ret != CC_SUCCESS) {
|
if (ret < 0) {
|
||||||
if (ret == TIMEOUT) {
|
if (ret == TIMEOUT) {
|
||||||
xhci_debug("Stopping ID %d EP %d\n",
|
xhci_debug("Stopping ID %d EP %d\n",
|
||||||
ep->dev->address, ep_id);
|
ep->dev->address, ep_id);
|
||||||
xhci_cmd_stop_endpoint(xhci, 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_reset_endpoint(ep->dev, ep, 1);
|
||||||
}
|
}
|
||||||
xhci_debug("Bulk transfer failed: %d\n"
|
xhci_debug("Bulk transfer failed: %d\n"
|
||||||
|
@ -707,10 +708,10 @@ xhci_bulk(endpoint_t *const ep,
|
||||||
ret, ep_state,
|
ret, ep_state,
|
||||||
EC_GET(STATE, di->devctx.eps[ep_id]),
|
EC_GET(STATE, di->devctx.eps[ep_id]),
|
||||||
xhci->opreg->usbsts);
|
xhci->opreg->usbsts);
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static trb_t *
|
static trb_t *
|
||||||
|
|
|
@ -308,7 +308,7 @@ xhci_wait_for_command_done(xhci_t *const xhci,
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns cc of transfer for given slot/endpoint pair */
|
/* returns amount of bytes transferred on success, negative CC on error */
|
||||||
int
|
int
|
||||||
xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id)
|
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)) {
|
while (xhci_wait_for_event_type(xhci, TRB_EV_TRANSFER, &timeout_us)) {
|
||||||
if (TRB_GET(ID, xhci->er.cur) == slot_id &&
|
if (TRB_GET(ID, xhci->er.cur) == slot_id &&
|
||||||
TRB_GET(EP, xhci->er.cur) == ep_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);
|
xhci_advance_event_ring(xhci);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue