# 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.383.20.1 -> 1.383.20.2
#	drivers/usb/hcd/ehci-q.c	1.1     -> 1.2    
#	drivers/usb/hcd/ehci-hub.c	1.1     -> 1.2    
#	drivers/usb/hcd/ehci.h	1.1     -> 1.2    
#	drivers/usb/hcd/ehci-sched.c	1.1     -> 1.2    
#	drivers/usb/hcd/ehci-mem.c	1.2     -> 1.3    
#	drivers/usb/hcd/ehci-hcd.c	1.3     -> 1.4    
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/02	david-b@pacbell.net	1.383.20.2
# [PATCH] PATCH 2.4.19-pre7 sync ehci-hcd with 2.5
# 
# USB ehci update
# 
# Short version of the story:  syncs the driver with the 2.5 version.
# Includes preliminary high speed ISO support, fixes for interrupt
# transfer scheduling and some (rare) submission path errors.
# That's most of the patch, by volume; there are other updates.
# --------------------------------------------
#
diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c
--- a/drivers/usb/hcd/ehci-hcd.c	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-hcd.c	Fri May  3 14:51:44 2002
@@ -70,6 +70,11 @@
  *
  * HISTORY:
  *
+ * 2002-04-19	Control/bulk/interrupt submit no longer uses giveback() on
+ *	errors in submit path.  Bugfixes to interrupt scheduling/processing.
+ * 2002-03-05	Initial high-speed ISO support; reduce ITD memory; shift
+ *	more checking to generic hcd framework (db).  Make it work with
+ *	Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
  * 2002-01-14	Minor cleanup; version synch.
  * 2002-01-08	Fix roothub handoff of FS/LS to companion controllers.
  * 2002-01-04	Control/Bulk queuing behaves.
@@ -84,7 +89,7 @@
 
 
 // #define EHCI_VERBOSE_DEBUG
-// #define have_iso
+// #define have_split_iso
 
 /* magic numbers that can affect system performance */
 #define	EHCI_TUNE_CERR		3	/* 0-3 qtd retries; 0 == don't stop */
@@ -180,6 +185,9 @@
 	dbg_hcs_params (ehci, "ehci_start");
 	dbg_hcc_params (ehci, "ehci_start");
 
+	/* cache this readonly data; minimize PCI reads */
+	ehci->hcs_params = readl (&ehci->caps->hcs_params);
+
 	/*
 	 * hw default: 1K periodic list heads, one per frame.
 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -197,11 +205,12 @@
 
 	ehci->async = 0;
 	ehci->reclaim = 0;
-	ehci->next_frame = -1;
+	ehci->next_uframe = -1;
 
 	/* controller state:  unknown --> reset */
 
 	/* EHCI spec section 4.1 */
+	// FIXME require STS_HALT before reset...
 	ehci_reset (ehci);
 	writel (INTR_MASK, &ehci->regs->intr_enable);
 	writel (ehci->periodic_dma, &ehci->regs->frame_list);
@@ -307,7 +316,7 @@
 
 	// root hub is shut down separately (first, when possible)
 	scan_async (ehci);
-	if (ehci->next_frame != -1)
+	if (ehci->next_uframe != -1)
 		scan_periodic (ehci);
 	ehci_mem_cleanup (ehci);
 
@@ -329,14 +338,12 @@
 static int ehci_suspend (struct usb_hcd *hcd, u32 state)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	u32			params;
 	int			ports;
 	int			i;
 
 	dbg ("%s: suspend to %d", hcd->bus_name, state);
 
-	params = readl (&ehci->caps->hcs_params);
-	ports = HCS_N_PORTS (params);
+	ports = HCS_N_PORTS (ehci->hcs_params);
 
 	// FIXME:  This assumes what's probably a D3 level suspend...
 
@@ -372,14 +379,12 @@
 static int ehci_resume (struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	u32			params;
 	int			ports;
 	int			i;
 
 	dbg ("%s: resume", hcd->bus_name);
 
-	params = readl (&ehci->caps->hcs_params);
-	ports = HCS_N_PORTS (params);
+	ports = HCS_N_PORTS (ehci->hcs_params);
 
 	// FIXME:  if controller didn't retain state,
 	// return and let generic code clean it up
@@ -425,7 +430,7 @@
 	if (ehci->reclaim_ready)
 		end_unlink_async (ehci);
 	scan_async (ehci);
-	if (ehci->next_frame != -1)
+	if (ehci->next_uframe != -1)
 		scan_periodic (ehci);
 
 	// FIXME:  when nothing is connected to the root hub,
@@ -510,8 +515,7 @@
 	case PIPE_BULK:
 		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
 			return -ENOMEM;
-		submit_async (ehci, urb, &qtd_list, mem_flags);
-		break;
+		return submit_async (ehci, urb, &qtd_list, mem_flags);
 
 	case PIPE_INTERRUPT:
 		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
@@ -519,20 +523,19 @@
 		return intr_submit (ehci, urb, &qtd_list, mem_flags);
 
 	case PIPE_ISOCHRONOUS:
-#ifdef have_iso
 		if (urb->dev->speed == USB_SPEED_HIGH)
-			return itd_submit (ehci, urb);
+			return itd_submit (ehci, urb, mem_flags);
+#ifdef have_split_iso
 		else
-			return sitd_submit (ehci, urb);
+			return sitd_submit (ehci, urb, mem_flags);
 #else
-		// FIXME highspeed iso stuff is written but never run/tested.
-		// and the split iso support isn't even written yet.
-		dbg ("no iso support yet");
+		dbg ("no split iso support yet");
 		return -ENOSYS;
-#endif /* have_iso */
+#endif /* have_split_iso */
 
+	default:	/* can't happen */
+		return -ENOSYS;
 	}
-	return 0;
 }
 
 /* remove from hardware lists
@@ -587,7 +590,7 @@
 		// itd or sitd ...
 
 		// wait till next completion, do it then.
-		// completion irqs can wait up to 128 msec,
+		// completion irqs can wait up to 1024 msec,
 		urb->transfer_flags |= EHCI_STATE_UNLINK;
 		return 0;
 	}
@@ -614,10 +617,7 @@
 		if (dev->ep [i]) {
 			struct ehci_qh		*qh;
 
-			// FIXME:  this might be an itd/sitd too ...
-			// or an interrupt urb (not on async list)
-			// can use "union ehci_shadow"
-
+			/* dev->ep never has ITDs or SITDs */
 			qh = (struct ehci_qh *) dev->ep [i];
 			vdbg ("free_config, ep 0x%02x qh %p", i, qh);
 			if (!list_empty (&qh->qtd_list)) {
@@ -644,7 +644,7 @@
 					spin_lock_irqsave (&ehci->lock, flags);
 				}
 			}
-			qh_unput (ehci, qh);
+			qh_put (ehci, qh);
 		}
 	}
 
diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c
--- a/drivers/usb/hcd/ehci-hub.c	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-hub.c	Fri May  3 14:51:44 2002
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
  * 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -68,8 +68,7 @@
 
 	/* init status to no-changes */
 	buf [0] = 0;
-	temp = readl (&ehci->caps->hcs_params);
-	ports = HCS_N_PORTS (temp);
+	ports = HCS_N_PORTS (ehci->hcs_params);
 	if (ports > 7) {
 		buf [1] = 0;
 		retval++;
@@ -92,7 +91,10 @@
 		if (!(temp & PORT_CONNECT))
 			ehci->reset_done [i] = 0;
 		if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
-			set_bit (i, buf);
+			if (i < 7)
+			    buf [0] |= 1 << (i + 1);
+			else
+			    buf [1] |= 1 << (i - 7);
 			status = STS_PCD;
 		}
 	}
@@ -107,8 +109,7 @@
 	struct ehci_hcd			*ehci,
 	struct usb_hub_descriptor	*desc
 ) {
-	u32		params = readl (&ehci->caps->hcs_params);
-	int		ports = HCS_N_PORTS (params);
+	int		ports = HCS_N_PORTS (ehci->hcs_params);
 	u16		temp;
 
 	desc->bDescriptorType = 0x29;
@@ -124,10 +125,10 @@
 	memset (&desc->bitmap [temp], 0xff, temp);
 
 	temp = 0x0008;			/* per-port overcurrent reporting */
-	if (HCS_PPC (params))		/* per-port power control */
-	    temp |= 0x0001;
-	if (HCS_INDICATOR (params))	/* per-port indicators (LEDs) */
-	    temp |= 0x0080;
+	if (HCS_PPC (ehci->hcs_params))
+		temp |= 0x0001;		/* per-port power control */
+	if (HCS_INDICATOR (ehci->hcs_params))
+		temp |= 0x0080;		/* per-port indicators (LEDs) */
 	desc->wHubCharacteristics = cpu_to_le16 (temp);
 }
 
@@ -142,9 +143,8 @@
 	u16		wLength
 ) {
 	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
-	u32		params = readl (&ehci->caps->hcs_params);
-	int		ports = HCS_N_PORTS (params);
-	u32		temp;
+	int		ports = HCS_N_PORTS (ehci->hcs_params);
+	u32		temp, status;
 	unsigned long	flags;
 	int		retval = 0;
 
@@ -189,7 +189,7 @@
 			/* ? */
 			break;
 		case USB_PORT_FEAT_POWER:
-			if (HCS_PPC (params))
+			if (HCS_PPC (ehci->hcs_params))
 				writel (temp & ~PORT_POWER,
 					&ehci->regs->port_status [wIndex]);
 			break;
@@ -222,22 +222,22 @@
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
-		memset (buf, 0, 4);
+		status = 0;
 		temp = readl (&ehci->regs->port_status [wIndex]);
 
 		// wPortChange bits
 		if (temp & PORT_CSC)
-			set_bit (USB_PORT_FEAT_C_CONNECTION, buf);
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
 		if (temp & PORT_PEC)
-			set_bit (USB_PORT_FEAT_C_ENABLE, buf);
+			status |= 1 << USB_PORT_FEAT_C_ENABLE;
 		// USB_PORT_FEAT_C_SUSPEND
 		if (temp & PORT_OCC)
-			set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf);
+			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
 		/* whoever resets must GetPortStatus to complete it!! */
 		if ((temp & PORT_RESET)
 				&& jiffies > ehci->reset_done [wIndex]) {
-			set_bit (USB_PORT_FEAT_C_RESET, buf);
+			status |= 1 << USB_PORT_FEAT_C_RESET;
 
 			/* force reset to complete */
 			writel (temp & ~PORT_RESET,
@@ -255,26 +255,27 @@
 		// don't show wPortStatus if it's owned by a companion hc
 		if (!(temp & PORT_OWNER)) {
 			if (temp & PORT_CONNECT) {
-				set_bit (USB_PORT_FEAT_CONNECTION, buf);
-				set_bit (USB_PORT_FEAT_HIGHSPEED, buf);
+				status |= 1 << USB_PORT_FEAT_CONNECTION;
+				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
 			}
 			if (temp & PORT_PE)
-				set_bit (USB_PORT_FEAT_ENABLE, buf);
+				status |= 1 << USB_PORT_FEAT_ENABLE;
 			if (temp & PORT_SUSPEND)
-				set_bit (USB_PORT_FEAT_SUSPEND, buf);
+				status |= 1 << USB_PORT_FEAT_SUSPEND;
 			if (temp & PORT_OC)
-				set_bit (USB_PORT_FEAT_OVER_CURRENT, buf);
+				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
 			if (temp & PORT_RESET)
-				set_bit (USB_PORT_FEAT_RESET, buf);
+				status |= 1 << USB_PORT_FEAT_RESET;
 			if (temp & PORT_POWER)
-				set_bit (USB_PORT_FEAT_POWER, buf);
+				status |= 1 << USB_PORT_FEAT_POWER;
 		}
 
 #ifndef	EHCI_VERBOSE_DEBUG
-	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */
+	if (status & ~0xffff)	/* only if wPortChange is interesting */
 #endif
 		dbg_port (hcd, "GetStatus", wIndex + 1, temp);
-		cpu_to_le32s ((u32 *) buf);
+		// we "know" this alignment is good, caller used kmalloc()...
+		*((u32 *) buf) = cpu_to_le32 (status);
 		break;
 	case SetHubFeature:
 		switch (wValue) {
@@ -300,7 +301,7 @@
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_POWER:
-			if (HCS_PPC (params))
+			if (HCS_PPC (ehci->hcs_params))
 				writel (temp | PORT_POWER,
 					&ehci->regs->port_status [wIndex]);
 			break;
diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c
--- a/drivers/usb/hcd/ehci-mem.c	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-mem.c	Fri May  3 14:51:44 2002
@@ -98,16 +98,16 @@
 }
 
 /* to share a qh (cpu threads, or hc) */
-static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh)
+static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
 {
-	// dbg ("put %p (%d++)", qh, qh->refcount.counter);
+	// dbg ("get %p (%d++)", qh, qh->refcount.counter);
 	atomic_inc (&qh->refcount);
 	return qh;
 }
 
-static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	// dbg ("unput %p (--%d)", qh, qh->refcount.counter);
+	// dbg ("put %p (--%d)", qh, qh->refcount.counter);
 	if (!atomic_dec_and_test (&qh->refcount))
 		return;
 	/* clean qtds first, and know this is not linked */
diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c
--- a/drivers/usb/hcd/ehci-q.c	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-q.c	Fri May  3 14:51:44 2002
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
  * 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -194,7 +194,7 @@
 			    ? PCI_DMA_FROMDEVICE
 			    : PCI_DMA_TODEVICE);
 	if (likely (urb->hcpriv != 0)) {
-		qh_unput (ehci, (struct ehci_qh *) urb->hcpriv);
+		qh_put (ehci, (struct ehci_qh *) urb->hcpriv);
 		urb->hcpriv = 0;
 	}
 
@@ -280,6 +280,8 @@
 
 		/* if these qtds were queued to the HC, some may be active.
 		 * else we're cleaning up after a failed URB submission.
+		 *
+		 * FIXME can simplify: cleanup case is gone now.
 		 */
 		if (likely (qh != 0)) {
 			int		qh_halted;
@@ -406,6 +408,49 @@
 /*-------------------------------------------------------------------------*/
 
 /*
+ * reverse of qh_urb_transaction:  free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*qtd_list
+) {
+	struct list_head	*entry, *temp;
+	int			unmapped = 0;
+
+	list_for_each_safe (entry, temp, qtd_list) {
+		struct ehci_qtd	*qtd;
+
+		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+		list_del (&qtd->qtd_list);
+		if (unmapped != 2) {
+			int	direction;
+			size_t	size;
+
+			/* for ctrl unmap twice: SETUP and DATA;
+			 * else (bulk, intr) just once: DATA
+			 */
+			if (!unmapped++ && usb_pipecontrol (urb->pipe)) {
+				direction = PCI_DMA_TODEVICE;
+				size = sizeof (devrequest);
+			} else {
+				direction = usb_pipein (urb->pipe)
+					? PCI_DMA_FROMDEVICE
+					: PCI_DMA_TODEVICE;
+				size = qtd->urb->transfer_buffer_length;
+				unmapped++;
+			}
+			if (qtd->buf_dma)
+				pci_unmap_single (ehci->hcd.pdev,
+					qtd->buf_dma,
+					size, direction);
+		}
+		ehci_qtd_free (ehci, qtd);
+	}
+}
+
+/*
  * create a list of filled qtds for this URB; won't link into qh.
  */
 static struct list_head *
