[PATCH] usb: host: xhci: reliable endpoint reset after halt

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux