[RFC 6/9] xHCI 1.0: Soft Retry mechanism

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

 



From: Alex He <alex.he@xxxxxxx>

xHCI 1.0 spec 4.6.8.1 describes Soft Retry mechanism.
A Soft Retry may effectively be used to recover from a USB Transaction Error
that was due to a temporary error condition. Oftern the delay introduced
between software detecting the error and attempting a Soft Retry is enough
to let the temporary condition clear and allow a successful transfer.

Signed-off-by: Alex He <alex.he@xxxxxxx>
Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
---
 drivers/usb/host/xhci-ring.c |   70 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci.h      |    6 +++
 2 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e07162d..d0b8c85 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1645,6 +1645,18 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		if (!xhci_requires_manual_halt_cleanup(xhci,
 					ep_ctx, trb_comp_code))
 			break;
+		if (trb_comp_code == COMP_TX_ERR &&
+				xhci->hci_version == 0x100) {
+			if (ep->soft_retries++ < SOFT_RETRY) {
+				xhci_soft_retry_ep(xhci, slot_id, ep_index,
+						ep_ring->stream_id, td,
+						event_trb);
+				return 0;
+			} else {
+				ep->soft_retries = 0;
+			}
+		}
+
 		xhci_dbg(xhci, "TRB error code %u, "
 				"halted endpoint index = %u\n",
 				trb_comp_code, ep_index);
@@ -1818,10 +1830,21 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	union xhci_trb *event_trb, struct xhci_transfer_event *event,
 	struct xhci_virt_ep *ep, int *status)
 {
+	struct xhci_virt_device *xdev;
+	unsigned int slot_id;
+	struct xhci_slot_ctx *slot_ctx;
+	int ep_index;
 	struct xhci_ring *ep_ring;
 	union xhci_trb *cur_trb;
 	struct xhci_segment *cur_seg;
 	u32 trb_comp_code;
+	u32 tt_hub_slot_id;
+
+	ep_index = TRB_TO_EP_ID(event->flags) - 1;
+	slot_id = TRB_TO_SLOT_ID(event->flags);
+	xdev = xhci->devs[slot_id];
+	slot_ctx = xhci_get_slot_ctx(xhci, xdev->out_ctx);
+	tt_hub_slot_id = slot_ctx->tt_info && 0xff;
 
 	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));
@@ -1852,6 +1875,25 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		else
 			*status = 0;
 		break;
+	case COMP_TX_ERR:
+		if (xhci->hci_version == 0x100) {
+			if (usb_endpoint_xfer_bulk(&td->urb->ep->desc) &&
+					(ep->soft_retries++ < SOFT_RETRY)) {
+				xhci_soft_retry_ep(xhci, slot_id, ep_index,
+					ep_ring->stream_id, td, event_trb);
+				return 0;
+
+			}
+			if (usb_endpoint_xfer_int(&td->urb->ep->desc) &&
+					tt_hub_slot_id == 0 &&
+					(ep->soft_retries++ < SOFT_RETRY)) {
+				xhci_soft_retry_ep(xhci, slot_id, ep_index,
+					ep_ring->stream_id, td, event_trb);
+				return 0;
+			}
+			ep->soft_retries = 0;
+		}
+		break;
 	default:
 		/* Others already handled above */
 		break;
@@ -3565,3 +3607,31 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
 	return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type,
 			false);
 }
+
+/*
+ * A Soft Retry may effectively be used to recver from a USB Transaction Error
+ * that was due to a temporary error codition. Details in the Chapter 4.6.8.1
+ * NOTE:
+ * 1. can't be called for isoc ep
+ * 2. can't be called for interrupt ep behind a TT
+ */
+void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id,
+		unsigned int ep_index, unsigned int stream_id,
+		struct xhci_td *td, union xhci_trb *event_trb)
+{
+	u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
+	u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+	u32 type = TRB_TYPE(TRB_RESET_EP);
+	u32 tsp_flag = 1 << 9;
+	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;
+	ep->stopped_stream = stream_id;
+
+	queue_command(xhci, 0, 0, 0,
+			trb_slot_id | trb_ep_index | type | tsp_flag, false);
+
+	xhci_ring_cmd_db(xhci);
+}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 33a49d5..dd45929 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -767,6 +767,9 @@ struct xhci_virt_ep {
 	 * process the missed tds on the endpoint ring.
 	 */
 	bool			skip;
+	/* Soft Retries to prevent an infinite loop */
+	unsigned int		soft_retries;
+#define SOFT_RETRY	2
 };
 
 struct xhci_virt_device {
@@ -1540,6 +1543,9 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
 		unsigned int slot_id, unsigned int ep_index,
 		struct xhci_dequeue_state *deq_state);
 void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+void xhci_soft_retry_ep(struct xhci_hcd *xhci, int slot_id,
+		unsigned int ep_index, unsigned int stream_id,
+		struct xhci_td *td, union xhci_trb *event_trb);
 void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
 		unsigned int ep_index, unsigned int stream_id);
 
-- 
1.7.1



--
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