@@ -436,14 +481,6 @@
 
 	if (usb_pipecontrol (urb->pipe)) {
 		/* control request data is passed in the "setup" pid */
-
-		/* NOTE:  this isn't smart about 64bit DMA, since it uses the
-		 * default (32bit) mask rather than using the whole address
-		 * space.  we could set pdev->dma_mask to all-ones while
-		 * getting this mapping, locking it and restoring before
-		 * allocating qtd/qh/... or maybe only do that for the main
-		 * data phase (below).
-		 */
 		qtd->buf_dma = pci_map_single (
 					ehci->hcd.pdev,
 					urb->setup_packet,
@@ -472,7 +509,6 @@
 	 */
 	len = urb->transfer_buffer_length;
 	if (likely (len > 0)) {
-		/* NOTE:  sub-optimal mapping with 64bit DMA (see above) */
 		buf = map_buf = pci_map_single (ehci->hcd.pdev,
 			urb->transfer_buffer, len,
 			usb_pipein (urb->pipe)
@@ -556,8 +592,7 @@
 	return head;
 
 cleanup:
-	urb->status = -ENOMEM;
-	qh_completions (ehci, head, 1);
+	qtd_list_free (ehci, urb, head);
 	return 0;
 }
 
@@ -643,12 +678,19 @@
 		if (usb_pipecontrol (urb->pipe)) {
 			info1 |= 64 << 16;	/* usb2 fixed maxpacket */
 			info1 |= 1 << 14;	/* toggle from qtd */
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
 		} else if (usb_pipebulk (urb->pipe)) {
 			info1 |= 512 << 16;	/* usb2 fixed maxpacket */
 			info2 |= (EHCI_TUNE_MULT_HS << 30);
-		} else
-			info1 |= usb_maxpacket (urb->dev, urb->pipe,
-						usb_pipeout (urb->pipe)) << 16;
+		} else {
+			u32	temp;
+			temp = usb_maxpacket (urb->dev, urb->pipe,
+						usb_pipeout (urb->pipe));
+			info1 |= (temp & 0x3ff) << 16;	/* maxpacket */
+			/* HS intr can be "high bandwidth" */
+			temp = 1 + ((temp >> 11) & 0x03);
+			info2 |= temp << 30;		/* mult */
+		}
 		break;
 	default:
 #ifdef DEBUG
@@ -715,7 +757,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void
+static int
 submit_async (
 	struct ehci_hcd		*ehci,
 	struct urb		*urb,
@@ -809,21 +851,23 @@
 		if (likely (qh != 0)) {
 			// dbg_qh ("new qh", ehci, qh);
 			dev->ep [epnum] = qh;
-		} else
-			urb->status = -ENOMEM;
+		}
 	}
 
 	/* Control/bulk operations through TTs don't need scheduling,
 	 * the HC and TT handle it when the TT has a buffer ready.
 	 */
 	if (likely (qh != 0)) {
-		urb->hcpriv = qh_put (qh);
+		urb->hcpriv = qh_get (qh);
 		if (likely (qh->qh_state == QH_STATE_IDLE))
-			qh_link_async (ehci, qh_put (qh));
+			qh_link_async (ehci, qh_get (qh));
 	}
 	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (unlikely (!qh))
-		qh_completions (ehci, qtd_list, 1);
+	if (unlikely (qh == 0)) {
+		qtd_list_free (ehci, urb, qtd_list);
+		return -ENOMEM;
+	}
+	return 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -837,7 +881,7 @@
 
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_next.qh = 0;
-	qh_unput (ehci, qh);			// refcount from reclaim 
+	qh_put (ehci, qh);			// refcount from reclaim 
 	ehci->reclaim = 0;
 	ehci->reclaim_ready = 0;
 
@@ -849,7 +893,7 @@
 			&& HCD_IS_RUNNING (ehci->hcd.state))
 		qh_link_async (ehci, qh);
 	else
-		qh_unput (ehci, qh);		// refcount from async list
+		qh_put (ehci, qh);		// refcount from async list
 }
 
 
@@ -874,7 +918,7 @@
 #endif
 
 	qh->qh_state = QH_STATE_UNLINK;
-	ehci->reclaim = qh = qh_put (qh);
+	ehci->reclaim = qh = qh_get (qh);
 
 	// dbg_qh ("start unlink", ehci, qh);
 
@@ -939,14 +983,14 @@
 			/* clean any finished work for this qh */
 			if (!list_empty (&qh->qtd_list)) {
 				// dbg_qh ("scan_async", ehci, qh);
-				qh = qh_put (qh);
+				qh = qh_get (qh);
 				spin_unlock_irqrestore (&ehci->lock, flags);
 
 				/* concurrent unlink could happen here */
 				qh_completions (ehci, &qh->qtd_list, 1);
 
 				spin_lock_irqsave (&ehci->lock, flags);
-				qh_unput (ehci, qh);
+				qh_put (ehci, qh);
 			}
 
 			/* unlink idle entries (reduces PCI usage) */
diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c
--- a/drivers/usb/hcd/ehci-sched.c	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-sched.c	Fri May  3 14:51:44 2002
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
  * 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -55,12 +55,12 @@
 		return &periodic->qh->qh_next;
 	case Q_TYPE_FSTN:
 		return &periodic->fstn->fstn_next;
-#ifdef have_iso
 	case Q_TYPE_ITD:
 		return &periodic->itd->itd_next;
+#ifdef have_split_iso
 	case Q_TYPE_SITD:
 		return &periodic->sitd->sitd_next;
-#endif /* have_iso */
+#endif /* have_split_iso */
 	}
 	dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
 	// BUG ();
@@ -109,9 +109,6 @@
 	u32			*hw_p = &ehci->periodic [frame];
 	union ehci_shadow	*q = &ehci->pshadow [frame];
 	unsigned		usecs = 0;
-#ifdef have_iso
-	u32			temp = 0;
-#endif
 
 	while (q->ptr) {
 		switch (Q_NEXT_TYPE (*hw_p)) {
@@ -130,15 +127,13 @@
 			}
 			q = &q->fstn->fstn_next;
 			break;
-#ifdef have_iso
 		case Q_TYPE_ITD:
-			temp = le32_to_cpu (q->itd->transaction [uframe]);
-			temp >>= 16;
-			temp &= 0x0fff;
-			if (temp)
-				usecs += HS_USECS_ISO (temp);
+			/* NOTE the "one uframe per itd" policy */
+			if (q->itd->hw_transaction [uframe] != 0)
+				usecs += q->itd->usecs;
 			q = &q->itd->itd_next;
 			break;
+#ifdef have_split_iso
 		case Q_TYPE_SITD:
 			temp = q->sitd->hw_fullspeed_ep &
 				__constant_cpu_to_le32 (1 << 31);
@@ -163,7 +158,7 @@
 			}
 			q = &q->sitd->sitd_next;
 			break;
-#endif /* have_iso */
+#endif /* have_split_iso */
 		default:
 			BUG ();
 		}
@@ -178,6 +173,45 @@
 
 /*-------------------------------------------------------------------------*/
 
+static void enable_periodic (struct ehci_hcd *ehci)
+{
+	u32	cmd;
+
+	/* did clearing PSE did take effect yet?
+	 * takes effect only at frame boundaries...
+	 */
+	while (readl (&ehci->regs->status) & STS_PSS)
+		udelay (20);
+
+	cmd = readl (&ehci->regs->command) | CMD_PSE;
+	writel (cmd, &ehci->regs->command);
+	/* posted write ... PSS happens later */
+	ehci->hcd.state = USB_STATE_RUNNING;
+
+	/* make sure tasklet scans these */
+	ehci->next_uframe = readl (&ehci->regs->frame_index)
+				% (ehci->periodic_size << 3);
+}
+
+static void disable_periodic (struct ehci_hcd *ehci)
+{
+	u32	cmd;
+
+	/* did setting PSE not take effect yet?
+	 * takes effect only at frame boundaries...
+	 */
+	while (!(readl (&ehci->regs->status) & STS_PSS))
+		udelay (20);
+
+	cmd = readl (&ehci->regs->command) & ~CMD_PSE;
+	writel (cmd, &ehci->regs->command);
+	/* posted write ... */
+
+	ehci->next_uframe = -1;
+}
+
+/*-------------------------------------------------------------------------*/
+
 static void intr_deschedule (
 	struct ehci_hcd	*ehci,
 	unsigned	frame,
@@ -186,11 +220,13 @@
 ) {
 	unsigned long	flags;
 
+	period >>= 3;		// FIXME microframe periods not handled yet
+
 	spin_lock_irqsave (&ehci->lock, flags);
 
 	do {
 		periodic_unlink (ehci, frame, qh);
-		qh_unput (ehci, qh);
+		qh_put (ehci, qh);
 		frame += period;
 	} while (frame < ehci->periodic_size);
 
@@ -199,21 +235,9 @@
 	ehci->periodic_urbs--;
 
 	/* maybe turn off periodic schedule */
-	if (!ehci->periodic_urbs) {
-		u32	cmd = readl (&ehci->regs->command);
-
-		/* did setting PSE not take effect yet?
-		 * takes effect only at frame boundaries...
-		 */
-		while (!(readl (&ehci->regs->status) & STS_PSS))
-			udelay (20);
-
-		cmd &= ~CMD_PSE;
-		writel (cmd, &ehci->regs->command);
-		/* posted write ... */
-
-		ehci->next_frame = -1;
-	} else
+	if (!ehci->periodic_urbs)
+		disable_periodic (ehci);
+	else
 		vdbg ("periodic schedule still enabled");
 
 	spin_unlock_irqrestore (&ehci->lock, flags);
@@ -234,6 +258,38 @@
 		atomic_read (&qh->refcount), ehci->periodic_urbs);
 }
 
