Commit bcef3fd5 authored by Sarah Sharp's avatar Sarah Sharp Committed by Greg Kroah-Hartman

USB: xhci: Handle errors that cause endpoint halts.

The xHCI 0.95 and 0.96 specification defines several transfer buffer
request completion codes that indicate a USB transaction error occurred.
When a stall, babble, transaction, or split transaction error completion code
is set, the xHCI has halted that endpoint ring.  Software must issue a
Reset Endpoint command and a Set Transfer Ring Dequeue Pointer command
to clean up the halted ring.

The USB device driver is supposed to call into usb_reset_endpoint() when
an endpoint stalls.  That calls into the xHCI driver to issue the proper
commands.  However, drivers don't call that function for the other
errors that cause the xHC to halt the endpoint ring.  If a babble,
transaction, or split transaction error occurs, check if the endpoint
context reports a halted condition, and clean up the endpoint ring if it
does.
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 5ad6a529
...@@ -1037,6 +1037,45 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, ...@@ -1037,6 +1037,45 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
return 0; return 0;
} }
static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_td *td, union xhci_trb *event_trb)
{
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;
xhci_queue_reset_ep(xhci, slot_id, ep_index);
xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
xhci_ring_cmd_db(xhci);
}
/* Check if an error has halted the endpoint ring. The class driver will
* cleanup the halt for a non-default control endpoint if we indicate a stall.
* However, a babble and other errors also halt the endpoint ring, and the class
* driver won't clear the halt in that case, so we need to issue a Set Transfer
* Ring Dequeue Pointer command manually.
*/
static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx,
unsigned int trb_comp_code)
{
/* TRB completion codes that may require a manual halt cleanup */
if (trb_comp_code == COMP_TX_ERR ||
trb_comp_code == COMP_BABBLE ||
trb_comp_code == COMP_SPLIT_ERR)
/* The 0.96 spec says a babbling control endpoint
* is not halted. The 0.96 spec says it is. Some HW
* claims to be 0.95 compliant, but it halts the control
* endpoint anyway. Check if a babble halted the
* endpoint.
*/
if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED)
return 1;
return 0;
}
/* /*
* If this function returns an error condition, it means it got a Transfer * If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
...@@ -1191,15 +1230,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -1191,15 +1230,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
else else
status = 0; status = 0;
break; break;
case COMP_BABBLE:
/* The 0.96 spec says a babbling control endpoint default:
* is not halted. The 0.96 spec says it is. Some HW if (!xhci_requires_manual_halt_cleanup(xhci,
* claims to be 0.95 compliant, but it halts the control ep_ctx, trb_comp_code))
* endpoint anyway. Check if a babble halted the
* endpoint.
*/
if (ep_ctx->ep_info != EP_STATE_HALTED)
break; break;
xhci_dbg(xhci, "TRB error code %u, "
"halted endpoint index = %u\n",
trb_comp_code, ep_index);
/* else fall through */ /* else fall through */
case COMP_STALL: case COMP_STALL:
/* Did we transfer part of the data (middle) phase? */ /* Did we transfer part of the data (middle) phase? */
...@@ -1211,15 +1249,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -1211,15 +1249,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
else else
td->urb->actual_length = 0; td->urb->actual_length = 0;
ep->stopped_td = td; xhci_cleanup_halted_endpoint(xhci,
ep->stopped_trb = event_trb; slot_id, ep_index, td, event_trb);
xhci_queue_reset_ep(xhci, slot_id, ep_index);
xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
xhci_ring_cmd_db(xhci);
goto td_cleanup; goto td_cleanup;
default:
/* Others already handled above */
break;
} }
/* /*
* Did we transfer any data, despite the errors that might have * Did we transfer any data, despite the errors that might have
...@@ -1357,16 +1389,25 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -1357,16 +1389,25 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->stopped_td = td; ep->stopped_td = td;
ep->stopped_trb = event_trb; ep->stopped_trb = event_trb;
} else { } else {
if (trb_comp_code == COMP_STALL || if (trb_comp_code == COMP_STALL) {
trb_comp_code == COMP_BABBLE) {
/* The transfer is completed from the driver's /* The transfer is completed from the driver's
* perspective, but we need to issue a set dequeue * perspective, but we need to issue a set dequeue
* command for this stalled endpoint to move the dequeue * command for this stalled endpoint to move the dequeue
* pointer past the TD. We can't do that here because * pointer past the TD. We can't do that here because
* the halt condition must be cleared first. * the halt condition must be cleared first. Let the
* USB class driver clear the stall later.
*/ */
ep->stopped_td = td; ep->stopped_td = td;
ep->stopped_trb = event_trb; ep->stopped_trb = event_trb;
} else if (xhci_requires_manual_halt_cleanup(xhci,
ep_ctx, trb_comp_code)) {
/* Other types of errors halt the endpoint, but the
* class driver doesn't call usb_reset_endpoint() unless
* the error is -EPIPE. Clear the halted status in the
* xHCI hardware manually.
*/
xhci_cleanup_halted_endpoint(xhci,
slot_id, ep_index, td, event_trb);
} else { } else {
/* Update ring dequeue pointer */ /* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb) while (ep_ring->dequeue != td->last_trb)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment