# 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.588   -> 1.588.1.1
#	drivers/usb/host/ehci-dbg.c	1.3     -> 1.4    
#	drivers/usb/host/ehci.h	1.3     -> 1.4    
#	drivers/usb/host/ehci-sched.c	1.12    -> 1.13   
#	drivers/usb/core/hcd.h	1.7     -> 1.8    
#	drivers/usb/core/hcd.c	1.20    -> 1.21   
#	drivers/usb/host/ehci-hcd.c	1.17    -> 1.18   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/26	david-b@pacbell.net	1.588.1.1
# [PATCH] ehci split interrupt transactions
# 
# This patch lets more devices hook up to USB 2.0 hubs, stuff
# like keyboards, mice, hubs that hasn't worked yet:
# 
# - schedules full/low speed interrupt transactions
# - tracks CSPLIT bandwidth for full/low speed interrupt
#   transactions
# - moves some bus bandwidth calculation out of the EHCI code
# - makes the bandwidth calculation primitive public, and
#   adds kerneldoc for it
# 
# It still takes a scheduling shortcut, placing at most one
# interrupt transaction in a frame (vs potentially over 100),
# but it should do for now.
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	Tue May 28 23:49:09 2002
+++ b/drivers/usb/core/hcd.c	Tue May 28 23:49:09 2002
@@ -745,12 +745,18 @@
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * usb_calc_bus_time:
+/**
+ * usb_calc_bus_time: approximate periodic transaction time in nanoseconds
+ * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
+ * @is_input: true iff the transaction sends data to the host
+ * @is_isoc: true for isochronous transactions, false for interrupt ones
+ * @bytecount: how many bytes in the transaction.
+ *
  * Returns approximate bus time in nanoseconds for a periodic transaction.
- * See USB 2.0 spec section 5.11.3
+ * See USB 2.0 spec section 5.11.3; only periodic transfers need to be
+ * scheduled in software, this function is only used for such scheduling.
  */
-static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
+long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
 {
 	unsigned long	tmp;
 
@@ -772,14 +778,18 @@
 			return (9107L + BW_HOST_DELAY + tmp);
 		}
 	case USB_SPEED_HIGH:	/* ISOC or INTR */
-		// FIXME merge from EHCI code; caller will need to handle
-		// each part of a split separately.
-		return 0;
+		// FIXME adjust for input vs output
+		if (isoc)
+			tmp = HS_USECS (bytecount);
+		else
+			tmp = HS_USECS_ISO (bytecount);
+		return tmp;
 	default:
 		dbg ("bogus device speed!");
 		return -1;
 	}
 }
+EXPORT_SYMBOL (usb_calc_bus_time);
 
 /*
  * usb_check_bandwidth():
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Tue May 28 23:49:09 2002
+++ b/drivers/usb/core/hcd.h	Tue May 28 23:49:09 2002
@@ -263,6 +263,22 @@
 
 extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
 
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define USB2_HOST_DELAY	5	/* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+	+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+	+ USB2_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+	+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+	+ USB2_HOST_DELAY)
+
+extern long usb_calc_bus_time (int speed, int is_input,
+			int isoc, int bytecount);
+
 /*-------------------------------------------------------------------------*/
 
 extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-dbg.c	Tue May 28 23:49:09 2002
@@ -102,8 +102,8 @@
 
 #ifdef	DEBUG
 
-#if 0
-static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
 		qh, qh->hw_info1, qh->hw_info2,
@@ -117,15 +117,13 @@
 			qh->hw_buf [4]);
 	}
 }
-#endif
 
 static const char *const fls_strings [] =
     { "1024", "512", "256", "??" };
 
 #else
-#if 0
-static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
-#endif
+static inline void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
 #endif	/* DEBUG */
 
 /* functions have the "wrong" filename when they're output... */
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-hcd.c	Tue May 28 23:49:09 2002
@@ -65,6 +65,7 @@
  *
  * HISTORY:
  *
+ * 2002-05-24	Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
  * 2002-05-07	Some error path cleanups to report better errors; wmb();
@@ -82,7 +83,7 @@
  * 2001-June	Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2002-May-11"
+#define DRIVER_VERSION "2002-May-24"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -622,7 +623,10 @@
 		return 0;
 
 	case PIPE_INTERRUPT:
-		intr_deschedule (ehci, urb->start_frame, qh, urb->interval);
+		intr_deschedule (ehci, urb->start_frame, qh,
+			(urb->dev->speed == USB_SPEED_HIGH)
+			    ? urb->interval
+			    : (urb->interval << 3));
 		if (ehci->hcd.state == USB_STATE_HALT)
 			urb->status = -ESHUTDOWN;
 		qh_completions (ehci, qh, 1);
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci-sched.c	Tue May 28 23:49:09 2002
@@ -33,19 +33,6 @@
  * or with "USB On The Go" additions to USB 2.0 ...)
  */
 