+static int check_period (
+	struct ehci_hcd *ehci, 
+	unsigned	frame,
+	int		uframe,
+	unsigned	period,
+	unsigned	usecs
+) {
+	/*
+	 * 80% periodic == 100 usec/uframe available
+	 * convert "usecs we need" to "max already claimed" 
+	 */
+	usecs = 100 - usecs;
+
+	do {
+		int	claimed;
+
+// FIXME delete when intr_submit handles non-empty queues
+// this gives us a one intr/frame limit (vs N/uframe)
+		if (ehci->pshadow [frame].ptr)
+			return 0;
+
+		claimed = periodic_usecs (ehci, frame, uframe);
+		if (claimed > usecs)
+			return 0;
+
+// FIXME update to handle sub-frame periods
+	} while ((frame += period) < ehci->periodic_size);
+
+	// success!
+	return 1;
+}
+
 static int intr_submit (
 	struct ehci_hcd		*ehci,
 	struct urb		*urb,
@@ -241,8 +297,7 @@
 	int			mem_flags
 ) {
 	unsigned		epnum, period;
-	unsigned		temp;
-	unsigned short		mult, usecs;
+	unsigned short		usecs;
 	unsigned long		flags;
 	struct ehci_qh		*qh;
 	struct hcd_dev		*dev;
@@ -250,17 +305,9 @@
 
 	/* get endpoint and transfer data */
 	epnum = usb_pipeendpoint (urb->pipe);
-	if (usb_pipein (urb->pipe)) {
-		temp = urb->dev->epmaxpacketin [epnum];
+	if (usb_pipein (urb->pipe))
 		epnum |= 0x10;
-	} else
-		temp = urb->dev->epmaxpacketout [epnum];
-	mult = 1;
-	if (urb->dev->speed == USB_SPEED_HIGH) {
-		/* high speed "high bandwidth" is coded in ep maxpacket */
-		mult += (temp >> 11) & 0x03;
-		temp &= 0x03ff;
-	} else {
+	if (urb->dev->speed != USB_SPEED_HIGH) {
 		dbg ("no intr/tt scheduling yet"); 
 		status = -ENOSYS;
 		goto done;
@@ -270,6 +317,13 @@
 	 * NOTE: current completion/restart logic doesn't handle more than
 	 * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this.
 	 * such big requests need many periods to transfer.
+	 *
+	 * FIXME want to change hcd core submit model to expect queuing
+	 * for all transfer types ... not just ISO and (with flag) BULK.
+	 * that means: getting rid of this check; handling the "interrupt
+	 * urb already queued" case below like bulk queuing is handled (no
+	 * errors possible!); and completly getting rid of that annoying
+	 * qh restart logic.  simpler/smaller overall, and more flexible.
 	 */
 	if (unlikely (qtd_list->next != qtd_list->prev)) {
 		dbg ("only one intr qtd per urb allowed"); 
@@ -279,20 +333,13 @@
 
 	usecs = HS_USECS (urb->transfer_buffer_length);
 
-	/*
-	 * force a power-of-two (frames) sized polling interval
-	 *
-	 * NOTE: endpoint->bInterval for highspeed is measured in uframes,
-	 * while for full/low speeds it's in frames.  Here we "know" that
-	 * urb->interval doesn't give acccess to high interrupt rates.
-	 */
-	period = ehci->periodic_size;
-	temp = period;
-	if (unlikely (urb->interval < 1))
-		urb->interval = 1;
-	while (temp > urb->interval)
-		temp >>= 1;
-	period = urb->interval = temp;
+	/* FIXME handle HS periods of less than 1 frame. */
+	period = urb->interval >> 3;
+	if (period < 1) {
+		dbg ("intr period %d uframes, NYET!", urb->interval);
+		status = -EINVAL;
+		goto done;
+	}
 
 	spin_lock_irqsave (&ehci->lock, flags);
 
@@ -318,55 +365,47 @@
 			list_splice (qtd_list, &qh->qtd_list);
 			qh_update (qh, list_entry (qtd_list->next,
 						struct ehci_qtd, qtd_list));
+			qtd_list = &qh->qtd_list;
 		}
 	} else {
 		/* can't sleep here, we have ehci->lock... */
 		qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
-		qtd_list = &qh->qtd_list;
 		if (likely (qh != 0)) {
 			// dbg ("new INTR qh %p", qh);
 			dev->ep [epnum] = qh;
+			qtd_list = &qh->qtd_list;
 		} else
 			status = -ENOMEM;
 	}
 
 	/* Schedule this periodic QH. */
 	if (likely (status == 0)) {
-		unsigned	frame = urb->interval;
+		unsigned	frame = period;
 
 		qh->hw_next = EHCI_LIST_END;
-		qh->hw_info2 |= cpu_to_le32 (mult << 30);
 		qh->usecs = usecs;
 
-		urb->hcpriv = qh_put (qh);
+		urb->hcpriv = qh_get (qh);
 		status = -ENOSPC;
 
 		/* pick a set of schedule slots, link the QH into them */
 		do {
 			int	uframe;
 
-			/* Select some frame 0..(urb->interval - 1) with a
-			 * microframe that can hold this transaction.
+			/* pick a set of slots such that all uframes have
+			 * enough periodic bandwidth available.
 			 *
 			 * FIXME for TT splits, need uframes for start and end.
 			 * FSTNs can put end into next frame (uframes 0 or 1).
 			 */
 			frame--;
 			for (uframe = 0; uframe < 8; uframe++) {
-				int	claimed;
-				claimed = periodic_usecs (ehci, frame, uframe);
-				/* 80% periodic == 100 usec max committed */
-				if ((claimed + usecs) <= 100) {
-					vdbg ("frame %d.%d: %d usecs, plus %d",
-						frame, uframe, claimed, usecs);
+				if (check_period (ehci, frame, uframe,
+						period, usecs) != 0)
 					break;
-				}
 			}
 			if (uframe == 8)
 				continue;
-// FIXME delete when code below handles non-empty queues
-			if (ehci->pshadow [frame].ptr)
-				continue;
 
 			/* QH will run once each period, starting there  */
 			urb->start_frame = frame;
@@ -378,15 +417,16 @@
 
 			/* stuff into the periodic schedule */
 			qh->qh_state = QH_STATE_LINKED;
-			vdbg ("qh %p usecs %d period %d starting frame %d.%d",
+			vdbg ("qh %p usecs %d period %d starting %d.%d",
 				qh, qh->usecs, period, frame, uframe);
 			do {
 				if (unlikely (ehci->pshadow [frame].ptr != 0)) {
-// FIXME -- just link to the end, before any qh with a shorter period,
+// FIXME -- just link toward the end, before any qh with a shorter period,
 // AND handle it already being (implicitly) linked into this frame
+// AS WELL AS updating the check_period() logic
 					BUG ();
 				} else {
-					ehci->pshadow [frame].qh = qh_put (qh);
+					ehci->pshadow [frame].qh = qh_get (qh);
 					ehci->periodic [frame] =
 						QH_NEXT (qh->qh_dma);
 				}
@@ -394,40 +434,20 @@
 			} while (frame < ehci->periodic_size);
 
 			/* update bandwidth utilization records (for usbfs) */
-			usb_claim_bandwidth (urb->dev, urb, usecs, 0);
+			usb_claim_bandwidth (urb->dev, urb, usecs/period, 0);
 
 			/* maybe enable periodic schedule processing */
-			if (!ehci->periodic_urbs++) {
-				u32	cmd;
-
-				/* did clearing PSE did take effect yet?
-				 * takes effect only at frame boundaries...
-				 */
-				while (readl (&ehci->regs->status) & STS_PSS)
-					udelay (20);
-
-				cmd = readl (&ehci->regs->command) | CMD_PSE;
-				writel (cmd, &ehci->regs->command);
-				/* posted write ... PSS happens later */
-				ehci->hcd.state = USB_STATE_RUNNING;
-
-				/* make sure tasklet scans these */
-				ehci->next_frame = ehci_get_frame (&ehci->hcd);
-			}
+			if (!ehci->periodic_urbs++)
+				enable_periodic (ehci);
 			break;
 
 		} while (frame);
 	}
 	spin_unlock_irqrestore (&ehci->lock, flags);
 done:
-	if (status) {
-		usb_complete_t	complete = urb->complete;
+	if (status)
+		qtd_list_free (ehci, urb, qtd_list);
 
-		urb->complete = 0;
-		urb->status = status;
-		qh_completions (ehci, qtd_list, 1);
-		urb->complete = complete;
-	}
 	return status;
 }
 
@@ -446,6 +466,10 @@
 	if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
 			!= 0))
 		return flags;
