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:
parent
f4fe5514fe
commit
829b8be432
10 changed files with 134 additions and 67 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ typedef enum {
|
|||
HCSTAT_NAK,
|
||||
HCSTAT_NYET,
|
||||
HCSTAT_UNKNOW,
|
||||
HCSTAT_TIMEOUT,
|
||||
HCSTAT_DISCONNECTED,
|
||||
} hcstat_t;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue