# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.565   -> 1.566  
#	drivers/usb/hcd/ohci-hcd.c	1.6     -> 1.7    
#	drivers/usb/hcd/ohci-hub.c	1.2     -> 1.3    
#	drivers/usb/hcd/ohci-dbg.c	1.2     -> 1.3    
#	drivers/usb/hcd/ohci-mem.c	1.3     -> 1.4    
#	drivers/usb/hcd/ohci-q.c	1.4     -> 1.5    
#	drivers/usb/hcd/ohci.h	1.2     -> 1.3    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/03/27	david-b@pacbell.net	1.566
# USB ohci-hcd driver update
#   
#       - bugfix: control endpoints can't stall
#       - bugfix: remove bogus intr unlink optimization,
#           by sharing intr/iso code
#       - bugfix: iso submit uses urb->interval
#       - removed iso urb->next ring logic
#           (belongs in hcd layer if anywhere)
#       - simplify/shorten/correct completion handling
#       - in debug, labels setup packets as such
#       - bring CVS ids back up to date
# --------------------------------------------
#
diff -Nru a/drivers/usb/hcd/ohci-dbg.c b/drivers/usb/hcd/ohci-dbg.c
--- a/drivers/usb/hcd/ohci-dbg.c	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-dbg.c	Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $
+ * $Id: ohci-dbg.c,v 1.4 2002/03/27 20:40:40 dbrownell Exp $
  */
  
 /*-------------------------------------------------------------------------*/