+	if (unlikely (list_empty (&qh->qtd_list))) {
+		dbg ("intr qh %p no TDs?", qh);
+		return flags;
+	}
 	
 	qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list);
 	urb = qtd->urb;
@@ -489,53 +513,56 @@
 
 /*-------------------------------------------------------------------------*/
 
-#ifdef	have_iso
-
-static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd)
+static void
+itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
 {
-	pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+	struct ehci_itd *first_itd = urb->hcpriv;
+
+	pci_unmap_single (ehci->hcd.pdev,
+		first_itd->buf_dma, urb->transfer_buffer_length,
+		usb_pipein (urb->pipe)
+		    ? PCI_DMA_FROMDEVICE
+		    : PCI_DMA_TODEVICE);
+	while (!list_empty (&first_itd->itd_list)) {
+		struct ehci_itd	*itd;
+
+		itd = list_entry (
+			first_itd->itd_list.next,
+			struct ehci_itd, itd_list);
+		list_del (&itd->itd_list);
+		pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+	}
+	pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
+	urb->hcpriv = 0;
 }
 
-/*
- * Create itd and allocate into uframes within specified frame.
- * Caller must update the resulting uframe links.
- */
-static struct ehci_itd *
-itd_make (
+static int
+itd_fill (
 	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd,
 	struct urb	*urb,
 	unsigned	index,		// urb->iso_frame_desc [index]
-	unsigned	frame,		// scheduled start
-	dma_addr_t	dma,		// mapped transfer buffer
-	int		mem_flags
+	dma_addr_t	dma		// mapped transfer buffer
 ) {
-	struct ehci_itd	*itd;
 	u64		temp;
 	u32		buf1;
-	unsigned	epnum, maxp, multi, usecs;
+	unsigned	i, epnum, maxp, multi;
 	unsigned	length;
-	unsigned	i, bufnum;
-
-	/* allocate itd, start to fill it */
-	itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma);
-	if (!itd)
-		return itd;
 
 	itd->hw_next = EHCI_LIST_END;
 	itd->urb = urb;
 	itd->index = index;
-	INIT_LIST_HEAD (&itd->itd_list);
-	itd->uframe = (frame * 8) % ehci->periodic_size;
 
-	/* tell itd about the buffer its transfers will consume */
+	/* tell itd about its transfer buffer, max 2 pages */
 	length = urb->iso_frame_desc [index].length;
 	dma += urb->iso_frame_desc [index].offset;
 	temp = dma & ~0x0fff;
-	for (i = 0; i < 7; i++) {
+	for (i = 0; i < 2; i++) {
 		itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
 		itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
-		temp += 0x0fff;
+		temp += 0x1000;
 	}
+	itd->buf_dma = dma;
 
 	/*
 	 * this might be a "high bandwidth" highspeed endpoint,
@@ -544,282 +571,407 @@
 	epnum = usb_pipeendpoint (urb->pipe);
 	if (usb_pipein (urb->pipe)) {
 		maxp = urb->dev->epmaxpacketin [epnum];
-		buf1 = (1 << 11) | maxp;
+		buf1 = (1 << 11);
 	} else {
 		maxp = urb->dev->epmaxpacketout [epnum];
-		buf1 = maxp;
+		buf1 = 0;
 	}
+	buf1 |= (maxp & 0x03ff);
 	multi = 1;
-	multi += (temp >> 11) & 0x03;
+	multi += (maxp >> 11) & 0x03;
 	maxp &= 0x03ff;
+	maxp *= multi;
+
+	/* transfer can't fit in any uframe? */ 
+	if (length < 0 || maxp < length) {
+		dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
+			length, maxp, urb, index,
+			urb->iso_frame_desc [index].length);
+		return -ENOSPC;
+	}
+	itd->usecs = HS_USECS_ISO (length);
 
 	/* "plus" info in low order bits of buffer pointers */
 	itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
 	itd->hw_bufp [1] |= cpu_to_le32 (buf1);
 	itd->hw_bufp [2] |= cpu_to_le32 (multi);
 
-	/* schedule as many uframes as needed */
-	maxp *= multi;
-	usecs = HS_USECS_ISO (maxp);
-	bufnum = 0;
-	for (i = 0; i < 8; i++) {
-		unsigned	t, offset, scratch;
+	/* figure hw_transaction[] value (it's scheduled later) */
+	itd->transaction = EHCI_ISOC_ACTIVE;
+	itd->transaction |= dma & 0x0fff;		/* offset; buffer=0 */
+	if ((index + 1) == urb->number_of_packets)
+		itd->transaction |= EHCI_ITD_IOC; 	/* end-of-urb irq */
+	itd->transaction |= length << 16;
+	cpu_to_le32s (&itd->transaction);
 
-		if (length <= 0) {
-			itd->hw_transaction [i] = 0;
-			continue;
-		}
+	return 0;
+}
 
-		/* don't commit more than 80% periodic == 100 usec */
-		if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100)
-			continue;
+static int
+itd_urb_transaction (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	int			mem_flags
+) {
+	int			frame_index;
+	struct ehci_itd		*first_itd, *itd;
+	int			status;
+	dma_addr_t		buf_dma, itd_dma;
 
-		/* we'll use this uframe; figure hw_transaction */
-		t = EHCI_ISOC_ACTIVE;
-		t |= bufnum << 12;		// which buffer?
-		offset = temp & 0x0fff;		// offset therein
-		t |= offset;
-		if ((offset + maxp) >= 4096) 	// hc auto-wraps end-of-"page"
-			bufnum++;
-		if (length <= maxp) {
-			// interrupt only needed at end-of-urb
-			if ((index + 1) == urb->number_of_packets)
-				t |= EHCI_ITD_IOC;
-			scratch = length;
-		} else
-			scratch = maxp;
-		t |= scratch << 16;
-		t = cpu_to_le32 (t);
-
-		itd->hw_transaction [i] = itd->transaction [i] = t;
-		length -= scratch;
-	}
-	if (length > 0) {
-		dbg ("iso frame too big, urb %p [%d], %d extra (of %d)",
-			urb, index, length, urb->iso_frame_desc [index].length);
-		itd_free (ehci, itd);
-		itd = 0;
+	/* set up one dma mapping for this urb */
+	buf_dma = pci_map_single (ehci->hcd.pdev,
+		urb->transfer_buffer, urb->transfer_buffer_length,
+		usb_pipein (urb->pipe)
+		    ? PCI_DMA_FROMDEVICE
+		    : PCI_DMA_TODEVICE);
+	if (buf_dma == 0)
+		return -ENOMEM;
+
+	/* allocate/init ITDs */
+	for (frame_index = 0, first_itd = 0;
+			frame_index < urb->number_of_packets;
+			frame_index++) {
+		itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
+		if (!itd) {
+			status = -ENOMEM;
+			goto fail;
+		}
+		memset (itd, 0, sizeof *itd);
+		itd->itd_dma = itd_dma;
+
+		status = itd_fill (ehci, itd, urb, frame_index, buf_dma);
+		if (status != 0)
+			goto fail;
+
+		if (first_itd)
+			list_add_tail (&itd->itd_list,
+					&first_itd->itd_list);
+		else {
+			INIT_LIST_HEAD (&itd->itd_list);
+			urb->hcpriv = first_itd = itd;
+		}
 	}
-	return itd;
+	urb->error_count = 0;
+	return 0;
+
+fail:
+	if (urb->hcpriv)
+		itd_free_list (ehci, urb);
+	return status;
 }
 
