<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">From: Greg KH &lt;greg@kroah.com&gt;
To: torvalds@transmeta.com
Cc: linux-usb-devel@lists.sourceforge.net
Subject: [PATCH 2 of 5] USB ehci driver update

Hi,

Here's a patch against 2.5.2-pre10 that provides the following changes
to the ehci driver:
	- Bulk/control queueing works now
	- Resolves two FIXMEs about unlinking with queued bulk or
	  control urbs
	- Removes old code to support a non-production stepping of the
	  NEC controller
	- Updates two messages to not use 2.5-only features
The patch was written by David Brownell.

thanks,

greg k-h


diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c
--- a/drivers/usb/hcd/ehci-q.c	Tue Jan  8 09:29:37 2002
+++ b/drivers/usb/hcd/ehci-q.c	Tue Jan  8 09:29:37 2002
@@ -39,12 +39,6 @@
  * buffer low/full speed data so the host collects it at high speed.
  */
 
-#ifdef	EHCI_SOFT_RETRIES
-static int soft_retries = EHCI_SOFT_RETRIES;
-MODULE_PARM (soft_retries, "i");
-MODULE_PARM_DESC (soft_retries, "Number of software retries for endpoint i/o");
-#endif
-
 /*-------------------------------------------------------------------------*/
 
 /* fill a qtd, returning how much of the buffer we were able to queue up */
@@ -134,8 +128,9 @@
 			urb-&gt;status = -EPIPE;
 		else	/* unknown */
 			urb-&gt;status = -EPROTO;
-		dbg ("devpath %s ep %d-%s qtd token %x --&gt; status %d",
-			urb-&gt;dev-&gt;devpath, usb_pipeendpoint (urb-&gt;pipe),
+		dbg ("ep %d-%s qtd token %08x --&gt; status %d",
+			/* devpath */
+			usb_pipeendpoint (urb-&gt;pipe),
 			usb_pipein (urb-&gt;pipe) ? "in" : "out",
 			token, urb-&gt;status);
 
@@ -148,8 +143,8 @@
 					usb_pipeendpoint (pipe),
 					usb_pipeout (pipe));
 			if (urb-&gt;dev-&gt;tt &amp;&amp; !usb_pipeint (pipe)) {
-err ("must CLEAR_TT_BUFFER, hub %s port %d%s addr %d ep %d",
-    urb-&gt;dev-&gt;tt-&gt;hub-&gt;devpath, urb-&gt;dev-&gt;ttport,
+err ("must CLEAR_TT_BUFFER, hub port %d%s addr %d ep %d",
+    urb-&gt;dev-&gt;ttport, /* devpath */
     urb-&gt;dev-&gt;tt-&gt;multi ? "" : " (all-ports TT)",
     urb-&gt;dev-&gt;devnum, usb_pipeendpoint (urb-&gt;pipe));
 				// FIXME something (khubd?) should make the hub
@@ -228,12 +223,10 @@
 	struct list_head	*qtd_list,
 	int			freeing
 ) {
-	struct ehci_qtd		*qtd = 0;
-	struct list_head	*next = 0;
-	u32			token;
+	struct ehci_qtd		*qtd, *last;
+	struct list_head	*next;
 	struct ehci_qh		*qh = 0;
-	struct urb		*urb = 0;
-	int			halted = 0;
+	int			unlink = 0, halted = 0;
 	unsigned long		flags;
 	int			retval = 0;
 
@@ -243,89 +236,116 @@
 		return retval;
 	}
 
-	for (qtd = list_entry (qtd_list-&gt;next, struct ehci_qtd, qtd_list);
+	/* scan QTDs till end of list, or we reach an active one */
+	for (qtd = list_entry (qtd_list-&gt;next, struct ehci_qtd, qtd_list),
+			    	last = 0, next = 0;
 			next != qtd_list;
-			qtd = list_entry (next, struct ehci_qtd, qtd_list)) {
-		token = le32_to_cpu (qtd-&gt;hw_token);
-		if (!qh) {
-			urb = qtd-&gt;urb;
-			qh = (struct ehci_qh *) urb-&gt;hcpriv;
+			last = qtd, qtd = list_entry (next,
+						struct ehci_qtd, qtd_list)) {
+		struct urb	*urb = qtd-&gt;urb;
+		u32		token = 0;
+
+		/* qh is non-null iff these qtds were queued to the HC */
+		qh = (struct ehci_qh *) urb-&gt;hcpriv;
+
+		/* clean up any state from previous QTD ...*/
+		if (last) {
+			if (likely (last-&gt;urb != urb)) {
+				/* complete() can reenter this HCD */
+				spin_unlock_irqrestore (&amp;ehci-&gt;lock, flags);
+				if (likely (freeing != 0))
+					ehci_urb_done (ehci, last-&gt;buf_dma,
+						last-&gt;urb);
+				else
+					ehci_urb_complete (ehci, last-&gt;buf_dma,
+						last-&gt;urb);
+				spin_lock_irqsave (&amp;ehci-&gt;lock, flags);
+				retval++;
+			}
+
+			/* qh overlays can have HC's old cached copies of
+			 * next qtd ptrs, if an URB was queued afterwards.
+			 */
+			if (qh &amp;&amp; cpu_to_le32 (last-&gt;qtd_dma) == qh-&gt;hw_current
+					&amp;&amp; last-&gt;hw_next != qh-&gt;hw_qtd_next) {
+				qh-&gt;hw_alt_next = last-&gt;hw_alt_next;
+				qh-&gt;hw_qtd_next = last-&gt;hw_next;
+			}
+
+			if (likely (freeing != 0))
+				ehci_qtd_free (ehci, last);
+			last = 0;
 		}
+		next = qtd-&gt;qtd_list.next;
+
+		/* if these qtds were queued to the HC, some may be active.
+		 * else we're cleaning up after a failed URB submission.
+		 */
 		if (likely (qh != 0)) {
+			int		qh_halted;
+
+			qh_halted = __constant_cpu_to_le32 (QTD_STS_HALT)
+					&amp; qh-&gt;hw_token;
+			token = le32_to_cpu (qtd-&gt;hw_token);
 			halted = halted
+				|| qh_halted
 				|| (ehci-&gt;hcd.state == USB_STATE_HALT)
 				|| (qh-&gt;qh_state == QH_STATE_IDLE);
 
-			if (unlikely ((token &amp; QTD_STS_HALT) != 0)) {
-#ifdef	EHCI_SOFT_RETRIES
-				/* extra soft retries for protocol errors */
-				if (!halted
-						&amp;&amp; qh-&gt;retries &lt; soft_retries
-						&amp;&amp; (QTD_STS_HALT|QTD_STS_XACT)
-							== (token &amp; 0xff)
-						&amp;&amp; QTD_CERR (token) == 0) {
-					if (qh-&gt;retries == 0)
-						dbg ("soft retry, qh %p qtd %p",
-							qh, qtd);
-					qh-&gt;retries++;
-					token &amp;= ~0x0ff;
-					token |= QTD_STS_ACTIVE;
-					token |= (EHCI_TUNE_CERR &lt;&lt; 10);
-					/* qtd update not needed */
-					qh-&gt;hw_token = cpu_to_le32 (token);
-					spin_unlock_irqrestore (&amp;ehci-&gt;lock,
-						flags);
-					return;
-
-				} else if (qh-&gt;retries &gt;= soft_retries
-						&amp;&amp; soft_retries) {
-					dbg ("retried %d times, qh %p qtd %p",
-						qh-&gt;retries, qh, qtd);
-				}
-#endif	/* EHCI_SOFT_RETRIES */
-				halted = 1;
-			}
-
-			if (unlikely ((token &amp; QTD_STS_ACTIVE) != 0)) {
-				/* stop scan if qtd is visible to the HC */
-				if (!halted) {
-					urb = 0;
-					break;
-				}
+			/* QH halts only because of fault or unlink; in both
+			 * cases, queued URBs get unlinked.  But for unlink,
+			 * URBs at the head of the queue can stay linked.
+			 */
+			if (unlikely (halted != 0)) {
 
-				/* continue cleanup if HC is halted */
+				/* unlink everything because of HC shutdown? */
 				if (ehci-&gt;hcd.state == USB_STATE_HALT) {
+					freeing = unlink = 1;
 					urb-&gt;status = -ESHUTDOWN;
-					goto scrub;
-				}
 
-				/* stall? some other urb was unlinked? */
-				if (urb-&gt;status == -EINPROGRESS) {
-dbg ("?why? qh %p, qtd %p halted, urb %p, token %8x, len %d",
-	qh, qtd, urb, token, urb-&gt;actual_length);
-spin_unlock_irqrestore (&amp;ehci-&gt;lock, flags);
-return retval;
-	    /*
-	     * FIXME: write this code.  When one queued urb is unlinked,
-	     * unlink every succeeding urb.
-	     */
+				/* explicit unlink, starting here? */
+				} else if (qh-&gt;qh_state == QH_STATE_IDLE
+					&amp;&amp; (urb-&gt;status == -ECONNRESET
+						|| urb-&gt;status == -ENOENT)) {
+					freeing = unlink = 1;
+
+				/* unlink everything because of error? */
+				} else if (qh_halted
+						&amp;&amp; !(token &amp; QTD_STS_HALT)) {
+					freeing = unlink = 1;
+					if (urb-&gt;status == -EINPROGRESS)
+						urb-&gt;status = -ECONNRESET;
+
+				/* unlink the rest? */
+				} else if (unlink) {
+					urb-&gt;status = -ECONNRESET;
+
+				/* QH halted to unlink urbs after this?  */
+				} else if ((token &amp; QTD_STS_ACTIVE) != 0) {
+					qtd = 0;
 					continue;
 				}
 
-				/* else stopped for some other reason */
-			} 
-scrub:
+			/* Else QH is active, so we must not modify QTDs
+			 * that HC may be working on.  Break from loop.
+			 */
+			} else if (unlikely ((token &amp; QTD_STS_ACTIVE) != 0)) {
+				next = qtd_list;
+				qtd = 0;
+				continue;
+			}
+
 			spin_lock (&amp;urb-&gt;lock);
 			qtd_copy_status (urb, qtd-&gt;length, token);
 			spin_unlock (&amp;urb-&gt;lock);
 		}
-		next = qtd-&gt;qtd_list.next;
 
 		/*
 		 * NOTE:  this won't work right with interrupt urbs that
 		 * need multiple qtds ... only the first scan of qh-&gt;qtd_list
 		 * starts at the right qtd, yet multiple scans could happen
 		 * for transfers that are scheduled across multiple uframes. 
+		 * (Such schedules are not currently allowed!)
 		 */
 		if (likely (freeing != 0))
 			list_del (&amp;qtd-&gt;qtd_list);
@@ -347,8 +367,6 @@
 			qtd-&gt;hw_buf [0] |= cpu_to_le32 (0x0fff &amp; qtd-&gt;buf_dma);
 		}
 
-		spin_unlock_irqrestore (&amp;ehci-&gt;lock, flags);
-
 #if 0
 		if (urb-&gt;status == -EINPROGRESS)
 			vdbg ("  qtd %p ok, urb %p, token %8x, len %d",
@@ -364,21 +382,6 @@
 			pci_unmap_single (ehci-&gt;hcd.pdev,
 				qtd-&gt;buf_dma, sizeof (struct usb_ctrlrequest),
 				PCI_DMA_TODEVICE);
-
-		/* another queued urb? */
-		if (unlikely (qtd-&gt;urb != urb)) {
-			if (likely (freeing != 0))
-				ehci_urb_done (ehci, qtd-&gt;buf_dma, urb);
-			else
-				ehci_urb_complete (ehci, qtd-&gt;buf_dma, urb);
-			retval++;
-			urb = qtd-&gt;urb;
-		}
-
-		if (likely (freeing != 0))
-			ehci_qtd_free (ehci, qtd);
-		spin_lock_irqsave (&amp;ehci-&gt;lock, flags);
-		qtd = list_entry (next, struct ehci_qtd, qtd_list);
 	}
 
 	/* patch up list head? */
@@ -389,11 +392,12 @@
 	spin_unlock_irqrestore (&amp;ehci-&gt;lock, flags);
 
 	/* last urb's completion might still need calling */
-	if (likely (qtd &amp;&amp; urb)) {
-		if (likely (freeing != 0))
-			ehci_urb_done (ehci, qtd-&gt;buf_dma, urb);
-		else
-			ehci_urb_complete (ehci, qtd-&gt;buf_dma, urb);
+	if (likely (last != 0)) {
+		if (likely (freeing != 0)) {
+			ehci_urb_done (ehci, last-&gt;buf_dma, last-&gt;urb);
+			ehci_qtd_free (ehci, last);
+		} else
+			ehci_urb_complete (ehci, last-&gt;buf_dma, last-&gt;urb);
 		retval++;
 	}
 	return retval;
@@ -749,7 +753,9 @@
 		/* is an URB is queued to this qh already? */
 		if (unlikely (!list_empty (&amp;qh-&gt;qtd_list))) {
 			struct ehci_qtd		*last_qtd;
+			int			short_rx = 0;
 
+			/* update the last qtd's "next" pointer */
 			// dbg_qh ("non-empty qh", ehci, qh);
 			last_qtd = list_entry (qh-&gt;qtd_list.prev,
 					struct ehci_qtd, qtd_list);
@@ -760,6 +766,21 @@
 					&amp;&amp; (epnum &amp; 0x10)) {
 				// only the last QTD for now
 				last_qtd-&gt;hw_alt_next = hw_next;
+				short_rx = 1;
+			}
+
+			/* Adjust any old copies in qh overlay too.
+			 * Interrupt code must cope with case of HC having it
+			 * cached, and clobbering these updates.
+			 * ... complicates getting rid of extra interrupts!
+			 */
+			if (qh-&gt;hw_current == cpu_to_le32 (last_qtd-&gt;qtd_dma)) {
+				wmb ();
+				qh-&gt;hw_qtd_next = hw_next;
+				if (short_rx)
+					qh-&gt;hw_alt_next = hw_next
+				    		| (qh-&gt;hw_alt_next &amp; 0x1e);
+				vdbg ("queue to qh %p, patch", qh);
 			}
 
 		/* no URB queued */
@@ -822,8 +843,8 @@
 
 	qh_completions (ehci, &amp;qh-&gt;qtd_list, 1);
 
-	// FIXME unlink any urb should unlink all following urbs,
-	// so that this will never happen
+	// unlink any urb should now unlink all following urbs, so that
+	// relinking only happens for urbs before the unlinked ones.
 	if (!list_empty (&amp;qh-&gt;qtd_list)
 			&amp;&amp; HCD_IS_RUNNING (ehci-&gt;hcd.state))
 		qh_link_async (ehci, qh);
</pre></body></html>