@@ -52,7 +52,7 @@
 		int i, len;
 
 		if (usb_pipecontrol (pipe)) {
-			printk (KERN_DEBUG __FILE__ ": cmd(8):");
+			printk (KERN_DEBUG __FILE__ ": setup(8):");
 			for (i = 0; i < 8 ; i++) 
 				printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
 			printk ("\n");
diff -Nru a/drivers/usb/hcd/ohci-hcd.c b/drivers/usb/hcd/ohci-hcd.c
--- a/drivers/usb/hcd/ohci-hcd.c	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-hcd.c	Wed Apr  3 16:39:30 2002
@@ -56,7 +56,7 @@
  * v1.0 1999/04/27 initial release
  *
  * This file is licenced under the GPL.
- * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $
+ * $Id: ohci-hcd.c,v 1.9 2002/03/27 20:41:57 dbrownell Exp $
  */
  
 #include <linux/config.h>
@@ -106,7 +106,7 @@
  *	- lots more testing!!
  */
 
-#define DRIVER_VERSION "$Revision: 1.7 $"
+#define DRIVER_VERSION "$Revision: 1.9 $"
 #define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
diff -Nru a/drivers/usb/hcd/ohci-hub.c b/drivers/usb/hcd/ohci-hub.c
--- a/drivers/usb/hcd/ohci-hub.c	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-hub.c	Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under GPL
- * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $
+ * $Id: ohci-hub.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
  */
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/hcd/ohci-mem.c b/drivers/usb/hcd/ohci-mem.c
--- a/drivers/usb/hcd/ohci-mem.c	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-mem.c	Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $
+ * $Id: ohci-mem.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
  */
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c
--- a/drivers/usb/hcd/ohci-q.c	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci-q.c	Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $
+ * $Id: ohci-q.c,v 1.8 2002/03/27 20:57:01 dbrownell Exp $
  */
 
 static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
@@ -54,124 +54,81 @@
 
 /*
  * URB goes back to driver, and isn't reissued.
- * It's completely gone from HC data structures, so no locking
- * is needed ... or desired! (Giveback can call back to hcd.)
+ * It's completely gone from HC data structures.
+ * PRECONDITION:  no locks held  (Giveback can call into HCD.)
  */
-static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
+static void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
 {
-	if (urb->hcpriv) {
-		urb_free_priv (ohci, urb->hcpriv);
-		urb->hcpriv = NULL;
-	}
-	usb_hcd_giveback_urb (&ohci->hcd, urb);
-}
-
-static void td_submit_urb (struct urb *urb);
-
-/*
- * URB is reported to driver, is reissued if it's periodic.
- */
-static int return_urb (struct ohci_hcd *hc, struct urb *urb)
-{
-	urb_priv_t	*urb_priv = urb->hcpriv;
-	struct urb	*urbt;
 	unsigned long	flags;
-	int		i;
 
 #ifdef DEBUG
-	if (!urb_priv) {
+	if (!urb->hcpriv) {
 		err ("already unlinked!");
 		BUG ();
 	}
-
-	/* just to be sure */
-	if (!urb->complete) {
-		err ("no completion!");
-		BUG ();
-	}
 #endif
 
+	urb_free_priv (ohci, urb->hcpriv);
+	urb->hcpriv = NULL;
+
+	spin_lock_irqsave (&urb->lock, flags);
+	if (likely (urb->status == -EINPROGRESS))
+		urb->status = 0;
+	spin_unlock_irqrestore (&urb->lock, flags);
+
 #ifdef OHCI_VERBOSE_DEBUG
 	urb_print (urb, "RET", usb_pipeout (urb->pipe));
 #endif
+	usb_hcd_giveback_urb (&ohci->hcd, urb);
+}
+
+static void td_submit_urb (struct urb *urb);
+
+/* Report interrupt transfer completion, maybe reissue */
+static void intr_resub (struct ohci_hcd *hc, struct urb *urb)
+{
+	urb_priv_t	*urb_priv = urb->hcpriv;
+	unsigned long	flags;
 
-	switch (usb_pipetype (urb->pipe)) {
-  		case PIPE_INTERRUPT:
 #ifdef CONFIG_PCI
 // FIXME rewrite this resubmit path.  use pci_dma_sync_single()
 // and requeue more cheaply, and only if needed.
-			pci_unmap_single (hc->hcd.pdev,
-				urb_priv->td [0]->data_dma,
-				urb->transfer_buffer_length,
-				usb_pipeout (urb->pipe)
-					? PCI_DMA_TODEVICE
-					: PCI_DMA_FROMDEVICE);
-#endif
-			/* FIXME: MP race.  If another CPU partially unlinks
-			 * this URB (urb->status was updated, hasn't yet told
-			 * us to dequeue) before we call complete() here, an
-			 * extra "unlinked" completion will be reported...
-			 */
-			urb->complete (urb);
+// Better yet ... abolish the notion of automagic resubmission.
+	pci_unmap_single (hc->hcd.pdev,
+		urb_priv->td [0]->data_dma,
+		urb->transfer_buffer_length,
+		usb_pipeout (urb->pipe)
+			? PCI_DMA_TODEVICE
+			: PCI_DMA_FROMDEVICE);
+#endif
+	/* FIXME: MP race.  If another CPU partially unlinks
+	 * this URB (urb->status was updated, hasn't yet told
+	 * us to dequeue) before we call complete() here, an
+	 * extra "unlinked" completion will be reported...
+	 */
 
-			/* always requeued, but ED_SKIP if complete() unlinks.
-			 * removed from periodic table only at SOF intr.
-			 */
-  			urb->actual_length = 0;
-			if (urb_priv->state != URB_DEL)
-				urb->status = -EINPROGRESS;
-			spin_lock_irqsave (&hc->lock, flags);
-			td_submit_urb (urb);
-			spin_unlock_irqrestore (&hc->lock, flags);
-  			break;
+	spin_lock_irqsave (&urb->lock, flags);
+	if (likely (urb->status == -EINPROGRESS))
+		urb->status = 0;
+	spin_unlock_irqrestore (&urb->lock, flags);
 
-		case PIPE_ISOCHRONOUS:
-			for (urbt = urb->next;
-					urbt && (urbt != urb);
-					urbt = urbt->next)
-				continue;
-			if (urbt) { /* send the reply and requeue URB */	
-#ifdef CONFIG_PCI
-// FIXME rewrite this resubmit path too
-				pci_unmap_single (hc->hcd.pdev,
-					urb_priv->td [0]->data_dma,
-					urb->transfer_buffer_length,
-					usb_pipeout (urb->pipe)
-						? PCI_DMA_TODEVICE
-						: PCI_DMA_FROMDEVICE);
-#endif
-				urb->complete (urb);
-				spin_lock_irqsave (&hc->lock, flags);
-				urb->actual_length = 0;
-  				urb->status = -EINPROGRESS;
-  				urb->start_frame = urb_priv->ed->last_iso + 1;
-  				if (urb_priv->state != URB_DEL) {
-  					for (i = 0; i < urb->number_of_packets;
-							i++) {
-  						urb->iso_frame_desc [i]
-							.actual_length = 0;
-  						urb->iso_frame_desc [i]
-							.status = -EXDEV;
-  					}
-  					td_submit_urb (urb);
-  				}
-// FIXME if not deleted, should have been "finished"
-  				spin_unlock_irqrestore (&hc->lock, flags);
-
-  			} else { /* not reissued */
-				finish_urb (hc, urb);
-			}		
-			break;
+#ifdef OHCI_VERBOSE_DEBUG
+	urb_print (urb, "INTR", usb_pipeout (urb->pipe));
+#endif
+	urb->complete (urb);
 
-		/*
-		 * C/B requests that get here are never reissued.
-		 */
-		case PIPE_BULK:
-		case PIPE_CONTROL:
-			finish_urb (hc, urb);
-			break;
-	}
-	return 0;
+	/* always requeued, but ED_SKIP if complete() unlinks.
+	 * EDs are removed from periodic table only at SOF intr.
+	 */
+	urb->actual_length = 0;
+	spin_lock_irqsave (&urb->lock, flags);
+	if (urb_priv->state != URB_DEL)
+		urb->status = -EINPROGRESS;
+	spin_unlock (&urb->lock);
+
+	spin_lock (&hc->lock);
+	td_submit_urb (urb);
+	spin_unlock_irqrestore (&hc->lock, flags);
 }
 
 
@@ -329,6 +286,26 @@
 
 /*-------------------------------------------------------------------------*/
 
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (
+	struct ohci_hcd	*ohci,
+	struct ed	*ed,
+	unsigned	index,
+	unsigned	period
+) {
+	for (; index < NUM_INTS; index += period) {
+		__u32	*ed_p = &ohci->hcca->int_table [index];
+
+		while (*ed_p != 0) {
+			if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+				*ed_p = ed->hwNextED;		
+				break;
+			}
+			ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED);
+		}
+	}	
+}
+
 /* unlink an ed from one of the HC chains. 
  * just the link to the ed is unlinked.
  * the link from the ed still points to another operational ed or 0
@@ -336,11 +313,7 @@
  */
 static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) 
 {
-	int	int_branch;
 	int	i;
-	int	inter;
-	int	interval;
-	__u32	*ed_p;
 
 	ed->hwINFO |= ED_SKIP;
 
@@ -384,22 +357,9 @@
 		break;
 
 	case PIPE_INTERRUPT:
-		int_branch = ed->int_branch;
-		interval = ed->int_interval;
-
-		for (i = 0; i < ep_rev (6, interval); i += inter) {
-			for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]), inter = 1; 
-				 (*ed_p != 0) && (*ed_p != ed->hwNextED); 
-				ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), 
-				inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {				
-					if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
-			  			*ed_p = ed->hwNextED;		
-			  			break;
-			  		}
-			  }
-		}
-		for (i = int_branch; i < NUM_INTS; i += interval)
-		    ohci->ohci_int_load [i] -= ed->int_load;
+		periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval);
+		for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval)
+			ohci->ohci_int_load [i] -= ed->int_load;
 #ifdef OHCI_VERBOSE_DEBUG
 		ohci_dump_periodic (ohci, "UNLINK_INT");
 #endif
