# 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.579.9.1 -> 1.579.9.2
#	drivers/usb/host/ehci-q.c	1.26    -> 1.27   
#	drivers/usb/host/ehci-dbg.c	1.8     -> 1.9    
#	drivers/usb/host/ehci-sched.c	1.20    -> 1.21   
#	drivers/usb/host/ehci-hub.c	1.10    -> 1.11   
#	drivers/usb/host/ehci-hcd.c	1.28    -> 1.29   
#	drivers/usb/host/ehci.h	1.11    -> 1.12   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/09/23	david-b@pacbell.net	1.579.9.2
# [PATCH] ehci-hcd: update
# 
# Here's an EHCI update, I'll send separate patches to sync 2.4 with
# this version.  Changes in this version include:
# 
#   - An earlier locking update would give trouble on SPARC, where
#     irqsave "flags" aren't flags.  This resolves that issue by
#     adding a module parameter to limit work done with irqs off.
#     (Some net drivers do the same thing.)
# 
#   - Optionally (now #ifdef DEBUG) collects some statistics on IRQs
#     and URBs.  There are more IAA interrupts than I want to see,
#     during extended usb-storage loading.
# 
#   - Adds a commented-out workaround for a problem I've seen on one
#     VT8235.  Seems likely an issue with this specific motherboard;
#     another tester hasn't reported such issues.
# 
#   - Includes the jiffies time_after() patch from Tim Schmielau.
# 
#   - Minor tweaks to the hcd portability (get rid of another #if).
# 
#   - Minor doc/diagnostic/... updates
# --------------------------------------------
#
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci-dbg.c	Mon Sep 23 15:16:08 2002
@@ -79,7 +79,7 @@
 
 	if (HCC_EXT_CAPS (params)) {
 		// EHCI 0.96 ... could interpret these (legacy?)
-		dbg ("%s extended capabilities at pci %d",
+		dbg ("%s extended capabilities at pci %2x",
 			label, HCC_EXT_CAPS (params));
 	}
 	if (HCC_ISOC_CACHE (params)) {
@@ -545,6 +545,18 @@
 		size -= temp;
 		next += temp;
 	}
+
+#ifdef EHCI_STATS
+	temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n",
+		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
+	size -= temp;
+	next += temp;
+
+	temp = snprintf (next, size, "complete %ld unlink %ld qpatch %ld\n",
+		ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
+	size -= temp;
+	next += temp;
+#endif
 
 	spin_unlock_irqrestore (&ehci->lock, flags);
 
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci-hcd.c	Mon Sep 23 15:16:08 2002
@@ -63,8 +63,7 @@
  * First was PCMCIA, like ISA; then CardBus, which is PCI.
  * Next comes "CardBay", using USB 2.0 signals.
  *
- * Contains additional contributions by: Brad Hards, Rory Bolt, ...
- *
+ * Contains additional contributions by Brad Hards, Rory Bolt, and others.
  * Special thanks to Intel and VIA for providing host controllers to
  * test this driver on, and Cypress (including In-System Design) for
  * providing early devices for those host controllers to talk to!
@@ -93,14 +92,20 @@
  * 2001-June	Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2002-Aug-28"
+#define DRIVER_VERSION "2002-Sep-23"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
+static const char	hcd_name [] = "ehci-hcd";
+
 
 // #define EHCI_VERBOSE_DEBUG
 // #define have_split_iso
 
+#ifdef DEBUG
+#define EHCI_STATS
+#endif
+
 #define INTR_AUTOMAGIC		/* to be removed later in 2.5 */
 
 /* magic numbers that can affect system performance */
@@ -118,6 +123,12 @@
 MODULE_PARM (log2_irq_thresh, "i");
 MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
 
+/* allow irqs at least every N URB completions */
+static int max_completions = 16;
+MODULE_PARM (max_completions, "i");
+MODULE_PARM_DESC (max_completions,
+		"limit for urb completions called with irqs disenabled");
+
 #define	INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
@@ -426,11 +437,10 @@
         /* PCI Serial Bus Release Number is at 0x60 offset */
 	pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
 	temp = readw (&ehci->caps->hci_version);
-	info ("USB %x.%x support enabled, EHCI rev %x.%02x",
-	      ((tempbyte & 0xf0)>>4),
-	      (tempbyte & 0x0f),
-	       temp >> 8,
-	       temp & 0xff);
+	info ("USB %x.%x support enabled, EHCI rev %x.%02x, %s %s",
+	      ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
+	       temp >> 8, temp & 0xff,
+	       hcd_name, DRIVER_VERSION);
 
 	/*
 	 * From here on, khubd concurrently accesses the root
@@ -441,11 +451,7 @@
 	 */
 	usb_connect (udev);
 	udev->speed = USB_SPEED_HIGH;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
-	if (usb_new_device (udev) != 0) {
-#else
-	if (usb_register_root_hub (udev, &ehci->hcd.pdev->dev) != 0) {
-#endif
+	if (hcd_register_root (hcd) != 0) {
 		if (hcd->state == USB_STATE_RUNNING)
 			ehci_ready (ehci);
 		ehci_reset (ehci);
@@ -487,6 +493,13 @@
 	ehci_tasklet ((unsigned long) ehci);
 	ehci_mem_cleanup (ehci);
 
+#ifdef	EHCI_STATS
+	dbg ("irq normal %ld err %ld reclaim %ld",
+		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
+	dbg ("complete %ld unlink %ld qpatch %ld",
+		ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
+#endif
+
 	dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
 }
 
@@ -591,21 +604,16 @@
 static void ehci_tasklet (unsigned long param)
 {
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
-	unsigned long		flags;
 
-	// FIXME don't pass flags; on sparc they aren't really flags.
-	// qh_completions can just leave irqs blocked,
-	// then have scan_async() allow IRQs if it's very busy 
-
-	spin_lock_irqsave (&ehci->lock, flags);
+	spin_lock_irq (&ehci->lock);
 
 	if (ehci->reclaim_ready)
-		flags = end_unlink_async (ehci, flags);
-	flags = scan_async (ehci, flags);
+		end_unlink_async (ehci);
+	scan_async (ehci);
 	if (ehci->next_uframe != -1)
-		flags = scan_periodic (ehci, flags);
+		scan_periodic (ehci);
 
-	spin_unlock_irqrestore (&ehci->lock, flags);
+	spin_unlock_irq (&ehci->lock);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -639,11 +647,17 @@
 	/* INT, ERR, and IAA interrupt rates can be throttled */
 
 	/* normal [4.15.1.2] or error [4.15.1.1] completion */
-	if (likely ((status & (STS_INT|STS_ERR)) != 0))
+	if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
+		if (likely ((status & STS_ERR) == 0))
+			COUNT (ehci->stats.normal);
+		else
+			COUNT (ehci->stats.error);
 		bh = 1;
+	}
 
 	/* complete the unlinking of some qh [4.15.2.3] */
 	if (status & STS_IAA) {
+		COUNT (ehci->stats.reclaim);
 		ehci->reclaim_ready = 1;
 		bh = 1;
 	}
@@ -765,10 +779,10 @@
 		spin_lock_irqsave (&ehci->lock, flags);
 		if (qh->qh_state == QH_STATE_LINKED) {
 			/* messy, can spin or block a microframe ... */
-			flags = intr_deschedule (ehci, qh, 1, flags);
+			intr_deschedule (ehci, qh, 1);
 			/* qh_state == IDLE */
 		}
-		flags = qh_completions (ehci, qh, flags);
+		qh_completions (ehci, qh);
 
 		/* reschedule QH iff another request is queued */
 		if (!list_empty (&qh->qtd_list)
@@ -880,8 +894,6 @@
 }
 
 /*-------------------------------------------------------------------------*/
-
-static const char	hcd_name [] = "ehci-hcd";
 
 static const struct hc_driver ehci_driver = {
 	.description =		hcd_name,
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci-hub.c	Mon Sep 23 15:16:08 2002
@@ -239,7 +239,8 @@
 
 		/* whoever resets must GetPortStatus to complete it!! */
 		if ((temp & PORT_RESET)
-				&& jiffies > ehci->reset_done [wIndex]) {
+				&& time_after (jiffies,
+					ehci->reset_done [wIndex])) {
 			status |= 1 << USB_PORT_FEAT_C_RESET;
 
 			/* force reset to complete */
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci-q.c	Mon Sep 23 15:16:08 2002
@@ -158,16 +158,13 @@
 	}
 }
 
-/* urb->lock ignored from here on (hcd is done with urb) */
-
-static unsigned long ehci_urb_done (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	unsigned long		flags
-) {
+static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
+{
 #ifdef	INTR_AUTOMAGIC
 	struct urb		*resubmit = 0;
 	struct usb_device	*dev = 0;
+
+	static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int);
 #endif
 
 	if (likely (urb->hcpriv != 0)) {
@@ -199,8 +196,15 @@
 			urb->status = 0;
 	}
 
+	if (likely (urb->status == 0))
+		COUNT (ehci->stats.complete);
+	else if (urb->status == -ECONNRESET || urb->status == -ENOENT)
+		COUNT (ehci->stats.unlink);
+	else
+		COUNT (ehci->stats.error);
+
 	/* complete() can reenter this HCD */
-	spin_unlock_irqrestore (&ehci->lock, flags);
+	spin_unlock (&ehci->lock);
 	usb_hcd_giveback_urb (&ehci->hcd, urb);
 
 #ifdef	INTR_AUTOMAGIC
@@ -222,24 +226,25 @@
 	}
 #endif
 
-	spin_lock_irqsave (&ehci->lock, flags);
-	return flags;
+	spin_lock (&ehci->lock);
 }
 
 
 /*
  * Process and free completed qtds for a qh, returning URBs to drivers.
- * Chases up to qh->hw_current, returns irqsave flags (maybe modified).
+ * Chases up to qh->hw_current.  Returns number of completions called,
+ * indicating how much "real" work we did.
  */
-static unsigned long
-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
+static unsigned
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	struct ehci_qtd		*qtd, *last;
 	struct list_head	*next, *qtd_list = &qh->qtd_list;
 	int			unlink = 0, halted = 0;
+	unsigned		count = 0;
 
 	if (unlikely (list_empty (qtd_list)))
-		return flags;
+		return count;
 
 	/* scan QTDs till end of list, or we reach an active one */
 	for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
@@ -252,8 +257,10 @@
 
 		/* clean up any state from previous QTD ...*/
 		if (last) {
-			if (likely (last->urb != urb))
-				flags = ehci_urb_done (ehci, last->urb, flags);
+			if (likely (last->urb != urb)) {
+				ehci_urb_done (ehci, last->urb);
+				count++;
+			}
 
 			/* qh overlays can have HC's old cached copies of
 			 * next qtd ptrs, if an URB was queued afterwards.
@@ -262,6 +269,7 @@
 					&& last->hw_next != qh->hw_qtd_next) {
 				qh->hw_alt_next = last->hw_alt_next;
 				qh->hw_qtd_next = last->hw_next;
+				COUNT (ehci->stats.qpatch);
 			}
 
 			ehci_qtd_free (ehci, last);
@@ -347,7 +355,8 @@
 
 	/* last urb's completion might still need calling */
 	if (likely (last != 0)) {
-		flags = ehci_urb_done (ehci, last->urb, flags);
+		ehci_urb_done (ehci, last->urb);
+		count++;
 		ehci_qtd_free (ehci, last);
 	}
 
@@ -357,7 +366,7 @@
 				struct ehci_qtd, qtd_list));
 	}
 
-	return flags;
+	return count;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -890,8 +899,7 @@
 /* the async qh for the qtds being reclaimed are now unlinked from the HC */
 /* caller must not own ehci->lock */
 
-static unsigned long
-end_unlink_async (struct ehci_hcd *ehci, unsigned long flags)
+static void end_unlink_async (struct ehci_hcd *ehci)
 {
 	struct ehci_qh		*qh = ehci->reclaim;
 
@@ -903,18 +911,15 @@
 	ehci->reclaim = 0;
 	ehci->reclaim_ready = 0;
 
-	flags = qh_completions (ehci, qh, flags);
+	qh_completions (ehci, qh);
 
 	if (!list_empty (&qh->qtd_list)
 			&& HCD_IS_RUNNING (ehci->hcd.state))
 		qh_link_async (ehci, qh);
 	else
 		qh_put (ehci, qh);		// refcount from async list
-
-	return flags;
 }
 
-
 /* makes sure the async qh will become idle */
 /* caller must own ehci->lock */
 
@@ -944,12 +949,14 @@
 	if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
 		/* can't get here without STS_ASS set */
 		if (ehci->hcd.state != USB_STATE_HALT) {
-			if (cmd & CMD_PSE) {
-				writel (cmd & ~CMD_ASE, &ehci->regs->command);
-				(void) handshake (&ehci->regs->status,
-						  STS_ASS, 0, 150);
-			} else
-				ehci_ready (ehci);
+			writel (cmd & ~CMD_ASE, &ehci->regs->command);
+			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
+#if 0
+			// one VT8235 system wants to die with STS_FATAL
+			// unless this qh is leaked here. others seem ok...
+			qh = qh_get (qh);
+			dbg_qh ("async/off", ehci, qh);
+#endif
 		}
 		qh->qh_next.qh = ehci->async = 0;
 
@@ -986,13 +993,15 @@
 
 /*-------------------------------------------------------------------------*/
 
-static unsigned long
-scan_async (struct ehci_hcd *ehci, unsigned long flags)
+static void
+scan_async (struct ehci_hcd *ehci)
 {
 	struct ehci_qh		*qh;
+	unsigned		count;
 
 rescan:
 	qh = ehci->async;
+	count = 0;
 	if (likely (qh != 0)) {
 		do {
 			/* clean any finished work for this qh */
@@ -1001,13 +1010,16 @@
 				qh = qh_get (qh);
 
 				/* concurrent unlink could happen here */
-				flags = qh_completions (ehci, qh, flags);
+				count += qh_completions (ehci, qh);
 				qh_put (ehci, qh);
 			}
 
 			/* unlink idle entries, reducing HC PCI usage as
 			 * well as HCD schedule-scanning costs.  removing
 			 * the last qh is deferred, since it's costly.
+			 *
+			 * FIXME don't unlink idle entries so quickly; it
+			 * can penalize (common) half duplex protocols.
 			 */
 			if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
 				if (qh->qh_next.qh != qh) {
@@ -1020,10 +1032,18 @@
 						jiffies + EHCI_ASYNC_JIFFIES);
 				}
 			}
+
+			/* keep latencies down: let any irqs in */
+			if (count > max_completions) {
+				spin_unlock_irq (&ehci->lock);
+				cpu_relax ();
+				spin_lock_irq (&ehci->lock);
+				goto rescan;
+			}
+
 			qh = qh->qh_next.qh;
 			if (!qh)		/* unlinked? */
 				goto rescan;
 		} while (qh != ehci->async);
 	}
-	return flags;
 }
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci-sched.c	Mon Sep 23 15:16:08 2002
@@ -222,11 +222,10 @@
 
 // FIXME microframe periods not yet handled
 
