# 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.600.1.7 -> 1.600.1.8
#	drivers/usb/host/ohci-hcd.c	1.23    -> 1.24   
#	drivers/usb/host/ohci-mem.c	1.7     -> 1.8    
#	drivers/usb/host/ohci-dbg.c	1.9     -> 1.10   
#	drivers/usb/host/ohci-q.c	1.20    -> 1.21   
#	drivers/usb/host/ohci.h	1.11    -> 1.12   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/09/04	david-b@pacbell.net	1.600.1.8
# [PATCH] ohci-hcd endpoint scheduling, driverfs
# 
# This patch cleans up some messy parts of this driver, and
# was pleasantly painless.
# 
#       - gets rid of ED dma hashtables
#          * less memory needed
#          * also less (+faster) code
#          * ... rewrites all ED scheduling ops, they now use
#            cpu addresses, like EHCI and UHCI do already
# 
#       - simplifies ED scheduling (no dma hashtables)
#          * control and bulk lists are now doubly linked
#          * periodic tree still singly linked; driver uses a
#            new CPU view "shadow" of the hardware framelist
#          * previous periodic code was cryptic, almost read-only
#          * simpler tree code for EDs with {branch,period}
# 
#       - bugfixes periodic scheduling
#          * when CONFIG_USB_BANDWIDTH, checks per-frame load
#            against the limit; no more dodgey accounting
#          * handles iso period != 1; interrupt and iso schedule
#            EDs with the same routine (HW sees special TDs)
#          * credit usbfs with bandwidth for endpoints, not URBs
# 
#       - adds driverfs output (when CONFIG_USB_DEBUG)
#          * resembles EHCI:  'async' (control+bulk) and
#            'periodic' (interrupt+iso) files show schedules
#          * shows only queue heads (EDs) just now (*)
# 
#       - has minor text and code cleanups, etc
# 
# Now that this logic has morphed into more comprehensible
# form, I know what to borrow into the EHCI code!
# 
# 
#      (*) It shows TDs on the td_list, but this patch won't
#          put them there.  A queue fault handling update will.
# --------------------------------------------
#
diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
--- a/drivers/usb/host/ohci-dbg.c	Thu Sep  5 08:51:20 2002
+++ b/drivers/usb/host/ohci-dbg.c	Thu Sep  5 08:51:20 2002
@@ -72,37 +72,6 @@
 #endif
 }
 
-static inline struct ed *
-dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma);
-
-/* print non-empty branches of the periodic ed tree */
-static void __attribute__ ((unused))
-ohci_dump_periodic (struct ohci_hcd *ohci, char *label)
-{
-	int i, j;
-	u32 *ed_p;
-	int printed = 0;
-
-	for (i= 0; i < 32; i++) {
-		j = 5;
-		ed_p = &(ohci->hcca->int_table [i]);
-		if (*ed_p == 0)
-			continue;
-		printed = 1;
-		printk (KERN_DEBUG "%s, ohci %s frame %2d:",
-				label, ohci->hcd.self.bus_name, i);
-		while (*ed_p != 0 && j--) {
-			struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
-			printk (" %p/%08x;", ed, ed->hwINFO);
-			ed_p = &ed->hwNextED;
-		}
-		printk ("\n");
-	}
-	if (!printed)
-		printk (KERN_DEBUG "%s, ohci %s, empty periodic schedule\n",
-				label, ohci->hcd.self.bus_name);
-}
-
 static void ohci_dump_intr_mask (char *label, __u32 mask)
 {
 	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
@@ -316,8 +285,9 @@
 	case ED_IN: type = "-IN"; break;
 	/* else from TDs ... control */
 	}
