# 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.540   -> 1.541  
#	drivers/usb/host/ehci-q.c	1.11    -> 1.12   
#	drivers/usb/host/ehci-sched.c	1.9     -> 1.10   
#	drivers/usb/host/ehci-hcd.c	1.13    -> 1.14   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/04/22	david-b@pacbell.net	1.541
# [PATCH] PATCH: 2.5.8 ehci, submit errors
# 
# It fixes problems with interrupt transfers, which I think that
# nobody else has run into (or I'd surely have heard of it :).
# Looks like not many folk are using USB 2.0 hubs yet.
# 
#     - wasn't checking enough of the periodic schedule to
#       detect bandwidth overcommit (would BUG out)
#     - frames to uframes is rightshift 3, not 8 :)
#     - properly cleans up (no oops!) after certain rare errors
#       in the interrupt submit path (just my luck to hit one)
#     - use that cleanup to bypass some old implementation
#       shortcuts in the control and bulk submit paths
#     - there are also some other minor updates/cleanups
# --------------------------------------------
#
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Mon Apr 22 14:28:12 2002
+++ b/drivers/usb/host/ehci-hcd.c	Mon Apr 22 14:28:12 2002
@@ -70,6 +70,8 @@
  *
  * 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).
@@ -81,7 +83,7 @@
  * 2001-June	Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "$Revision: 0.27 $"
+#define DRIVER_VERSION "$Revision: 0.31 $"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -512,8 +514,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))
@@ -531,8 +532,9 @@
 		return -ENOSYS;
 #endif /* have_split_iso */
 
+	default:	/* can't happen */
+		return -ENOSYS;
 	}
-	return 0;
 }
 
 /* remove from hardware lists
@@ -587,7 +589,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 +616,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)) {
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	Mon Apr 22 14:28:12 2002
+++ b/drivers/usb/host/ehci-q.c	Mon Apr 22 14:28:12 2002
@@ -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,47 @@
 /*-------------------------------------------------------------------------*/
 
 /*
+ * 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;
+
+			/* for ctrl unmap twice: SETUP and DATA;
+			 * else (bulk, intr) just once: DATA
+			 */
+			if (!unmapped++ && usb_pipecontrol (urb->pipe))
+				direction = PCI_DMA_TODEVICE;
+			else {
+				direction = usb_pipein (urb->pipe)
+					? PCI_DMA_FROMDEVICE
+					: PCI_DMA_TODEVICE;
+				unmapped++;
+			}
+			if (qtd->buf_dma)
+				pci_unmap_single (ehci->hcd.pdev,
+					qtd->buf_dma,
+					qtd->urb->transfer_buffer_length,
+					direction);
+		}
+		ehci_qtd_free (ehci, qtd);
+	}
+}
+
+/*
  * create a list of filled qtds for this URB; won't link into qh.
  */
 static struct list_head *
@@ -547,8 +590,7 @@
 	return head;
 
 cleanup:
-	urb->status = -ENOMEM;
-	qh_completions (ehci, head, 1);
+	qtd_list_free (ehci, urb, head);
 	return 0;
 }
 
@@ -713,7 +755,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void
+static int
 submit_async (
 	struct ehci_hcd		*ehci,
 	struct urb		*urb,
@@ -807,8 +849,7 @@
 		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,
@@ -820,8 +861,11 @@
 			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;
 }
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Mon Apr 22 14:28:12 2002
+++ b/drivers/usb/host/ehci-sched.c	Mon Apr 22 14:28:12 2002
@@ -220,6 +220,8 @@
 ) {
 	unsigned long	flags;
 
+	period >>= 3;		// FIXME microframe periods not handled yet
+
 	spin_lock_irqsave (&ehci->lock, flags);
 
 	do {
@@ -256,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,
@@ -263,7 +297,6 @@
 	int			mem_flags
 ) {
 	unsigned		epnum, period;
-	unsigned		temp;
 	unsigned short		usecs;
 	unsigned long		flags;
 	struct ehci_qh		*qh;
@@ -272,11 +305,8 @@
 
 	/* 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];
 	if (urb->dev->speed != USB_SPEED_HIGH) {
 		dbg ("no intr/tt scheduling yet"); 
 		status = -ENOSYS;
@@ -287,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"); 
@@ -297,11 +334,13 @@
 	usecs = HS_USECS (urb->transfer_buffer_length);
 
 	/* FIXME handle HS periods of less than 1 frame. */
-	if (urb->interval < 8)
-		period = 1;
-	else
-		period = urb->interval >> 8;
-		
+	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) */
@@ -326,21 +365,22 @@
 			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->usecs = usecs;
@@ -352,28 +392,20 @@
 		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;
@@ -389,8 +421,9 @@
 				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_get (qh);
@@ -401,7 +434,7 @@
 			} 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++)
@@ -412,14 +445,9 @@
 	}
 	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;
 }
 
@@ -438,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;
