From: Gavin Li <git@xxxxxxxxxxxxxx> If a stalling TRB is cancelled and NOOP'ed in xhci_handle_cmd_stop_ep(), finish_td() never gets called to reset the halted endpoint and the endpoint remains indefinitely stalled. This patch ensures that xhci_cleanup_halted_endpoint() is called after a TRB completion if the endpoint is halted, irregardless of the status of any pending TRBs. --- drivers/usb/host/xhci-ring.c | 34 +++++++++++++++++++++++++++++----- drivers/usb/host/xhci.h | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 03f63f50afb6..85bc53d0d43e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1113,11 +1113,13 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, { struct xhci_virt_device *vdev; struct xhci_ep_ctx *ep_ctx; + struct xhci_virt_ep *ep; unsigned int ep_index; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); vdev = xhci->devs[slot_id]; ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index); + ep = &xhci->devs[slot_id]->eps[ep_index]; trace_xhci_handle_cmd_reset_ep(ep_ctx); /* This command will only fail if the endpoint wasn't halted, @@ -1130,6 +1132,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, * command complete before the endpoint can be used. Queue that here * because the HW can't handle two commands being queued in a row. */ + ep->ep_state &= ~EP_RESET_PENDING; if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; @@ -1145,7 +1148,11 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, xhci_ring_cmd_db(xhci); } else { /* Clear our internal halted state */ - xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; + ep->ep_state &= ~EP_HALTED; + if (!(ep->ep_state & SET_DEQ_PENDING)) { + /* Restart any rings with pending URBs */ + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } } } @@ -1800,19 +1807,25 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id, - struct xhci_td *td, union xhci_trb *ep_trb) + struct xhci_td *td) { struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; struct xhci_command *command; + + if (ep->ep_state & EP_RESET_PENDING) + return; + command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); if (!command) return; - ep->ep_state |= EP_HALTED; + ep->ep_state |= EP_HALTED | EP_RESET_PENDING; ep->stopped_stream = stream_id; xhci_queue_reset_ep(xhci, command, slot_id, ep_index); - xhci_cleanup_stalled_ring(xhci, ep_index, td); + if (td != NULL && !(ep->ep_state & SET_DEQ_PENDING)) { + xhci_cleanup_stalled_ring(xhci, ep_index, td); + } ep->stopped_stream = 0; @@ -1947,7 +1960,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, * The class driver clears the device side halt later. */ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, - ep_ring->stream_id, td, ep_trb); + ep_ring->stream_id, td); } else { /* Update ring dequeue pointer */ while (ep_ring->dequeue != td->last_trb) @@ -2588,6 +2601,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ } while (handling_skipped_tds); + /* + * If a cancelled TRB halts the endpoint, reset it here. + */ + if (trb_comp_code == COMP_STALL_ERROR || + xhci_requires_manual_halt_cleanup(xhci, ep_ctx, + trb_comp_code)) { + /* No harm in calling this twice; second call will be a no-op */ + xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, + ep_ring->stream_id, td); + } + return 0; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 73a28a986d5e..0f7439f4d414 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -922,6 +922,7 @@ struct xhci_virt_ep { #define EP_HAS_STREAMS (1 << 4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ #define EP_GETTING_NO_STREAMS (1 << 5) +#define EP_RESET_PENDING (1 << 6) /* For stall recovery */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_td *stopped_td; -- 2.12.2 -- 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