-	dbg ("  info %08x MAX=%d%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
-		0x0fff & (le32_to_cpu (tmp) >> 16),
+	dbg ("  info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
+		0x03ff & (le32_to_cpu (tmp) >> 16),
+		(tmp & ED_DEQUEUE) ? " DQ" : "",
 		(tmp & ED_ISO) ? " ISO" : "",
 		(tmp & ED_SKIP) ? " SKIP" : "",
 		(tmp & ED_LOWSPEED) ? " LOW" : "",
@@ -344,5 +314,222 @@
 	}
 }
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,32)
+#	define DRIVERFS_DEBUG_FILES
 #endif
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DRIVERFS_DEBUG_FILES
+
+static ssize_t
+show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
+{
+	unsigned		temp, size = count;
+
+	if (!ed)
+		return 0;
+
+	/* print first --> last */
+	while (ed->ed_prev)
+		ed = ed->ed_prev;
+
+	/* dump a snapshot of the bulk or control schedule */
+	while (ed) {
+		u32			info = ed->hwINFO;
+		u32			scratch = cpu_to_le32p (&ed->hwINFO);
+		struct list_head	*entry;
+		struct td		*td;
+
+		temp = snprintf (buf, size,
+			"ed/%p %cs dev%d ep%d-%s max %d %08x%s%s %s",
+			ed,
+			(info & ED_LOWSPEED) ? 'l' : 'f',
+			scratch & 0x7f,
+			(scratch >> 7) & 0xf,
+			(info & ED_IN) ? "in" : "out",
+			0x03ff & (scratch >> 16),
+			scratch,
+			(info & ED_SKIP) ? " s" : "",
+			(ed->hwHeadP & ED_H) ? " H" : "",
+			(ed->hwHeadP & ED_C) ? data1 : data0);
+		size -= temp;
+		buf += temp;
+
+		list_for_each (entry, &ed->td_list) {
+			u32		cbp, be;
+
+			td = list_entry (entry, struct td, td_list);
+			scratch = cpu_to_le32p (&td->hwINFO);
+			cbp = le32_to_cpup (&td->hwCBP);
+			be = le32_to_cpup (&td->hwBE);
+			temp = snprintf (buf, size,
+					"\n\ttd %p %s %d cc=%x urb %p (%08x)",
+					td,
+					({ char *pid;
+					switch (scratch & TD_DP) {
+					case TD_DP_SETUP: pid = "setup"; break;
+					case TD_DP_IN: pid = "in"; break;
+					case TD_DP_OUT: pid = "out"; break;
+					default: pid = "(?)"; break;
+					 } pid;}),
+					cbp ? (be + 1 - cbp) : 0,
+					TD_CC_GET (scratch), td->urb, scratch);
+			size -= temp;
+			buf += temp;
+		}
+
+		temp = snprintf (buf, size, "\n");
+		size -= temp;
+		buf += temp;
+
+		ed = ed->ed_next;
+	}
+	return count - size;
+}
+
+static ssize_t
+show_async (struct device *dev, char *buf, size_t count, loff_t off)
+{
+	struct pci_dev		*pdev;
+	struct ohci_hcd		*ohci;
+	size_t			temp;
+	unsigned long		flags;
+
+	if (off != 0)
+		return 0;
+	pdev = container_of (dev, struct pci_dev, dev);
+	ohci = container_of (pci_get_drvdata (pdev), struct ohci_hcd, hcd);
+
+	/* display control and bulk lists together, for simplicity */
+	spin_lock_irqsave (&ohci->lock, flags);
+	temp = show_list (ohci, buf, count, ohci->ed_controltail);
+	count = show_list (ohci, buf + temp, count - temp, ohci->ed_bulktail);
+	spin_unlock_irqrestore (&ohci->lock, flags);
+
+	return temp + count;
+}
+static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
+
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t
+show_periodic (struct device *dev, char *buf, size_t count, loff_t off)
+{
+	struct pci_dev		*pdev;
+	struct ohci_hcd		*ohci;
+	struct ed		**seen, *ed;
+	unsigned long		flags;
+	unsigned		temp, size, seen_count;
+	char			*next;
+	unsigned		i;
+
+	if (off != 0)
+		return 0;
+	if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
+		return 0;
+	seen_count = 0;
+
+	pdev = container_of (dev, struct pci_dev, dev);
+	ohci = container_of (pci_get_drvdata (pdev), struct ohci_hcd, hcd);
+	next = buf;
+	size = count;
+
+	temp = snprintf (next, size, "size = %d\n", NUM_INTS);
+	size -= temp;
+	next += temp;
+
+	/* dump a snapshot of the periodic schedule (and load) */
+	spin_lock_irqsave (&ohci->lock, flags);
+	for (i = 0; i < NUM_INTS; i++) {
+		if (!(ed = ohci->periodic [i]))
+			continue;
+
+		temp = snprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
+		size -= temp;
+		next += temp;
+
+		do {
+			temp = snprintf (next, size, " ed%d/%p",
+				ed->interval, ed);
+			size -= temp;
+			next += temp;
+			for (temp = 0; temp < seen_count; temp++) {
+				if (seen [temp] == ed)
+					break;
+			}
+
+			/* show more info the first time around */
+			if (temp == seen_count) {
+				u32	info = ed->hwINFO;
+				u32	scratch = cpu_to_le32p (&ed->hwINFO);
+
+				temp = snprintf (next, size,
+					" (%cs dev%d%s ep%d-%s"
+					" max %d %08x%s%s)",
+					(info & ED_LOWSPEED) ? 'l' : 'f',
+					scratch & 0x7f,
+					(info & ED_ISO) ? " iso" : "",
+					(scratch >> 7) & 0xf,
+					(info & ED_IN) ? "in" : "out",
+					0x03ff & (scratch >> 16),
+					scratch,
+					(info & ED_SKIP) ? " s" : "",
+					(ed->hwHeadP & ED_H) ? " H" : "");
+				size -= temp;
+				next += temp;
+
+				// FIXME some TD info too
+
+				if (seen_count < DBG_SCHED_LIMIT)
+					seen [seen_count++] = ed;
+
+				ed = ed->ed_next;
+
+			} else {
+				/* we've seen it and what's after */
+				temp = 0;
+				ed = 0;
+			}
+
+		} while (ed);
+
+		temp = snprintf (next, size, "\n");
+		size -= temp;
+		next += temp;
+	}
+	spin_unlock_irqrestore (&ohci->lock, flags);
+	kfree (seen);
+
+	return count - size;
+}
+static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
+
+#undef DBG_SCHED_LIMIT
+
+static inline void create_debug_files (struct ohci_hcd *bus)
+{
+	device_create_file (&bus->hcd.pdev->dev, &dev_attr_async);
+	device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
+	// registers
+	dbg ("%s: created debug files", bus->hcd.self.bus_name);
+}
+
+static inline void remove_debug_files (struct ohci_hcd *bus)
+{
+	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async);
+	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
+}
+
+#else /* empty stubs for creating those files */
+
+static inline void create_debug_files (struct ohci_hcd *bus) { }
+static inline void remove_debug_files (struct ohci_hcd *bus) { }
+
+#endif /* DRIVERFS_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
 
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c	Thu Sep  5 08:51:20 2002
+++ b/drivers/usb/host/ohci-hcd.c	Thu Sep  5 08:51:20 2002
@@ -17,6 +17,8 @@
  *
  * History:
  * 
+ * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
+ * 	bandwidth accounting; if debugging, show schedules in driverfs
  * 2002/07/19 fixes to management of ED and schedule state.
  * 2002/06/09 SA-1111 support (Christopher Hoover)
  * 2002/06/01 remember frame when HC won't see EDs any more; use that info
@@ -66,7 +68,6 @@
  * v1.0 1999/04/27 initial release
  *
  * This file is licenced under the GPL.
- * $Id: ohci-hcd.c,v 1.9 2002/03/27 20:41:57 dbrownell Exp $
  */
  
 #include <linux/config.h>
@@ -107,8 +108,8 @@
  *	- lots more testing!!
  */
 
-#define DRIVER_VERSION "2002-Jul-19"
-#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
+#define DRIVER_VERSION "2002-Sep-03"
+#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
 /*-------------------------------------------------------------------------*/
@@ -152,7 +153,6 @@
 	unsigned int	pipe = urb->pipe;
 	int		i, size = 0;
 	unsigned long	flags;
-	int		bustime = 0;
 	int		retval = 0;
 	
 #ifdef OHCI_VERBOSE_DEBUG
@@ -230,43 +230,32 @@
 		}
 	}	
 
-// FIXME:  much of this switch should be generic, move to hcd code ...
-// ... and what's not generic can't really be handled this way.
-// need to consider periodicity for both types!
-
-	/* allocate and claim bandwidth if needed; ISO
-	 * needs start frame index if it was't provided.
-	 */
-	switch (usb_pipetype (pipe)) {
-		case PIPE_ISOCHRONOUS:
-			if (urb->transfer_flags & USB_ISO_ASAP) { 
-				urb->start_frame = ((ed->state != ED_IDLE)
-					? (ed->intriso.last_iso + 1)
-					: (le16_to_cpu (ohci->hcca->frame_no)
-						+ 10)) & 0xffff;
-			}
-			/* FALLTHROUGH */
-		case PIPE_INTERRUPT:
-			if (urb->bandwidth == 0) {
-				bustime = usb_check_bandwidth (urb->dev, urb);
-			}
-			if (bustime < 0) {
-				retval = bustime;
-				goto fail;
-			}
-			usb_claim_bandwidth (urb->dev, urb,
-				bustime, usb_pipeisoc (urb->pipe));
-	}
+	/* schedule the ed if needed */
+	if (ed->state == ED_IDLE) {
+		retval = ed_schedule (ohci, ed);
+		if (retval < 0)
+			goto fail;
+		if (ed->type == PIPE_ISOCHRONOUS) {
+			u16	frame = le16_to_cpu (ohci->hcca->frame_no);
 
-	urb->hcpriv = urb_priv;
+			/* delay a few frames before the first TD */
+			frame += max_t (u16, 8, ed->interval);
+			frame &= ~(ed->interval - 1);
+			frame |= ed->branch;
+			urb->start_frame = frame;
 
-	/* schedule the ed if needed */
-	if (ed->state == ED_IDLE)
-		ed_schedule (ohci, ed);
+			/* yes, only USB_ISO_ASAP is supported, and
+			 * urb->start_frame is never used as input.
+			 */
+		}
+	} else if (ed->type == PIPE_ISOCHRONOUS)
+		urb->start_frame = ed->last_iso + ed->interval;
 
 	/* fill the TDs and link them to the ed; and
 	 * enable that part of the schedule, if needed
+	 * and update count of queued periodic urbs
 	 */
+	urb->hcpriv = urb_priv;
 	td_submit_urb (ohci, urb);
 
 fail:
@@ -537,6 +526,7 @@
 		return -ENODEV;
 	}
 
+	create_debug_files (ohci);
 	return 0;
 }
 
