From 829b8be43202b960d5be066cbd8c6ba25552a1d0 Mon Sep 17 00:00:00 2001 From: Tomasz Michalec Date: Thu, 13 Nov 2025 16:07:19 +0100 Subject: [PATCH] libpayload: Add bulk with timeout callback to USB Add bulk_timeout() callback to USB controllers that allows to issue bulk transaction with configurable timeout. This allows to peek if there is any incoming data from USB device without needing to wait 5 seconds if there is no data. 'finalize' argument is omitted in bulk_timeout(), because any recent controller doesn't use it in bulk() method anyway. For OHCI and UHCI, which are only controllers using 'finalize', issuing bulk_timeout() with USB_MAX_PROCESSING_TIME_US timeout is the same as issuing bulk() with 'finalize' set to zero. Change-Id: I82dbe307b566e4fc6cca314924168f7ad677efe7 Signed-off-by: Tomasz Michalec Reviewed-on: https://review.coreboot.org/c/coreboot/+/90043 Reviewed-by: Julius Werner Tested-by: build bot (Jenkins) Reviewed-by: Paul Menzel --- payloads/libpayload/drivers/usb/dwc2.c | 49 ++++++++++++------- .../libpayload/drivers/usb/dwc2_private.h | 1 - payloads/libpayload/drivers/usb/ehci.c | 26 ++++++---- payloads/libpayload/drivers/usb/ohci.c | 28 ++++++++--- payloads/libpayload/drivers/usb/uhci.c | 40 ++++++++++----- payloads/libpayload/drivers/usb/xhci.c | 24 +++++---- .../libpayload/drivers/usb/xhci_commands.c | 2 +- payloads/libpayload/drivers/usb/xhci_events.c | 15 +++--- .../libpayload/drivers/usb/xhci_private.h | 6 +-- payloads/libpayload/include/usb/usb.h | 10 ++++ 10 files changed, 134 insertions(+), 67 deletions(-) diff --git a/payloads/libpayload/drivers/usb/dwc2.c b/payloads/libpayload/drivers/usb/dwc2.c index e0b290f58c..5d9bd4cb59 100644 --- a/payloads/libpayload/drivers/usb/dwc2.c +++ b/payloads/libpayload/drivers/usb/dwc2.c @@ -152,13 +152,13 @@ static int dwc2_disconnected(hci_t *controller) * or an error code if the transfer failed */ static int -wait_for_complete(endpoint_t *ep, uint32_t ch_num) +wait_for_complete(endpoint_t *ep, uint32_t ch_num, int timeout_us) { hcint_t hcint; hcchar_t hcchar; hctsiz_t hctsiz; dwc2_reg_t *reg = DWC2_REG(ep->dev->controller); - int timeout = USB_MAX_PROCESSING_TIME_US / DWC2_SLEEP_TIME_US; + int timeout = timeout_us / DWC2_SLEEP_TIME_US; /* * TODO: We should take care of up to three times of transfer error @@ -211,12 +211,12 @@ wait_for_complete(endpoint_t *ep, uint32_t ch_num) hcint.d32 = ~0; writel(hcint.d32, ®->host.hchn[ch_num].hcintn); - return -HCSTAT_TIMEOUT; + return USB_TIMEOUT; } static int dwc2_do_xfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, - uint32_t ch_num, u8 *data_buf, int *short_pkt) + uint32_t ch_num, u8 *data_buf, int *short_pkt, int timeout_us) { uint32_t do_copy; int ret; @@ -279,7 +279,7 @@ dwc2_do_xfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, ®->host.hchn[ch_num].hcdman); writel(hcchar.d32, ®->host.hchn[ch_num].hccharn); - ret = wait_for_complete(ep, ch_num); + ret = wait_for_complete(ep, ch_num, timeout_us); if (ret >= 0) { /* Calculate actual transferred length */ @@ -307,7 +307,7 @@ dwc2_do_xfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, static int dwc2_split_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, uint32_t ch_num, u8 *data_buf, split_info_t *split, - int *short_pkt) + int *short_pkt, int timeout_us) { dwc2_reg_t *reg = DWC2_REG(ep->dev->controller); hfnum_t hfnum; @@ -331,7 +331,7 @@ dwc2_split_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, /* Handle Start-Split */ ret = dwc2_do_xfer(ep, dir == EPDIR_IN ? 0 : size, pid, dir, ch_num, - data_buf, NULL); + data_buf, NULL, timeout_us); if (ret < 0) goto out; @@ -346,7 +346,7 @@ dwc2_split_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, /* Handle Complete-Split */ do { ret = dwc2_do_xfer(ep, dir == EPDIR_OUT ? 0 : size, ep->toggle, - dir, ch_num, data_buf, short_pkt); + dir, ch_num, data_buf, short_pkt, timeout_us); } while (ret == -HCSTAT_NYET); if (dir == EPDIR_IN) @@ -379,11 +379,11 @@ static int dwc2_need_split(usbdev_t *dev, split_info_t *split) static int dwc2_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, uint32_t ch_num, - u8 *src, uint8_t skip_nak) + u8 *src, uint8_t skip_nak, int timeout_us) { split_info_t split; int ret, short_pkt, transferred = 0; - int timeout = USB_MAX_PROCESSING_TIME_US / USB_FULL_LOW_SPEED_FRAME_US; + int timeout = timeout_us / USB_FULL_LOW_SPEED_FRAME_US; ep->toggle = pid; @@ -393,7 +393,7 @@ dwc2_transfer(endpoint_t *ep, int size, int pid, ep_dir_t dir, uint32_t ch_num, nak_retry: ret = dwc2_split_transfer(ep, MIN(ep->maxpacketsize, size), ep->toggle, dir, 0, src, &split, - &short_pkt); + &short_pkt, timeout_us); /* * dwc2_split_transfer() waits for the next FullSpeed @@ -403,10 +403,12 @@ nak_retry: if (ret == -HCSTAT_NAK && !skip_nak && --timeout) { udelay(USB_FULL_LOW_SPEED_FRAME_US / 2); goto nak_retry; + } else if (timeout <= 0 && ret < 0) { + return USB_TIMEOUT; } } else { ret = dwc2_do_xfer(ep, MIN(DMA_SIZE, size), pid, dir, 0, - src, &short_pkt); + src, &short_pkt, timeout_us); } if (ret < 0) @@ -422,7 +424,7 @@ nak_retry: } static int -dwc2_bulk(endpoint_t *ep, int size, u8 *src, int finalize) +dwc2_bulk_timeout(endpoint_t *ep, int size, u8 *src, int timeout_us) { ep_dir_t data_dir; @@ -433,7 +435,13 @@ dwc2_bulk(endpoint_t *ep, int size, u8 *src, int finalize) else return -1; - return dwc2_transfer(ep, size, ep->toggle, data_dir, 0, src, 0); + return dwc2_transfer(ep, size, ep->toggle, data_dir, 0, src, 0, timeout_us); +} + +static int +dwc2_bulk(endpoint_t *ep, int size, u8 *src, int finalize) +{ + return dwc2_bulk_timeout(ep, size, src, USB_MAX_PROCESSING_TIME_US); } static int @@ -452,19 +460,22 @@ dwc2_control(usbdev_t *dev, direction_t dir, int drlen, void *setup, return -1; /* Setup Phase */ - if (dwc2_transfer(ep, drlen, PID_SETUP, EPDIR_OUT, 0, setup, 0) < 0) + if (dwc2_transfer(ep, drlen, PID_SETUP, EPDIR_OUT, 0, setup, 0, + USB_MAX_PROCESSING_TIME_US) < 0) return -1; /* Data Phase */ ep->toggle = PID_DATA1; if (dalen > 0) { - ret = dwc2_transfer(ep, dalen, ep->toggle, data_dir, 0, src, 0); + ret = dwc2_transfer(ep, dalen, ep->toggle, data_dir, 0, src, 0, + USB_MAX_PROCESSING_TIME_US); if (ret < 0) return -1; } /* Status Phase */ - if (dwc2_transfer(ep, 0, PID_DATA1, !data_dir, 0, NULL, 0) < 0) + if (dwc2_transfer(ep, 0, PID_DATA1, !data_dir, 0, NULL, 0, + USB_MAX_PROCESSING_TIME_US) < 0) return -1; return ret; @@ -482,7 +493,8 @@ dwc2_intr(endpoint_t *ep, int size, u8 *src) else return -1; - return dwc2_transfer(ep, size, ep->toggle, data_dir, 0, src, 1); + return dwc2_transfer(ep, size, ep->toggle, data_dir, 0, src, 1, + USB_MAX_PROCESSING_TIME_US); } static u32 dwc2_intr_get_timestamp(intr_queue_t *q) @@ -583,6 +595,7 @@ hci_t *dwc2_init(void *bar) controller->init = dwc2_reinit; controller->shutdown = dwc2_shutdown; controller->bulk = dwc2_bulk; + controller->bulk_timeout = dwc2_bulk_timeout; controller->control = dwc2_control; controller->set_address = generic_set_address; controller->finish_device_config = NULL; diff --git a/payloads/libpayload/drivers/usb/dwc2_private.h b/payloads/libpayload/drivers/usb/dwc2_private.h index 3616ff3d10..86c9da2fa8 100644 --- a/payloads/libpayload/drivers/usb/dwc2_private.h +++ b/payloads/libpayload/drivers/usb/dwc2_private.h @@ -48,7 +48,6 @@ typedef enum { HCSTAT_NAK, HCSTAT_NYET, HCSTAT_UNKNOW, - HCSTAT_TIMEOUT, HCSTAT_DISCONNECTED, } hcstat_t; #endif diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c index 822fdb19a9..fe3186d5ca 100644 --- a/payloads/libpayload/drivers/usb/ehci.c +++ b/payloads/libpayload/drivers/usb/ehci.c @@ -249,23 +249,25 @@ static void free_qh_and_tds(ehci_qh_t *qh, qtd_t *cur) #define EHCI_SLEEP_TIME_US 50 -static int wait_for_tds(qtd_t *head) +static int wait_for_tds(qtd_t *head, int timeout_us) { - /* returns the amount of bytes *not* transmitted, or -1 for error */ + /* returns the amount of bytes *not* transmitted, USB_TIMEOUT on timeout, + * or -1 for error + */ int result = 0; qtd_t *cur = head; while (1) { if (0) dump_td(virt_to_phys(cur)); /* wait for results */ - int timeout = USB_MAX_PROCESSING_TIME_US / EHCI_SLEEP_TIME_US; + int timeout = timeout_us / EHCI_SLEEP_TIME_US; while ((cur->token & QTD_ACTIVE) && !(cur->token & QTD_HALTED) && timeout--) udelay(EHCI_SLEEP_TIME_US); if (timeout < 0) { usb_debug("Error: ehci: queue transfer " "processing timed out.\n"); - return -1; + return USB_TIMEOUT; } if (cur->token & QTD_HALTED) { usb_debug("ERROR with packet\n"); @@ -319,7 +321,7 @@ static int ehci_set_async_schedule(ehci_t *ehcic, int enable) } static int ehci_process_async_schedule( - ehci_t *ehcic, ehci_qh_t *qhead, qtd_t *head) + ehci_t *ehcic, ehci_qh_t *qhead, qtd_t *head, int timeout_us) { int result; @@ -333,7 +335,7 @@ static int ehci_process_async_schedule( if (ehci_set_async_schedule(ehcic, 1)) return -1; /* wait for result */ - result = wait_for_tds(head); + result = wait_for_tds(head, timeout_us); /* disable async schedule */ ehci_set_async_schedule(ehcic, 0); @@ -341,7 +343,7 @@ static int ehci_process_async_schedule( return result; } -static int ehci_bulk(endpoint_t *ep, int size, u8 *src, int finalize) +static int ehci_bulk_timeout(endpoint_t *ep, int size, u8 *src, int timeout_us) { int result = 0; u8 *end = src + size; @@ -410,7 +412,7 @@ static int ehci_bulk(endpoint_t *ep, int size, u8 *src, int finalize) head->token |= (ep->toggle?QTD_TOGGLE_DATA1:0); result = ehci_process_async_schedule( - EHCI_INST(ep->dev->controller), qh, head); + EHCI_INST(ep->dev->controller), qh, head, timeout_us); if (result >= 0) { result = size - result; if (pid == EHCI_IN && end != src + size) @@ -429,6 +431,11 @@ oom: return -1; } +static int ehci_bulk(endpoint_t *ep, int size, u8 *src, int finalize) +{ + return ehci_bulk_timeout(ep, size, src, USB_MAX_PROCESSING_TIME_US); +} + /* FIXME: Handle control transfers as 3 QHs, so the 2nd stage can be >0x4000 bytes */ static int ehci_control(usbdev_t *dev, direction_t dir, int drlen, void *setup, int dalen, u8 *src) @@ -530,7 +537,7 @@ static int ehci_control(usbdev_t *dev, direction_t dir, int drlen, void *setup, qh->td.next_qtd = virt_to_phys(head); result = ehci_process_async_schedule( - EHCI_INST(dev->controller), qh, head); + EHCI_INST(dev->controller), qh, head, USB_MAX_PROCESSING_TIME_US); if (result >= 0) { result = dalen - result; if (dir == IN && data != src) @@ -784,6 +791,7 @@ ehci_init(unsigned long physical_bar) controller->init = ehci_reinit; controller->shutdown = ehci_shutdown; controller->bulk = ehci_bulk; + controller->bulk_timeout = ehci_bulk_timeout; controller->control = ehci_control; controller->set_address = generic_set_address; controller->finish_device_config = NULL; diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c index 79add33fe7..55bdf66826 100644 --- a/payloads/libpayload/drivers/usb/ohci.c +++ b/payloads/libpayload/drivers/usb/ohci.c @@ -39,6 +39,7 @@ static void ohci_stop(hci_t *controller); static void ohci_reset(hci_t *controller); static void ohci_shutdown(hci_t *controller); static int ohci_bulk(endpoint_t *ep, int size, u8 *data, int finalize); +static int ohci_bulk_timeout(endpoint_t *ep, int size, u8 *data, int timeout_us); static int ohci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, u8 *data); static void* ohci_create_intr_queue(endpoint_t *ep, int reqsize, int reqcount, int reqtiming); @@ -181,6 +182,7 @@ ohci_init(unsigned long physical_bar) controller->init = ohci_reinit; controller->shutdown = ohci_shutdown; controller->bulk = ohci_bulk; + controller->bulk_timeout = ohci_bulk_timeout; controller->control = ohci_control; controller->set_address = generic_set_address; controller->finish_device_config = NULL; @@ -295,10 +297,10 @@ ohci_stop(hci_t *controller) #define OHCI_SLEEP_TIME_US 1000 static int -wait_for_ed(usbdev_t *dev, ed_t *head, int pages) +wait_for_ed(usbdev_t *dev, ed_t *head, int pages, int timeout_us) { /* wait for results */ - int timeout = USB_MAX_PROCESSING_TIME_US / OHCI_SLEEP_TIME_US; + int timeout = timeout_us / OHCI_SLEEP_TIME_US; while (((head->head_pointer & ~3) != head->tail_pointer) && !(head->head_pointer & 1) && ((((td_t*)phys_to_virt(head->head_pointer & ~3))->config @@ -326,7 +328,7 @@ wait_for_ed(usbdev_t *dev, ed_t *head, int pages) usb_debug("HALTED!\n"); return -1; } - return result; + return timeout <= 0 ? USB_TIMEOUT : result; } static void @@ -481,7 +483,8 @@ ohci_control(usbdev_t *dev, direction_t dir, int drlen, void *setup, int dalen, OHCI_INST(dev->controller)->opreg->HcCommandStatus = ControlListFilled; int result = wait_for_ed(dev, head, - (dalen == 0)?0:(last_page - first_page + 1)); + (dalen == 0)?0:(last_page - first_page + 1), + USB_MAX_PROCESSING_TIME_US); /* Wait some frames before and one after disabling list access. */ mdelay(4); OHCI_INST(dev->controller)->opreg->HcControl &= ~ControlListEnable; @@ -501,7 +504,7 @@ ohci_control(usbdev_t *dev, direction_t dir, int drlen, void *setup, int dalen, /* finalize == 1: if data is of packet aligned size, add a zero length packet */ static int -ohci_bulk(endpoint_t *ep, int dalen, u8 *src, int finalize) +ohci_bulk_finalize_timeout(endpoint_t *ep, int dalen, u8 *src, int finalize, int timeout_us) { int i; td_t *cur, *next; @@ -608,7 +611,7 @@ ohci_bulk(endpoint_t *ep, int dalen, u8 *src, int finalize) OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = BulkListFilled; int result = wait_for_ed(ep->dev, head, - (dalen == 0)?0:(last_page - first_page + 1)); + (dalen == 0)?0:(last_page - first_page + 1), timeout_us); /* Wait some frames before and one after disabling list access. */ mdelay(4); OHCI_INST(ep->dev->controller)->opreg->HcControl &= ~BulkListEnable; @@ -628,6 +631,19 @@ ohci_bulk(endpoint_t *ep, int dalen, u8 *src, int finalize) return result; } +static int +ohci_bulk_timeout(endpoint_t *ep, int dalen, u8 *src, int timeout_us) +{ + /* usbdev_hc bulk_timeout API doesn't have finalize argument. Assume it should be 0. */ + return ohci_bulk_finalize_timeout(ep, dalen, src, 0, timeout_us); +} + +static int +ohci_bulk(endpoint_t *ep, int dalen, u8 *src, int finalize) +{ + return ohci_bulk_finalize_timeout(ep, dalen, src, finalize, USB_MAX_PROCESSING_TIME_US); +} + struct _intr_queue; struct _intrq_td { diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c index 7590ab3c85..53fb123c8c 100644 --- a/payloads/libpayload/drivers/usb/uhci.c +++ b/payloads/libpayload/drivers/usb/uhci.c @@ -39,6 +39,7 @@ static void uhci_stop(hci_t *controller); static void uhci_reset(hci_t *controller); static void uhci_shutdown(hci_t *controller); static int uhci_bulk(endpoint_t *ep, int size, u8 *data, int finalize); +static int uhci_bulk_timeout(endpoint_t *ep, int size, u8 *data, int timeout_us); static int uhci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, u8 *data); static void* uhci_create_intr_queue(endpoint_t *ep, int reqsize, int reqcount, int reqtiming); @@ -162,6 +163,7 @@ uhci_pci_init(pcidev_t addr) controller->init = uhci_reinit; controller->shutdown = uhci_shutdown; controller->bulk = uhci_bulk; + controller->bulk_timeout = uhci_bulk_timeout; controller->control = uhci_control; controller->set_address = generic_set_address; controller->finish_device_config = NULL; @@ -273,23 +275,23 @@ uhci_stop(hci_t *controller) } #define UHCI_SLEEP_TIME_US 30 -#define UHCI_TIMEOUT (USB_MAX_PROCESSING_TIME_US / UHCI_SLEEP_TIME_US) #define GET_TD(x) ((void *)(((unsigned long)(x))&~0xf)) static td_t * -wait_for_completed_qh(hci_t *controller, qh_t *qh) +wait_for_completed_qh(hci_t *controller, qh_t *qh, int *timeout_us) { - int timeout = UHCI_TIMEOUT; + int timeout = *timeout_us / UHCI_SLEEP_TIME_US; void *current = GET_TD(qh->elementlinkptr); while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) { if (current != GET_TD(qh->elementlinkptr)) { current = GET_TD(qh->elementlinkptr); - timeout = UHCI_TIMEOUT; + timeout = *timeout_us / UHCI_SLEEP_TIME_US; } uhci_reg_write16(controller, USBSTS, uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers udelay(UHCI_SLEEP_TIME_US); } + *timeout_us = timeout * UHCI_SLEEP_TIME_US; return (GET_TD(qh->elementlinkptr) == 0) ? 0 : GET_TD(phys_to_virt(qh->elementlinkptr)); } @@ -370,9 +372,10 @@ uhci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq, const int TD_STATUS_ACTIVE; UHCI_INST(dev->controller)->qh_data->elementlinkptr = virt_to_phys(tds) & ~(FLISTP_QH | FLISTP_TERMINATE); + int timeout_us = USB_MAX_PROCESSING_TIME_US; td_t *td = wait_for_completed_qh(dev->controller, UHCI_INST(dev->controller)-> - qh_data); + qh_data, &timeout_us); int result; if (td == 0) { result = dalen; /* TODO: We should return the actually transferred length. */ @@ -423,23 +426,24 @@ fill_schedule(td_t *td, endpoint_t *ep, int length, unsigned char *data, } static int -run_schedule(usbdev_t *dev, td_t *td) +run_schedule(usbdev_t *dev, td_t *td, int timeout_us) { UHCI_INST(dev->controller)->qh_data->elementlinkptr = virt_to_phys(td) & ~(FLISTP_QH | FLISTP_TERMINATE); td = wait_for_completed_qh(dev->controller, - UHCI_INST(dev->controller)->qh_data); + UHCI_INST(dev->controller)->qh_data, &timeout_us); if (td == 0) { return 0; } else { td_dump(td); - return 1; + return timeout_us <= 0 ? USB_TIMEOUT : -2; } } /* finalize == 1: if data is of packet aligned size, add a zero length packet */ static int -uhci_bulk(endpoint_t *ep, const int dalen, u8 *data, int finalize) +uhci_bulk_finalize_timeout(endpoint_t *ep, const int dalen, u8 *data, int finalize, + int timeout_us) { int maxpsize = ep->maxpacketsize; if (maxpsize == 0) @@ -460,15 +464,29 @@ uhci_bulk(endpoint_t *ep, const int dalen, u8 *data, int finalize) data += maxpsize; len_left -= maxpsize; } - if (run_schedule(ep->dev, tds) == 1) { + int ret = run_schedule(ep->dev, tds, timeout_us); + if (ret) { free(tds); - return -1; + return ret; } ep->toggle = toggle; free(tds); return dalen; /* TODO: We should return the actually transferred length. */ } +static int +uhci_bulk_timeout(endpoint_t *ep, int dalen, u8 *src, int timeout_us) +{ + /* usbdev_hc bulk_timeout API doesn't have finalize argument. Assume it should be 0. */ + return uhci_bulk_finalize_timeout(ep, dalen, src, 0, timeout_us); +} + +static int +uhci_bulk(endpoint_t *ep, const int dalen, u8 *data, int finalize) +{ + return uhci_bulk_finalize_timeout(ep, dalen, data, finalize, USB_MAX_PROCESSING_TIME_US); +} + typedef struct { qh_t *qh; td_t *tds; diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index e9a7ead18b..2f9206efd5 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -40,6 +40,7 @@ static void xhci_reset(hci_t *controller); static void xhci_reinit(hci_t *controller); static void xhci_shutdown(hci_t *controller); static int xhci_bulk(endpoint_t *ep, int size, u8 *data, int finalize); +static int xhci_bulk_timeout(endpoint_t *ep, int size, u8 *data, int timeout_us); static int xhci_control(usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, u8 *data); static void* xhci_create_intr_queue(endpoint_t *ep, int reqsize, int reqcount, int reqtiming); @@ -168,6 +169,7 @@ xhci_init(unsigned long physical_bar) controller->init = xhci_reinit; controller->shutdown = xhci_shutdown; controller->bulk = xhci_bulk; + controller->bulk_timeout = xhci_bulk_timeout; controller->control = xhci_control; controller->set_address = xhci_set_address; controller->finish_device_config = xhci_finish_device_config; @@ -705,10 +707,11 @@ xhci_control(usbdev_t *const dev, const direction_t dir, 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); + const int ret = xhci_wait_for_transfer(xhci, dev->address, 1, + USB_MAX_PROCESSING_TIME_US); transferred += ret; if (ret < 0) { - if (ret == TIMEOUT) { + if (ret == USB_TIMEOUT) { xhci_debug("Stopping ID %d EP 1\n", dev->address); xhci_cmd_stop_endpoint(xhci, dev->address, 1); @@ -732,14 +735,10 @@ xhci_control(usbdev_t *const dev, const direction_t dir, return transferred; } -/* finalize == 1: if data is of packet aligned size, add a zero length packet */ static int -xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, - const int finalize) +xhci_bulk_timeout(endpoint_t *const ep, const int size, u8 *const src, + int timeout_us) { - /* finalize: Hopefully the xHCI controller always does this. - We have no control over the packets. */ - u8 *data = src; xhci_t *const xhci = XHCI_INST(ep->dev->controller); const int slot_id = ep->dev->address; @@ -777,9 +776,9 @@ xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, xhci_ring_doorbell(ep); /* 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, timeout_us); if (ret < 0) { - if (ret == TIMEOUT) { + if (ret == USB_TIMEOUT) { xhci_debug("Stopping ID %d EP %d\n", ep->dev->address, ep_id); xhci_cmd_stop_endpoint(xhci, ep->dev->address, ep_id); @@ -798,6 +797,11 @@ xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, return ret; } +static int xhci_bulk(endpoint_t *ep, int size, u8 *data, int finalize) +{ + return xhci_bulk_timeout(ep, size, data, USB_MAX_PROCESSING_TIME_US); +} + static trb_t * xhci_next_trb(trb_t *cur, int *const pcs) { diff --git a/payloads/libpayload/drivers/usb/xhci_commands.c b/payloads/libpayload/drivers/usb/xhci_commands.c index a4078030c6..1cebb5432a 100644 --- a/payloads/libpayload/drivers/usb/xhci_commands.c +++ b/payloads/libpayload/drivers/usb/xhci_commands.c @@ -69,7 +69,7 @@ xhci_wait_for_command(xhci_t *const xhci, int cc; cc = xhci_wait_for_command_done(xhci, cmd_trb, clear_event); - if (cc != TIMEOUT) + if (cc != USB_TIMEOUT) return cc; /* Abort command on timeout */ diff --git a/payloads/libpayload/drivers/usb/xhci_events.c b/payloads/libpayload/drivers/usb/xhci_events.c index 139ea59619..3c36a31355 100644 --- a/payloads/libpayload/drivers/usb/xhci_events.c +++ b/payloads/libpayload/drivers/usb/xhci_events.c @@ -232,7 +232,7 @@ xhci_wait_for_event_type(xhci_t *const xhci, * * Process events from xHCI Abort command. * - * Returns CC_COMMAND_RING_STOPPED on success and TIMEOUT on failure. + * Returns CC_COMMAND_RING_STOPPED on success and USB_TIMEOUT on failure. */ int @@ -244,7 +244,7 @@ xhci_wait_for_command_aborted(xhci_t *const xhci, const trb_t *const address) * what to do then. */ unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US; /* 5s */ - int cc = TIMEOUT; + int cc = USB_TIMEOUT; /* * Expects two command completion events: * The first with CC == COMMAND_ABORTED should point to address @@ -292,7 +292,7 @@ update_and_return: /* * returns cc of command in question (pointed to by `address`) - * caller should abort command if cc is TIMEOUT + * caller should abort command if cc is USB_TIMEOUT */ int xhci_wait_for_command_done(xhci_t *const xhci, @@ -300,7 +300,7 @@ xhci_wait_for_command_done(xhci_t *const xhci, const int clear_event) { unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US; /* 5s */ - int cc = TIMEOUT; + int cc = USB_TIMEOUT; while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) { if ((xhci->er.cur->ptr_low == virt_to_phys(address)) && (xhci->er.cur->ptr_high == 0)) { @@ -321,12 +321,11 @@ xhci_wait_for_command_done(xhci_t *const xhci, /* 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) +xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id, + unsigned long timeout_us) { xhci_spew("Waiting for transfer on ID %d EP %d\n", slot_id, ep_id); - /* 5s for all types of transfers */ - unsigned long timeout_us = USB_MAX_PROCESSING_TIME_US; - int ret = TIMEOUT; + int ret = USB_TIMEOUT; 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) { diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h index 45c301391e..c9464ce423 100644 --- a/payloads/libpayload/drivers/usb/xhci_private.h +++ b/payloads/libpayload/drivers/usb/xhci_private.h @@ -45,8 +45,7 @@ #define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit)) -/* Make these high enough to not collide with negative XHCI CCs */ -#define TIMEOUT -65 +/* Make these high enough to not collide with negative XHCI CCs and generic USB_TIMEOUT */ #define CONTROLLER_ERROR -66 #define COMMUNICATION_ERROR -67 #define OUT_OF_MEMORY -68 @@ -496,7 +495,8 @@ void xhci_update_event_dq(xhci_t *); void xhci_handle_events(xhci_t *); int xhci_wait_for_command_aborted(xhci_t *, const trb_t *); int xhci_wait_for_command_done(xhci_t *, const trb_t *, int clear_event); -int xhci_wait_for_transfer(xhci_t *, const int slot_id, const int ep_id); +int xhci_wait_for_transfer(xhci_t *, const int slot_id, const int ep_id, + unsigned long timeout_us); void xhci_clear_trb(trb_t *, int pcs); diff --git a/payloads/libpayload/include/usb/usb.h b/payloads/libpayload/include/usb/usb.h index 43c7b4279d..f36bb5c5be 100644 --- a/payloads/libpayload/include/usb/usb.h +++ b/payloads/libpayload/include/usb/usb.h @@ -231,6 +231,12 @@ struct usbdev { typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3, DWC2 = 4} hc_type; +/* + * Use higher value so it is less likely to conflict with error codes that + * controller drivers may already use. + */ +#define USB_TIMEOUT -65 + struct usbdev_hc { hci_t *next; uintptr_t reg_base; @@ -255,6 +261,10 @@ struct usbdev_hc { void (*shutdown) (hci_t *controller); int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); + /* bulk_timeout(): Perform bulk transfer, but timeout after + specified time in us. Returns + USB_TIMEOUT in that case. */ + int (*bulk_timeout) (endpoint_t *ep, int size, u8 *data, int timeout_us); int (*control) (usbdev_t *dev, direction_t pid, int dr_length, void *devreq, int data_length, u8 *data); void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);