@@ -412,21 +372,10 @@
 			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
 		    		->ed_prev = ed->ed_prev;
 
-		if (ed->ed_prev != NULL) {
+		if (ed->ed_prev != NULL)
 			ed->ed_prev->hwNextED = ed->hwNextED;
-		} else {
-			for (i = 0; i < NUM_INTS; i++) {
-				for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); 
-						*ed_p != 0; 
-						ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
-					// inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
-					if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
-						*ed_p = ed->hwNextED;		
-						break;
-					}
-				}
-			}	
-		}	
+		else
+			periodic_unlink (ohci, ed, 0, 1);
 #ifdef OHCI_VERBOSE_DEBUG
 		ohci_dump_periodic (ohci, "UNLINK_ISO");
 #endif
@@ -584,6 +533,12 @@
 		return;
 	}
 
+#if 0
+	/* no interrupt needed except for URB's last TD */
+	if (index != (urb_priv->length - 1))
+		info |= TD_DI;
+#endif
+
 	/* use this td as the next dummy */
 	td_pt = urb_priv->td [index];
 	td_pt->hwNextTD = 0;
@@ -660,6 +615,9 @@
 	} else
 		data = 0;
 
+	/* NOTE:  TD_CC is set so we can tell which TDs the HC processed by
+	 * using TD_CC_GET, as well as by seeing them on the done list.
+	 */
 	switch (usb_pipetype (urb->pipe)) {
 		case PIPE_BULK:
 			info = usb_pipeout (urb->pipe)
@@ -726,8 +684,14 @@
 
 		case PIPE_ISOCHRONOUS:
 			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
-				td_fill (ohci, TD_CC | TD_ISO
-					| ((urb->start_frame + cnt) & 0xffff), 
+				int	frame = urb->start_frame;
+
+				// FIXME scheduling should handle frame counter
+				// roll-around ... exotic case (and OHCI has
+				// a 2^16 iso range, vs other HCs max of 2^10)
+				frame += cnt * urb->interval;
+				frame &= 0xffff;
+				td_fill (ohci, TD_CC | TD_ISO | frame,
 				    data + urb->iso_frame_desc [cnt].offset, 
 				    urb->iso_frame_desc [cnt].length, urb, cnt); 
 			}
@@ -741,50 +705,77 @@
  * Done List handling functions
  *-------------------------------------------------------------------------*/
 
-/* calculate the transfer length and update the urb */
-
-static void dl_transfer_length (struct td *td)
+/* calculate transfer length/status and update the urb
+ * PRECONDITION:  irqsafe (only for urb->status locking)
+ */
+static void td_done (struct urb *urb, struct td *td)
 {
-	__u32		tdINFO, tdBE, tdCBP;
- 	__u16		tdPSW;
- 	struct urb	*urb = td->urb;
- 	urb_priv_t	*urb_priv = urb->hcpriv;
-	int		dlen = 0;
-	int		cc = 0;
-
-	tdINFO = le32_to_cpup (&td->hwINFO);
-  	tdBE   = le32_to_cpup (&td->hwBE);
-  	tdCBP  = le32_to_cpup (&td->hwCBP);
+	u32	tdINFO = le32_to_cpup (&td->hwINFO);
+	int	cc = 0;
 
 
+	/* ISO ... drivers see per-TD length/status */
   	if (tdINFO & TD_ISO) {
- 		tdPSW = le16_to_cpu (td->hwPSW [0]);
+ 		u16	tdPSW = le16_to_cpu (td->hwPSW [0]);
+		int	dlen = 0;
+
  		cc = (tdPSW >> 12) & 0xF;
-		if (cc < 0xE)  {
-			if (usb_pipeout (urb->pipe)) {
-				dlen = urb->iso_frame_desc [td->index].length;
-			} else {
-				dlen = tdPSW & 0x3ff;
-			}
-			urb->actual_length += dlen;
-			urb->iso_frame_desc [td->index].actual_length = dlen;
-			if (! (urb->transfer_flags & USB_DISABLE_SPD)
-					&& (cc == TD_DATAUNDERRUN))
-				cc = TD_CC_NOERROR;
-
-			urb->iso_frame_desc [td->index].status
-				= cc_to_error [cc];
-		}
-	} else { /* BULK, INT, CONTROL DATA */
-		if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL && 
-				 ((td->index == 0)
-				 || (td->index == urb_priv->length - 1)))) {
- 			if (tdBE != 0) {
-				urb->actual_length += (td->hwCBP == 0)
-					? (tdBE - td->data_dma + 1)
-					: (tdCBP - td->data_dma);
-			}
-  		}
+		if (! ((urb->transfer_flags & USB_DISABLE_SPD)
+				&& (cc == TD_DATAUNDERRUN)))
+			cc = TD_CC_NOERROR;
+
+		if (usb_pipeout (urb->pipe))
+			dlen = urb->iso_frame_desc [td->index].length;
+		else
+			dlen = tdPSW & 0x3ff;
+		urb->actual_length += dlen;
+		urb->iso_frame_desc [td->index].actual_length = dlen;
+		urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+
+		if (cc != 0)
+			dbg ("  urb %p iso TD %d len %d CC %d",
+				urb, td->index, dlen, cc);
+
+	/* BULK, INT, CONTROL ... drivers see aggregate length/status,
+	 * except that "setup" bytes aren't counted and "short" transfers
+	 * might not be reported as errors.
+	 */
+	} else {
+		int	type = usb_pipetype (urb->pipe);
+		u32	tdBE = le32_to_cpup (&td->hwBE);
+
+  		cc = TD_CC_GET (tdINFO);
+
+		/* control endpoints only have soft stalls */
+  		if (type != PIPE_CONTROL && cc == TD_CC_STALL)
+			usb_endpoint_halt (urb->dev,
+				usb_pipeendpoint (urb->pipe),
+				usb_pipeout (urb->pipe));
+
+		/* update packet status if needed (short may be ok) */
+		if (((urb->transfer_flags & USB_DISABLE_SPD) != 0
+				&& cc == TD_DATAUNDERRUN))
+			cc = TD_CC_NOERROR;
+		if (cc != TD_CC_NOERROR) {
+			spin_lock (&urb->lock);
+			if (urb->status == -EINPROGRESS)
+				urb->status = cc_to_error [cc];
+			spin_unlock (&urb->lock);
+		}
+
+		/* count all non-empty packets except control SETUP packet */
+		if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
+			if (td->hwCBP == 0)
+				urb->actual_length += tdBE - td->data_dma + 1;
+			else
+				urb->actual_length +=
+					  le32_to_cpup (&td->hwCBP)
+					- td->data_dma;
+		}
+
+		if (cc != 0)
+			dbg ("  urb %p TD %d CC %d, len=%d",
+				urb, td->index, cc, urb->actual_length);
   	}
 }
 