-/*
- * Ceiling microseconds (typical) for that many bytes at high speed
- * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
- * to preallocate bandwidth)
- */
-#define EHCI_HOST_DELAY	5	/* nsec, guess */
-#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
-	+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
-	+ EHCI_HOST_DELAY)
-#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
-	+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
-	+ EHCI_HOST_DELAY)
-	
 static int ehci_get_frame (struct usb_hcd *hcd);
 
 /*-------------------------------------------------------------------------*/
@@ -124,6 +111,9 @@
 			/* is it in the S-mask? */
 			if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
 				usecs += q->qh->usecs;
+			/* ... or C-mask? */
+			if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+				usecs += q->qh->c_usecs;
 			q = &q->qh->qh_next;
 			break;
 		case Q_TYPE_FSTN:
@@ -273,6 +263,12 @@
 	unsigned	period,
 	unsigned	usecs
 ) {
+	/* complete split running into next frame?
+	 * given FSTN support, we could sometimes check...
+	 */
+	if (uframe >= 8)
+		return 0;
+
 	/*
 	 * 80% periodic == 100 usec/uframe available
 	 * convert "usecs we need" to "max already claimed" 
@@ -284,6 +280,8 @@
 
 // FIXME delete when intr_submit handles non-empty queues
 // this gives us a one intr/frame limit (vs N/uframe)
+// ... and also lets us avoid tracking split transactions
+// that might collide at a given TT/hub.
 		if (ehci->pshadow [frame].ptr)
 			return 0;
 
@@ -305,20 +303,54 @@
 	int			mem_flags
 ) {
 	unsigned		epnum, period;
-	unsigned short		usecs;
+	unsigned short		usecs, c_usecs, gap_uf;
 	unsigned long		flags;
 	struct ehci_qh		*qh;
 	struct hcd_dev		*dev;
+	int			is_input;
 	int			status = 0;
 
-	/* get endpoint and transfer data */
+	/* get endpoint and transfer/schedule data */
 	epnum = usb_pipeendpoint (urb->pipe);
-	if (usb_pipein (urb->pipe))
+	is_input = usb_pipein (urb->pipe);
+	if (is_input)
 		epnum |= 0x10;
-	if (urb->dev->speed != USB_SPEED_HIGH) {
-		dbg ("no intr/tt scheduling yet"); 
-		status = -ENOSYS;
-		goto done;
+
+	/*
+	 * HS interrupt transfers are simple -- only one microframe.  FS/LS
+	 * interrupt transfers involve a SPLIT in one microframe and CSPLIT
+	 * sometime later.  We need to know how much time each will be
+	 * needed in each microframe and, for FS/LS, how many microframes
+	 * separate the two in the best case.
+	 */
+	usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
+			urb->transfer_buffer_length);
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		gap_uf = 0;
+		c_usecs = 0;
+
+		/* 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;
+		}
+	} else {
+		/* gap is a function of full/low speed transfer times */
+		gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0,
+				urb->transfer_buffer_length) / (125 * 1000);
+
+		/* FIXME this just approximates SPLIT/CSPLIT times */
+		if (is_input) {		// SPLIT, gap, CSPLIT+DATA
+			c_usecs = usecs + HS_USECS (0);
+			usecs = HS_USECS (1);
+		} else {		// SPLIT+DATA, gap, CSPLIT
+			usecs = usecs + HS_USECS (1);
+			c_usecs = HS_USECS (0);
+		}
+
+		period = urb->interval;
 	}
 
 	/*
@@ -339,16 +371,6 @@
 		goto done;
 	}
 
-	usecs = HS_USECS (urb->transfer_buffer_length);
-
-	/* 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);
 
 	/* get the qh (must be empty and idle) */
@@ -392,6 +414,7 @@
 
 		qh->hw_next = EHCI_LIST_END;
 		qh->usecs = usecs;
+		qh->c_usecs = c_usecs;
 
 		urb->hcpriv = qh_get (qh);
 		status = -ENOSPC;
