From: Alex He <alex.he@xxxxxxx> xHCI 1.0 spec 4.6.8.1 describes Soft Retry mechanism. A Soft Retry may effectively be used to recover from a USB Transaction Error that was due to a temporary error condition. Oftern the delay introduced between software detecting the error and attempting a Soft Retry is enough to let the temporary condition clear and allow a successful transfer. Signed-off-by: Alex He <alex.he@xxxxxxx> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> --- drivers/usb/host/xhci-ring.c | 70 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 6 +++ 2 files changed, 76 insertions(+), 0 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e07162d..d0b8c85 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1645,6 +1645,18 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, if (!xhci_requires_manual_halt_cleanup(xhci, ep_ctx, trb_comp_code)) break; + if (trb_comp_code == COMP_TX_ERR && + xhci->hci_version == 0x100) { + if (ep->soft_retries++ < SOFT_RETRY) { + xhci_soft_retry_ep(xhci, slot_id, ep_index, + ep_ring->stream_id, td, + event_trb); + return 0; + } else { + ep->soft_retries = 0; + } + } + xhci_dbg(xhci, "TRB error code %u, " "halted endpoint index = %u\n", trb_comp_code, ep_index); @@ -1818,10 +1830,21 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, union xhci_trb *event_trb, struct xhci_transfer_event *event, struct xhci_virt_ep *ep, int *status) { + struct xhci_virt_device *xdev; + unsigned int slot_id; + struct xhci_slot_ctx *slot_ctx; + int ep_index; struct xhci_ring *ep_ring; union xhci_trb *cur_trb; struct xhci_segment *cur_seg; u32 trb_comp_code; + u32 tt_hub_slot_id; + + ep_index = TRB_TO_EP_ID(event->flags) - 1; + slot_id = TRB_TO_SLOT_ID(event->flags); + xdev = xhci->devs[slot_id]; + slot_ctx = xhci_get_slot_ctx(xhci, xdev->out_ctx); + tt_hub_slot_id = slot_ctx->tt_info && 0xff; ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); @@ -1852,6 +1875,25 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, else *status = 0; break; + case COMP_TX_ERR: + if (xhci->hci_version == 0x100) { + if (usb_endpoint_xfer_bulk(&td->urb->ep->desc) && + (ep->soft_retries++ < SOFT_RETRY)) { + xhci_soft_retry_ep(xhci, slot_id, ep_index, + ep_ring->stream_id, td, event_trb); + return 0; + + } + if (usb_endpoint_xfer_int(&td->urb->ep->desc) && + tt_hub_slot_id == 0 && + (ep->soft_retries++ < SOFT_RETRY)) { + xhci_soft_retry_ep(xhci, slot_id, ep_index, + ep_ring->stream_id, td, event_trb); + return 0; + } + ep->soft_retries = 0; + } + break; default: /* Others already handled above */ break; @@ -3565,3 +3607,31 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type, false); } + +/* + * A Soft Retry may effectively be used to recver from a USB Transaction Error + * that was due to a temporary error codition. Details in the Chapter 4.6.8.1 + * NOTE: + * 1. can't be called for isoc ep + * 2. can't be called for interrupt ep behind a TT + */ +void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id, + unsigned int ep_index, unsigned int stream_id, + struct xhci_td *td, union xhci_trb *event_trb) +{ + u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id); + u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); + u32 type = TRB_TYPE(TRB_RESET_EP); + u32 tsp_flag = 1 << 9; + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; + + ep->ep_state |= EP_HALTED; + ep->stopped_td = td; + ep->stopped_trb = event_trb; + ep->stopped_stream = stream_id; + + queue_command(xhci, 0, 0, 0, + trb_slot_id | trb_ep_index | type | tsp_flag, false); + + xhci_ring_cmd_db(xhci); +} diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 33a49d5..dd45929 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -767,6 +767,9 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + /* Soft Retries to prevent an infinite loop */ + unsigned int soft_retries; +#define SOFT_RETRY 2 }; struct xhci_virt_device { @@ -1540,6 +1543,9 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state); void xhci_stop_endpoint_command_watchdog(unsigned long arg); +void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id, + unsigned int ep_index, unsigned int stream_id, + struct xhci_td *td, union xhci_trb *event_trb); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html