@@ -571,7 +561,8 @@
 
 	if (ints & OHCI_INTR_UE) {
 		disable (ohci);
-		err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name);
+		err ("OHCI Unrecoverable Error, %s disabled",
+				hcd->self.bus_name);
 		// e.g. due to PCI Master/Target Abort
 
 #ifdef	DEBUG
@@ -620,6 +611,7 @@
 	if (!ohci->disabled)
 		hc_reset (ohci);
 	
+	remove_debug_files (ohci);
 	ohci_mem_cleanup (ohci);
 	if (ohci->hcca) {
 		pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
@@ -649,14 +641,13 @@
 		usb_disconnect (&ohci->hcd.self.root_hub);
 	
 	/* empty the interrupt branches */
-	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0;
+	for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
 	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
 	
 	/* no EDs to remove */
 	ohci->ed_rm_list = NULL;
 
 	/* empty control and bulk lists */	 
-	ohci->ed_isotail     = NULL;
 	ohci->ed_controltail = NULL;
 	ohci->ed_bulktail    = NULL;
 
diff -Nru a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
--- a/drivers/usb/host/ohci-mem.c	Thu Sep  5 08:51:20 2002
+++ b/drivers/usb/host/ohci-mem.c	Thu Sep  5 08:51:20 2002
@@ -5,7 +5,6 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci-mem.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $
  */
 
 /*-------------------------------------------------------------------------*/
@@ -52,13 +51,6 @@
 	return scan->virt;
 }
 
-static struct ed *
-dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma)
-{
-	return (struct ed *) dma_to_ed_td(&(hc->ed_hash [ED_HASH_FUNC(ed_dma)]),
-				      ed_dma);
-}
-
 static struct td *
 dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
 {
@@ -98,13 +90,6 @@
 }
 
 static inline int
-hash_add_ed (struct ohci_hcd *hc, struct ed *ed, int mem_flags)
-{
-	return hash_add_ed_td (&(hc->ed_hash [ED_HASH_FUNC (ed->dma)]),
-			ed, ed->dma, mem_flags);
-}
-
-static inline int
 hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags)
 {
 	return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]),
@@ -139,12 +124,6 @@
 }
 
 static inline void
-hash_free_ed (struct ohci_hcd *hc, struct ed * ed)
-{
-	hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
-}
-
-static inline void
 hash_free_td (struct ohci_hcd *hc, struct td * td)
 {
 	hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
@@ -223,11 +202,6 @@
 		memset (ed, 0, sizeof (*ed));
 		INIT_LIST_HEAD (&ed->td_list);
 		ed->dma = dma;
-		/* hash it for later reverse mapping */
-		if (!hash_add_ed (hc, ed, mem_flags)) {
-			pci_pool_free (hc->ed_cache, ed, dma);
-			return NULL;
-		}
 	}
 	return ed;
 }
@@ -235,7 +209,6 @@
 static void
 ed_free (struct ohci_hcd *hc, struct ed *ed)
 {
-	hash_free_ed (hc, ed);
 	pci_pool_free (hc->ed_cache, ed, ed->dma);
 }
 
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c	Thu Sep  5 08:51:20 2002
+++ b/drivers/usb/host/ohci-q.c	Thu Sep  5 08:51:20 2002
@@ -5,7 +5,6 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $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)
@@ -52,6 +51,15 @@
 		urb->status = 0;
 	spin_unlock_irqrestore (&urb->lock, flags);
 
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		ohci->hcd.self.bandwidth_isoc_reqs--;
+		break;
+	case PIPE_INTERRUPT:
+		ohci->hcd.self.bandwidth_int_reqs--;
+		break;
+	}
+
 #ifdef OHCI_VERBOSE_DEBUG
 	urb_print (urb, "RET", usb_pipeout (urb->pipe));
 #endif
@@ -111,67 +119,111 @@
  * ED handling functions
  *-------------------------------------------------------------------------*/  
 
-/* search for the right branch to insert an interrupt ed into the int tree 
- * do some load balancing;
- * returns the branch
- * FIXME allow for failure, when there's no bandwidth left;
- * and consider iso loads too
+/* search for the right schedule branch to use for a periodic ed.
+ * does some load balancing; returns the branch, or negative errno.
  */
-static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load)
+static int balance (struct ohci_hcd *ohci, int interval, int load)
 {
-	int	i, branch = 0;
+	int	i, branch = -ENOSPC;
 
-	/* search for the least loaded interrupt endpoint branch */
-	for (i = 0; i < NUM_INTS ; i++) 
-		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
+	/* iso periods can be huge; iso tds specify frame numbers */
+	if (interval > NUM_INTS)
+		interval = NUM_INTS;
+
+	/* search for the least loaded schedule branch of that period
+	 * that has enough bandwidth left unreserved.
+	 */
+	for (i = 0; i < interval ; i++) {
+		if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
+#ifdef CONFIG_USB_BANDWIDTH
+			int	j;
+
+			/* usb 1.1 says 90% of one frame */
+			for (j = i; j < NUM_INTS; j += interval) {
+				if ((ohci->load [j] + load) > 900)
+					break;
+			}
+			if (j < NUM_INTS)
+				continue;
+#endif
 			branch = i; 
-
-	branch = branch % interval;
-	for (i = branch; i < NUM_INTS; i += interval)
-		ohci->ohci_int_load [i] += load;
-
+		}
+	}
 	return branch;
 }
 
 /*-------------------------------------------------------------------------*/
 
