ChangeSet 1.1276.8.7, 2004/01/28 12:40:08-08:00, david-b@pacbell.net

[PATCH] USB: ehci update:  2/3, microframe scanning

This patch is needed to make high bandwidth ISO streams behave,
but could resolve some other scanning glitches.  Current users
of periodic transfers (interrupt transfer modes for hubs, mice,
and keyboards) shouldn't even notice this change.

It makes the periodic schedule scan handle cases where a given
frame's schedule slot reports completions in several different
microframes.  So far that's been uncommon, but it's typical
for high bandwidth iso (or even with busier interrupt trees than
this driver has supported yet).

It also starts to remove the assumption that each ITD only uses
one microframe; but most of those changes are in the next patch.
And it fixes a bug where some status bits were mis-interpreted as
significant bits in the ITD transfer length.


 drivers/usb/host/ehci-sched.c |  128 +++++++++++++++++++++---------------------
 drivers/usb/host/ehci.h       |    4 -
 2 files changed, 70 insertions(+), 62 deletions(-)


diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Wed Jan 28 13:36:19 2004
+++ b/drivers/usb/host/ehci-sched.c	Wed Jan 28 13:36:19 2004
@@ -868,37 +868,43 @@
 itd_complete (
 	struct ehci_hcd	*ehci,
 	struct ehci_itd	*itd,
-	unsigned	uframe,
 	struct pt_regs	*regs
 ) {
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
+	unsigned				uframe;
+	int					urb_index = -1;
 
-	/* update status for this uframe's transfers */
-	desc = &urb->iso_frame_desc [itd->index];
-
-	t = itd->hw_transaction [uframe];
-	itd->hw_transaction [uframe] = 0;
-	if (t & EHCI_ISOC_ACTIVE)
-		desc->status = -EXDEV;
-	else if (t & ISO_ERRS) {
-		urb->error_count++;
-		if (t & EHCI_ISOC_BUF_ERR)
-			desc->status = usb_pipein (urb->pipe)
-				? -ENOSR  /* couldn't read */
-				: -ECOMM; /* couldn't write */
-		else if (t & EHCI_ISOC_BABBLE)
-			desc->status = -EOVERFLOW;
-		else /* (t & EHCI_ISOC_XACTERR) */
-			desc->status = -EPROTO;
-
-		/* HC need not update length with this error */
-		if (!(t & EHCI_ISOC_BABBLE))
-			desc->actual_length += EHCI_ITD_LENGTH (t);
-	} else {
-		desc->status = 0;
-		desc->actual_length += EHCI_ITD_LENGTH (t);
+	/* for each uframe with a packet */
+	for (uframe = 0; uframe < 8; uframe++) {
+		if (itd->hw_transaction [uframe] == 0)
+			continue;
+		urb_index = itd->index;
+		desc = &urb->iso_frame_desc [urb_index];
+
+		t = le32_to_cpup (&itd->hw_transaction [uframe]);
+		itd->hw_transaction [uframe] = 0;
+
+		/* report transfer status */
+		if (unlikely (t & ISO_ERRS)) {
+			urb->error_count++;
+			if (t & EHCI_ISOC_BUF_ERR)
+				desc->status = usb_pipein (urb->pipe)
+					? -ENOSR  /* hc couldn't read */
+					: -ECOMM; /* hc couldn't write */
+			else if (t & EHCI_ISOC_BABBLE)
+				desc->status = -EOVERFLOW;
+			else /* (t & EHCI_ISOC_XACTERR) */
+				desc->status = -EPROTO;
+
+			/* HC need not update length with this error */
+			if (!(t & EHCI_ISOC_BABBLE))
+				desc->actual_length = EHCI_ITD_LENGTH (t);
+		} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
+			desc->status = 0;
+			desc->actual_length = EHCI_ITD_LENGTH (t);
+		}
 	}
 
 	vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
@@ -906,7 +912,7 @@
 		t, desc->status, desc->actual_length);
 
 	/* handle completion now? */