+/*-------------------------------------------------------------------------*/
+
 static inline void
 itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
 {
-	u32		ptr;
+	/* always prepend ITD/SITD ... only QH tree is order-sensitive */
+	itd->itd_next = ehci->pshadow [frame];
+	itd->hw_next = ehci->periodic [frame];
+	ehci->pshadow [frame].itd = itd;
+	ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+}
 
-	ptr = cpu_to_le32 (itd->itd_dma);	// type 0 == itd
-	if (ehci->pshadow [frame].ptr) {
-		if (!itd->itd_next.ptr) {
-			itd->itd_next = ehci->pshadow [frame];
-			itd->hw_next = ehci->periodic [frame];
-		} else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) {
-			dbg ("frame %d itd link goof", frame);
-			BUG ();
+/*
+ * return zero on success, else -errno
+ * - start holds first uframe to start scheduling into
+ * - max is the first uframe it's NOT (!) OK to start scheduling into
+ * math to be done modulo "mod" (ehci->periodic_size << 3)
+ */
+static int get_iso_range (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	unsigned		*start,
+	unsigned		*max,
+	unsigned		mod
+) {
+	struct list_head	*lh;
+	struct hcd_dev		*dev = urb->dev->hcpriv;
+	int			last = -1;
+	unsigned		now, span, end;
+
+	span = urb->interval * urb->number_of_packets;
+
+	/* first see if we know when the next transfer SHOULD happen */
+	list_for_each (lh, &dev->urb_list) {
+		struct urb	*u;
+		struct ehci_itd	*itd;
+		unsigned	s;
+
+		u = list_entry (lh, struct urb, urb_list);
+		if (u == urb || u->pipe != urb->pipe)
+			continue;
+		if (u->interval != urb->interval) {	/* must not change! */ 
+			dbg ("urb %p interval %d ... != %p interval %d",
+				u, u->interval, urb, urb->interval);
+			return -EINVAL;
+		}
+		
+		/* URB for this endpoint... covers through when?  */
+		itd = urb->hcpriv;
+		s = itd->uframe + u->interval * u->number_of_packets;
+		if (last < 0)
+			last = s;
+		else {
+			/*
+			 * So far we can only queue two ISO URBs...
+			 *
+			 * FIXME do interval math, figure out whether
+			 * this URB is "before" or not ... also, handle
+			 * the case where the URB might have completed,
+			 * but hasn't yet been processed.
+			 */
+			dbg ("NYET: queue >2 URBs per ISO endpoint");
+			return -EDOM;
 		}
 	}
-	ehci->pshadow [frame].itd = itd;
-	ehci->periodic [frame] = ptr;
-}
 
-#define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+	/* calculate the legal range [start,max) */
+	now = readl (&ehci->regs->frame_index) + 1;	/* next uframe */
+	if (!ehci->periodic_urbs)
+		now += 8;				/* startup delay */
+	now %= mod;
+	end = now + mod;
+	if (last < 0) {
+		*start = now + ehci->i_thresh + /* paranoia */ 1;
+		*max = end - span;
+		if (*max < *start + 1)
+			*max = *start + 1;
+	} else {
+		*start = last % mod;
+		*max = (last + 1) % mod;
+	}
 
-static unsigned long
-itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags)
+	/* explicit start frame? */
+	if (!(urb->transfer_flags & USB_ISO_ASAP)) {
+		unsigned	temp;
+
+		/* sanity check: must be in range */
+		urb->start_frame %= ehci->periodic_size;
+		temp = urb->start_frame << 3;
+		if (temp < *start)
+			temp += mod;
+		if (temp > *max)
+			return -EDOM;
+
+		/* use that explicit start frame */
+		*start = urb->start_frame << 3;
+		temp += 8;
+		if (temp < *max)
+			*max = temp;
+	}
+
+	// FIXME minimize wraparound to "now" ... insist max+span
+	// (and start+span) remains a few frames short of "end"
+
+	*max %= ehci->periodic_size;
+	if ((*start + span) < end)
+		return 0;
+	return -EFBIG;
+}
+
+static int
+itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
 {
-	struct urb		*urb = itd->urb;
+	unsigned	start, max, i;
+	int		status;
+	unsigned	mod = ehci->periodic_size << 3;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		urb->iso_frame_desc [i].status = -EINPROGRESS;
+		urb->iso_frame_desc [i].actual_length = 0;
+	}
 
-	/* if not unlinking: */
-	if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
-			&& ehci->hcd.state != USB_STATE_HALT) {
-		int			i;
-		iso_packet_descriptor_t	*desc;
-		struct ehci_itd		*first_itd = urb->hcpriv;
+	if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
+		return status;
 
-		/* update status for this frame's transfers */
-		desc = &urb->iso_frame_desc [itd->index];
-		desc->status = 0;
-		desc->actual_length = 0;
-		for (i = 0; i < 8; i++) {
-			u32	 t = itd->hw_transaction [i];
-			if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) {
-				if (t & EHCI_ISOC_ACTIVE)
-					desc->status = -EXDEV;
-				else 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;
+	do {
+		unsigned	uframe;
+		unsigned	usecs;
+		struct ehci_itd	*itd;
+
+		/* check schedule: enough space? */
+		itd = urb->hcpriv;
+		uframe = start;
+		for (i = 0, uframe = start;
+				i < urb->number_of_packets;
+				i++, uframe += urb->interval) {
+			uframe %= mod;
+
+			/* can't commit more than 80% periodic == 100 usec */
+			if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
+					> (100 - itd->usecs)) {
+				itd = 0;
 				break;
 			}
-			desc->actual_length += EHCI_ITD_LENGTH (t);
+			itd = list_entry (itd->itd_list.next,
+				struct ehci_itd, itd_list);
 		}
+		if (!itd)
+			continue;
+		
+		/* that's where we'll schedule this! */
+		itd = urb->hcpriv;
+		urb->start_frame = start >> 3;
+		vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
+			urb, urb->number_of_packets, urb->interval,
+			urb->start_frame, start & 0x7);
+		for (i = 0, uframe = start, usecs = 0;
+				i < urb->number_of_packets;
+				i++, uframe += urb->interval) {
+			uframe %= mod;
+
+			itd->uframe = uframe;
+			itd->hw_transaction [uframe & 0x07] = itd->transaction;
+			itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
+				itd);
+			usecs += itd->usecs;
 
-		/* handle completion now? */
-		if ((itd->index + 1) != urb->number_of_packets)
-			return flags;
+			itd = list_entry (itd->itd_list.next,
+				struct ehci_itd, itd_list);
+		}
 
-		i = usb_pipein (urb->pipe);
-		if (i)
-			pci_dma_sync_single (ehci->hcd.pdev,
-				first_itd->buf_dma,
-				urb->transfer_buffer_length,
-				PCI_DMA_FROMDEVICE);
+		/* update bandwidth utilization records (for usbfs) */
+		/* FIXME usbcore expects per-frame average, which isn't
+		 * most accurate model... this provides the total claim,
+		 * and expects the average to be computed only display.
+		 */
+		usb_claim_bandwidth (urb->dev, urb, usecs, 1);
 
