[RFC] xHCI 1.0: Force Stopped Event(FSE)

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

 



FSE shall occur on TD natural boundary. There are two conner cases need more
processing:
	1. Stopped on TD boundary
	2. Stopped on Link TD
e.g. The prev TD has been transfered sucessfully and the stop endpoint command
run before move to the next TD to start an new transfers. A link TD maybe
between the two TD. For these special cases, the TR Dequeue Pointer of software
exceed the TR Dequeue Pointer of hardware(xHCI). i.e. the driver move the
current TD to the next TD in the ep_ring->td_list and mark the new current TD
as the stoppded TD. But the hardware stop on the Link TD or the last TRB of the
prev TD which reportted by the FSE TRB. There is an outer-sync for hardware and
driver for recording the stop TD and TRB. The real stopped TRB pointed by FSE
TRB just can be found between the prev TD and the current TD. These should be
figured out in handle_tx_event().

Signed-off-by: Alex He <alex.he@xxxxxxx>
---
 drivers/usb/host/xhci-ring.c |   38 ++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci.h      |    5 +++++
 2 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 7437386..1ec17b3 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1566,6 +1566,8 @@ td_cleanup:
 				*status = 0;
 		}
 		list_del(&td->td_list);
+		ep_ring->prev_td = td;
+
 		/* Was this TD slated to be cancelled but completed anyway? */
 		if (!list_empty(&td->cancelled_td_list))
 			list_del(&td->cancelled_td_list);
@@ -1913,6 +1915,19 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	return finish_td(xhci, td, event_trb, event, ep, status, false);
 }
 
+static bool is_force_stopeed_event(struct xhci_segment *seg,
+		union xhci_trb *last_trb, dma_addr_t event_dma)
+{
+	dma_addr_t last_trb_dma;
+	dma_addr_t end_seg_dma;
+
+	last_trb_dma = xhci_trb_virt_to_dma(seg, last_trb);
+	end_seg_dma = xhci_trb_virt_to_dma(seg,
+			&seg->trbs[TRBS_PER_SEGMENT - 1]);
+
+	return (last_trb_dma == event_dma || end_seg_dma == event_dma);
+}
+
 /*
  * 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.
@@ -1927,6 +1942,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 	unsigned int slot_id;
 	int ep_index;
 	struct xhci_td *td = NULL;
+	struct xhci_td *prev_td = NULL;
 	dma_addr_t event_dma;
 	struct xhci_segment *event_seg;
 	union xhci_trb *event_trb;
@@ -2061,10 +2077,32 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		}
 
 		td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+		prev_td = ep_ring->prev_td;
 
 		/* Is this a TRB in the currently executing TD? */
 		event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
 				td->last_trb, event_dma);
+
+		/*
+		 * Force Stopped Event decribed in Table 3 of xHCI spec.
+		 * It maybe a TRB pointed by a FSE TRB:
+		 *  1. The link TD bewteen prev_td and td
+		 *  2. The last TRB of prev_td
+		 */
+		if (!event_seg) {
+			event_seg = trb_in_td(prev_td->start_seg,
+					prev_td->first_trb,
+					td->first_trb,
+					event_dma);
+			if (event_seg && (trb_comp_code == COMP_STOP_INVAL))
+				if (is_force_stopeed_event(event_seg,
+							prev_td->last_trb,
+							event_dma)) {
+					ret = 0;
+					goto cleanup;
+				}
+		}
+
 		if (!event_seg) {
 			if (!ep->skip ||
 			    !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index ba1be6b..c9c2fda 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1108,6 +1108,11 @@ struct xhci_ring {
 	unsigned int		deq_updates;
 	struct list_head	td_list;
 	/*
+	 * Record the last td removedd from the td_list for process the
+	 * Force Stopped Event(FSE). Only used for Transfer Ring.
+	 */
+	struct xhci_td		*prev_td;
+	/*
 	 * Write the cycle state into the TRB cycle field to give ownership of
 	 * the TRB to the host controller (if we are the producer), or to check
 	 * if we own the TRB (if we are the consumer).  See section 4.9.1.
-- 
1.7.0.4



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