@@ -811,13 +802,16 @@
 
 		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
 			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
-			dbg (" USB-error/status: %x : %p", 
-				TD_CC_GET (le32_to_cpup (&td_list->hwINFO)),
-				td_list);
-			/* typically the endpoint halted too */
+			/* typically the endpoint halts on error; un-halt,
+			 * and maybe dequeue other TDs from this urb
+			 */
 			if (td_list->ed->hwHeadP & ED_H) {
 				if (urb_priv && ((td_list->index + 1)
 						< urb_priv->length)) {
+					dbg ("urb %p TD %d of %d, patch ED",
+						td_list->urb,
+						1 + td_list->index,
+						urb_priv->length);
 					td_list->ed->hwHeadP = 
 			    (urb_priv->td [urb_priv->length - 1]->hwNextTD
 				    & __constant_cpu_to_le32 (TD_MASK))
@@ -870,16 +864,19 @@
 				le32_to_cpup (&td->hwNextTD));
 			if ((urb_priv->state == URB_DEL)) {
 				tdINFO = le32_to_cpup (&td->hwINFO);
+				/* HC may have partly processed this TD */
 				if (TD_CC_GET (tdINFO) < 0xE)
-					dl_transfer_length (td);
+					td_done (urb, td);
 				*td_p = td->hwNextTD | (*td_p
 					& __constant_cpu_to_le32 (0x3));
 
 				/* URB is done; clean up */
-				if (++ (urb_priv->td_cnt) == urb_priv->length)
-// FIXME:  we shouldn't hold ohci->lock here, else the
-// completion function can't talk to this hcd ...
+				if (++ (urb_priv->td_cnt) == urb_priv->length) {
+     					spin_unlock_irqrestore (&ohci->lock,
+						flags);
 					finish_urb (ohci, urb);
+					spin_lock_irqsave (&ohci->lock, flags);
+				}
 			} else {
 				td_p = &td->hwNextTD;
 			}
@@ -931,71 +928,52 @@
 /*-------------------------------------------------------------------------*/
 
 /*
- * process normal completions (error or success) and some unlinked eds
- * this is the main path for handing urbs back to drivers
+ * Process normal completions (error or success) and clean the schedules.
+ *
+ * This is the main path for handing urbs back to drivers.  The only other
+ * path is dl_del_list(), which unlinks URBs by scanning EDs, instead of
+ * scanning the (re-reversed) donelist as this does.
  */
-static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list)
+static void dl_done_list (struct ohci_hcd *ohci, struct td *td)
 {
-  	struct td	*td_list_next = NULL;
-	struct ed	*ed;
-	int		cc = 0;
-	struct urb	*urb;
-	urb_priv_t	*urb_priv;
- 	__u32		tdINFO;
-
- 	unsigned long flags;
-
-  	while (td_list) {
-   		td_list_next = td_list->next_dl_td;
-
-  		urb = td_list->urb;
-  		urb_priv = urb->hcpriv;
-  		tdINFO = le32_to_cpup (&td_list->hwINFO);
-
-   		ed = td_list->ed;
-
-   		dl_transfer_length (td_list);
+	unsigned long	flags;
 
-  		/* error code of transfer */
-  		cc = TD_CC_GET (tdINFO);
-  		if (cc == TD_CC_STALL)
-			usb_endpoint_halt (urb->dev,
-				usb_pipeendpoint (urb->pipe),
-				usb_pipeout (urb->pipe));
+  	spin_lock_irqsave (&ohci->lock, flags);
+  	while (td) {
+		struct td	*td_next = td->next_dl_td;
+		struct urb	*urb = td->urb;
+		urb_priv_t	*urb_priv = urb->hcpriv;
+		struct ed	*ed = td->ed;
+
+		/* update URB's length and status from TD */
+   		td_done (urb, td);
+  		urb_priv->td_cnt++;
+
+		/* If all this urb's TDs are done, call complete().
+		 * Interrupt transfers are the only special case:
+		 * they're reissued, until "deleted" by usb_unlink_urb
+		 * (real work done in a SOF intr, by dl_del_list).
+		 */
+  		if (urb_priv->td_cnt == urb_priv->length) {
+			int	resubmit;
 
-  		if (! (urb->transfer_flags & USB_DISABLE_SPD)
-				&& (cc == TD_DATAUNDERRUN))
-			cc = TD_CC_NOERROR;
+			resubmit = usb_pipeint (urb->pipe)
+					&& (urb_priv->state != URB_DEL);
 
-  		if (++ (urb_priv->td_cnt) == urb_priv->length) {
-			/*
-			 * Except for periodic transfers, both branches do
-			 * the same thing.  Periodic urbs get reissued until
-			 * they're "deleted" (in SOF intr) by usb_unlink_urb.
-			 */
-			if ((ed->state & (ED_OPER | ED_UNLINK))
-					&& (urb_priv->state != URB_DEL)) {
-				spin_lock (&urb->lock);
-				if (urb->status == -EINPROGRESS)
-  					urb->status = cc_to_error [cc];
-				spin_unlock (&urb->lock);
-  				return_urb (ohci, urb);
-  			} else
+     			spin_unlock_irqrestore (&ohci->lock, flags);
+			if (resubmit)
+  				intr_resub (ohci, urb);
+  			else
   				finish_urb (ohci, urb);
+  			spin_lock_irqsave (&ohci->lock, flags);
   		}
 
-  		spin_lock_irqsave (&ohci->lock, flags);
-  		if (ed->state != ED_NEW) { 
-  			u32 edHeadP = ed->hwHeadP;
-
-			/* unlink eds if they are not busy */
-			edHeadP &= __constant_cpu_to_le32 (ED_MASK);
-     			if ((edHeadP == ed->hwTailP) && (ed->state == ED_OPER)) 
-     				ep_unlink (ohci, ed);
-     		}	
-     		spin_unlock_irqrestore (&ohci->lock, flags);
-
-    		td_list = td_list_next;
+		/* clean schedule:  unlink EDs that are no longer busy */
+		if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK))
+					== ed->hwTailP
+				&& (ed->state == ED_OPER)) 
+			ep_unlink (ohci, ed);
+    		td = td_next;
   	}  
+	spin_unlock_irqrestore (&ohci->lock, flags);
 }
-
diff -Nru a/drivers/usb/hcd/ohci.h b/drivers/usb/hcd/ohci.h
--- a/drivers/usb/hcd/ohci.h	Wed Apr  3 16:39:30 2002
+++ b/drivers/usb/hcd/ohci.h	Wed Apr  3 16:39:30 2002
@@ -5,7 +5,7 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $
+ * $Id: ohci.h,v 1.6 2002/03/22 16:04:54 dbrownell Exp $
  */
  
 /*
