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 <tmichalec@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90043
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
This commit is contained in:
Tomasz Michalec 2025-11-13 16:07:19 +01:00 committed by Matt DeVillier
commit 829b8be432
10 changed files with 134 additions and 67 deletions

View file

@ -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, &reg->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,
&reg->host.hchn[ch_num].hcdman);
writel(hcchar.d32, &reg->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;

View file

@ -48,7 +48,6 @@ typedef enum {
HCSTAT_NAK,
HCSTAT_NYET,
HCSTAT_UNKNOW,
HCSTAT_TIMEOUT,
HCSTAT_DISCONNECTED,
} hcstat_t;
#endif

View file

@ -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;

View file

@ -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 {

View file

@ -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;

View file

@ -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)
{

View file

@ -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 */

View file

@ -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) {

View file

@ -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);

View file

@ -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);