-	if ((itd->index + 1) != urb->number_of_packets)
+	if (likely ((urb_index + 1) != urb->number_of_packets))
 		return 0;
 
 	/*
@@ -986,24 +992,23 @@
 	 * When running, scan from last scan point up to "now"
 	 * else clean up by scanning everything that's left.
 	 * Touches as few pages as possible:  cache-friendly.
-	 * Don't scan ISO entries more than once, though.
 	 */
-	frame = ehci->next_uframe >> 3;
+	now_uframe = ehci->next_uframe;
 	if (HCD_IS_RUNNING (ehci->hcd.state))
-		now_uframe = readl (&ehci->regs->frame_index);
+		clock = readl (&ehci->regs->frame_index) % mod;
 	else
-		now_uframe = (frame << 3) - 1;
-	now_uframe %= mod;
-	clock = now_uframe >> 3;
+		clock = now_uframe + mod - 1;
 
 	for (;;) {
 		union ehci_shadow	q, *q_p;
 		u32			type, *hw_p;
 		unsigned		uframes;
 
+		frame = now_uframe >> 3;
 restart:
 		/* scan schedule to _before_ current frame index */
-		if (frame == clock)
+		if ((frame == (clock >> 3))
+				&& HCD_IS_RUNNING (ehci->hcd.state))
 			uframes = now_uframe & 0x07;
 		else
 			uframes = 8;
@@ -1043,34 +1048,31 @@
 			case Q_TYPE_ITD:
 				last = (q.itd->hw_next == EHCI_LIST_END);
 
-				/* Unlink each (S)ITD we see, since the ISO
-				 * URB model forces constant rescheduling.
-				 * That complicates sharing uframes in ITDs,
-				 * and means we need to skip uframes the HC
-				 * hasn't yet processed.
-				 */
-				for (uf = 0; uf < uframes; uf++) {
-					if (q.itd->hw_transaction [uf] != 0) {
-						temp = q;
-						*q_p = q.itd->itd_next;
-						*hw_p = q.itd->hw_next;
-						type = Q_NEXT_TYPE (*hw_p);
-
-						/* might free q.itd ... */
-						count += itd_complete (ehci,
-							temp.itd, uf, regs);
-						break;
-					}
-				}
-				/* we might skip this ITD's uframe ... */
-				if (uf == uframes) {
+				/* skip itds for later in the frame */
+				rmb ();
+				for (uf = uframes; uf < 8; uf++) {
+					if (0 == (q.itd->hw_transaction [uf]
+							& ISO_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;
 
-				q = *q_p;
-				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;
+				wmb();
+
+				/* always rescan here; simpler */
+				count += itd_complete (ehci, q.itd, regs);
+				goto restart;
 #ifdef have_split_iso
 			case Q_TYPE_SITD:
 				last = (q.sitd->hw_next == EHCI_LIST_END);
@@ -1104,7 +1106,7 @@
 
 		// FIXME:  likewise assumes HC doesn't halt mid-scan
 
-		if (frame == clock) {
+		if (now_uframe == clock) {
 			unsigned	now;
 
 			if (!HCD_IS_RUNNING (ehci->hcd.state))
@@ -1115,9 +1117,13 @@
 				break;
 
 			/* rescan the rest of this frame, then ... */
-			now_uframe = now;
-			clock = now_uframe >> 3;
-		} else
-			frame = (frame + 1) % ehci->periodic_size;
+			clock = now;
+		} else {
+			/* FIXME sometimes we can scan the next frame
+			 * right away, not always inching up on it ...
+			 */
+			now_uframe++;
+			now_uframe %= mod;
+		}
 	} 
 }
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Wed Jan 28 13:36:19 2004
+++ b/drivers/usb/host/ehci.h	Wed Jan 28 13:36:19 2004
@@ -400,8 +400,10 @@
 #define EHCI_ISOC_BUF_ERR       (1<<30)        /* Data buffer error */
 #define EHCI_ISOC_BABBLE        (1<<29)        /* babble detected */
 #define EHCI_ISOC_XACTERR       (1<<28)        /* XactErr - transaction error */
-#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x0fff)
 #define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
+
+#define ISO_ACTIVE	__constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
 
 	u32			hw_bufp [7];	/* see EHCI 3.3.3 */ 
 	u32			hw_bufp_hi [7];	/* Appendix B */