-		/* call completion with no locks; it can unlink ... */
-		spin_unlock_irqrestore (&ehci->lock, flags);
-		urb->complete (urb);
-		spin_lock_irqsave (&ehci->lock, flags);
-
-		/* re-activate this URB? or unlink? */
-		if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
-				&& ehci->hcd.state != USB_STATE_HALT) {
-			if (!i)
-				pci_dma_sync_single (ehci->hcd.pdev,
-					first_itd->buf_dma,
-					urb->transfer_buffer_length,
-					PCI_DMA_TODEVICE);
+		/* maybe enable periodic schedule processing */
+		if (!ehci->periodic_urbs++)
+			enable_periodic (ehci);
 
-			itd = urb->hcpriv;
-			do {
-				for (i = 0; i < 8; i++)
-					itd->hw_transaction [i]
-						= itd->transaction [i];
-				itd = list_entry (itd->itd_list.next,
-						struct ehci_itd, itd_list);
-			} while (itd != urb->hcpriv);
-			return flags;
-		}
+		return 0;
+
+	} while ((start = ++start % mod) != max);
+
+	/* no room in the schedule */
+	dbg ("urb %p, CAN'T SCHEDULE", urb);
+	return -ENOSPC;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned long
+itd_complete (
+	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd,
+	unsigned	uframe,
+	unsigned long	flags
+) {
+	struct urb				*urb = itd->urb;
+	iso_packet_descriptor_t			*desc;
+	u32					t;
+
+	/* 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);
+	}
 
-	/* unlink done only on the last itd */
-	} else if ((itd->index + 1) != urb->number_of_packets)
+	vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
+		itd, urb, itd->index + 1, urb->number_of_packets,
+		t, desc->status, desc->actual_length);
+
+	/* handle completion now? */
+	if ((itd->index + 1) != urb->number_of_packets)
 		return flags;
 
-	/* we're unlinking ... */
+	/*
+	 * For now, always give the urb back to the driver ... expect it
+	 * to submit a new urb (or resubmit this), and to have another
+	 * already queued when un-interrupted transfers are needed.
+	 * No, that's not what OHCI or UHCI are now doing.
+	 *
+	 * FIXME Revisit the ISO URB model.  It's cleaner not to have all
+	 * the special case magic, but it'd be faster to reuse existing
+	 * ITD/DMA setup and schedule state.  Easy to dma_sync/complete(),
+	 * then either reschedule or, if unlinking, free and giveback().
+	 * But we can't overcommit like the full and low speed HCs do, and
+	 * there's no clean way to report an error when rescheduling...
+	 *
+	 * NOTE that for now we don't accelerate ISO unlinks; they just
+	 * happen according to the current schedule.  Means a delay of
+	 * up to about a second (max).
+	 */
+	itd_free_list (ehci, urb);
+	if (urb->status == -EINPROGRESS)
+		urb->status = 0;
 
-	/* decouple urb from the hcd */
 	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (ehci->hcd.state == USB_STATE_HALT)
-		urb->status = -ESHUTDOWN;
-	itd = urb->hcpriv;
-	urb->hcpriv = 0;
-	ehci_urb_done (ehci, itd->buf_dma, urb);
+	usb_hcd_giveback_urb (&ehci->hcd, urb);
 	spin_lock_irqsave (&ehci->lock, flags);
 
-	/* take itds out of the hc's periodic schedule */
-	list_entry (itd->itd_list.prev, struct ehci_itd, itd_list)
-		->itd_list.next = 0;
-	do {
-		struct ehci_itd	*next;
-
-		if (itd->itd_list.next)
-			next = list_entry (itd->itd_list.next,
-				struct ehci_itd, itd_list);
-		else
-			next = 0;
+	/* defer stopping schedule; completion can submit */
+	ehci->periodic_urbs--;
+	if (!ehci->periodic_urbs)
+		disable_periodic (ehci);
 
-		// FIXME:  hc WILL (!) lap us here, if we get behind
-		// by 128 msec (or less, with smaller periodic_size).
-		// Reading/caching these itds will cause trouble...
-
-		periodic_unlink (ehci, itd->uframe, itd);
-		itd_free (ehci, itd);
-		itd = next;
-	} while (itd);
 	return flags;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static int itd_submit (struct ehci_hcd *ehci, struct urb *urb)
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
 {
-	struct ehci_itd		*first_itd = 0, *itd;
-	unsigned		frame_index;
-	dma_addr_t		dma;
-	unsigned long		flags;
-
-	dbg ("itd_submit");
+	int		status;
+	unsigned long	flags;
 
-	/* set up one dma mapping for this urb */
-	dma = pci_map_single (ehci->hcd.pdev,
-		urb->transfer_buffer, urb->transfer_buffer_length,
-		usb_pipein (urb->pipe)
-		    ? PCI_DMA_FROMDEVICE
-		    : PCI_DMA_TODEVICE);
-	if (dma == 0)
-		return -ENOMEM;
+	dbg ("itd_submit urb %p", urb);
 
+	/* NOTE DMA mapping assumes this ... */
+	if (urb->iso_frame_desc [0].offset != 0)
+		return -EINVAL;
+	
 	/*
-	 * Schedule as needed.  This is VERY optimistic about free
-	 * bandwidth!  But the API assumes drivers can pick frames
-	 * intelligently (how?), so there's no other good option.
-	 *
-	 * FIXME  this doesn't handle urb->next rings, or try to
-	 * use the iso periodicity.
+	 * NOTE doing this for now, anticipating periodic URB models
+	 * get updated to be "explicit resubmit".
 	 */
-	if (urb->transfer_flags & USB_ISO_ASAP) { 
-		urb->start_frame = ehci_get_frame (&ehci->hcd);
-		urb->start_frame++;
+	if (urb->next) {
+		dbg ("use explicit resubmit for ISO");
+		return -EINVAL;
 	}
-	urb->start_frame %= ehci->periodic_size;
 
-	/* create and populate itds (doing uframe scheduling) */
-	spin_lock_irqsave (&ehci->lock, flags);
-	for (frame_index = 0;
-			frame_index < urb->number_of_packets;
-			frame_index++) {
-		itd = itd_make (ehci, urb, frame_index,
-			urb->start_frame + frame_index,
-			dma, SLAB_ATOMIC);
-		if (itd) {
-			if (first_itd)
-				list_add_tail (&itd->itd_list,
-						&first_itd->itd_list);
-			else
-				first_itd = itd;
-		} else {
-			spin_unlock_irqrestore (&ehci->lock, flags);
-			if (first_itd) {
-				while (!list_empty (&first_itd->itd_list)) {
-					itd = list_entry (
-						first_itd->itd_list.next,
-						struct ehci_itd, itd_list);
-					list_del (&itd->itd_list);
-					itd_free (ehci, itd);
-				}
-				itd_free (ehci, first_itd);
-			}
-			pci_unmap_single (ehci->hcd.pdev,
-				dma, urb->transfer_buffer_length,
-				usb_pipein (urb->pipe)
-				    ? PCI_DMA_FROMDEVICE
-				    : PCI_DMA_TODEVICE);
-			return -ENOMEM;
-		}
-	}
-
-	/* stuff into the schedule */
-	itd = first_itd;
-	do {
-		unsigned	i;
-
-		for (i = 0; i < 8; i++) {
-			if (!itd->hw_transaction [i])
-				continue;
-			itd_link (ehci, itd->uframe + i, itd);
-		}
-		itd = list_entry (itd->itd_list.next,
-			struct ehci_itd, itd_list);
-	} while (itd != first_itd);
-	urb->hcpriv = first_itd;
+	/* allocate ITDs w/o locking anything */
+	status = itd_urb_transaction (ehci, urb, mem_flags);
+	if (status < 0)
+		return status;
 
+	/* schedule ... need to lock */
+	spin_lock_irqsave (&ehci->lock, flags);
+	status = itd_schedule (ehci, urb);
 	spin_unlock_irqrestore (&ehci->lock, flags);
-	return 0;
+	if (status < 0)
+		itd_free_list (ehci, urb);
+
+	return status;
 }
 
+#ifdef have_split_iso
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -827,7 +979,7 @@
  * the TTs in USB 2.0 hubs.
  */
 
-static inline void
+static void
 sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd)
 {
 	pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma);