-/* the int tree is a binary tree 
- * in order to process it sequentially the indexes of the branches have
- * to be mapped the mapping reverses the bits of a word of num_bits length
+/* both iso and interrupt requests have periods; this routine puts them
+ * into the schedule tree in the apppropriate place.  most iso devices use
+ * 1msec periods, but that's not required.
  */
-static int ep_rev (int num_bits, int word)
+static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)
 {
-	int			i, wout = 0;
+	unsigned	i;
 
-	for (i = 0; i < num_bits; i++)
-		wout |= (( (word >> i) & 1) << (num_bits - i - 1));
-	return wout;
+	dbg ("%s: link %sed %p branch %d [%dus.], interval %d",
+		ohci->hcd.self.bus_name,
+		(ed->hwINFO & ED_ISO) ? "iso " : "",
+		ed, ed->branch, ed->load, ed->interval);
+
+	for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+		struct ed	**prev = &ohci->periodic [i];
+		u32		*prev_p = &ohci->hcca->int_table [i];
+		struct ed	*here = *prev;
+
+		/* sorting each branch by period (slow before fast)
+		 * lets us share the faster parts of the tree.
+		 * (plus maybe: put interrupt eds before iso)
+		 */
+		while (here && ed != here) {
+			if (ed->interval > here->interval)
+				break;
+			prev = &here->ed_next;
+			prev_p = &here->hwNextED;
+			here = *prev;
+		}
+		if (ed != here) {
+			ed->ed_next = here;
+			if (here)
+				ed->hwNextED = *prev_p;
+			wmb ();
+			*prev = ed;
+			*prev_p = cpu_to_le32p (&ed->dma);
+		}
+		ohci->load [i] += ed->load;
+	}
+	ohci->hcd.self.bandwidth_allocated += ed->load / ed->interval;
 }
 
-/*-------------------------------------------------------------------------*/
-
 /* link an ed into one of the HC chains */
 
-static void ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
+static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
 {	 
-	int			int_branch, i;
-	int			inter, interval, load;
-	__u32			*ed_p;
+	int	branch;
 
 	ed->state = ED_OPER;
+	ed->ed_prev = 0;
+	ed->ed_next = 0;
 	ed->hwNextED = 0;
 	wmb ();
 
 	/* we care about rm_list when setting CLE/BLE in case the HC was at
 	 * work on some TD when CLE/BLE was turned off, and isn't quiesced
 	 * yet.  finish_unlinks() restarts as needed, some upcoming INTR_SF.
+	 *
+	 * control and bulk EDs are doubly linked (ed_next, ed_prev), but
+	 * periodic ones are singly linked (ed_next). that's because the
+	 * periodic schedule encodes a tree like figure 3-5 in the ohci
+	 * spec:  each qh can have several "previous" nodes, and the tree
+	 * doesn't have unused/idle descriptors.
 	 */
-
 	switch (ed->type) {
 	case PIPE_CONTROL:
 		if (ohci->ed_controltail == NULL) {
 			writel (ed->dma, &ohci->regs->ed_controlhead);
 		} else {
+			ohci->ed_controltail->ed_next = ed;
 			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_controltail;
@@ -187,6 +239,7 @@
 		if (ohci->ed_bulktail == NULL) {
 			writel (ed->dma, &ohci->regs->ed_bulkhead);
 		} else {
+			ohci->ed_bulktail->ed_next = ed;
 			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_bulktail;
@@ -198,74 +251,55 @@
 		ohci->ed_bulktail = ed;
 		break;
 
-	case PIPE_INTERRUPT:
-		load = ed->intriso.intr_info.int_load;
-		interval = ed->interval;
-		int_branch = ep_int_balance (ohci, interval, load);
-		ed->intriso.intr_info.int_branch = int_branch;
-
-		for (i = 0; i < ep_rev (6, interval); i += inter) {
-			inter = 1;
-			for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]); 
-				 (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->interval >= interval); 
-				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)))->interval);
-			ed->hwNextED = *ed_p; 
-			*ed_p = cpu_to_le32 (ed->dma);
+	// case PIPE_INTERRUPT:
+	// case PIPE_ISOCHRONOUS:
+	default:
+		branch = balance (ohci, ed->interval, ed->load);
+		if (branch < 0) {
+			dbg ("%s: ERR %d, interval %d msecs, load %d",
+				ohci->hcd.self.bus_name,
+				branch, ed->interval, ed->load);
+			// FIXME if there are TDs queued, fail them!
+			return branch;
 		}
-		wmb ();
-#ifdef OHCI_VERBOSE_DEBUG
-		ohci_dump_periodic (ohci, "LINK_INT");
-#endif
-		break;
-
-	case PIPE_ISOCHRONOUS:
-		ed->ed_prev = ohci->ed_isotail;
-		if (ohci->ed_isotail != NULL) {
-			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
-		} else {
-			for ( i = 0; i < NUM_INTS; i += inter) {
-				inter = 1;
-				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)))->interval);
-				*ed_p = cpu_to_le32 (ed->dma);	
-			}	
-		}	
-		wmb ();
-		ohci->ed_isotail = ed;
-#ifdef OHCI_VERBOSE_DEBUG
-		ohci_dump_periodic (ohci, "LINK_ISO");
-#endif
-		break;
+		ed->branch = branch;
+		periodic_link (ohci, ed);
 	}	 	
 
 	/* the HC may not see the schedule updates yet, but if it does
 	 * then they'll be properly ordered.
 	 */
+	return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
 /* 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];
+static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+	int	i;
 
-		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);
+	for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+		struct ed	*temp;
+		struct ed	**prev = &ohci->periodic [i];
+		u32		*prev_p = &ohci->hcca->int_table [i];
+
+		while (*prev && (temp = *prev) != ed) {
+			prev_p = &temp->hwNextED;
+			prev = &temp->ed_next;
+		}
+		if (*prev) {
+			*prev_p = ed->hwNextED;
+			*prev = ed->ed_next;
 		}
+		ohci->load [i] -= ed->load;
 	}	
+	ohci->hcd.self.bandwidth_allocated -= ed->load / ed->interval;
+
+	dbg ("%s: unlink %sed %p branch %d [%dus.], interval %d",
+		ohci->hcd.self.bus_name,
+		(ed->hwINFO & ED_ISO) ? "iso " : "",
+		ed, ed->branch, ed->load, ed->interval);
 }
 
 /* unlink an ed from one of the HC chains. 
@@ -275,8 +309,6 @@
  */
 static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed) 
 {
-	int	i;
-
 	ed->hwINFO |= ED_SKIP;
 
 	switch (ed->type) {
@@ -289,13 +321,15 @@
 			writel (le32_to_cpup (&ed->hwNextED),
 				&ohci->regs->ed_controlhead);
 		} else {
+			ed->ed_prev->ed_next = ed->ed_next;
 			ed->ed_prev->hwNextED = ed->hwNextED;
 		}
 		if (ohci->ed_controltail == ed) {
 			ohci->ed_controltail = ed->ed_prev;
+			if (ohci->ed_controltail)
+				ohci->ed_controltail->ed_next = 0;
 		} else {
-			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
-			 	->ed_prev = ed->ed_prev;
+			ed->ed_next->ed_prev = ed->ed_prev;
 		}
 		break;
 
@@ -308,50 +342,33 @@
 			writel (le32_to_cpup (&ed->hwNextED),
 				&ohci->regs->ed_bulkhead);
 		} else {
+			ed->ed_prev->ed_next = ed->ed_next;
 			ed->ed_prev->hwNextED = ed->hwNextED;
 		}
 		if (ohci->ed_bulktail == ed) {
 			ohci->ed_bulktail = ed->ed_prev;
+			if (ohci->ed_bulktail)
+				ohci->ed_bulktail->ed_next = 0;
 		} else {
-			 (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
-			 	->ed_prev = ed->ed_prev;
+			ed->ed_next->ed_prev = ed->ed_prev;
 		}
 		break;
 
-	case PIPE_INTERRUPT:
-		periodic_unlink (ohci, ed, ed->intriso.intr_info.int_branch, ed->interval);
-		for (i = ed->intriso.intr_info.int_branch; i < NUM_INTS; i += ed->interval)
-			ohci->ohci_int_load [i] -= ed->intriso.intr_info.int_load;
-#ifdef OHCI_VERBOSE_DEBUG
-		ohci_dump_periodic (ohci, "UNLINK_INT");
-#endif
-		break;
-
-	case PIPE_ISOCHRONOUS:
-		if (ohci->ed_isotail == ed)
-			ohci->ed_isotail = ed->ed_prev;
-		if (ed->hwNextED != 0) 
-			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
-		    		->ed_prev = ed->ed_prev;
-
-		if (ed->ed_prev != NULL)
-			ed->ed_prev->hwNextED = ed->hwNextED;
-		else
-			periodic_unlink (ohci, ed, 0, 1);
-#ifdef OHCI_VERBOSE_DEBUG
-		ohci_dump_periodic (ohci, "UNLINK_ISO");
-#endif
+	// case PIPE_INTERRUPT:
+	// case PIPE_ISOCHRONOUS:
+	default:
+		periodic_unlink (ohci, ed);
 		break;
 	}
 
