Use soft retry to recover from a USB Transaction Errors that are caused by
temporary error conditions. The USB device is not aware that the xHC
has halted the endpoint, and will be waiting for another retry

A Soft Retry perform additional retries and recover from an error which has
caused the xHC to halt an endpoint.

Soft retry has some limitations:
Soft Retry attempts shall not be performed on Isoch endpoints
Soft Retry attempts shall not be performed if the device is behind a TT in
a HS Hub

Software shall limit the number of unsuccessful Soft Retry attempts to
prevent an infinite loop.

For more details on Soft retry see xhci specs  4.6.8.1

Signed-off-by: Mathias Nyman <mathias.ny...@linux.intel.com>
---
 drivers/usb/host/xhci-ring.c | 19 +++++++++++++++++++
 drivers/usb/host/xhci.h      |  2 ++
 2 files changed, 21 insertions(+)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f0a99aa..c41341e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1155,6 +1155,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
                /* Clear our internal halted state */
                xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
        }
+
+       /* if this was a soft reset, then restart */
+       if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
+               ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
 static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
@@ -2132,10 +2136,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, 
struct xhci_td *td,
        union xhci_trb *ep_trb, struct xhci_transfer_event *event,
        struct xhci_virt_ep *ep, int *status)
 {
+       struct xhci_slot_ctx *slot_ctx;
        struct xhci_ring *ep_ring;
        u32 trb_comp_code;
        u32 remaining, requested, ep_trb_len;
+       unsigned int slot_id;
+       int ep_index;
 
+       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+       slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx);
+       ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
        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));
        remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
@@ -2144,6 +2154,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, 
struct xhci_td *td,
 
        switch (trb_comp_code) {
        case COMP_SUCCESS:
+               ep_ring->err_count = 0;
                /* handle success with untransferred data as short packet */
                if (ep_trb != td->last_trb || remaining) {
                        xhci_warn(xhci, "WARN Successful completion on short 
TX\n");
@@ -2167,6 +2178,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, 
struct xhci_td *td,
                ep_trb_len      = 0;
                remaining       = 0;
                break;
+       case COMP_USB_TRANSACTION_ERROR:
+               if ((ep_ring->err_count++ > MAX_SOFT_RETRY) ||
+                   le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
+                       break;
+               *status = 0;
+               xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+                                       ep_ring->stream_id, td, EP_SOFT_RESET);
+               return 0;
        default:
                /* do nothing */
                break;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6230a57..b635785 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1496,6 +1496,7 @@ static inline const char *xhci_trb_type_string(u8 type)
 /* How much data is left before the 64KB boundary? */
 #define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr)      (TRB_MAX_BUFF_SIZE - \
                                        (addr & (TRB_MAX_BUFF_SIZE - 1)))
+#define MAX_SOFT_RETRY         3
 
 struct xhci_segment {
        union xhci_trb          *trbs;
@@ -1583,6 +1584,7 @@ struct xhci_ring {
         * if we own the TRB (if we are the consumer).  See section 4.9.1.
         */
        u32                     cycle_state;
+       unsigned int            err_count;
        unsigned int            stream_id;
        unsigned int            num_segs;
        unsigned int            num_trbs_free;
-- 
2.7.4

Reply via email to