-static unsigned long intr_deschedule (
+static void intr_deschedule (
 	struct ehci_hcd	*ehci,
 	struct ehci_qh	*qh,
-	int		wait,
-	unsigned long	flags
+	int		wait
 ) {
 	int		status;
 	unsigned	frame = qh->start;
@@ -256,10 +255,8 @@
 	 */
 	if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
 		if (wait) {
-			spin_unlock_irqrestore (&ehci->lock, flags);
 			udelay (125);
 			qh->hw_next = EHCI_LIST_END;
-			spin_lock_irqsave (&ehci->lock, flags);
 		} else {
 			/* we may not be IDLE yet, but if the qh is empty
 			 * the race is very short.  then if qh also isn't
@@ -276,10 +273,9 @@
 	hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= 
 		(qh->usecs + qh->c_usecs) / qh->period;
 
-	vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d",
+	dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
 		qh, qh->period, frame,
 		atomic_read (&qh->refcount), ehci->periodic_sched);
-	return flags;
 }
 
 static int check_period (
@@ -414,7 +410,7 @@
 
 	/* stuff into the periodic schedule */
 	qh->qh_state = QH_STATE_LINKED;
-	dbg ("qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
+	dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
 		qh, qh->usecs, qh->c_usecs,
 		qh->period, frame, uframe, qh->gap_uf);
 	do {
@@ -495,29 +491,30 @@
 	return status;
 }
 
-static unsigned long
+static unsigned
 intr_complete (
 	struct ehci_hcd	*ehci,
 	unsigned	frame,
-	struct ehci_qh	*qh,
-	unsigned long	flags		/* caller owns ehci->lock ... */
+	struct ehci_qh	*qh
 ) {
+	unsigned	count;
+
 	/* nothing to report? */
 	if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
 			!= 0))
-		return flags;
+		return 0;
 	if (unlikely (list_empty (&qh->qtd_list))) {
 		dbg ("intr qh %p no TDs?", qh);
-		return flags;
+		return 0;
 	}
 	
 	/* handle any completions */
-	flags = qh_completions (ehci, qh, flags);
+	count = qh_completions (ehci, qh);
 
 	if (unlikely (list_empty (&qh->qtd_list)))
-		flags = intr_deschedule (ehci, qh, 0, flags);
+		intr_deschedule (ehci, qh, 0);
 
-	return flags;
+	return count;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -866,12 +863,11 @@
 
 #define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
 
-static unsigned long
+static unsigned
 itd_complete (
 	struct ehci_hcd	*ehci,
 	struct ehci_itd	*itd,
-	unsigned	uframe,
-	unsigned long	flags
+	unsigned	uframe
 ) {
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
@@ -909,7 +905,7 @@
 
 	/* handle completion now? */
 	if ((itd->index + 1) != urb->number_of_packets)
-		return flags;
+		return 0;
 
 	/*
 	 * Always give the urb back to the driver ... expect it to submit
@@ -924,16 +920,17 @@
 	if (urb->status == -EINPROGRESS)
 		urb->status = 0;
 
-	spin_unlock_irqrestore (&ehci->lock, flags);
+	/* complete() can reenter this HCD */
+	spin_unlock (&ehci->lock);
 	usb_hcd_giveback_urb (&ehci->hcd, urb);
-	spin_lock_irqsave (&ehci->lock, flags);
+	spin_lock (&ehci->lock);
 
 	/* defer stopping schedule; completion can submit */
 	ehci->periodic_sched--;
 	if (!ehci->periodic_sched)
 		(void) disable_periodic (ehci);
 
-	return flags;
+	return 1;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -945,10 +942,6 @@
 
 	dbg ("itd_submit urb %p", urb);
 
-	/* NOTE DMA mapping assumes this ... */
-	if (urb->iso_frame_desc [0].offset != 0)
-		return -EINVAL;
-	
 	/* allocate ITDs w/o locking anything */
 	status = itd_urb_transaction (ehci, urb, mem_flags);
 	if (status < 0)
@@ -979,10 +972,11 @@
 
 /*-------------------------------------------------------------------------*/
 
-static unsigned long
-scan_periodic (struct ehci_hcd *ehci, unsigned long flags)
+static void
+scan_periodic (struct ehci_hcd *ehci)
 {
 	unsigned	frame, clock, now_uframe, mod;
+	unsigned	count = 0;
 
 	mod = ehci->periodic_size << 3;
 
@@ -1005,6 +999,14 @@
 		u32			type, *hw_p;
 		unsigned		uframes;
 
+		/* keep latencies down: let any irqs in */
+		if (count > max_completions) {
+			spin_unlock_irq (&ehci->lock);
+			cpu_relax ();
+			count = 0;
+			spin_lock_irq (&ehci->lock);
+		}
+
 restart:
 		/* scan schedule to _before_ current frame index */
 		if (frame == clock)
@@ -1028,8 +1030,8 @@
 				last = (q.qh->hw_next == EHCI_LIST_END);
 				temp = q.qh->qh_next;
 				type = Q_NEXT_TYPE (q.qh->hw_next);
-				flags = intr_complete (ehci, frame,
-						qh_get (q.qh), flags);
+				count += intr_complete (ehci, frame,
+						qh_get (q.qh));
 				qh_put (ehci, q.qh);
 				q = temp;
 				break;
@@ -1061,8 +1063,8 @@
 						type = Q_NEXT_TYPE (*hw_p);
 
 						/* might free q.itd ... */
-						flags = itd_complete (ehci,
-							temp.itd, uf, flags);
+						count += itd_complete (ehci,
+							temp.itd, uf);
 						break;
 					}
 				}
@@ -1078,7 +1080,7 @@
 #ifdef have_split_iso
 			case Q_TYPE_SITD:
 				last = (q.sitd->hw_next == EHCI_LIST_END);
-				flags = sitd_complete (ehci, q.sitd, flags);
+				sitd_complete (ehci, q.sitd);
 				type = Q_NEXT_TYPE (q.sitd->hw_next);
 
 				// FIXME unlink SITD after split completes
@@ -1124,5 +1126,4 @@
 		} else
 			frame = (frame + 1) % ehci->periodic_size;
 	} 
