# 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.537   -> 1.538  
#	drivers/usb/core/hub.h	1.9     -> 1.10   
#	 include/linux/usb.h	1.30    -> 1.31   
#	drivers/usb/core/hub.c	1.25    -> 1.26   
#	drivers/usb/core/usb.c	1.50    -> 1.51   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/11	david-b@pacbell.net	1.538
# [PATCH] -- hub/tt error recovery
# 
# This patch adds missing functionality to the transaction translator
# support for USB 2.0 hubs:
# 
#     - moves the 'struct usb_tt' definition to "hub.h" from <linux/usb.h>
#     - adds state to it as neeed for some control/bulk error recovery
#     - teaches the hub driver how to use that state (via keventd)
#     - adds a call letting HCDs trigger that recovery
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Sat May 11 22:29:25 2002
+++ b/drivers/usb/core/hub.c	Sat May 11 22:29:25 2002
@@ -149,6 +149,98 @@
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 }
 
+/* USB 2.0 spec Section 11.24.2.3 */
+static inline int
+hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt)
+{
+	return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0),
+		HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER,
+		devinfo, tt, 0, 0, HZ);
+}
+
+/*
+ * enumeration blocks khubd for a long time. we use keventd instead, since
+ * long blocking there is the exception, not the rule.  accordingly, HCDs
+ * talking to TTs must queue control transfers (not just bulk and iso), so
+ * both can talk to the same hub concurrently.
+ */
+static void hub_tt_kevent (void *arg)
+{
+	struct usb_hub		*hub = arg;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&hub->tt.lock, flags);
+	while (!list_empty (&hub->tt.clear_list)) {
+		struct list_head	*temp;
+		struct usb_tt_clear	*clear;
+		int			status;
+
+		temp = hub->tt.clear_list.next;
+		clear = list_entry (temp, struct usb_tt_clear, clear_list);
+		list_del (&clear->clear_list);
+
+		/* drop lock so HCD can concurrently report other TT errors */
+		spin_unlock_irqrestore (&hub->tt.lock, flags);
+		status = hub_clear_tt_buffer (hub->dev,
+				clear->devinfo, clear->tt);
+		spin_lock_irqsave (&hub->tt.lock, flags);
+
+		if (status)
+			err ("usb-%s-%s clear tt %d (%04x) error %d",
+				hub->dev->bus->bus_name, hub->dev->devpath,
+				clear->tt, clear->devinfo, status);
+		kfree (clear);
+	}
+	spin_unlock_irqrestore (&hub->tt.lock, flags);
+}
+
+/**
+ * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
+ * @dev: the device whose split transaction failed
+ * @pipe: identifies the endpoint of the failed transaction
+ *
+ * High speed HCDs use this to tell the hub driver that some split control or
+ * bulk transaction failed in a way that requires clearing internal state of
+ * a transaction translator.  This is normally detected (and reported) from
+ * interrupt context.
+ *
+ * It may not be possible for that hub to handle additional full (or low)
+ * speed transactions until that state is fully cleared out.
+ */
+void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)
+{
+	struct usb_tt		*tt = dev->tt;
+	unsigned long		flags;
+	struct usb_tt_clear	*clear;
+
+	/* we've got to cope with an arbitrary number of pending TT clears,
+	 * since each TT has "at least two" buffers that can need it (and
+	 * there can be many TTs per hub).  even if they're uncommon.
+	 */
+	if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {
+		err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s",
+			dev->bus->bus_name, tt->hub->devpath);
+		/* FIXME recover somehow ... RESET_TT? */
+		return;
+	}
+
+	/* info that CLEAR_TT_BUFFER needs */
+	clear->tt = tt->multi ? dev->ttport : 1;
+	clear->devinfo = usb_pipeendpoint (pipe);
+	clear->devinfo |= dev->devnum << 4;
+	clear->devinfo |= usb_pipecontrol (pipe)
+			? (USB_ENDPOINT_XFER_CONTROL << 11)
+			: (USB_ENDPOINT_XFER_BULK << 11);
+	if (usb_pipein (pipe))
+		clear->devinfo |= 1 << 15;
+	
+	/* tell keventd to clear state for this TT */
+	spin_lock_irqsave (&tt->lock, flags);
+	list_add_tail (&clear->clear_list, &tt->clear_list);
+	schedule_task (&tt->kevent);
+	spin_unlock_irqrestore (&tt->lock, flags);
+}
+
 static void usb_hub_power_on(struct usb_hub *hub)
 {
 	int i;
@@ -231,6 +323,9 @@
                         break;
 	}
 