@@ -399,18 +422,47 @@
 		/* pick a set of schedule slots, link the QH into them */
 		do {
 			int	uframe;
+			u32	c_mask = 0;
 
 			/* 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++) {
 				if (check_period (ehci, frame, uframe,
-						period, usecs) != 0)
-					break;
+						period, usecs) == 0)
+					continue;
+
+				/* If this is a split transaction, check the
+				 * bandwidth available for the completion
+				 * too.  check both best and worst case gaps:
+				 * worst case is SPLIT near uframe end, and
+				 * CSPLIT near start ... best is vice versa.
+				 * Difference can be almost two uframe times.
+				 *
+				 * FIXME don't even bother unless we know
+				 * this TT is idle in that uframe ... right
+				 * now we know only one interrupt transfer
+				 * will be scheduled per frame, so we don't
+				 * need to update/check TT state when we
+				 * schedule a split (QH, SITD, or FSTN).
+				 *
+				 * FIXME ehci 0.96 and above can use FSTNs
+				 */
+				if (!c_usecs)
+				    	break;
+				if (check_period (ehci, frame,
+						uframe + gap_uf,
+						period, c_usecs) == 0)
+					continue;
+				if (check_period (ehci, frame,
+						uframe + gap_uf + 1,
+						period, c_usecs) == 0)
+					continue;
+
+				c_mask = 0x03 << (8 + uframe + gap_uf);
+				c_mask = cpu_to_le32 (c_mask);
+				break;
 			}
 			if (uframe == 8)
 				continue;
@@ -419,13 +471,14 @@
 			urb->start_frame = frame;
 			status = 0;
 
-			/* set S-frame mask */
-			qh->hw_info2 |= cpu_to_le32 (1 << uframe);
+			/* reset S-frame and (maybe) C-frame masks */
+			qh->hw_info2 &= ~0xffff;
+			qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
 			// dbg_qh ("Schedule INTR qh", ehci, qh);
 
 			/* stuff into the periodic schedule */
 			qh->qh_state = QH_STATE_LINKED;
-			vdbg ("qh %p usecs %d period %d starting %d.%d",
+			vdbg ("qh %p usecs %d period %d.0 starting %d.%d",
 				qh, qh->usecs, period, frame, uframe);
 			do {
 				if (unlikely (ehci->pshadow [frame].ptr != 0)) {
@@ -443,7 +496,8 @@
 			} while (frame < ehci->periodic_size);
 
 			/* update bandwidth utilization records (for usbfs) */
-			usb_claim_bandwidth (urb->dev, urb, usecs/period, 0);
+			usb_claim_bandwidth (urb->dev, urb,
+				(usecs + c_usecs) / period, 0);
 
 			/* maybe enable periodic schedule processing */
 			if (!ehci->periodic_urbs++)
@@ -557,6 +611,7 @@
 	u32		buf1;
 	unsigned	i, epnum, maxp, multi;
 	unsigned	length;
+	int		is_input;
 
 	itd->hw_next = EHCI_LIST_END;
 	itd->urb = urb;
@@ -578,7 +633,8 @@
 	 * as encoded in the ep descriptor's maxpacket field
 	 */
 	epnum = usb_pipeendpoint (urb->pipe);
-	if (usb_pipein (urb->pipe)) {
+	is_input = usb_pipein (urb->pipe);
+	if (is_input) {
 		maxp = urb->dev->epmaxpacketin [epnum];
 		buf1 = (1 << 11);
 	} else {
@@ -598,7 +654,7 @@
 			urb->iso_frame_desc [index].length);
 		return -ENOSPC;
 	}
-	itd->usecs = HS_USECS_ISO (length);
+	itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
 
 	/* "plus" info in low order bits of buffer pointers */
 	itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
@@ -919,17 +975,9 @@
 		return flags;
 
 	/*
-	 * 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...
+	 * 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.
 	 *
 	 * NOTE that for now we don't accelerate ISO unlinks; they just
 	 * happen according to the current schedule.  Means a delay of
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Tue May 28 23:49:09 2002
+++ b/drivers/usb/host/ehci.h	Tue May 28 23:49:09 2002
@@ -288,14 +288,11 @@
 
 	atomic_t		refcount;
 	unsigned short		usecs;		/* intr bandwidth */
+	unsigned short		c_usecs;	/* ... split completion bw */
 	short			qh_state;
 #define	QH_STATE_LINKED		1		/* HC sees this */
 #define	QH_STATE_UNLINK		2		/* HC may still see this */
 #define	QH_STATE_IDLE		3		/* HC doesn't see this */
-
-#ifdef EHCI_SOFT_RETRIES
-	int			retries;
-#endif
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
@@ -360,6 +357,9 @@
 	union ehci_shadow	sitd_next;	/* ptr to periodic q entry */
 	struct urb		*urb;
 	dma_addr_t		buf_dma;	/* buffer address */
+
+	unsigned short		usecs;		/* start bandwidth */
+	unsigned short		c_usecs;	/* completion bandwidth */
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
