[PATCH 09/15] usb: xhci: reset endpoint on USB stall

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Feb 19 05:38:29 PST 2024


This ports U-Boot commit d5daa02d8d9e7c403a3339db1966e8413e64e408:

| Author:     Stefan Agner <stefan at agner.ch>
| AuthorDate: Mon Sep 27 14:42:58 2021 +0200
|
| usb: xhci: reset endpoint on USB stall
|
| There are devices which cause a USB stall when trying to read strings.
| Specifically Arduino Mega R3 stalls when trying to read the product
| string.
|
| The stall currently remains unhandled, and subsequent retries submit new
| transfers on a stopped endpoint which ultimately cause a crash in
| abort_td():
| WARN halted endpoint, queueing URB anyway.
| XHCI control transfer timed out, aborting...
| Unexpected XHCI event TRB, skipping... (3affe040 00000000 13000000 02008401)
| BUG at drivers/usb/host/xhci-ring.c:505/abort_td()!
| BUG!
| resetting ...
|
| Linux seems to be able to recover from the stall by issuing a
| TRB_RESET_EP command.
|
| Introduce reset_ep() which issues a TRB_RESET_EP followed by setting the
| transfer ring dequeue pointer via TRB_SET_DEQ. This allows to properly
| recover from a USB stall error and continue communicating with the USB
| device.
|
| Signed-off-by: Stefan Agner <stefan at agner.ch>

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 drivers/usb/host/xhci-ring.c | 40 ++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index c645285da7fc..056613445330 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -500,6 +500,42 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected,
 	BUG();
 }
 
+/*
+ * Send reset endpoint command for given endpoint. This recovers from a
+ * halted endpoint (e.g. due to a stall error).
+ */
+static void reset_ep(struct usb_device *udev, int ep_index, unsigned int timeout_ms)
+{
+	struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
+	struct xhci_ring *ring =  ctrl->devs[udev->slot_id]->eps[ep_index].ring;
+	union xhci_trb *event;
+	u64 addr;
+	u32 field;
+
+	dev_info(&udev->dev, "Resetting EP %d...\n", ep_index);
+
+	xhci_queue_command(ctrl, 0, udev->slot_id, ep_index, TRB_RESET_EP);
+	event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms);
+	if (!event)
+		return;
+
+	field = le32_to_cpu(event->trans_event.flags);
+	BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
+	xhci_acknowledge_event(ctrl);
+
+	addr = xhci_trb_virt_to_dma(ring->enq_seg,
+		(void *)((uintptr_t)ring->enqueue | ring->cycle_state));
+	xhci_queue_command(ctrl, addr, udev->slot_id, ep_index, TRB_SET_DEQ);
+	event = xhci_wait_for_event(ctrl, TRB_COMPLETION, timeout_ms);
+	if (!event)
+		return;
+
+	BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
+		!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
+		event->event_cmd.status)) != COMP_SUCCESS);
+	xhci_acknowledge_event(ctrl);
+}
+
 /*
  * Stops transfer processing for an endpoint and throws away all unprocessed
  * TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next
@@ -979,6 +1015,10 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
 
 	record_transfer_result(udev, event, length);
 	xhci_acknowledge_event(ctrl);
+	if (udev->status == USB_ST_STALLED) {
+		reset_ep(udev, ep_index, timeout_ms);
+		return -EPIPE;
+	}
 
 	/* Invalidate buffer to make it available to usb-core */
 	if (length > 0)
-- 
2.39.2




More information about the barebox mailing list