-	/* FIXME Except for a couple of exceptionally clean unlink cases
+	/* NOTE: Except for a couple of exceptionally clean unlink cases
 	 * (like unlinking the only c/b ED, with no TDs) HCs may still be
-	 * caching this (till SOF).
-	 *
-	 * To avoid racing with the hardware, this needs to use ED_UNLINK
-	 * and delay til next INTR_SF.  Merge with start_urb_unlink().
+	 * caching this operational ED (or its address).  Safe unlinking
+	 * involves not marking it ED_IDLE till INTR_SF; we always do that
+	 * if td_list isn't empty.  Otherwise the race is small; but ...
 	 */
-	ed->state = ED_IDLE;
+	if (ed->state == ED_OPER)
+		ed->state = ED_IDLE;
 }
 
 
@@ -369,7 +386,6 @@
 ) {
 	int			is_out = !usb_pipein (pipe);
 	int			type = usb_pipetype (pipe);
-	int			bus_msecs = 0;
 	struct hcd_dev		*dev = (struct hcd_dev *) udev->hcpriv;
 	struct ed		*ed; 
 	unsigned		ep;
@@ -378,9 +394,6 @@
 	ep = usb_pipeendpoint (pipe) << 1;
 	if (type != PIPE_CONTROL && is_out)
 		ep |= 1;
-	if (type == PIPE_INTERRUPT)
-		bus_msecs = usb_calc_bus_time (udev->speed, !is_out, 0,
-			usb_maxpacket (udev, pipe, is_out)) / 1000;
 
 	spin_lock_irqsave (&ohci->lock, flags);
 
@@ -422,23 +435,25 @@
 		info = cpu_to_le32 (info);
 		if (udev->speed == USB_SPEED_LOW)
 			info |= ED_LOWSPEED;
-		/* control transfers store pids in tds */
+		/* only control transfers store pids in tds */
 		if (type != PIPE_CONTROL) {
 			info |= is_out ? ED_OUT : ED_IN;
-			if (type == PIPE_ISOCHRONOUS)
-				info |= ED_ISO;
-			if (type == PIPE_INTERRUPT) {
-				ed->intriso.intr_info.int_load = bus_msecs;
-				if (interval > 32)
+			if (type != PIPE_BULK) {
+				/* periodic transfers... */
+				if (type == PIPE_ISOCHRONOUS)
+					info |= ED_ISO;
+				else if (interval > 32)	/* iso can be bigger */
 					interval = 32;
+				ed->interval = interval;
+				ed->load = usb_calc_bus_time (
+					udev->speed, !is_out,
+					type == PIPE_ISOCHRONOUS,
+					usb_maxpacket (udev, pipe, is_out))
+						/ 1000;
 			}
 		}
 		ed->hwINFO = info;
 
-		/* value ignored except on periodic EDs, where
-		 * we know it's already a power of 2
-		 */
-		ed->interval = interval;
 #ifdef DEBUG
 	/*
 	 * There are two other cases we ought to change hwINFO, both during
@@ -473,8 +488,9 @@
  */
 static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
 {    
-	ed_deschedule (ohci, ed);
+	ed->hwINFO |= ED_DEQUEUE;
 	ed->state = ED_UNLINK;
+	ed_deschedule (ohci, ed);
 
 	/* SF interrupt might get delayed; record the frame counter value that
 	 * indicates when the HC isn't looking at it, so concurrent unlinks
@@ -483,7 +499,9 @@
 	 */
 	ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
 
+	/* rm_list is just singly linked, for simplicity */
 	ed->ed_next = ohci->ed_rm_list;
+	ed->ed_prev = 0;
 	ohci->ed_rm_list = ed;
 
 	/* enable SOF interrupt */
@@ -529,7 +547,6 @@
 
 	/* use this td as the next dummy */
 	td_pt = urb_priv->td [index];
-	td_pt->hwNextTD = 0;
 
 	/* fill the old dummy TD */
 	td = urb_priv->td [index] = urb_priv->ed->dummy;
@@ -547,7 +564,7 @@
 	if (is_iso) {
 		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
 		td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
-		td->ed->intriso.last_iso = info & 0xffff;
+		td->ed->last_iso = info & 0xffff;
 	} else {
 		td->hwCBP = cpu_to_le32 (data); 
 	}			
@@ -608,9 +625,11 @@
 	/* Bulk and interrupt are identical except for where in the schedule
 	 * their EDs live.
 	 */
-	// case PIPE_BULK:
-	// case PIPE_INTERRUPT:
-	default:
+	case PIPE_INTERRUPT:
+		/* ... and periodic urbs have extra accounting */
+		ohci->hcd.self.bandwidth_int_reqs++;
+		/* FALLTHROUGH */
+	case PIPE_BULK:
 		info = is_out
 			? TD_T_TOGGLE | TD_CC | TD_DP_OUT
 			: TD_T_TOGGLE | TD_CC | TD_DP_IN;
@@ -676,6 +695,7 @@
 				data + urb->iso_frame_desc [cnt].offset,
 				urb->iso_frame_desc [cnt].length, urb, cnt);
 		}
+		ohci->hcd.self.bandwidth_isoc_reqs++;
 		break;
 	}
 	if (urb_priv->length != cnt)