+	spin_lock_init (&hub->tt.lock);
+	INIT_LIST_HEAD (&hub->tt.clear_list);
+	INIT_TQUEUE (&hub->tt.kevent, hub_tt_kevent, hub);
 	switch (dev->descriptor.bDeviceProtocol) {
 		case 0:
 			break;
@@ -431,6 +526,10 @@
 
 	down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */
 	up(&hub->khubd_sem);
+
+	/* assuming we used keventd, it must quiesce too */
+	if (hub->tt.hub)
+		flush_scheduled_tasks ();
 
 	if (hub->urb) {
 		usb_unlink_urb(hub->urb);
diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
--- a/drivers/usb/core/hub.h	Sat May 11 22:29:25 2002
+++ b/drivers/usb/core/hub.h	Sat May 11 22:29:25 2002
@@ -136,6 +136,34 @@
 
 struct usb_device;
 
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now).  They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+	struct usb_device	*hub;	/* upstream highspeed hub */
+	int			multi;	/* true means one TT per port */
+
+	/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
+	spinlock_t		lock;
+	struct list_head	clear_list;	/* of usb_tt_clear */
+	struct tq_struct	kevent;
+};
+
+struct usb_tt_clear {
+	struct list_head	clear_list;
+	unsigned		tt;
+	u16			devinfo;
+};
+
+extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
+
 struct usb_hub {
 	struct usb_device	*dev;		/* the "real" device */
 	struct urb		*urb;		/* for interrupt polling pipe */
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Sat May 11 22:29:25 2002
+++ b/drivers/usb/core/usb.c	Sat May 11 22:29:25 2002
@@ -2743,9 +2743,8 @@
 
 /*
  * USB may be built into the kernel or be built as modules.
- * If the USB core [and maybe a host controller driver] is built
- * into the kernel, and other device drivers are built as modules,
- * then these symbols need to be exported for the modules to use.
+ * These symbols are exported for device (or host controller)
+ * driver modules to use.
  */
 EXPORT_SYMBOL(usb_ifnum_to_ifpos);
 EXPORT_SYMBOL(usb_ifnum_to_if);
@@ -2762,6 +2761,7 @@
 
 EXPORT_SYMBOL(usb_alloc_dev);
 EXPORT_SYMBOL(usb_free_dev);
+EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
 EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum);
 EXPORT_SYMBOL(usb_driver_claim_interface);
@@ -2799,6 +2799,5 @@
 EXPORT_SYMBOL(usb_set_configuration);
 EXPORT_SYMBOL(usb_set_interface);
 
-EXPORT_SYMBOL(usb_make_path);
 EXPORT_SYMBOL(usb_devfs_handle);
 MODULE_LICENSE("GPL");
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Sat May 11 22:29:25 2002
+++ b/include/linux/usb.h	Sat May 11 22:29:25 2002
@@ -363,22 +363,6 @@
 extern int usb_root_hub_string(int id, int serial,
 		char *type, __u8 *data, int len);
 
-/*
- * As of USB 2.0, full/low speed devices are segregated into trees.
- * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
- * The other type grows from high speed hubs when they connect to
- * full/low speed devices using "Transaction Translators" (TTs).
- *
- * TTs should only be known to the hub driver, and high speed bus
- * drivers (only EHCI for now).  They affect periodic scheduling and
- * sometimes control/bulk error recovery.
- */
-struct usb_tt {
-	struct usb_device	*hub;	/* upstream highspeed hub */
-	int			multi;	/* true means one TT per port */
-};
-
-
 /* -------------------------------------------------------------------------- */
 
 /* This is arbitrary.
@@ -387,6 +371,8 @@
  */
 #define USB_MAXCHILDREN		(16)
 
+struct usb_tt;
+
 struct usb_device {
 	int		devnum;		/* Address on USB bus */
 	char		devpath [16];	/* Use in messages: /port/port/... */
@@ -1176,6 +1162,7 @@
  * appropriately.
  */
 
+/* NOTE:  these are not the standard USB_ENDPOINT_XFER_* values!! */
 #define PIPE_ISOCHRONOUS		0
 #define PIPE_INTERRUPT			1
 #define PIPE_CONTROL			2