@@ -861,7 +1013,7 @@
 
 }
 
-static inline void
+static void
 sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
 {
 	u32		ptr;
@@ -894,12 +1046,11 @@
 
 /*-------------------------------------------------------------------------*/
 
-static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb)
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
 {
 	// struct ehci_sitd	*first_sitd = 0;
 	unsigned		frame_index;
 	dma_addr_t		dma;
-	int			mem_flags;
 
 	dbg ("NYI -- sitd_submit");
 
@@ -908,8 +1059,6 @@
 	// FIXME:  setup one big dma mapping
 	dma = 0;
 
-	mem_flags = SLAB_ATOMIC;
-
 	for (frame_index = 0;
 			frame_index < urb->number_of_packets;
 			frame_index++) {
@@ -941,54 +1090,63 @@
 
 	return -ENOSYS;
 }
-
-#endif	/* have_iso */
+#endif /* have_split_iso */
 
 /*-------------------------------------------------------------------------*/
 
 static void scan_periodic (struct ehci_hcd *ehci)
 {
-	unsigned	frame;
-	unsigned	clock;
+	unsigned	frame, clock, now_uframe, mod;
 	unsigned long	flags;
 
+	mod = ehci->periodic_size << 3;
 	spin_lock_irqsave (&ehci->lock, flags);
 
 	/*
 	 * 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.
-	 * It's safe to scan entries more than once, though.
+	 * Don't scan ISO entries more than once, though.
 	 */
-	if (HCD_IS_RUNNING (ehci->hcd.state)) {
-		frame = ehci->next_frame;
-		clock = ehci_get_frame (&ehci->hcd);
+	frame = ehci->next_uframe >> 3;
+	if (HCD_IS_RUNNING (ehci->hcd.state))
+		now_uframe = readl (&ehci->regs->frame_index);
+	else
+		now_uframe = (frame << 3) - 1;
+	now_uframe %= mod;
+	clock = now_uframe >> 3;
 
-	/* when shutting down, scan everything for thoroughness */
-	} else {
-		frame = 0;
-		clock = ehci->periodic_size - 1;
-	}
 	for (;;) {
-		union ehci_shadow	 q;
-		u32	type;
+		union ehci_shadow	q, *q_p;
+		u32			type, *hw_p;
+		unsigned		uframes;
 
 restart:
-		q.ptr = ehci->pshadow [frame].ptr;
-		type = Q_NEXT_TYPE (ehci->periodic [frame]);
+		/* scan schedule to _before_ current frame index */
+		if (frame == clock)
+			uframes = now_uframe & 0x07;
+		else
+			uframes = 8;
+
+		q_p = &ehci->pshadow [frame];
+		hw_p = &ehci->periodic [frame];
+		q.ptr = q_p->ptr;
+		type = Q_NEXT_TYPE (*hw_p);
 
 		/* scan each element in frame's queue for completions */
 		while (q.ptr != 0) {
 			int			last;
+			unsigned		uf;
 			union ehci_shadow	temp;
 
 			switch (type) {
 			case Q_TYPE_QH:
 				last = (q.qh->hw_next == EHCI_LIST_END);
-				flags = intr_complete (ehci, frame,
-						qh_put (q.qh), flags);
-				type = Q_NEXT_TYPE (q.qh->hw_next);
 				temp = q.qh->qh_next;
-				qh_unput (ehci, q.qh);
+				type = Q_NEXT_TYPE (q.qh->hw_next);
+				flags = intr_complete (ehci, frame,
+						qh_get (q.qh), flags);
+				qh_put (ehci, q.qh);
 				q = temp;
 				break;
 			case Q_TYPE_FSTN:
@@ -1000,22 +1158,49 @@
 					dbg ("ignoring completions from FSTNs");
 				}
 				type = Q_NEXT_TYPE (q.fstn->hw_next);
-				temp = q.fstn->fstn_next;
+				q = q.fstn->fstn_next;
 				break;
-#ifdef have_iso
 			case Q_TYPE_ITD:
 				last = (q.itd->hw_next == EHCI_LIST_END);
-				flags = itd_complete (ehci, q.itd, flags);
-				type = Q_NEXT_TYPE (q.itd->hw_next);
-				q = q.itd->itd_next;
+
+				/* 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 ... */
+						flags = itd_complete (ehci,
+							temp.itd, uf, flags);
+						break;
+					}
+				}
+				/* we might skip this ITD's uframe ... */
+				if (uf == uframes) {
+					q_p = &q.itd->itd_next;
+					hw_p = &q.itd->hw_next;
+					type = Q_NEXT_TYPE (q.itd->hw_next);
+				}
+
+				q = *q_p;
 				break;
+#ifdef have_split_iso
 			case Q_TYPE_SITD:
 				last = (q.sitd->hw_next == EHCI_LIST_END);
 				flags = sitd_complete (ehci, q.sitd, flags);
 				type = Q_NEXT_TYPE (q.sitd->hw_next);
+
+				// FIXME unlink SITD after split completes
 				q = q.sitd->sitd_next;
 				break;
-#endif /* have_iso */
+#endif /* have_split_iso */
 			default:
 				dbg ("corrupt type %d frame %d shadow %p",
 					type, frame, q.ptr);
@@ -1044,13 +1229,16 @@
 
 			if (!HCD_IS_RUNNING (ehci->hcd.state))
 				break;
-			ehci->next_frame = clock;
-			now = ehci_get_frame (&ehci->hcd);
-			if (clock == now)
+			ehci->next_uframe = now_uframe;
+			now = readl (&ehci->regs->frame_index) % mod;
+			if (now_uframe == now)
 				break;
-			clock = now;
-		} else if (++frame >= ehci->periodic_size)
-			frame = 0;
+
+			/* rescan the rest of this frame, then ... */
+			now_uframe = now;
+			clock = now_uframe >> 3;
+		} else
+			frame = (frame + 1) % ehci->periodic_size;
 	} 
 	spin_unlock_irqrestore (&ehci->lock, flags);
- }
+}
diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h
--- a/drivers/usb/hcd/ehci.h	Fri May  3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci.h	Fri May  3 14:51:44 2002
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
  * 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -49,7 +49,7 @@
 	unsigned		i_thresh;	/* uframes HC might cache */
 
 	union ehci_shadow	*pshadow;	/* mirror hw periodic table */
-	int			next_frame;	/* scan periodic, start here */
+	int			next_uframe;	/* scan periodic, start here */
 	unsigned		periodic_urbs;	/* how many urbs scheduled? */
 
 	/* deferred work from IRQ, etc */
@@ -62,6 +62,7 @@
 	struct usb_hcd		hcd;
 	struct ehci_caps	*caps;
 	struct ehci_regs	*regs;
+	u32			hcs_params;	/* cached register copy */
 
 	/* per-HC memory pools (could be per-PCI-bus, but ...) */
 	struct pci_pool		*qh_pool;	/* qh per active urb */
@@ -324,13 +325,14 @@
 	union ehci_shadow	itd_next;	/* ptr to periodic q entry */
 
 	struct urb		*urb;
-	unsigned		index;		/* in urb->iso_frame_desc */
 	struct list_head	itd_list;	/* list of urb frames' itds */
 	dma_addr_t		buf_dma;	/* frame's buffer address */
 
-	unsigned		uframe;		/* in periodic schedule */
-	u32			transaction [8]; /* copy of hw_transaction */
-
+	/* for now, only one hw_transaction per itd */
+	u32			transaction;
+	u16			index;		/* in urb->iso_frame_desc */
+	u16			uframe;		/* in periodic schedule */
+	u16			usecs;
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