@@ -802,6 +822,7 @@
 			if (td_list->ed->hwHeadP & ED_H) {
 				if (urb_priv && ((td_list->index + 1)
 						< urb_priv->length)) {
+#ifdef DEBUG
 					struct urb *urb = td_list->urb;
 
 					/* help for troubleshooting: */
@@ -817,6 +838,7 @@
 						1 + td_list->index,
 						urb_priv->length,
 						cc, cc_to_error [cc]);
+#endif
 					td_list->ed->hwHeadP = 
 			    (urb_priv->td [urb_priv->length - 1]->hwNextTD
 				    & __constant_cpu_to_le32 (TD_MASK))
@@ -872,8 +894,7 @@
 		 * we call a completion since it might have unlinked
 		 * another (earlier) urb
 		 *
-		 * FIXME use td_list to scan, not ed hashtables.
-		 * completely abolish ed hashtables!
+		 * FIXME use td_list to scan, not td hashtables.
 		 */
 rescan_this:
 		completed = 0;
@@ -894,8 +915,7 @@
 				td_done (urb, td);
 				urb_priv->td_cnt++;
 
-				*td_p = td->hwNextTD | (*td_p
-					& __constant_cpu_to_le32 (0x3));
+				*td_p = td->hwNextTD | (*td_p & ~TD_MASK);
 
 				/* URB is done; clean up */
 				if (urb_priv->td_cnt == urb_priv->length) {
diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
--- a/drivers/usb/host/ohci.h	Thu Sep  5 08:51:20 2002
+++ b/drivers/usb/host/ohci.h	Thu Sep  5 08:51:20 2002
@@ -5,7 +5,6 @@
  * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
  * 
  * This file is licenced under the GPL.
- * $Id: ohci.h,v 1.6 2002/03/22 16:04:54 dbrownell Exp $
  */
  
 /*
@@ -18,13 +17,16 @@
 struct ed {
 	/* first fields are hardware-specified, le32 */
 	__u32			hwINFO;       	/* endpoint config bitmap */
+	/* info bits defined by hcd */
+#define ED_DEQUEUE	__constant_cpu_to_le32(1 << 27)
+	/* info bits defined by the hardware */
 #define ED_ISO		__constant_cpu_to_le32(1 << 15)
 #define ED_SKIP		__constant_cpu_to_le32(1 << 14)
 #define ED_LOWSPEED	__constant_cpu_to_le32(1 << 13)
 #define ED_OUT		__constant_cpu_to_le32(0x01 << 11)
 #define ED_IN		__constant_cpu_to_le32(0x02 << 11)
 	__u32			hwTailP;	/* tail of TD list */
-	__u32			hwHeadP;	/* head of TD list */
+	__u32			hwHeadP;	/* head of TD list (hc r/w) */
 #define ED_C		__constant_cpu_to_le32(0x02)	/* toggle carry */
 #define ED_H		__constant_cpu_to_le32(0x01)	/* halted */
 	__u32			hwNextED;	/* next ED in list */
@@ -48,14 +50,12 @@
 #define ED_OPER		0x02		/* IS linked to hc */
 
 	u8			type; 		/* PIPE_{BULK,...} */
-	u16			interval;	/* interrupt, isochronous */
-	union {
-		struct intr_info {		/* interrupt */
-			u8	int_branch;
-			u8	int_load; 
-		} intr_info;
-		u16		last_iso;	/* isochronous */
-	} intriso;
+
+	/* periodic scheduling params (for intr and iso) */
+	u8			branch;
+	u16			interval;
+	u16			load;
+	u16			last_iso;	/* iso only */
 
 	/* HC may see EDs on rm_list until next frame (frame_no == tick) */
 	u16			tick;
@@ -335,10 +335,8 @@
 };
 
 #define TD_HASH_SIZE    64    /* power'o'two */
-#define ED_HASH_SIZE    64    /* power'o'two */
 
 #define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
-#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
 
 
 /*
@@ -373,7 +371,7 @@
 
 	struct ed		*ed_bulktail;		/* last in bulk list */
 	struct ed		*ed_controltail;	/* last in ctrl list */
- 	struct ed		*ed_isotail;		/* last in iso list */
+ 	struct ed		*periodic [NUM_INTS];	/* shadow int_table */
 
 	/*
 	 * memory management for queue data structures
@@ -381,14 +379,13 @@
 	struct pci_pool		*td_cache;
 	struct pci_pool		*ed_cache;
 	struct hash_list_t	td_hash [TD_HASH_SIZE];
-	struct hash_list_t	ed_hash [ED_HASH_SIZE];
 
 	/*
 	 * driver state
 	 */
 	int			disabled;	/* e.g. got a UE, we're hung */
 	int			sleeping;
-	int			ohci_int_load [NUM_INTS];
+	int			load [NUM_INTS];
 	u32 			hc_control;	/* copy of hc control reg */
 
 	unsigned long		flags;		/* for HC bugs */
