[PATCH] EHCI: update all iso uframe stati before completing

Karsten Wiese <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Enables ie a soundcard driver to use capture uframe state,
when a playback urb has completed and a capture urb has not yet.

itd completion is further split up so the itd is only unlinked,
if the usb-frame has completed. Without this, uframe stati were not updated
on a via K8T800 ehci-hcd, where an itd was unlinked while the hcd was still
working on the usb-frame.

Signed-off-by: Karsten Wiese <[email protected]>
---
 drivers/usb/host/ehci-sched.c |  218 +++++++++++++++++++++++++++--------------
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 144 insertions(+), 75 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7b5ae71..71362c4 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1549,22 +1549,19 @@ itd_link_urb (
 }
 
 #define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
-
-static unsigned
-itd_complete (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd
-) {
+bool itd_scan(struct ehci_itd *itd,
+	      unsigned uframe_from, unsigned uframe_after)
+{
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
 	unsigned				uframe;
 	int					urb_index = -1;
 	struct ehci_iso_stream			*stream = itd->stream;
-	struct usb_device			*dev;
+	bool					completed_urb = false;
 
 	/* for each uframe with a packet */
-	for (uframe = 0; uframe < 8; uframe++) {
+	for (uframe = uframe_from; uframe < uframe_after; uframe++) {
 		if (likely (itd->index[uframe] == -1))
 			continue;
 		urb_index = itd->index[uframe];
@@ -1593,17 +1590,42 @@ itd_complete (
 			desc->status = 0;
 			desc->actual_length = EHCI_ITD_LENGTH (t);
 		}
+		if (urb_index + 1 == urb->number_of_packets)
+			completed_urb = true;
 	}
 
+	return completed_urb;
+}
+
+static void
+itd_complete (
+	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd
+) {
+	struct urb				*urb = itd->urb;
+	struct ehci_iso_stream			*stream = itd->stream;
+
 	usb_put_urb (urb);
 	itd->urb = NULL;
 	itd->stream = NULL;
 	list_move (&itd->itd_list, &stream->free_list);
 	iso_stream_put (ehci, stream);
 
-	/* handle completion now? */
-	if (likely ((urb_index + 1) != urb->number_of_packets))
-		return 0;
+	if (unlikely (!ehci->periodic_sched))
+		disable_periodic(ehci);
+}
+
+static void
+itd_complete_urb (
+	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd
+) {
+	struct urb				*urb = itd->urb;
+	struct ehci_iso_stream			*stream = itd->stream;
+	struct usb_device			*dev;
+
+	usb_put_urb (urb);
+	itd->urb = NULL;
 
 	/* ASSERT: it's really the last itd for this urb
 	list_for_each_entry (itd, &stream->td_list, itd_list)
@@ -1617,8 +1639,6 @@ itd_complete (
 
 	/* defer stopping schedule; completion can submit */
 	ehci->periodic_sched--;
-	if (unlikely (!ehci->periodic_sched))
-		(void) disable_periodic (ehci);
 	ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
 	if (unlikely (list_empty (&stream->td_list))) {
@@ -1630,8 +1650,6 @@ itd_complete (
 			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
 	}
 	iso_stream_put (ehci, stream);
-
-	return 1;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1926,21 +1944,22 @@ sitd_link_urb (
 #define	SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
 				| SITD_STS_XACT | SITD_STS_MMF)
 
-static unsigned
-sitd_complete (
-	struct ehci_hcd		*ehci,
-	struct ehci_sitd	*sitd
+static int
+sitd_scan (
+	struct ehci_sitd *sitd,
+	bool live
 ) {
 	struct urb				*urb = sitd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
 	int					urb_index = -1;
-	struct ehci_iso_stream			*stream = sitd->stream;
-	struct usb_device			*dev;
+
+	t = le32_to_cpup(&sitd->hw_results);
+	if ((t & SITD_STS_ACTIVE) && live)
+		return -1;
 
 	urb_index = sitd->index;
 	desc = &urb->iso_frame_desc [urb_index];
-	t = le32_to_cpup (&sitd->hw_results);
 
 	/* report transfer status */
 	if (t & SITD_ERRS) {
@@ -1958,6 +1977,19 @@ sitd_complete (
 		desc->actual_length = desc->length - SITD_LENGTH (t);
 	}
 
+	return  urb_index + 1 == urb->number_of_packets ? 1 : 0;
+}
+
+static void
+sitd_complete (
+	struct ehci_hcd		*ehci,
+	struct ehci_sitd	*sitd,
+	bool urb_done
+) {
+	struct urb				*urb = sitd->urb;
+	struct ehci_iso_stream			*stream = sitd->stream;
+	struct usb_device			*dev;
+
 	usb_put_urb (urb);
 	sitd->urb = NULL;
 	sitd->stream = NULL;
@@ -1966,9 +1998,8 @@ sitd_complete (
 	iso_stream_put (ehci, stream);
 
 	/* handle completion now? */
-	if ((urb_index + 1) != urb->number_of_packets)
-		return 0;
-
+	if (!urb_done)
+		return;
 	/* ASSERT: it's really the last sitd for this urb
 	list_for_each_entry (sitd, &stream->td_list, sitd_list)
 		BUG_ON (sitd->urb == urb);
@@ -1994,8 +2025,6 @@ sitd_complete (
 			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
 	}
 	iso_stream_put (ehci, stream);
-
-	return 1;
 }
 
 
@@ -2095,17 +2124,22 @@ scan_periodic (struct ehci_hcd *ehci)
 
 	for (;;) {
 		union ehci_shadow	q, *q_p;
+		struct ehci_itd		*itd_done;
+		struct ehci_sitd	*sitd_done;
 		__le32			type, *hw_p;
-		unsigned		uframes;
+		unsigned		uframe_from, uframe_after;
+		int			live;
+
+		uframe_from = now_uframe & 0x07;
 
 		/* don't scan past the live uframe */
 		frame = now_uframe >> 3;
 		if (frame == (clock >> 3))
-			uframes = now_uframe & 0x07;
+			uframe_after = min(clock & 0x07, uframe_from + 1);
 		else {
 			/* safe to scan the whole frame at once */
 			now_uframe |= 0x07;
-			uframes = 8;
+			uframe_after = 8;
 		}
 
 restart:
@@ -2115,11 +2149,85 @@ restart:
 		q.ptr = q_p->ptr;
 		type = Q_NEXT_TYPE (*hw_p);
 		modified = 0;
+		itd_done = NULL;
+		sitd_done = NULL;
+
+		live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
+		if (!live && uframe_after)
+			uframe_after = 8;
+
+		while (q.ptr && (type == Q_TYPE_ITD || type == Q_TYPE_SITD)) {
+			int done;
+			switch (type) {
+			case Q_TYPE_ITD:
+				rmb ();
+				done = itd_scan(q.itd,
+						uframe_from, uframe_after);
+				if (uframe_after < 8) {
+					q_p = &q.itd->itd_next;
+					hw_p = &q.itd->hw_next;
+					type = Q_NEXT_TYPE (q.itd->hw_next);
+				} else {
+					*q_p = q.itd->itd_next;
+					*hw_p = q.itd->hw_next;
+					type = Q_NEXT_TYPE (q.itd->hw_next);
+					wmb();
+					if (!done)
+						itd_complete(ehci, q.itd);
+				}
+				if (done) {
+					q.itd->itd_next_done = itd_done;
+					itd_done = q.itd;
+				}
+				q = *q_p;
+				break;
+			case Q_TYPE_SITD:
+				done = sitd_scan(q.sitd, live);
+				if (done < 0) {
+					q_p = &q.sitd->sitd_next;
+					hw_p = &q.sitd->hw_next;
+					type = Q_NEXT_TYPE (q.sitd->hw_next);
+					q = *q_p;
+					break;
+				}
+				*q_p = q.sitd->sitd_next;
+				*hw_p = q.sitd->hw_next;
+				type = Q_NEXT_TYPE (q.sitd->hw_next);
+				wmb();
+				if (done > 0) {
+					q.sitd->sitd_next.sitd = sitd_done;
+					sitd_done = q.sitd;
+				} else
+					sitd_complete(ehci, q.sitd, false);
+				q = *q_p;
+				break;
+			}
+		}
+
+		while (itd_done) {
+			struct ehci_itd	*itd = itd_done;
+			itd_done = itd->itd_next_done;
+			itd_complete_urb(ehci, itd);
+			if (uframe_after >= 8)
+				itd_complete(ehci, itd);
+			modified = true;
+		}
+
+		while (sitd_done) {
+			struct ehci_sitd *sitd = sitd_done;
+			sitd_done = sitd->sitd_next.sitd;
+			sitd_complete(ehci, sitd, true);
+			modified = true;
+		}
+
+		/* assume completion callbacks modify the queue */
+		if (unlikely (modified)) {
+			uframe_after = 0;
+			goto restart;
+		}
 
 		while (q.ptr != NULL) {
-			unsigned		uf;
 			union ehci_shadow	temp;
-			int			live;
 
 			live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
 			switch (type) {
@@ -2143,48 +2251,6 @@ restart:
 				type = Q_NEXT_TYPE (q.fstn->hw_next);
 				q = q.fstn->fstn_next;
 				break;
-			case Q_TYPE_ITD:
-				/* skip itds for later in the frame */
-				rmb ();
-				for (uf = live ? uframes : 8; uf < 8; uf++) {
-					if (0 == (q.itd->hw_transaction [uf]
-							& ITD_ACTIVE))
-						continue;
-					q_p = &q.itd->itd_next;
-					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE (q.itd->hw_next);
-					q = *q_p;
-					break;
-				}
-				if (uf != 8)
-					break;
-
-				/* this one's ready ... HC won't cache the
-				 * pointer for much longer, if at all.
-				 */
-				*q_p = q.itd->itd_next;
-				*hw_p = q.itd->hw_next;
-				type = Q_NEXT_TYPE (q.itd->hw_next);
-				wmb();
-				modified = itd_complete (ehci, q.itd);
-				q = *q_p;
-				break;
-			case Q_TYPE_SITD:
-				if ((q.sitd->hw_results & SITD_ACTIVE)
-						&& live) {
-					q_p = &q.sitd->sitd_next;
-					hw_p = &q.sitd->hw_next;
-					type = Q_NEXT_TYPE (q.sitd->hw_next);
-					q = *q_p;
-					break;
-				}
-				*q_p = q.sitd->sitd_next;
-				*hw_p = q.sitd->hw_next;
-				type = Q_NEXT_TYPE (q.sitd->hw_next);
-				wmb();
-				modified = sitd_complete (ehci, q.sitd);
-				q = *q_p;
-				break;
 			default:
 				dbg ("corrupt type %d frame %d shadow %p",
 					type, frame, q.ptr);
@@ -2193,8 +2259,10 @@ restart:
 			}
 
 			/* assume completion callbacks modify the queue */
-			if (unlikely (modified))
+			if (unlikely (modified)) {
+				uframe_after = 0;
 				goto restart;
+			}
 		}
 
 		/* stop when we catch up to the HC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 46fa57a..1fd8304 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -532,6 +532,7 @@ struct ehci_itd {
 	/* the rest is HCD-private */
 	dma_addr_t		itd_dma;	/* for this itd */
 	union ehci_shadow	itd_next;	/* ptr to periodic q entry */
+	struct ehci_itd		*itd_next_done;	/* lists completed itds */
 
 	struct urb		*urb;
 	struct ehci_iso_stream	*stream;	/* endpoint's queue */
-- 
1.5.0.3

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.