Skip to content

Commit

Permalink
xhci: Use soft retry to recover faster from transaction errors
Browse files Browse the repository at this point in the history
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.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
matnyman authored and gregkh committed Sep 28, 2018
1 parent c94d41e commit f8f80be
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 0 deletions.
19 changes: 19 additions & 0 deletions drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand All @@ -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");
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit f8f80be

Please sign in to comment.