-	return flags;
 }
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Mon Sep 23 15:16:08 2002
+++ b/drivers/usb/host/ehci.h	Mon Sep 23 15:16:08 2002
@@ -21,6 +21,23 @@
 
 /* definitions used for the EHCI driver */
 
+/* statistics can be kept for for tuning/monitoring */
+struct ehci_stats {
+	/* irq usage */
+	unsigned long		normal;
+	unsigned long		error;
+	unsigned long		reclaim;
+
+	/* termination of urbs from core */
+	unsigned long		complete;
+	unsigned long		unlink;
+
+	/* qhs patched to recover from td queueing race
+	 * (can avoid by using 'dummy td', allowing fewer irqs)
+	 */
+	unsigned long		qpatch;
+};
+
 /* ehci_hcd->lock guards shared data against other CPUs:
  *   ehci_hcd:	async, reclaim, periodic (and shadow), ...
  *   hcd_dev:	ep[]
@@ -72,6 +89,13 @@
 	struct pci_pool		*sitd_pool;	/* sitd per split iso urb */
 
 	struct timer_list	watchdog;
+
+#ifdef EHCI_STATS
+	struct ehci_stats	stats;
+#	define COUNT(x) do { (x)++; } while (0)
+#else
+#	define COUNT(x) do {} while (0)
+#endif
 };
 
 /* unwrap an HCD pointer to get an EHCI_HCD pointer */ 
@@ -400,10 +424,22 @@
 #define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb)
 #define STUB_DEBUG_FILES
 
+static inline int hcd_register_root (struct usb_hcd *hcd)
+{
+	return usb_new_device (hcd_to_bus (hcd)->root_hub);
+}
+
 #else	/* LINUX_VERSION_CODE */
 
+// hcd_to_bus() eventually moves to hcd.h on 2.5 too
 static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
 	{ return &hcd->self; }
+// ... as does hcd_register_root()
+static inline int hcd_register_root (struct usb_hcd *hcd)
+{
+	return usb_register_root_hub (
+		hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev);
+}
 
 #define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)