Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
new file mode 100644
index 0000000..1a9ff61
--- /dev/null
+++ b/drivers/usb/core/Kconfig
@@ -0,0 +1,99 @@
+#
+# USB Core configuration
+#
+config USB_DEBUG
+	bool "USB verbose debug messages"
+	depends on USB
+	help
+	  Say Y here if you want the USB core & hub drivers to produce a bunch
+	  of debug messages to the system log. Select this if you are having a
+	  problem with USB support and want to see more of what is going on.
+
+comment "Miscellaneous USB options"
+	depends on USB
+
+config USB_DEVICEFS
+	bool "USB device filesystem"
+	depends on USB
+	---help---
+	  If you say Y here (and to "/proc file system support" in the "File
+	  systems" section, above), you will get a file /proc/bus/usb/devices
+	  which lists the devices currently connected to your USB bus or
+	  busses, and for every connected device a file named
+	  "/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the
+	  device number; the latter files can be used by user space programs
+	  to talk directly to the device. These files are "virtual", meaning
+	  they are generated on the fly and not stored on the hard drive.
+
+	  You may need to mount the usbfs file system to see the files, use
+	  mount -t usbfs none /proc/bus/usb
+
+	  For the format of the various /proc/bus/usb/ files, please read
+	  <file:Documentation/usb/proc_usb_info.txt>.
+
+	  Please note that this code is completely unrelated to devfs, the
+	  "/dev file system support".
+
+	  Most users want to say Y here.
+
+config USB_BANDWIDTH
+	bool "Enforce USB bandwidth allocation (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	help
+	  If you say Y here, the USB subsystem enforces USB bandwidth
+	  allocation and will prevent some device opens from succeeding
+	  if they would cause USB bandwidth usage to go above 90% of
+	  the bus bandwidth.
+
+	  If you say N here, these conditions will cause warning messages
+	  about USB bandwidth usage to be logged and some devices or
+	  drivers may not work correctly.
+
+config USB_DYNAMIC_MINORS
+	bool "Dynamic USB minor allocation (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	help
+	  If you say Y here, the USB subsystem will use dynamic minor
+	  allocation for any device that uses the USB major number.
+	  This means that you can have more than 16 of a single type
+	  of device (like USB printers).
+
+	  If you are unsure about this, say N here.
+
+config USB_SUSPEND
+	bool "USB suspend/resume (EXPERIMENTAL)"
+	depends on USB && PM && EXPERIMENTAL
+	help
+	  If you say Y here, you can use driver calls or the sysfs
+	  "power/state" file to suspend or resume individual USB
+	  peripherals.  There are many related features, such as
+	  remote wakeup and driver-specific suspend processing, that
+	  may not yet work as expected.
+
+	  If you are unsure about this, say N here.
+
+
+config USB_OTG
+	bool
+	depends on USB && EXPERIMENTAL
+	select USB_SUSPEND
+	default n
+
+
+config USB_OTG_WHITELIST
+	bool "Rely on OTG Targeted Peripherals List"
+	depends on USB_OTG
+	default y
+	help
+	  If you say Y here, the "otg_whitelist.h" file will be used as a
+	  product whitelist, so USB peripherals not listed there will be
+	  rejected during enumeration.  This behavior is required by the
+	  USB OTG specification for all devices not on your product's
+	  "Targeted Peripherals List".
+
+	  Otherwise, peripherals not listed there will only generate a
+	  warning and enumeration will continue.  That's more like what
+	  normal Linux-USB hosts do (other than the warning), and is
+	  convenient for many stages of product development.
+
+
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
new file mode 100644
index 0000000..9e8c377
--- /dev/null
+++ b/drivers/usb/core/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for USB Core files and filesystem
+#
+
+usbcore-objs	:= usb.o hub.o hcd.o urb.o message.o \
+			config.o file.o buffer.o sysfs.o
+
+ifeq ($(CONFIG_PCI),y)
+	usbcore-objs	+= hcd-pci.o
+endif
+
+ifeq ($(CONFIG_USB_DEVICEFS),y)
+	usbcore-objs	+= devio.o inode.o devices.o
+endif
+
+obj-$(CONFIG_USB)	+= usbcore.o
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
new file mode 100644
index 0000000..b7827df
--- /dev/null
+++ b/drivers/usb/core/buffer.c
@@ -0,0 +1,154 @@
+/*
+ * DMA memory management for framework level HCD code (hc_driver)
+ *
+ * This implementation plugs in through generic "usb_bus" level methods,
+ * and should work with all USB controllers, regardles of bus type.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "hcd.h"
+
+
+/*
+ * DMA-Coherent Buffers
+ */
+
+/* FIXME tune these based on pool statistics ... */
+static const size_t	pool_max [HCD_BUFFER_POOLS] = {
+	/* platforms without dma-friendly caches might need to
+	 * prevent cacheline sharing...
+	 */
+	32,
+	128,
+	512,
+	PAGE_SIZE / 2
+	/* bigger --> allocate pages */
+};
+
+
+/* SETUP primitives */
+
+/**
+ * hcd_buffer_create - initialize buffer pools
+ * @hcd: the bus whose buffer pools are to be initialized
+ * Context: !in_interrupt()
+ *
+ * Call this as part of initializing a host controller that uses the dma
+ * memory allocators.  It initializes some pools of dma-coherent memory that
+ * will be shared by all drivers using that controller, or returns a negative
+ * errno value on error.
+ *
+ * Call hcd_buffer_destroy() to clean up after using those pools.
+ */
+int hcd_buffer_create (struct usb_hcd *hcd)
+{
+	char		name [16];
+	int 		i, size;
+
+	for (i = 0; i < HCD_BUFFER_POOLS; i++) { 
+		if (!(size = pool_max [i]))
+			continue;
+		snprintf (name, sizeof name, "buffer-%d", size);
+		hcd->pool [i] = dma_pool_create (name, hcd->self.controller,
+				size, size, 0);
+		if (!hcd->pool [i]) {
+			hcd_buffer_destroy (hcd);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * hcd_buffer_destroy - deallocate buffer pools
+ * @hcd: the bus whose buffer pools are to be destroyed
+ * Context: !in_interrupt()
+ *
+ * This frees the buffer pools created by hcd_buffer_create().
+ */
+void hcd_buffer_destroy (struct usb_hcd *hcd)
+{
+	int		i;
+
+	for (i = 0; i < HCD_BUFFER_POOLS; i++) { 
+		struct dma_pool		*pool = hcd->pool [i];
+		if (pool) {
+			dma_pool_destroy (pool);
+			hcd->pool[i] = NULL;
+		}
+	}
+}
+
+
+/* sometimes alloc/free could use kmalloc with SLAB_DMA, for
+ * better sharing and to leverage mm/slab.c intelligence.
+ */
+
+void *hcd_buffer_alloc (
+	struct usb_bus 		*bus,
+	size_t			size,
+	int			mem_flags,
+	dma_addr_t		*dma
+)
+{
+	struct usb_hcd		*hcd = bus->hcpriv;
+	int 			i;
+
+	/* some USB hosts just use PIO */
+	if (!bus->controller->dma_mask) {
+		*dma = ~(dma_addr_t) 0;
+		return kmalloc (size, mem_flags);
+	}
+
+	for (i = 0; i < HCD_BUFFER_POOLS; i++) {
+		if (size <= pool_max [i])
+			return dma_pool_alloc (hcd->pool [i], mem_flags, dma);
+	}
+	return dma_alloc_coherent (hcd->self.controller, size, dma, 0);
+}
+
+void hcd_buffer_free (
+	struct usb_bus 		*bus,
+	size_t			size,
+	void 			*addr,
+	dma_addr_t		dma
+)
+{
+	struct usb_hcd		*hcd = bus->hcpriv;
+	int 			i;
+
+	if (!addr)
+		return;
+
+	if (!bus->controller->dma_mask) {
+		kfree (addr);
+		return;
+	}
+
+	for (i = 0; i < HCD_BUFFER_POOLS; i++) {
+		if (size <= pool_max [i]) {
+			dma_pool_free (hcd->pool [i], addr, dma);
+			return;
+		}
+	}
+	dma_free_coherent (hcd->self.controller, size, addr, dma);
+}
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
new file mode 100644
index 0000000..0b092bd
--- /dev/null
+++ b/drivers/usb/core/config.c
@@ -0,0 +1,534 @@
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <asm/byteorder.h>
+
+
+#define USB_MAXALTSETTING		128	/* Hard limit */
+#define USB_MAXENDPOINTS		30	/* Hard limit */
+
+#define USB_MAXCONFIG			8	/* Arbitrary limit */
+
+
+static inline const char *plural(int n)
+{
+	return (n == 1 ? "" : "s");
+}
+
+static int find_next_descriptor(unsigned char *buffer, int size,
+    int dt1, int dt2, int *num_skipped)
+{
+	struct usb_descriptor_header *h;
+	int n = 0;
+	unsigned char *buffer0 = buffer;
+
+	/* Find the next descriptor of type dt1 or dt2 */
+	while (size > 0) {
+		h = (struct usb_descriptor_header *) buffer;
+		if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
+			break;
+		buffer += h->bLength;
+		size -= h->bLength;
+		++n;
+	}
+
+	/* Store the number of descriptors skipped and return the
+	 * number of bytes skipped */
+	if (num_skipped)
+		*num_skipped = n;
+	return buffer - buffer0;
+}
+
+static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
+    int asnum, struct usb_host_interface *ifp, int num_ep,
+    unsigned char *buffer, int size)
+{
+	unsigned char *buffer0 = buffer;
+	struct usb_endpoint_descriptor *d;
+	struct usb_host_endpoint *endpoint;
+	int n, i;
+
+	d = (struct usb_endpoint_descriptor *) buffer;
+	buffer += d->bLength;
+	size -= d->bLength;
+
+	if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
+		n = USB_DT_ENDPOINT_AUDIO_SIZE;
+	else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
+		n = USB_DT_ENDPOINT_SIZE;
+	else {
+		dev_warn(ddev, "config %d interface %d altsetting %d has an "
+		    "invalid endpoint descriptor of length %d, skipping\n",
+		    cfgno, inum, asnum, d->bLength);
+		goto skip_to_next_endpoint_or_interface_descriptor;
+	}
+
+	i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
+	if (i >= 16 || i == 0) {
+		dev_warn(ddev, "config %d interface %d altsetting %d has an "
+		    "invalid endpoint with address 0x%X, skipping\n",
+		    cfgno, inum, asnum, d->bEndpointAddress);
+		goto skip_to_next_endpoint_or_interface_descriptor;
+	}
+
+	/* Only store as many endpoints as we have room for */
+	if (ifp->desc.bNumEndpoints >= num_ep)
+		goto skip_to_next_endpoint_or_interface_descriptor;
+
+	endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
+	++ifp->desc.bNumEndpoints;
+
+	memcpy(&endpoint->desc, d, n);
+	INIT_LIST_HEAD(&endpoint->urb_list);
+
+	/* Skip over any Class Specific or Vendor Specific descriptors;
+	 * find the next endpoint or interface descriptor */
+	endpoint->extra = buffer;
+	i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+	    USB_DT_INTERFACE, &n);
+	endpoint->extralen = i;
+	if (n > 0)
+		dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+		    n, plural(n), "endpoint");
+	return buffer - buffer0 + i;
+
+skip_to_next_endpoint_or_interface_descriptor:
+	i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+	    USB_DT_INTERFACE, NULL);
+	return buffer - buffer0 + i;
+}
+
+void usb_release_interface_cache(struct kref *ref)
+{
+	struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
+	int j;
+
+	for (j = 0; j < intfc->num_altsetting; j++)
+		kfree(intfc->altsetting[j].endpoint);
+	kfree(intfc);
+}
+
+static int usb_parse_interface(struct device *ddev, int cfgno,
+    struct usb_host_config *config, unsigned char *buffer, int size,
+    u8 inums[], u8 nalts[])
+{
+	unsigned char *buffer0 = buffer;
+	struct usb_interface_descriptor	*d;
+	int inum, asnum;
+	struct usb_interface_cache *intfc;
+	struct usb_host_interface *alt;
+	int i, n;
+	int len, retval;
+	int num_ep, num_ep_orig;
+
+	d = (struct usb_interface_descriptor *) buffer;
+	buffer += d->bLength;
+	size -= d->bLength;
+
+	if (d->bLength < USB_DT_INTERFACE_SIZE)
+		goto skip_to_next_interface_descriptor;
+
+	/* Which interface entry is this? */
+	intfc = NULL;
+	inum = d->bInterfaceNumber;
+	for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+		if (inums[i] == inum) {
+			intfc = config->intf_cache[i];
+			break;
+		}
+	}
+	if (!intfc || intfc->num_altsetting >= nalts[i])
+		goto skip_to_next_interface_descriptor;
+
+	/* Check for duplicate altsetting entries */
+	asnum = d->bAlternateSetting;
+	for ((i = 0, alt = &intfc->altsetting[0]);
+	      i < intfc->num_altsetting;
+	     (++i, ++alt)) {
+		if (alt->desc.bAlternateSetting == asnum) {
+			dev_warn(ddev, "Duplicate descriptor for config %d "
+			    "interface %d altsetting %d, skipping\n",
+			    cfgno, inum, asnum);
+			goto skip_to_next_interface_descriptor;
+		}
+	}
+
+	++intfc->num_altsetting;
+	memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
+
+	/* Skip over any Class Specific or Vendor Specific descriptors;
+	 * find the first endpoint or interface descriptor */
+	alt->extra = buffer;
+	i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+	    USB_DT_INTERFACE, &n);
+	alt->extralen = i;
+	if (n > 0)
+		dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+		    n, plural(n), "interface");
+	buffer += i;
+	size -= i;
+
+	/* Allocate space for the right(?) number of endpoints */
+	num_ep = num_ep_orig = alt->desc.bNumEndpoints;
+	alt->desc.bNumEndpoints = 0;		// Use as a counter
+	if (num_ep > USB_MAXENDPOINTS) {
+		dev_warn(ddev, "too many endpoints for config %d interface %d "
+		    "altsetting %d: %d, using maximum allowed: %d\n",
+		    cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);
+		num_ep = USB_MAXENDPOINTS;
+	}
+
+	len = sizeof(struct usb_host_endpoint) * num_ep;
+	alt->endpoint = kmalloc(len, GFP_KERNEL);
+	if (!alt->endpoint)
+		return -ENOMEM;
+	memset(alt->endpoint, 0, len);
+
+	/* Parse all the endpoint descriptors */
+	n = 0;
+	while (size > 0) {
+		if (((struct usb_descriptor_header *) buffer)->bDescriptorType
+		     == USB_DT_INTERFACE)
+			break;
+		retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,
+		    num_ep, buffer, size);
+		if (retval < 0)
+			return retval;
+		++n;
+
+		buffer += retval;
+		size -= retval;
+	}
+
+	if (n != num_ep_orig)
+		dev_warn(ddev, "config %d interface %d altsetting %d has %d "
+		    "endpoint descriptor%s, different from the interface "
+		    "descriptor's value: %d\n",
+		    cfgno, inum, asnum, n, plural(n), num_ep_orig);
+	return buffer - buffer0;
+
+skip_to_next_interface_descriptor:
+	i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
+	    USB_DT_INTERFACE, NULL);
+	return buffer - buffer0 + i;
+}
+
+static int usb_parse_configuration(struct device *ddev, int cfgidx,
+    struct usb_host_config *config, unsigned char *buffer, int size)
+{
+	unsigned char *buffer0 = buffer;
+	int cfgno;
+	int nintf, nintf_orig;
+	int i, j, n;
+	struct usb_interface_cache *intfc;
+	unsigned char *buffer2;
+	int size2;
+	struct usb_descriptor_header *header;
+	int len, retval;
+	u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
+
+	memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
+	if (config->desc.bDescriptorType != USB_DT_CONFIG ||
+	    config->desc.bLength < USB_DT_CONFIG_SIZE) {
+		dev_err(ddev, "invalid descriptor for config index %d: "
+		    "type = 0x%X, length = %d\n", cfgidx,
+		    config->desc.bDescriptorType, config->desc.bLength);
+		return -EINVAL;
+	}
+	cfgno = config->desc.bConfigurationValue;
+
+	buffer += config->desc.bLength;
+	size -= config->desc.bLength;
+
+	nintf = nintf_orig = config->desc.bNumInterfaces;
+	if (nintf > USB_MAXINTERFACES) {
+		dev_warn(ddev, "config %d has too many interfaces: %d, "
+		    "using maximum allowed: %d\n",
+		    cfgno, nintf, USB_MAXINTERFACES);
+		nintf = USB_MAXINTERFACES;
+	}
+
+	/* Go through the descriptors, checking their length and counting the
+	 * number of altsettings for each interface */
+	n = 0;
+	for ((buffer2 = buffer, size2 = size);
+	      size2 > 0;
+	     (buffer2 += header->bLength, size2 -= header->bLength)) {
+
+		if (size2 < sizeof(struct usb_descriptor_header)) {
+			dev_warn(ddev, "config %d descriptor has %d excess "
+			    "byte%s, ignoring\n",
+			    cfgno, size2, plural(size2));
+			break;
+		}
+
+		header = (struct usb_descriptor_header *) buffer2;
+		if ((header->bLength > size2) || (header->bLength < 2)) {
+			dev_warn(ddev, "config %d has an invalid descriptor "
+			    "of length %d, skipping remainder of the config\n",
+			    cfgno, header->bLength);
+			break;
+		}
+
+		if (header->bDescriptorType == USB_DT_INTERFACE) {
+			struct usb_interface_descriptor *d;
+			int inum;
+
+			d = (struct usb_interface_descriptor *) header;
+			if (d->bLength < USB_DT_INTERFACE_SIZE) {
+				dev_warn(ddev, "config %d has an invalid "
+				    "interface descriptor of length %d, "
+				    "skipping\n", cfgno, d->bLength);
+				continue;
+			}
+
+			inum = d->bInterfaceNumber;
+			if (inum >= nintf_orig)
+				dev_warn(ddev, "config %d has an invalid "
+				    "interface number: %d but max is %d\n",
+				    cfgno, inum, nintf_orig - 1);
+
+			/* Have we already encountered this interface?
+			 * Count its altsettings */
+			for (i = 0; i < n; ++i) {
+				if (inums[i] == inum)
+					break;
+			}
+			if (i < n) {
+				if (nalts[i] < 255)
+					++nalts[i];
+			} else if (n < USB_MAXINTERFACES) {
+				inums[n] = inum;
+				nalts[n] = 1;
+				++n;
+			}
+
+		} else if (header->bDescriptorType == USB_DT_DEVICE ||
+			    header->bDescriptorType == USB_DT_CONFIG)
+			dev_warn(ddev, "config %d contains an unexpected "
+			    "descriptor of type 0x%X, skipping\n",
+			    cfgno, header->bDescriptorType);
+
+	}	/* for ((buffer2 = buffer, size2 = size); ...) */
+	size = buffer2 - buffer;
+	config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
+
+	if (n != nintf)
+		dev_warn(ddev, "config %d has %d interface%s, different from "
+		    "the descriptor's value: %d\n",
+		    cfgno, n, plural(n), nintf_orig);
+	else if (n == 0)
+		dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
+	config->desc.bNumInterfaces = nintf = n;
+
+	/* Check for missing interface numbers */
+	for (i = 0; i < nintf; ++i) {
+		for (j = 0; j < nintf; ++j) {
+			if (inums[j] == i)
+				break;
+		}
+		if (j >= nintf)
+			dev_warn(ddev, "config %d has no interface number "
+			    "%d\n", cfgno, i);
+	}
+
+	/* Allocate the usb_interface_caches and altsetting arrays */
+	for (i = 0; i < nintf; ++i) {
+		j = nalts[i];
+		if (j > USB_MAXALTSETTING) {
+			dev_warn(ddev, "too many alternate settings for "
+			    "config %d interface %d: %d, "
+			    "using maximum allowed: %d\n",
+			    cfgno, inums[i], j, USB_MAXALTSETTING);
+			nalts[i] = j = USB_MAXALTSETTING;
+		}
+
+		len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
+		config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL);
+		if (!intfc)
+			return -ENOMEM;
+		memset(intfc, 0, len);
+		kref_init(&intfc->ref);
+	}
+
+	/* Skip over any Class Specific or Vendor Specific descriptors;
+	 * find the first interface descriptor */
+	config->extra = buffer;
+	i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
+	    USB_DT_INTERFACE, &n);
+	config->extralen = i;
+	if (n > 0)
+		dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+		    n, plural(n), "configuration");
+	buffer += i;
+	size -= i;
+
+	/* Parse all the interface/altsetting descriptors */
+	while (size > 0) {
+		retval = usb_parse_interface(ddev, cfgno, config,
+		    buffer, size, inums, nalts);
+		if (retval < 0)
+			return retval;
+
+		buffer += retval;
+		size -= retval;
+	}
+
+	/* Check for missing altsettings */
+	for (i = 0; i < nintf; ++i) {
+		intfc = config->intf_cache[i];
+		for (j = 0; j < intfc->num_altsetting; ++j) {
+			for (n = 0; n < intfc->num_altsetting; ++n) {
+				if (intfc->altsetting[n].desc.
+				    bAlternateSetting == j)
+					break;
+			}
+			if (n >= intfc->num_altsetting)
+				dev_warn(ddev, "config %d interface %d has no "
+				    "altsetting %d\n", cfgno, inums[i], j);
+		}
+	}
+
+	return 0;
+}
+
+// hub-only!! ... and only exported for reset/reinit path.
+// otherwise used internally on disconnect/destroy path
+void usb_destroy_configuration(struct usb_device *dev)
+{
+	int c, i;
+
+	if (!dev->config)
+		return;
+
+	if (dev->rawdescriptors) {
+		for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+			kfree(dev->rawdescriptors[i]);
+
+		kfree(dev->rawdescriptors);
+		dev->rawdescriptors = NULL;
+	}
+
+	for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+		struct usb_host_config *cf = &dev->config[c];
+
+		kfree(cf->string);
+		cf->string = NULL;
+
+		for (i = 0; i < cf->desc.bNumInterfaces; i++) {
+			if (cf->intf_cache[i])
+				kref_put(&cf->intf_cache[i]->ref, 
+					  usb_release_interface_cache);
+		}
+	}
+	kfree(dev->config);
+	dev->config = NULL;
+}
+
+
+// hub-only!! ... and only in reset path, or usb_new_device()
+// (used by real hubs and virtual root hubs)
+int usb_get_configuration(struct usb_device *dev)
+{
+	struct device *ddev = &dev->dev;
+	int ncfg = dev->descriptor.bNumConfigurations;
+	int result = -ENOMEM;
+	unsigned int cfgno, length;
+	unsigned char *buffer;
+	unsigned char *bigbuffer;
+ 	struct usb_config_descriptor *desc;
+
+	if (ncfg > USB_MAXCONFIG) {
+		dev_warn(ddev, "too many configurations: %d, "
+		    "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
+		dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
+	}
+
+	if (ncfg < 1) {
+		dev_err(ddev, "no configurations\n");
+		return -EINVAL;
+	}
+
+	length = ncfg * sizeof(struct usb_host_config);
+	dev->config = kmalloc(length, GFP_KERNEL);
+	if (!dev->config)
+		goto err2;
+	memset(dev->config, 0, length);
+
+	length = ncfg * sizeof(char *);
+	dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
+	if (!dev->rawdescriptors)
+		goto err2;
+	memset(dev->rawdescriptors, 0, length);
+
+	buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
+	if (!buffer)
+		goto err2;
+	desc = (struct usb_config_descriptor *)buffer;
+
+	for (cfgno = 0; cfgno < ncfg; cfgno++) {
+		/* We grab just the first descriptor so we know how long
+		 * the whole configuration is */
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
+		    buffer, USB_DT_CONFIG_SIZE);
+		if (result < 0) {
+			dev_err(ddev, "unable to read config index %d "
+			    "descriptor/%s\n", cfgno, "start");
+			goto err;
+		} else if (result < 4) {
+			dev_err(ddev, "config index %d descriptor too short "
+			    "(expected %i, got %i)\n", cfgno,
+			    USB_DT_CONFIG_SIZE, result);
+			result = -EINVAL;
+			goto err;
+		}
+		length = max((int) le16_to_cpu(desc->wTotalLength),
+		    USB_DT_CONFIG_SIZE);
+
+		/* Now that we know the length, get the whole thing */
+		bigbuffer = kmalloc(length, GFP_KERNEL);
+		if (!bigbuffer) {
+			result = -ENOMEM;
+			goto err;
+		}
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
+		    bigbuffer, length);
+		if (result < 0) {
+			dev_err(ddev, "unable to read config index %d "
+			    "descriptor/%s\n", cfgno, "all");
+			kfree(bigbuffer);
+			goto err;
+		}
+		if (result < length) {
+			dev_warn(ddev, "config index %d descriptor too short "
+			    "(expected %i, got %i)\n", cfgno, length, result);
+			length = result;
+		}
+
+		dev->rawdescriptors[cfgno] = bigbuffer;
+
+		result = usb_parse_configuration(&dev->dev, cfgno,
+		    &dev->config[cfgno], bigbuffer, length);
+		if (result < 0) {
+			++cfgno;
+			goto err;
+		}
+	}
+	result = 0;
+
+err:
+	kfree(buffer);
+	dev->descriptor.bNumConfigurations = cfgno;
+err2:
+	if (result == -ENOMEM)
+		dev_err(ddev, "out of memory\n");
+	return result;
+}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
new file mode 100644
index 0000000..8b61bcd
--- /dev/null
+++ b/drivers/usb/core/devices.c
@@ -0,0 +1,677 @@
+/*
+ * devices.c
+ * (C) Copyright 1999 Randy Dunlap.
+ * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device)
+ * (C) Copyright 1999 Deti Fliegl (new USB architecture)
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *************************************************************
+ *
+ * <mountpoint>/devices contains USB topology, device, config, class,
+ * interface, & endpoint data.
+ *
+ * I considered using /proc/bus/usb/devices/device# for each device
+ * as it is attached or detached, but I didn't like this for some
+ * reason -- maybe it's just too deep of a directory structure.
+ * I also don't like looking in multiple places to gather and view
+ * the data.  Having only one file for ./devices also prevents race
+ * conditions that could arise if a program was reading device info
+ * for devices that are being removed (unplugged).  (That is, the
+ * program may find a directory for devnum_12 then try to open it,
+ * but it was just unplugged, so the directory is now deleted.
+ * But programs would just have to be prepared for situations like
+ * this in any plug-and-play environment.)
+ *
+ * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Converted the whole proc stuff to real
+ *   read methods. Now not the whole device list needs to fit
+ *   into one page, only the device list for one bus.
+ *   Added a poll method to /proc/bus/usb/devices, to wake
+ *   up an eventual usbd
+ * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *   Turned into its own filesystem
+ * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
+ *   Converted file reading routine to dump to buffer once
+ *   per device, not per bus
+ *
+ * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+
+#include "hcd.h"
+
+#define MAX_TOPO_LEVEL		6
+
+/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
+#define ALLOW_SERIAL_NUMBER
+
+static char *format_topo =
+/* T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
+"\nT:  Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
+
+static char *format_string_manufacturer =
+/* S:  Manufacturer=xxxx */
+  "S:  Manufacturer=%.100s\n";
+
+static char *format_string_product =
+/* S:  Product=xxxx */
+  "S:  Product=%.100s\n";
+
+#ifdef ALLOW_SERIAL_NUMBER
+static char *format_string_serialnumber =
+/* S:  SerialNumber=xxxx */
+  "S:  SerialNumber=%.100s\n";
+#endif
+
+static char *format_bandwidth =
+/* B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
+  "B:  Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
+  
+static char *format_device1 =
+/* D:  Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
+  "D:  Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
+
+static char *format_device2 =
+/* P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx */
+  "P:  Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
+
+static char *format_config =
+/* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
+  "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
+  
+static char *format_iface =
+/* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
+  "I:  If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
+
+static char *format_endpt =
+/* E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
+  "E:  Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
+
+
+/*
+ * Need access to the driver and USB bus lists.
+ * extern struct list_head usb_bus_list;
+ * However, these will come from functions that return ptrs to each of them.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
+static unsigned int conndiscevcnt = 0;
+
+/* this struct stores the poll state for <mountpoint>/devices pollers */
+struct usb_device_status {
+	unsigned int lastev;
+};
+
+struct class_info {
+	int class;
+	char *class_name;
+};
+
+static const struct class_info clas_info[] =
+{					/* max. 5 chars. per name string */
+	{USB_CLASS_PER_INTERFACE,	">ifc"},
+	{USB_CLASS_AUDIO,		"audio"},
+	{USB_CLASS_COMM,		"comm."},
+	{USB_CLASS_HID,			"HID"},
+	{USB_CLASS_HUB,			"hub"},
+	{USB_CLASS_PHYSICAL,		"PID"},
+	{USB_CLASS_PRINTER,		"print"},
+	{USB_CLASS_MASS_STORAGE,	"stor."},
+	{USB_CLASS_CDC_DATA,		"data"},
+	{USB_CLASS_APP_SPEC,		"app."},
+	{USB_CLASS_VENDOR_SPEC,		"vend."},
+	{USB_CLASS_STILL_IMAGE,		"still"},
+	{USB_CLASS_CSCID,		"scard"},
+	{USB_CLASS_CONTENT_SEC,		"c-sec"},
+	{-1,				"unk."}		/* leave as last */
+};
+
+/*****************************************************************/
+
+void usbfs_conn_disc_event(void)
+{
+	conndiscevcnt++;
+	wake_up(&deviceconndiscwq);
+}
+
+static const char *class_decode(const int class)
+{
+	int ix;
+
+	for (ix = 0; clas_info[ix].class != -1; ix++)
+		if (clas_info[ix].class == class)
+			break;
+	return (clas_info[ix].class_name);
+}
+
+static char *usb_dump_endpoint_descriptor (
+	int speed,
+	char *start,
+	char *end,
+	const struct usb_endpoint_descriptor *desc
+)
+{
+	char dir, unit, *type;
+	unsigned interval, in, bandwidth = 1;
+
+	if (start > end)
+		return start;
+	in = (desc->bEndpointAddress & USB_DIR_IN);
+	dir = in ? 'I' : 'O';
+	if (speed == USB_SPEED_HIGH) {
+		switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
+		case 1 << 11:	bandwidth = 2; break;
+		case 2 << 11:	bandwidth = 3; break;
+		}
+	}
+
+	/* this isn't checking for illegal values */
+	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		type = "Ctrl";
+		if (speed == USB_SPEED_HIGH) 	/* uframes per NAK */
+			interval = desc->bInterval;
+		else
+			interval = 0;
+		dir = 'B';			/* ctrl is bidirectional */
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		type = "Isoc";
+		interval = 1 << (desc->bInterval - 1);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = "Bulk";
+		if (speed == USB_SPEED_HIGH && !in)	/* uframes per NAK */
+			interval = desc->bInterval;
+		else
+			interval = 0;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = "Int.";
+		if (speed == USB_SPEED_HIGH) {
+			interval = 1 << (desc->bInterval - 1);
+		} else
+			interval = desc->bInterval;
+		break;
+	default:	/* "can't happen" */
+		return start;
+	}
+	interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
+	if (interval % 1000)
+		unit = 'u';
+	else {
+		unit = 'm';
+		interval /= 1000;
+	}
+
+	start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
+			 desc->bmAttributes, type,
+			 (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * bandwidth,
+			 interval, unit);
+	return start;
+}
+
+static char *usb_dump_interface_descriptor(char *start, char *end,
+	const struct usb_interface_cache *intfc,
+	const struct usb_interface *iface,
+	int setno)
+{
+	const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc;
+	char *driver_name = "";
+
+	if (start > end)
+		return start;
+	down_read(&usb_bus_type.subsys.rwsem);
+	if (iface)
+		driver_name = (iface->dev.driver
+				? iface->dev.driver->name
+				: "(none)");
+	start += sprintf(start, format_iface,
+			 desc->bInterfaceNumber,
+			 desc->bAlternateSetting,
+			 desc->bNumEndpoints,
+			 desc->bInterfaceClass,
+			 class_decode(desc->bInterfaceClass),
+			 desc->bInterfaceSubClass,
+			 desc->bInterfaceProtocol,
+			 driver_name);
+	up_read(&usb_bus_type.subsys.rwsem);
+	return start;
+}
+
+static char *usb_dump_interface(
+	int speed,
+	char *start,
+	char *end,
+	const struct usb_interface_cache *intfc,
+	const struct usb_interface *iface,
+	int setno
+) {
+	const struct usb_host_interface *desc = &intfc->altsetting[setno];
+	int i;
+
+	start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
+	for (i = 0; i < desc->desc.bNumEndpoints; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_endpoint_descriptor(speed,
+				start, end, &desc->endpoint[i].desc);
+	}
+	return start;
+}
+
+/* TBD:
+ * 0. TBDs
+ * 1. marking active interface altsettings (code lists all, but should mark
+ *    which ones are active, if any)
+ */
+
+static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, format_config,
+			 active ? '*' : ' ',	/* mark active/actual/current cfg. */
+			 desc->bNumInterfaces,
+			 desc->bConfigurationValue,
+			 desc->bmAttributes,
+			 desc->bMaxPower * 2);
+	return start;
+}
+
+static char *usb_dump_config (
+	int speed,
+	char *start,
+	char *end,
+	const struct usb_host_config *config,
+	int active
+)
+{
+	int i, j;
+	struct usb_interface_cache *intfc;
+	struct usb_interface *interface;
+
+	if (start > end)
+		return start;
+	if (!config)		/* getting these some in 2.3.7; none in 2.3.6 */
+		return start + sprintf(start, "(null Cfg. desc.)\n");
+	start = usb_dump_config_descriptor(start, end, &config->desc, active);
+	for (i = 0; i < config->desc.bNumInterfaces; i++) {
+		intfc = config->intf_cache[i];
+		interface = config->interface[i];
+		for (j = 0; j < intfc->num_altsetting; j++) {
+			if (start > end)
+				return start;
+			start = usb_dump_interface(speed,
+				start, end, intfc, interface, j);
+		}
+	}
+	return start;
+}
+
+/*
+ * Dump the different USB descriptors.
+ */
+static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc)
+{
+	u16 bcdUSB = le16_to_cpu(desc->bcdUSB);
+	u16 bcdDevice = le16_to_cpu(desc->bcdDevice);
+
+	if (start > end)
+		return start;
+	start += sprintf (start, format_device1,
+			  bcdUSB >> 8, bcdUSB & 0xff,
+			  desc->bDeviceClass,
+			  class_decode (desc->bDeviceClass),
+			  desc->bDeviceSubClass,
+			  desc->bDeviceProtocol,
+			  desc->bMaxPacketSize0,
+			  desc->bNumConfigurations);
+	if (start > end)
+		return start;
+	start += sprintf(start, format_device2,
+			 le16_to_cpu(desc->idVendor),
+			 le16_to_cpu(desc->idProduct),
+			 bcdDevice >> 8, bcdDevice & 0xff);
+	return start;
+}
+
+/*
+ * Dump the different strings that this device holds.
+ */
+static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev)
+{
+	if (start > end)
+		return start;
+	if (dev->manufacturer)
+		start += sprintf(start, format_string_manufacturer, dev->manufacturer);
+	if (start > end)
+		goto out;
+	if (dev->product)
+		start += sprintf(start, format_string_product, dev->product);
+	if (start > end)
+		goto out;
+#ifdef ALLOW_SERIAL_NUMBER
+	if (dev->serial)
+		start += sprintf(start, format_string_serialnumber, dev->serial);
+#endif
+ out:
+	return start;
+}
+
+static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
+{
+	int i;
+
+	if (start > end)
+		return start;
+		
+	start = usb_dump_device_descriptor(start, end, &dev->descriptor);
+
+	if (start > end)
+		return start;
+	
+	start = usb_dump_device_strings (start, end, dev);
+
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+		if (start > end)
+			return start;
+		start = usb_dump_config(dev->speed,
+				start, end, dev->config + i,
+				/* active ? */
+				(dev->config + i) == dev->actconfig);
+	}
+	return start;
+}
+
+
+#ifdef PROC_EXTRA /* TBD: may want to add this code later */
+
+static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc)
+{
+	int leng = USB_DT_HUB_NONVAR_SIZE;
+	unsigned char *ptr = (unsigned char *)desc;
+
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	while (leng && start <= end) {
+		start += sprintf(start, " %02x", *ptr);
+		ptr++; leng--;
+	}
+	*start++ = '\n';
+	return start;
+}
+
+static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, "Interface:");
+	if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+		start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]);
+	return start;
+}
+
+#endif /* PROC_EXTRA */
+
+/*****************************************************************/
+
+/* This is a recursive function. Parameters:
+ * buffer - the user-space buffer to write data into
+ * nbytes - the maximum number of bytes to write
+ * skip_bytes - the number of bytes to skip before writing anything
+ * file_offset - the offset into the devices file on completion
+ * The caller must own the device lock.
+ */
+static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
+				struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
+{
+	int chix;
+	int ret, cnt = 0;
+	int parent_devnum = 0;
+	char *pages_start, *data_end, *speed;
+	unsigned int length;
+	ssize_t total_written = 0;
+	
+	/* don't bother with anything else if we're not writing any data */
+	if (*nbytes <= 0)
+		return 0;
+	
+	if (level > MAX_TOPO_LEVEL)
+		return 0;
+	/* allocate 2^1 pages = 8K (on i386); should be more than enough for one device */
+        if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1)))
+                return -ENOMEM;
+		
+	if (usbdev->parent && usbdev->parent->devnum != -1)
+		parent_devnum = usbdev->parent->devnum;
+	/*
+	 * So the root hub's parent is 0 and any device that is
+	 * plugged into the root hub has a parent of 0.
+	 */
+	switch (usbdev->speed) {
+	case USB_SPEED_LOW:
+		speed = "1.5"; break;
+	case USB_SPEED_UNKNOWN:		/* usb 1.1 root hub code */
+	case USB_SPEED_FULL:
+		speed = "12 "; break;
+	case USB_SPEED_HIGH:
+		speed = "480"; break;
+	default:
+		speed = "?? ";
+	}
+	data_end = pages_start + sprintf(pages_start, format_topo,
+			bus->busnum, level, parent_devnum,
+			index, count, usbdev->devnum,
+			speed, usbdev->maxchild);
+	/*
+	 * level = topology-tier level;
+	 * parent_devnum = parent device number;
+	 * index = parent's connector number;
+	 * count = device count at this level
+	 */
+	/* If this is the root hub, display the bandwidth information */
+	if (level == 0) {
+		int	max;
+
+		/* high speed reserves 80%, full/low reserves 90% */
+		if (usbdev->speed == USB_SPEED_HIGH)
+			max = 800;
+		else
+			max = FRAME_TIME_MAX_USECS_ALLOC;
+
+		/* report "average" periodic allocation over a microsecond.
+		 * the schedules are actually bursty, HCDs need to deal with
+		 * that and just compute/report this average.
+		 */
+		data_end += sprintf(data_end, format_bandwidth,
+				bus->bandwidth_allocated, max,
+				(100 * bus->bandwidth_allocated + max / 2)
+					/ max,
+			         bus->bandwidth_int_reqs,
+				 bus->bandwidth_isoc_reqs);
+	
+	}
+	data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev);
+	
+	if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))
+		data_end += sprintf(data_end, "(truncated)\n");
+	
+	length = data_end - pages_start;
+	/* if we can start copying some data to the user */
+	if (length > *skip_bytes) {
+		length -= *skip_bytes;
+		if (length > *nbytes)
+			length = *nbytes;
+		if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) {
+			free_pages((unsigned long)pages_start, 1);
+			return -EFAULT;
+		}
+		*nbytes -= length;
+		*file_offset += length;
+		total_written += length;
+		*buffer += length;
+		*skip_bytes = 0;
+	} else
+		*skip_bytes -= length;
+	
+	free_pages((unsigned long)pages_start, 1);
+	
+	/* Now look at all of this device's children. */
+	for (chix = 0; chix < usbdev->maxchild; chix++) {
+		struct usb_device *childdev = usbdev->children[chix];
+
+		if (childdev) {
+			down(&childdev->serialize);
+			ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
+					bus, level + 1, chix, ++cnt);
+			up(&childdev->serialize);
+			if (ret == -EFAULT)
+				return total_written;
+			total_written += ret;
+		}
+	}
+	return total_written;
+}
+
+static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+{
+	struct usb_bus *bus;
+	ssize_t ret, total_written = 0;
+	loff_t skip_bytes = *ppos;
+
+	if (*ppos < 0)
+		return -EINVAL;
+	if (nbytes <= 0)
+		return 0;
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EFAULT;
+
+	down (&usb_bus_list_lock);
+	/* print devices for all busses */
+	list_for_each_entry(bus, &usb_bus_list, bus_list) {
+		/* recurse through all children of the root hub */
+		if (!bus->root_hub)
+			continue;
+		usb_lock_device(bus->root_hub);
+		ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
+		usb_unlock_device(bus->root_hub);
+		if (ret < 0) {
+			up(&usb_bus_list_lock);
+			return ret;
+		}
+		total_written += ret;
+	}
+	up (&usb_bus_list_lock);
+	return total_written;
+}
+
+/* Kernel lock for "lastev" protection */
+static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+	unsigned int mask = 0;
+
+	lock_kernel();
+	if (!st) {
+		st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
+		if (!st) {
+			unlock_kernel();
+			return POLLIN;
+		}
+		
+		/* we may have dropped BKL - need to check for having lost the race */
+		if (file->private_data) {
+			kfree(st);
+			st = file->private_data;
+			goto lost_race;
+		}
+
+		/*
+		 * need to prevent the module from being unloaded, since
+		 * proc_unregister does not call the release method and
+		 * we would have a memory leak
+		 */
+		st->lastev = conndiscevcnt;
+		file->private_data = st;
+		mask = POLLIN;
+	}
+lost_race:
+	if (file->f_mode & FMODE_READ)
+                poll_wait(file, &deviceconndiscwq, wait);
+	if (st->lastev != conndiscevcnt)
+		mask |= POLLIN;
+	st->lastev = conndiscevcnt;
+	unlock_kernel();
+	return mask;
+}
+
+static int usb_device_open(struct inode *inode, struct file *file)
+{
+        file->private_data = NULL;
+        return 0;
+}
+
+static int usb_device_release(struct inode *inode, struct file *file)
+{
+	if (file->private_data) {
+		kfree(file->private_data);
+		file->private_data = NULL;
+	}
+
+        return 0;
+}
+
+static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
+{
+	loff_t ret;
+
+	lock_kernel();
+
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		ret = file->f_pos;
+		break;
+	case 1:
+		file->f_pos += offset;
+		ret = file->f_pos;
+		break;
+	case 2:
+	default:
+		ret = -EINVAL;
+	}
+
+	unlock_kernel();
+	return ret;
+}
+
+struct file_operations usbfs_devices_fops = {
+	.llseek =	usb_device_lseek,
+	.read =		usb_device_read,
+	.poll =		usb_device_poll,
+	.open =		usb_device_open,
+	.release =	usb_device_release,
+};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
new file mode 100644
index 0000000..a047bc3
--- /dev/null
+++ b/drivers/usb/core/devio.c
@@ -0,0 +1,1483 @@
+/*****************************************************************************/
+
+/*
+ *      devio.c  --  User space communication with USB devices.
+ *
+ *      Copyright (C) 1999-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      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 Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
+ *
+ *  This file implements the usbfs/x/y files, where
+ *  x is the bus number and y the device number.
+ *
+ *  It allows user space programs/"drivers" to communicate directly
+ *  with USB devices without intervening kernel driver.
+ *
+ *  Revision history
+ *    22.12.1999   0.1   Initial release (split from proc_usb.c)
+ *    04.01.2000   0.2   Turned into its own filesystem
+ */
+
+/*****************************************************************************/
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/moduleparam.h>
+
+#include "hcd.h"	/* for usbcore internals */
+#include "usb.h"
+
+struct async {
+	struct list_head asynclist;
+	struct dev_state *ps;
+	struct task_struct *task;
+	unsigned int signr;
+	unsigned int ifnum;
+	void __user *userbuffer;
+	void __user *userurb;
+	struct urb *urb;
+};
+
+static int usbfs_snoop = 0;
+module_param (usbfs_snoop, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
+
+#define snoop(dev, format, arg...)				\
+	do {							\
+		if (usbfs_snoop)				\
+			dev_info( dev , format , ## arg);	\
+	} while (0)
+
+
+#define	MAX_USBFS_BUFFER_SIZE	16384
+
+static inline int connected (struct usb_device *dev)
+{
+	return dev->state != USB_STATE_NOTATTACHED;
+}
+
+static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t ret;
+
+	lock_kernel();
+
+	switch (orig) {
+	case 0:
+		file->f_pos = offset;
+		ret = file->f_pos;
+		break;
+	case 1:
+		file->f_pos += offset;
+		ret = file->f_pos;
+		break;
+	case 2:
+	default:
+		ret = -EINVAL;
+	}
+
+	unlock_kernel();
+	return ret;
+}
+
+static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct usb_device *dev = ps->dev;
+	ssize_t ret = 0;
+	unsigned len;
+	loff_t pos;
+	int i;
+
+	pos = *ppos;
+	usb_lock_device(dev);
+	if (!connected(dev)) {
+		ret = -ENODEV;
+		goto err;
+	} else if (pos < 0) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (pos < sizeof(struct usb_device_descriptor)) {
+		struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL);
+		if (!desc) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		memcpy(desc, &dev->descriptor, sizeof(dev->descriptor));
+		le16_to_cpus(&desc->bcdUSB);
+		le16_to_cpus(&desc->idVendor);
+		le16_to_cpus(&desc->idProduct);
+		le16_to_cpus(&desc->bcdDevice);
+
+		len = sizeof(struct usb_device_descriptor) - pos;
+		if (len > nbytes)
+			len = nbytes;
+		if (copy_to_user(buf, ((char *)desc) + pos, len)) {
+			kfree(desc);
+			ret = -EFAULT;
+			goto err;
+		}
+		kfree(desc);
+
+		*ppos += len;
+		buf += len;
+		nbytes -= len;
+		ret += len;
+	}
+
+	pos = sizeof(struct usb_device_descriptor);
+	for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) {
+		struct usb_config_descriptor *config =
+			(struct usb_config_descriptor *)dev->rawdescriptors[i];
+		unsigned int length = le16_to_cpu(config->wTotalLength);
+
+		if (*ppos < pos + length) {
+
+			/* The descriptor may claim to be longer than it
+			 * really is.  Here is the actual allocated length. */
+			unsigned alloclen =
+				le16_to_cpu(dev->config[i].desc.wTotalLength);
+
+			len = length - (*ppos - pos);
+			if (len > nbytes)
+				len = nbytes;
+
+			/* Simply don't write (skip over) unallocated parts */
+			if (alloclen > (*ppos - pos)) {
+				alloclen -= (*ppos - pos);
+				if (copy_to_user(buf,
+				    dev->rawdescriptors[i] + (*ppos - pos),
+				    min(len, alloclen))) {
+					ret = -EFAULT;
+					goto err;
+				}
+			}
+
+			*ppos += len;
+			buf += len;
+			nbytes -= len;
+			ret += len;
+		}
+
+		pos += length;
+	}
+
+err:
+	usb_unlock_device(dev);
+	return ret;
+}
+
+/*
+ * async list handling
+ */
+
+static struct async *alloc_async(unsigned int numisoframes)
+{
+        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
+        struct async *as = kmalloc(assize, GFP_KERNEL);
+        if (!as)
+                return NULL;
+        memset(as, 0, assize);
+	as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
+	if (!as->urb) {
+		kfree(as);
+		return NULL;
+	}
+        return as;
+}
+
+static void free_async(struct async *as)
+{
+        if (as->urb->transfer_buffer)
+                kfree(as->urb->transfer_buffer);
+        if (as->urb->setup_packet)
+                kfree(as->urb->setup_packet);
+	usb_free_urb(as->urb);
+        kfree(as);
+}
+
+static inline void async_newpending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_add_tail(&as->asynclist, &ps->async_pending);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+static inline void async_removepending(struct async *as)
+{
+        struct dev_state *ps = as->ps;
+        unsigned long flags;
+        
+        spin_lock_irqsave(&ps->lock, flags);
+        list_del_init(&as->asynclist);
+        spin_unlock_irqrestore(&ps->lock, flags);
+}
+
+static inline struct async *async_getcompleted(struct dev_state *ps)
+{
+        unsigned long flags;
+        struct async *as = NULL;
+
+        spin_lock_irqsave(&ps->lock, flags);
+        if (!list_empty(&ps->async_completed)) {
+                as = list_entry(ps->async_completed.next, struct async, asynclist);
+                list_del_init(&as->asynclist);
+        }
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return as;
+}
+
+static inline struct async *async_getpending(struct dev_state *ps, void __user *userurb)
+{
+        unsigned long flags;
+        struct async *as;
+
+        spin_lock_irqsave(&ps->lock, flags);
+	list_for_each_entry(as, &ps->async_pending, asynclist)
+		if (as->userurb == userurb) {
+			list_del_init(&as->asynclist);
+			spin_unlock_irqrestore(&ps->lock, flags);
+			return as;
+		}
+        spin_unlock_irqrestore(&ps->lock, flags);
+        return NULL;
+}
+
+static void async_completed(struct urb *urb, struct pt_regs *regs)
+{
+        struct async *as = (struct async *)urb->context;
+        struct dev_state *ps = as->ps;
+	struct siginfo sinfo;
+
+        spin_lock(&ps->lock);
+        list_move_tail(&as->asynclist, &ps->async_completed);
+        spin_unlock(&ps->lock);
+	if (as->signr) {
+		sinfo.si_signo = as->signr;
+		sinfo.si_errno = as->urb->status;
+		sinfo.si_code = SI_ASYNCIO;
+		sinfo.si_addr = as->userurb;
+		send_sig_info(as->signr, &sinfo, as->task);
+	}
+        wake_up(&ps->wait);
+}
+
+static void destroy_async (struct dev_state *ps, struct list_head *list)
+{
+	struct async *as;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ps->lock, flags);
+	while (!list_empty(list)) {
+		as = list_entry(list->next, struct async, asynclist);
+		list_del_init(&as->asynclist);
+
+		/* drop the spinlock so the completion handler can run */
+		spin_unlock_irqrestore(&ps->lock, flags);
+		usb_kill_urb(as->urb);
+		spin_lock_irqsave(&ps->lock, flags);
+	}
+	spin_unlock_irqrestore(&ps->lock, flags);
+	as = async_getcompleted(ps);
+	while (as) {
+		free_async(as);
+		as = async_getcompleted(ps);
+	}
+}
+
+static void destroy_async_on_interface (struct dev_state *ps, unsigned int ifnum)
+{
+	struct list_head *p, *q, hitlist;
+	unsigned long flags;
+
+	INIT_LIST_HEAD(&hitlist);
+	spin_lock_irqsave(&ps->lock, flags);
+	list_for_each_safe(p, q, &ps->async_pending)
+		if (ifnum == list_entry(p, struct async, asynclist)->ifnum)
+			list_move_tail(p, &hitlist);
+	spin_unlock_irqrestore(&ps->lock, flags);
+	destroy_async(ps, &hitlist);
+}
+
+static inline void destroy_all_async(struct dev_state *ps)
+{
+	        destroy_async(ps, &ps->async_pending);
+}
+
+/*
+ * interface claims are made only at the request of user level code,
+ * which can also release them (explicitly or by closing files).
+ * they're also undone when devices disconnect.
+ */
+
+static int driver_probe (struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	return -ENODEV;
+}
+
+static void driver_disconnect(struct usb_interface *intf)
+{
+	struct dev_state *ps = usb_get_intfdata (intf);
+	unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;
+
+	if (!ps)
+		return;
+
+	/* NOTE:  this relies on usbcore having canceled and completed
+	 * all pending I/O requests; 2.6 does that.
+	 */
+
+	if (likely(ifnum < 8*sizeof(ps->ifclaimed)))
+		clear_bit(ifnum, &ps->ifclaimed);
+	else
+		warn("interface number %u out of range", ifnum);
+
+	usb_set_intfdata (intf, NULL);
+
+	/* force async requests to complete */
+	destroy_async_on_interface(ps, ifnum);
+}
+
+struct usb_driver usbfs_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"usbfs",
+	.probe =	driver_probe,
+	.disconnect =	driver_disconnect,
+};
+
+static int claimintf(struct dev_state *ps, unsigned int ifnum)
+{
+	struct usb_device *dev = ps->dev;
+	struct usb_interface *intf;
+	int err;
+
+	if (ifnum >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	/* already claimed */
+	if (test_bit(ifnum, &ps->ifclaimed))
+		return 0;
+
+	/* lock against other changes to driver bindings */
+	down_write(&usb_bus_type.subsys.rwsem);
+	intf = usb_ifnum_to_if(dev, ifnum);
+	if (!intf)
+		err = -ENOENT;
+	else
+		err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
+	up_write(&usb_bus_type.subsys.rwsem);
+	if (err == 0)
+		set_bit(ifnum, &ps->ifclaimed);
+	return err;
+}
+
+static int releaseintf(struct dev_state *ps, unsigned int ifnum)
+{
+	struct usb_device *dev;
+	struct usb_interface *intf;
+	int err;
+
+	err = -EINVAL;
+	if (ifnum >= 8*sizeof(ps->ifclaimed))
+		return err;
+	dev = ps->dev;
+	/* lock against other changes to driver bindings */
+	down_write(&usb_bus_type.subsys.rwsem);
+	intf = usb_ifnum_to_if(dev, ifnum);
+	if (!intf)
+		err = -ENOENT;
+	else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
+		usb_driver_release_interface(&usbfs_driver, intf);
+		err = 0;
+	}
+	up_write(&usb_bus_type.subsys.rwsem);
+	return err;
+}
+
+static int checkintf(struct dev_state *ps, unsigned int ifnum)
+{
+	if (ps->dev->state != USB_STATE_CONFIGURED)
+		return -EHOSTUNREACH;
+	if (ifnum >= 8*sizeof(ps->ifclaimed))
+		return -EINVAL;
+	if (test_bit(ifnum, &ps->ifclaimed))
+		return 0;
+	/* if not yet claimed, claim it for the driver */
+	dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim interface %u before use\n",
+	       current->pid, current->comm, ifnum);
+	return claimintf(ps, ifnum);
+}
+
+static int findintfep(struct usb_device *dev, unsigned int ep)
+{
+	unsigned int i, j, e;
+        struct usb_interface *intf;
+	struct usb_host_interface *alts;
+	struct usb_endpoint_descriptor *endpt;
+
+	if (ep & ~(USB_DIR_IN|0xf))
+		return -EINVAL;
+	if (!dev->actconfig)
+		return -ESRCH;
+	for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+		intf = dev->actconfig->interface[i];
+		for (j = 0; j < intf->num_altsetting; j++) {
+                        alts = &intf->altsetting[j];
+			for (e = 0; e < alts->desc.bNumEndpoints; e++) {
+				endpt = &alts->endpoint[e].desc;
+				if (endpt->bEndpointAddress == ep)
+					return alts->desc.bInterfaceNumber;
+			}
+		}
+	}
+	return -ENOENT; 
+}
+
+static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsigned int index)
+{
+	int ret = 0;
+
+	if (ps->dev->state != USB_STATE_CONFIGURED)
+		return -EHOSTUNREACH;
+	if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
+		return 0;
+
+	index &= 0xff;
+	switch (requesttype & USB_RECIP_MASK) {
+	case USB_RECIP_ENDPOINT:
+		if ((ret = findintfep(ps->dev, index)) >= 0)
+			ret = checkintf(ps, ret);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		ret = checkintf(ps, index);
+		break;
+	}
+	return ret;
+}
+
+/*
+ * file operations
+ */
+static int usbdev_open(struct inode *inode, struct file *file)
+{
+	struct usb_device *dev;
+	struct dev_state *ps;
+	int ret;
+
+	/* 
+	 * no locking necessary here, as chrdev_open has the kernel lock
+	 * (still acquire the kernel lock for safety)
+	 */
+	ret = -ENOMEM;
+	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
+		goto out_nolock;
+
+	lock_kernel();
+	ret = -ENOENT;
+	dev = usb_get_dev(inode->u.generic_ip);
+	if (!dev) {
+		kfree(ps);
+		goto out;
+	}
+	ret = 0;
+	ps->dev = dev;
+	ps->file = file;
+	spin_lock_init(&ps->lock);
+	INIT_LIST_HEAD(&ps->async_pending);
+	INIT_LIST_HEAD(&ps->async_completed);
+	init_waitqueue_head(&ps->wait);
+	ps->discsignr = 0;
+	ps->disctask = current;
+	ps->disccontext = NULL;
+	ps->ifclaimed = 0;
+	wmb();
+	list_add_tail(&ps->list, &dev->filelist);
+	file->private_data = ps;
+ out:
+	unlock_kernel();
+ out_nolock:
+        return ret;
+}
+
+static int usbdev_release(struct inode *inode, struct file *file)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct usb_device *dev = ps->dev;
+	unsigned int ifnum;
+
+	usb_lock_device(dev);
+	list_del_init(&ps->list);
+	for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
+			ifnum++) {
+		if (test_bit(ifnum, &ps->ifclaimed))
+			releaseintf(ps, ifnum);
+	}
+	destroy_all_async(ps);
+	usb_unlock_device(dev);
+	usb_put_dev(dev);
+	ps->dev = NULL;
+	kfree(ps);
+        return 0;
+}
+
+static int proc_control(struct dev_state *ps, void __user *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_ctrltransfer ctrl;
+	unsigned int tmo;
+	unsigned char *tbuf;
+	int i, j, ret;
+
+	if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+		return -EFAULT;
+	if ((ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex)))
+		return ret;
+	if (ctrl.wLength > PAGE_SIZE)
+		return -EINVAL;
+	if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = ctrl.timeout;
+	if (ctrl.bRequestType & 0x80) {
+		if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) {
+			free_page((unsigned long)tbuf);
+			return -EINVAL;
+		}
+		snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", 
+			ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex);
+
+		usb_unlock_device(dev);
+		i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
+				       ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
+		usb_lock_device(dev);
+		if ((i > 0) && ctrl.wLength) {
+			if (usbfs_snoop) {
+				dev_info(&dev->dev, "control read: data ");
+				for (j = 0; j < ctrl.wLength; ++j)
+					printk ("%02x ", (unsigned char)(tbuf)[j]);
+				printk("\n");
+			}
+			if (copy_to_user(ctrl.data, tbuf, ctrl.wLength)) {
+				free_page((unsigned long)tbuf);
+				return -EFAULT;
+			}
+		}
+	} else {
+		if (ctrl.wLength) {
+			if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) {
+				free_page((unsigned long)tbuf);
+				return -EFAULT;
+			}
+		}
+		snoop(&dev->dev, "control write: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", 
+			ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex);
+		if (usbfs_snoop) {
+			dev_info(&dev->dev, "control write: data: ");
+			for (j = 0; j < ctrl.wLength; ++j)
+				printk ("%02x ", (unsigned char)(tbuf)[j]);
+			printk("\n");
+		}
+		usb_unlock_device(dev);
+		i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
+				       ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
+		usb_lock_device(dev);
+	}
+	free_page((unsigned long)tbuf);
+	if (i<0 && i != -EPIPE) {
+		dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
+			   "failed cmd %s rqt %u rq %u len %u ret %d\n",
+			   current->comm, ctrl.bRequestType, ctrl.bRequest,
+			   ctrl.wLength, i);
+	}
+	return i;
+}
+
+static int proc_bulk(struct dev_state *ps, void __user *arg)
+{
+	struct usb_device *dev = ps->dev;
+	struct usbdevfs_bulktransfer bulk;
+	unsigned int tmo, len1, pipe;
+	int len2;
+	unsigned char *tbuf;
+	int i, ret;
+
+	if (copy_from_user(&bulk, arg, sizeof(bulk)))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, bulk.ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (bulk.ep & USB_DIR_IN)
+		pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
+	else
+		pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
+	if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
+		return -EINVAL;
+	len1 = bulk.len;
+	if (len1 > MAX_USBFS_BUFFER_SIZE)
+		return -EINVAL;
+	if (!(tbuf = kmalloc(len1, GFP_KERNEL)))
+		return -ENOMEM;
+	tmo = bulk.timeout;
+	if (bulk.ep & 0x80) {
+		if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
+			kfree(tbuf);
+			return -EINVAL;
+		}
+		usb_unlock_device(dev);
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		usb_lock_device(dev);
+		if (!i && len2) {
+			if (copy_to_user(bulk.data, tbuf, len2)) {
+				kfree(tbuf);
+				return -EFAULT;
+			}
+		}
+	} else {
+		if (len1) {
+			if (copy_from_user(tbuf, bulk.data, len1)) {
+				kfree(tbuf);
+				return -EFAULT;
+			}
+		}
+		usb_unlock_device(dev);
+		i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
+		usb_lock_device(dev);
+	}
+	kfree(tbuf);
+	if (i < 0)
+		return i;
+	return len2;
+}
+
+static int proc_resetep(struct dev_state *ps, void __user *arg)
+{
+	unsigned int ep;
+	int ret;
+
+	if (get_user(ep, (unsigned int __user *)arg))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
+	return 0;
+}
+
+static int proc_clearhalt(struct dev_state *ps, void __user *arg)
+{
+	unsigned int ep;
+	int pipe;
+	int ret;
+
+	if (get_user(ep, (unsigned int __user *)arg))
+		return -EFAULT;
+	if ((ret = findintfep(ps->dev, ep)) < 0)
+		return ret;
+	if ((ret = checkintf(ps, ret)))
+		return ret;
+	if (ep & USB_DIR_IN)
+                pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
+        else
+                pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f);
+
+	return usb_clear_halt(ps->dev, pipe);
+}
+		
+
+static int proc_getdriver(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_getdriver gd;
+	struct usb_interface *intf;
+	int ret;
+
+	if (copy_from_user(&gd, arg, sizeof(gd)))
+		return -EFAULT;
+	down_read(&usb_bus_type.subsys.rwsem);
+	intf = usb_ifnum_to_if(ps->dev, gd.interface);
+	if (!intf || !intf->dev.driver)
+		ret = -ENODATA;
+	else {
+		strncpy(gd.driver, intf->dev.driver->name,
+				sizeof(gd.driver));
+		ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
+	}
+	up_read(&usb_bus_type.subsys.rwsem);
+	return ret;
+}
+
+static int proc_connectinfo(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_connectinfo ci;
+
+	ci.devnum = ps->dev->devnum;
+	ci.slow = ps->dev->speed == USB_SPEED_LOW;
+	if (copy_to_user(arg, &ci, sizeof(ci)))
+		return -EFAULT;
+	return 0;
+}
+
+static int proc_resetdevice(struct dev_state *ps)
+{
+	return usb_reset_device(ps->dev);
+
+}
+
+static int proc_setintf(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_setinterface setintf;
+	int ret;
+
+	if (copy_from_user(&setintf, arg, sizeof(setintf)))
+		return -EFAULT;
+	if ((ret = checkintf(ps, setintf.interface)))
+		return ret;
+	return usb_set_interface(ps->dev, setintf.interface,
+			setintf.altsetting);
+}
+
+static int proc_setconfig(struct dev_state *ps, void __user *arg)
+{
+	unsigned int u;
+	int status = 0;
+ 	struct usb_host_config *actconfig;
+
+	if (get_user(u, (unsigned int __user *)arg))
+		return -EFAULT;
+
+ 	actconfig = ps->dev->actconfig;
+ 
+ 	/* Don't touch the device if any interfaces are claimed.
+ 	 * It could interfere with other drivers' operations, and if
+	 * an interface is claimed by usbfs it could easily deadlock.
+	 */
+ 	if (actconfig) {
+ 		int i;
+ 
+ 		for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
+ 			if (usb_interface_claimed(actconfig->interface[i])) {
+				dev_warn (&ps->dev->dev,
+					"usbfs: interface %d claimed "
+					"while '%s' sets config #%d\n",
+					actconfig->interface[i]
+						->cur_altsetting
+						->desc.bInterfaceNumber,
+					current->comm, u);
+#if 0	/* FIXME:  enable in 2.6.10 or so */
+ 				status = -EBUSY;
+				break;
+#endif
+			}
+ 		}
+ 	}
+
+	/* SET_CONFIGURATION is often abused as a "cheap" driver reset,
+	 * so avoid usb_set_configuration()'s kick to sysfs
+	 */
+	if (status == 0) {
+		if (actconfig && actconfig->desc.bConfigurationValue == u)
+			status = usb_reset_configuration(ps->dev);
+		else
+			status = usb_set_configuration(ps->dev, u);
+	}
+
+	return status;
+}
+
+
+static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
+			     struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
+			     void __user *arg)
+{
+	struct usbdevfs_iso_packet_desc *isopkt = NULL;
+	struct usb_host_endpoint *ep;
+	struct async *as;
+	struct usb_ctrlrequest *dr = NULL;
+	unsigned int u, totlen, isofrmlen;
+	int ret, interval = 0, ifnum = -1;
+
+	if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
+			   URB_NO_FSBR|URB_ZERO_PACKET))
+		return -EINVAL;
+	if (!uurb->buffer)
+		return -EINVAL;
+	if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || uurb->signr > SIGRTMAX))
+		return -EINVAL;
+	if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
+		if ((ifnum = findintfep(ps->dev, uurb->endpoint)) < 0)
+			return ifnum;
+		if ((ret = checkintf(ps, ifnum)))
+			return ret;
+	}
+	if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0)
+		ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
+	else
+		ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
+	if (!ep)
+		return -ENOENT;
+	switch(uurb->type) {
+	case USBDEVFS_URB_TYPE_CONTROL:
+		if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				!= USB_ENDPOINT_XFER_CONTROL)
+			return -EINVAL;
+		/* min 8 byte setup packet, max arbitrary */
+		if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE)
+			return -EINVAL;
+		if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(dr, uurb->buffer, 8)) {
+			kfree(dr);
+			return -EFAULT;
+		}
+		if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) {
+			kfree(dr);
+			return -EINVAL;
+		}
+		if ((ret = check_ctrlrecip(ps, dr->bRequestType, le16_to_cpup(&dr->wIndex)))) {
+			kfree(dr);
+			return ret;
+		}
+		uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK);
+		uurb->number_of_packets = 0;
+		uurb->buffer_length = le16_to_cpup(&dr->wLength);
+		uurb->buffer += 8;
+		if (!access_ok((uurb->endpoint & USB_DIR_IN) ?  VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) {
+			kfree(dr);
+			return -EFAULT;
+		}
+		break;
+
+	case USBDEVFS_URB_TYPE_BULK:
+		switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_CONTROL:
+		case USB_ENDPOINT_XFER_ISOC:
+			return -EINVAL;
+		/* allow single-shot interrupt transfers, at bogus rates */
+		}
+		uurb->number_of_packets = 0;
+		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
+			return -EINVAL;
+		if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
+			return -EFAULT;
+		break;
+
+	case USBDEVFS_URB_TYPE_ISO:
+		/* arbitrary limit */
+		if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128)
+			return -EINVAL;
+		if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				!= USB_ENDPOINT_XFER_ISOC)
+			return -EINVAL;
+		interval = 1 << min (15, ep->desc.bInterval - 1);
+		isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets;
+		if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
+			kfree(isopkt);
+			return -EFAULT;
+		}
+		for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+			if (isopkt[u].length > 1023) {
+				kfree(isopkt);
+				return -EINVAL;
+			}
+			totlen += isopkt[u].length;
+		}
+		if (totlen > 32768) {
+			kfree(isopkt);
+			return -EINVAL;
+		}
+		uurb->buffer_length = totlen;
+		break;
+
+	case USBDEVFS_URB_TYPE_INTERRUPT:
+		uurb->number_of_packets = 0;
+		if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+				!= USB_ENDPOINT_XFER_INT)
+			return -EINVAL;
+		if (ps->dev->speed == USB_SPEED_HIGH)
+			interval = 1 << min (15, ep->desc.bInterval - 1);
+		else
+			interval = ep->desc.bInterval;
+		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
+			return -EINVAL;
+		if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
+			return -EFAULT;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (!(as = alloc_async(uurb->number_of_packets))) {
+		if (isopkt)
+			kfree(isopkt);
+		if (dr)
+			kfree(dr);
+		return -ENOMEM;
+	}
+	if (!(as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL))) {
+		if (isopkt)
+			kfree(isopkt);
+		if (dr)
+			kfree(dr);
+		free_async(as);
+		return -ENOMEM;
+	}
+        as->urb->dev = ps->dev;
+        as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN);
+        as->urb->transfer_flags = uurb->flags;
+	as->urb->transfer_buffer_length = uurb->buffer_length;
+	as->urb->setup_packet = (unsigned char*)dr;
+	as->urb->start_frame = uurb->start_frame;
+	as->urb->number_of_packets = uurb->number_of_packets;
+	as->urb->interval = interval;
+        as->urb->context = as;
+        as->urb->complete = async_completed;
+	for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+		as->urb->iso_frame_desc[u].offset = totlen;
+		as->urb->iso_frame_desc[u].length = isopkt[u].length;
+		totlen += isopkt[u].length;
+	}
+	if (isopkt)
+		kfree(isopkt);
+	as->ps = ps;
+        as->userurb = arg;
+	if (uurb->endpoint & USB_DIR_IN)
+		as->userbuffer = uurb->buffer;
+	else
+		as->userbuffer = NULL;
+	as->signr = uurb->signr;
+	as->ifnum = ifnum;
+	as->task = current;
+	if (!(uurb->endpoint & USB_DIR_IN)) {
+		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) {
+			free_async(as);
+			return -EFAULT;
+		}
+	}
+        async_newpending(as);
+        if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
+		dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret);
+                async_removepending(as);
+                free_async(as);
+                return ret;
+        }
+        return 0;
+}
+
+static int proc_submiturb(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_urb uurb;
+
+	if (copy_from_user(&uurb, arg, sizeof(uurb)))
+		return -EFAULT;
+
+	return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg);
+}
+
+static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
+{
+	struct async *as;
+
+	as = async_getpending(ps, arg);
+	if (!as)
+		return -EINVAL;
+	usb_kill_urb(as->urb);
+	return 0;
+}
+
+static int processcompl(struct async *as, void __user * __user *arg)
+{
+	struct urb *urb = as->urb;
+	struct usbdevfs_urb __user *userurb = as->userurb;
+	void __user *addr = as->userurb;
+	unsigned int i;
+
+	if (as->userbuffer)
+		if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
+			return -EFAULT;
+	if (put_user(urb->status, &userurb->status))
+		return -EFAULT;
+	if (put_user(urb->actual_length, &userurb->actual_length))
+		return -EFAULT;
+	if (put_user(urb->error_count, &userurb->error_count))
+		return -EFAULT;
+
+	if (!(usb_pipeisoc(urb->pipe)))
+		return 0;
+	for (i = 0; i < urb->number_of_packets; i++) {
+		if (put_user(urb->iso_frame_desc[i].actual_length,
+			     &userurb->iso_frame_desc[i].actual_length))
+			return -EFAULT;
+		if (put_user(urb->iso_frame_desc[i].status,
+			     &userurb->iso_frame_desc[i].status))
+			return -EFAULT;
+	}
+
+	free_async(as);
+
+	if (put_user(addr, (void __user * __user *)arg))
+		return -EFAULT;
+	return 0;
+}
+
+static struct async* reap_as(struct dev_state *ps)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	struct async *as = NULL;
+	struct usb_device *dev = ps->dev;
+
+	add_wait_queue(&ps->wait, &wait);
+	for (;;) {
+		__set_current_state(TASK_INTERRUPTIBLE);
+		if ((as = async_getcompleted(ps)))
+			break;
+		if (signal_pending(current))
+			break;
+		usb_unlock_device(dev);
+		schedule();
+		usb_lock_device(dev);
+	}
+	remove_wait_queue(&ps->wait, &wait);
+	set_current_state(TASK_RUNNING);
+	return as;
+}
+
+static int proc_reapurb(struct dev_state *ps, void __user *arg)
+{
+	struct async *as = reap_as(ps);
+	if (as)
+		return processcompl(as, (void __user * __user *)arg);
+	if (signal_pending(current))
+		return -EINTR;
+	return -EIO;
+}
+
+static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
+{
+	struct async *as;
+
+	if (!(as = async_getcompleted(ps)))
+		return -EAGAIN;
+	return processcompl(as, (void __user * __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+
+static int get_urb32(struct usbdevfs_urb *kurb,
+		     struct usbdevfs_urb32 __user *uurb)
+{
+	__u32  uptr;
+	if (get_user(kurb->type, &uurb->type) ||
+	    __get_user(kurb->endpoint, &uurb->endpoint) ||
+	    __get_user(kurb->status, &uurb->status) ||
+	    __get_user(kurb->flags, &uurb->flags) ||
+	    __get_user(kurb->buffer_length, &uurb->buffer_length) ||
+	    __get_user(kurb->actual_length, &uurb->actual_length) ||
+	    __get_user(kurb->start_frame, &uurb->start_frame) ||
+	    __get_user(kurb->number_of_packets, &uurb->number_of_packets) ||
+	    __get_user(kurb->error_count, &uurb->error_count) ||
+	    __get_user(kurb->signr, &uurb->signr))
+		return -EFAULT;
+
+	if (__get_user(uptr, &uurb->buffer))
+		return -EFAULT;
+	kurb->buffer = compat_ptr(uptr);
+	if (__get_user(uptr, &uurb->buffer))
+		return -EFAULT;
+	kurb->usercontext = compat_ptr(uptr);
+
+	return 0;
+}
+
+static int proc_submiturb_compat(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_urb uurb;
+
+	if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg))
+		return -EFAULT;
+
+	return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb __user *)arg)->iso_frame_desc, arg);
+}
+
+static int processcompl_compat(struct async *as, void __user * __user *arg)
+{
+	struct urb *urb = as->urb;
+	struct usbdevfs_urb32 __user *userurb = as->userurb;
+	void __user *addr = as->userurb;
+	unsigned int i;
+
+	if (as->userbuffer)
+		if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length))
+			return -EFAULT;
+	if (put_user(urb->status, &userurb->status))
+		return -EFAULT;
+	if (put_user(urb->actual_length, &userurb->actual_length))
+		return -EFAULT;
+	if (put_user(urb->error_count, &userurb->error_count))
+		return -EFAULT;
+
+	if (!(usb_pipeisoc(urb->pipe)))
+		return 0;
+	for (i = 0; i < urb->number_of_packets; i++) {
+		if (put_user(urb->iso_frame_desc[i].actual_length,
+			     &userurb->iso_frame_desc[i].actual_length))
+			return -EFAULT;
+		if (put_user(urb->iso_frame_desc[i].status,
+			     &userurb->iso_frame_desc[i].status))
+			return -EFAULT;
+	}
+
+	free_async(as);
+	if (put_user((u32)(u64)addr, (u32 __user *)arg))
+		return -EFAULT;
+	return 0;
+}
+
+static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
+{
+	struct async *as = reap_as(ps);
+	if (as)
+		return processcompl_compat(as, (void __user * __user *)arg);
+	if (signal_pending(current))
+		return -EINTR;
+	return -EIO;
+}
+
+static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
+{
+	struct async *as;
+
+	printk("reapurbnblock\n");
+	if (!(as = async_getcompleted(ps)))
+		return -EAGAIN;
+	printk("reap got as %p\n", as);
+	return processcompl_compat(as, (void __user * __user *)arg);
+}
+
+#endif
+
+static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_disconnectsignal ds;
+
+	if (copy_from_user(&ds, arg, sizeof(ds)))
+		return -EFAULT;
+	if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
+		return -EINVAL;
+	ps->discsignr = ds.signr;
+	ps->disccontext = ds.context;
+	return 0;
+}
+
+static int proc_claiminterface(struct dev_state *ps, void __user *arg)
+{
+	unsigned int ifnum;
+
+	if (get_user(ifnum, (unsigned int __user *)arg))
+		return -EFAULT;
+	return claimintf(ps, ifnum);
+}
+
+static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
+{
+	unsigned int ifnum;
+	int ret;
+
+	if (get_user(ifnum, (unsigned int __user *)arg))
+		return -EFAULT;
+	if ((ret = releaseintf(ps, ifnum)) < 0)
+		return ret;
+	destroy_async_on_interface (ps, ifnum);
+	return 0;
+}
+
+static int proc_ioctl (struct dev_state *ps, void __user *arg)
+{
+	struct usbdevfs_ioctl	ctrl;
+	int			size;
+	void			*buf = NULL;
+	int			retval = 0;
+	struct usb_interface    *intf = NULL;
+	struct usb_driver       *driver = NULL;
+	int			i;
+
+	/* get input parameters and alloc buffer */
+	if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
+		return -EFAULT;
+	if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
+		if ((buf = kmalloc (size, GFP_KERNEL)) == NULL)
+			return -ENOMEM;
+		if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
+			if (copy_from_user (buf, ctrl.data, size)) {
+				kfree (buf);
+				return -EFAULT;
+			}
+		} else {
+			memset (buf, 0, size);
+		}
+	}
+
+	if (!connected(ps->dev)) {
+		if (buf)
+			kfree(buf);
+		return -ENODEV;
+	}
+
+	if (ps->dev->state != USB_STATE_CONFIGURED)
+		retval = -EHOSTUNREACH;
+	else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
+               retval = -EINVAL;
+	else switch (ctrl.ioctl_code) {
+
+	/* disconnect kernel driver from interface */
+	case USBDEVFS_DISCONNECT:
+
+		/* don't allow the user to unbind the hub driver from
+		 * a hub with children to manage */
+		for (i = 0; i < ps->dev->maxchild; ++i) {
+			if (ps->dev->children[i])
+				retval = -EBUSY;
+		}
+		if (retval)
+			break;
+
+		down_write(&usb_bus_type.subsys.rwsem);
+		if (intf->dev.driver) {
+			driver = to_usb_driver(intf->dev.driver);
+			dev_dbg (&intf->dev, "disconnect by usbfs\n");
+			usb_driver_release_interface(driver, intf);
+		} else
+			retval = -ENODATA;
+		up_write(&usb_bus_type.subsys.rwsem);
+		break;
+
+	/* let kernel drivers try to (re)bind to the interface */
+	case USBDEVFS_CONNECT:
+		usb_unlock_device(ps->dev);
+		usb_lock_all_devices();
+		bus_rescan_devices(intf->dev.bus);
+		usb_unlock_all_devices();
+		usb_lock_device(ps->dev);
+		break;
+
+	/* talk directly to the interface's driver */
+	default:
+		down_read(&usb_bus_type.subsys.rwsem);
+		if (intf->dev.driver)
+			driver = to_usb_driver(intf->dev.driver);
+		if (driver == NULL || driver->ioctl == NULL) {
+			retval = -ENOTTY;
+		} else {
+			retval = driver->ioctl (intf, ctrl.ioctl_code, buf);
+			if (retval == -ENOIOCTLCMD)
+				retval = -ENOTTY;
+		}
+		up_read(&usb_bus_type.subsys.rwsem);
+	}
+
+	/* cleanup and return */
+	if (retval >= 0
+			&& (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
+			&& size > 0
+			&& copy_to_user (ctrl.data, buf, size) != 0)
+		retval = -EFAULT;
+	if (buf != NULL)
+		kfree (buf);
+	return retval;
+}
+
+/*
+ * NOTE:  All requests here that have interface numbers as parameters
+ * are assuming that somehow the configuration has been prevented from
+ * changing.  But there's no mechanism to ensure that...
+ */
+static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct usb_device *dev = ps->dev;
+	void __user *p = (void __user *)arg;
+	int ret = -ENOTTY;
+
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EPERM;
+	usb_lock_device(dev);
+	if (!connected(dev)) {
+		usb_unlock_device(dev);
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case USBDEVFS_CONTROL:
+		snoop(&dev->dev, "%s: CONTROL\n", __FUNCTION__);
+		ret = proc_control(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_BULK:
+		snoop(&dev->dev, "%s: BULK\n", __FUNCTION__);
+		ret = proc_bulk(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_RESETEP:
+		snoop(&dev->dev, "%s: RESETEP\n", __FUNCTION__);
+		ret = proc_resetep(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_RESET:
+		snoop(&dev->dev, "%s: RESET\n", __FUNCTION__);
+		ret = proc_resetdevice(ps);
+		break;
+
+	case USBDEVFS_CLEAR_HALT:
+		snoop(&dev->dev, "%s: CLEAR_HALT\n", __FUNCTION__);
+		ret = proc_clearhalt(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_GETDRIVER:
+		snoop(&dev->dev, "%s: GETDRIVER\n", __FUNCTION__);
+		ret = proc_getdriver(ps, p);
+		break;
+
+	case USBDEVFS_CONNECTINFO:
+		snoop(&dev->dev, "%s: CONNECTINFO\n", __FUNCTION__);
+		ret = proc_connectinfo(ps, p);
+		break;
+
+	case USBDEVFS_SETINTERFACE:
+		snoop(&dev->dev, "%s: SETINTERFACE\n", __FUNCTION__);
+		ret = proc_setintf(ps, p);
+		break;
+
+	case USBDEVFS_SETCONFIGURATION:
+		snoop(&dev->dev, "%s: SETCONFIGURATION\n", __FUNCTION__);
+		ret = proc_setconfig(ps, p);
+		break;
+
+	case USBDEVFS_SUBMITURB:
+		snoop(&dev->dev, "%s: SUBMITURB\n", __FUNCTION__);
+		ret = proc_submiturb(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+#ifdef CONFIG_COMPAT
+
+	case USBDEVFS_SUBMITURB32:
+		snoop(&dev->dev, "%s: SUBMITURB32\n", __FUNCTION__);
+		ret = proc_submiturb_compat(ps, p);
+		if (ret >= 0)
+			inode->i_mtime = CURRENT_TIME;
+		break;
+
+	case USBDEVFS_REAPURB32:
+		snoop(&dev->dev, "%s: REAPURB32\n", __FUNCTION__);
+		ret = proc_reapurb_compat(ps, p);
+		break;
+
+	case USBDEVFS_REAPURBNDELAY32:
+		snoop(&dev->dev, "%s: REAPURBDELAY32\n", __FUNCTION__);
+		ret = proc_reapurbnonblock_compat(ps, p);
+		break;
+
+#endif
+
+	case USBDEVFS_DISCARDURB:
+		snoop(&dev->dev, "%s: DISCARDURB\n", __FUNCTION__);
+		ret = proc_unlinkurb(ps, p);
+		break;
+
+	case USBDEVFS_REAPURB:
+		snoop(&dev->dev, "%s: REAPURB\n", __FUNCTION__);
+		ret = proc_reapurb(ps, p);
+		break;
+
+	case USBDEVFS_REAPURBNDELAY:
+		snoop(&dev->dev, "%s: REAPURBDELAY\n", __FUNCTION__);
+		ret = proc_reapurbnonblock(ps, p);
+		break;
+
+	case USBDEVFS_DISCSIGNAL:
+		snoop(&dev->dev, "%s: DISCSIGNAL\n", __FUNCTION__);
+		ret = proc_disconnectsignal(ps, p);
+		break;
+
+	case USBDEVFS_CLAIMINTERFACE:
+		snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __FUNCTION__);
+		ret = proc_claiminterface(ps, p);
+		break;
+
+	case USBDEVFS_RELEASEINTERFACE:
+		snoop(&dev->dev, "%s: RELEASEINTERFACE\n", __FUNCTION__);
+		ret = proc_releaseinterface(ps, p);
+		break;
+
+	case USBDEVFS_IOCTL:
+		snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
+		ret = proc_ioctl(ps, p);
+		break;
+	}
+	usb_unlock_device(dev);
+	if (ret >= 0)
+		inode->i_atime = CURRENT_TIME;
+	return ret;
+}
+
+/* No kernel lock - fine */
+static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct dev_state *ps = (struct dev_state *)file->private_data;
+        unsigned int mask = 0;
+
+	poll_wait(file, &ps->wait, wait);
+	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
+		mask |= POLLOUT | POLLWRNORM;
+	if (!connected(ps->dev))
+		mask |= POLLERR | POLLHUP;
+	return mask;
+}
+
+struct file_operations usbfs_device_file_operations = {
+	.llseek =	usbdev_lseek,
+	.read =		usbdev_read,
+	.poll =		usbdev_poll,
+	.ioctl =	usbdev_ioctl,
+	.open =		usbdev_open,
+	.release =	usbdev_release,
+};
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
new file mode 100644
index 0000000..80ce964
--- /dev/null
+++ b/drivers/usb/core/file.c
@@ -0,0 +1,227 @@
+/*
+ * drivers/usb/file.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
+ 	more docs, etc)
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ *     (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#define MAX_USB_MINORS	256
+static struct file_operations *usb_minors[MAX_USB_MINORS];
+static DEFINE_SPINLOCK(minor_lock);
+
+static int usb_open(struct inode * inode, struct file * file)
+{
+	int minor = iminor(inode);
+	struct file_operations *c;
+	int err = -ENODEV;
+	struct file_operations *old_fops, *new_fops = NULL;
+
+	spin_lock (&minor_lock);
+	c = usb_minors[minor];
+
+	if (!c || !(new_fops = fops_get(c))) {
+		spin_unlock(&minor_lock);
+		return err;
+	}
+	spin_unlock(&minor_lock);
+
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+	/* Curiouser and curiouser... NULL ->open() as "no device" ? */
+	if (file->f_op->open)
+		err = file->f_op->open(inode,file);
+	if (err) {
+		fops_put(file->f_op);
+		file->f_op = fops_get(old_fops);
+	}
+	fops_put(old_fops);
+	return err;
+}
+
+static struct file_operations usb_fops = {
+	.owner =	THIS_MODULE,
+	.open =		usb_open,
+};
+
+static struct class_simple *usb_class;
+
+int usb_major_init(void)
+{
+	int error;
+
+	error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
+	if (error) {
+		err("unable to get major %d for usb devices", USB_MAJOR);
+		goto out;
+	}
+
+	usb_class = class_simple_create(THIS_MODULE, "usb");
+	if (IS_ERR(usb_class)) {
+		err("class_simple_create failed for usb devices");
+		unregister_chrdev(USB_MAJOR, "usb");
+		goto out;
+	}
+
+	devfs_mk_dir("usb");
+
+out:
+	return error;
+}
+
+void usb_major_cleanup(void)
+{
+	class_simple_destroy(usb_class);
+	devfs_remove("usb");
+	unregister_chrdev(USB_MAJOR, "usb");
+}
+
+/**
+ * usb_register_dev - register a USB device, and ask for a minor number
+ * @intf: pointer to the usb_interface that is being registered
+ * @class_driver: pointer to the usb_class_driver for this device
+ *
+ * This should be called by all USB drivers that use the USB major number.
+ * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
+ * dynamically allocated out of the list of available ones.  If it is not
+ * enabled, the minor number will be based on the next available free minor,
+ * starting at the class_driver->minor_base.
+ *
+ * This function also creates the devfs file for the usb device, if devfs
+ * is enabled, and creates a usb class device in the sysfs tree.
+ *
+ * usb_deregister_dev() must be called when the driver is done with
+ * the minor numbers given out by this function.
+ *
+ * Returns -EINVAL if something bad happens with trying to register a
+ * device, and 0 on success.
+ */
+int usb_register_dev(struct usb_interface *intf,
+		     struct usb_class_driver *class_driver)
+{
+	int retval = -EINVAL;
+	int minor_base = class_driver->minor_base;
+	int minor = 0;
+	char name[BUS_ID_SIZE];
+	char *temp;
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+	/* 
+	 * We don't care what the device tries to start at, we want to start
+	 * at zero to pack the devices into the smallest available space with
+	 * no holes in the minor range.
+	 */
+	minor_base = 0;
+#endif
+	intf->minor = -1;
+
+	dbg ("looking for a minor, starting at %d", minor_base);
+
+	if (class_driver->fops == NULL)
+		goto exit;
+
+	spin_lock (&minor_lock);
+	for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
+		if (usb_minors[minor])
+			continue;
+
+		usb_minors[minor] = class_driver->fops;
+
+		retval = 0;
+		break;
+	}
+	spin_unlock (&minor_lock);
+
+	if (retval)
+		goto exit;
+
+	intf->minor = minor;
+
+	/* handle the devfs registration */
+	snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
+	devfs_mk_cdev(MKDEV(USB_MAJOR, minor), class_driver->mode, name);
+
+	/* create a usb class device for this usb interface */
+	temp = strrchr(name, '/');
+	if (temp && (temp[1] != 0x00))
+		++temp;
+	else
+		temp = name;
+	intf->class_dev = class_simple_device_add(usb_class, MKDEV(USB_MAJOR, minor), &intf->dev, "%s", temp);
+	if (IS_ERR(intf->class_dev)) {
+		spin_lock (&minor_lock);
+		usb_minors[intf->minor] = NULL;
+		spin_unlock (&minor_lock);
+		devfs_remove (name);
+		retval = PTR_ERR(intf->class_dev);
+	}
+exit:
+	return retval;
+}
+EXPORT_SYMBOL(usb_register_dev);
+
+/**
+ * usb_deregister_dev - deregister a USB device's dynamic minor.
+ * @intf: pointer to the usb_interface that is being deregistered
+ * @class_driver: pointer to the usb_class_driver for this device
+ *
+ * Used in conjunction with usb_register_dev().  This function is called
+ * when the USB driver is finished with the minor numbers gotten from a
+ * call to usb_register_dev() (usually when the device is disconnected
+ * from the system.)
+ *
+ * This function also cleans up the devfs file for the usb device, if devfs
+ * is enabled, and removes the usb class device from the sysfs tree.
+ * 
+ * This should be called by all drivers that use the USB major number.
+ */
+void usb_deregister_dev(struct usb_interface *intf,
+			struct usb_class_driver *class_driver)
+{
+	int minor_base = class_driver->minor_base;
+	char name[BUS_ID_SIZE];
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+	minor_base = 0;
+#endif
+
+	if (intf->minor == -1)
+		return;
+
+	dbg ("removing %d minor", intf->minor);
+
+	spin_lock (&minor_lock);
+	usb_minors[intf->minor] = NULL;
+	spin_unlock (&minor_lock);
+
+	snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
+	devfs_remove (name);
+	class_simple_device_remove(MKDEV(USB_MAJOR, intf->minor));
+	intf->class_dev = NULL;
+	intf->minor = -1;
+}
+EXPORT_SYMBOL(usb_deregister_dev);
+
+
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
new file mode 100644
index 0000000..b9a3dae
--- /dev/null
+++ b/drivers/usb/core/hcd-pci.c
@@ -0,0 +1,358 @@
+/*
+ * (C) Copyright David Brownell 2000-2002
+ * 
+ * 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/usb.h>
+#include "hcd.h"
+
+
+/* PCI-based HCs are normal, but custom bus glue should be ok */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_pci_probe - initialize PCI-based HCDs
+ * @dev: USB Host Controller being probed
+ * @id: pci hotplug id connecting controller to HCD framework
+ * Context: !in_interrupt()
+ *
+ * Allocates basic PCI resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct hc_driver	*driver;
+	struct usb_hcd		*hcd;
+	int			retval;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (!id || !(driver = (struct hc_driver *) id->driver_data))
+		return -EINVAL;
+
+	if (pci_enable_device (dev) < 0)
+		return -ENODEV;
+	dev->current_state = 0;
+	dev->dev.power.power_state = 0;
+	
+        if (!dev->irq) {
+        	dev_err (&dev->dev,
+			"Found HC with no IRQ.  Check BIOS/PCI %s setup!\n",
+			pci_name(dev));
+   	        retval = -ENODEV;
+		goto err1;
+        }
+
+	hcd = usb_create_hcd (driver, &dev->dev, pci_name(dev));
+	if (!hcd) {
+		retval = -ENOMEM;
+		goto err1;
+	}
+
+	if (driver->flags & HCD_MEMORY) {	// EHCI, OHCI
+		hcd->rsrc_start = pci_resource_start (dev, 0);
+		hcd->rsrc_len = pci_resource_len (dev, 0);
+		if (!request_mem_region (hcd->rsrc_start, hcd->rsrc_len,
+				driver->description)) {
+			dev_dbg (&dev->dev, "controller already in use\n");
+			retval = -EBUSY;
+			goto err2;
+		}
+		hcd->regs = ioremap_nocache (hcd->rsrc_start, hcd->rsrc_len);
+		if (hcd->regs == NULL) {
+			dev_dbg (&dev->dev, "error mapping memory\n");
+			retval = -EFAULT;
+			goto err3;
+		}
+
+	} else { 				// UHCI
+		int	region;
+
+		for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+			if (!(pci_resource_flags (dev, region) &
+					IORESOURCE_IO))
+				continue;
+
+			hcd->rsrc_start = pci_resource_start (dev, region);
+			hcd->rsrc_len = pci_resource_len (dev, region);
+			if (request_region (hcd->rsrc_start, hcd->rsrc_len,
+					driver->description))
+				break;
+		}
+		if (region == PCI_ROM_RESOURCE) {
+			dev_dbg (&dev->dev, "no i/o regions available\n");
+			retval = -EBUSY;
+			goto err1;
+		}
+	}
+
+#ifdef CONFIG_PCI_NAMES
+	hcd->product_desc = dev->pretty_name;
+#endif
+
+	pci_set_master (dev);
+
+	retval = usb_add_hcd (hcd, dev->irq, SA_SHIRQ);
+	if (retval != 0)
+		goto err4;
+	return retval;
+
+ err4:
+	if (driver->flags & HCD_MEMORY) {
+		iounmap (hcd->regs);
+ err3:
+		release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
+	} else
+		release_region (hcd->rsrc_start, hcd->rsrc_len);
+ err2:
+	usb_put_hcd (hcd);
+ err1:
+	pci_disable_device (dev);
+	dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
+	return retval;
+} 
+EXPORT_SYMBOL (usb_hcd_pci_probe);
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pci_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * Store this function in the HCD's struct pci_driver as remove().
+ */
+void usb_hcd_pci_remove (struct pci_dev *dev)
+{
+	struct usb_hcd		*hcd;
+
+	hcd = pci_get_drvdata(dev);
+	if (!hcd)
+		return;
+
+	usb_remove_hcd (hcd);
+	if (hcd->driver->flags & HCD_MEMORY) {
+		iounmap (hcd->regs);
+		release_mem_region (hcd->rsrc_start, hcd->rsrc_len);
+	} else {
+		release_region (hcd->rsrc_start, hcd->rsrc_len);
+	}
+	usb_put_hcd (hcd);
+	pci_disable_device(dev);
+}
+EXPORT_SYMBOL (usb_hcd_pci_remove);
+
+
+#ifdef	CONFIG_PM
+
+static char __attribute_used__ *pci_state(u32 state)
+{
+	switch (state) {
+	case 0:		return "D0";
+	case 1:		return "D1";
+	case 2:		return "D2";
+	case 3:		return "D3hot";
+	case 4:		return "D3cold";
+	}
+	return NULL;
+}
+
+/**
+ * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
+ * @dev: USB Host Controller being suspended
+ * @state: state that the controller is going into
+ *
+ * Store this function in the HCD's struct pci_driver as suspend().
+ */
+int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
+{
+	struct usb_hcd		*hcd;
+	int			retval = 0;
+	int			has_pci_pm;
+
+	hcd = pci_get_drvdata(dev);
+
+	/* even when the PCI layer rejects some of the PCI calls
+	 * below, HCs can try global suspend and reduce DMA traffic.
+	 * PM-sensitive HCDs may already have done this.
+	 */
+	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+	if (state > 4)
+		state = 4;
+
+	switch (hcd->state) {
+
+	/* entry if root hub wasn't yet suspended ... from sysfs,
+	 * without autosuspend, or if USB_SUSPEND isn't configured.
+	 */
+	case HC_STATE_RUNNING:
+		hcd->state = HC_STATE_QUIESCING;
+		retval = hcd->driver->suspend (hcd, state);
+		if (retval) {
+			dev_dbg (hcd->self.controller, 
+					"suspend fail, retval %d\n",
+					retval);
+			break;
+		}
+		hcd->state = HC_STATE_SUSPENDED;
+		/* FALLTHROUGH */
+
+	/* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
+	 * controller and/or root hub will already have been suspended,
+	 * but it won't be ready for a PCI resume call.
+	 *
+	 * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
+	 * have been called, otherwise root hub timers still run ...
+	 */
+	case HC_STATE_SUSPENDED:
+		if (state <= dev->current_state)
+			break;
+
+		/* no DMA or IRQs except in D0 */
+		if (!dev->current_state) {
+			pci_save_state (dev);
+			pci_disable_device (dev);
+			free_irq (hcd->irq, hcd);
+		}
+
+		if (!has_pci_pm) {
+			dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
+			break;
+		}
+
+		/* POLICY: ignore D1/D2/D3hot differences;
+		 * we know D3hot will always work.
+		 */
+		retval = pci_set_power_state (dev, state);
+		if (retval < 0 && state < 3) {
+			retval = pci_set_power_state (dev, 3);
+			if (retval == 0)
+				state = 3;
+		}
+		if (retval == 0) {
+			dev_dbg (hcd->self.controller, "--> PCI %s\n",
+					pci_state(dev->current_state));
+#ifdef	CONFIG_USB_SUSPEND
+			pci_enable_wake (dev, state, hcd->remote_wakeup);
+			pci_enable_wake (dev, 4, hcd->remote_wakeup);
+#endif
+		} else if (retval < 0) {
+			dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n",
+					pci_state(state), retval);
+			(void) usb_hcd_pci_resume (dev);
+			break;
+		}
+		break;
+	default:
+		dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
+			hcd->state);
+		retval = -EINVAL;
+		break;
+	}
+
+	/* update power_state **ONLY** to make sysfs happier */
+	if (retval == 0)
+		dev->dev.power.power_state = state;
+	return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_suspend);
+
+/**
+ * usb_hcd_pci_resume - power management resume of a PCI-based HCD
+ * @dev: USB Host Controller being resumed
+ *
+ * Store this function in the HCD's struct pci_driver as resume().
+ */
+int usb_hcd_pci_resume (struct pci_dev *dev)
+{
+	struct usb_hcd		*hcd;
+	int			retval;
+	int			has_pci_pm;
+
+	hcd = pci_get_drvdata(dev);
+	if (hcd->state != HC_STATE_SUSPENDED) {
+		dev_dbg (hcd->self.controller, 
+				"can't resume, not suspended!\n");
+		return 0;
+	}
+	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+	/* D3cold resume isn't usually reported this way... */
+	dev_dbg(hcd->self.controller, "resume from PCI %s%s\n",
+			pci_state(dev->current_state),
+			has_pci_pm ? "" : " (legacy)");
+
+	hcd->state = HC_STATE_RESUMING;
+
+	if (has_pci_pm)
+		pci_set_power_state (dev, 0);
+	dev->dev.power.power_state = 0;
+	retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
+				hcd->driver->description, hcd);
+	if (retval < 0) {
+		dev_err (hcd->self.controller,
+			"can't restore IRQ after resume!\n");
+		return retval;
+	}
+	hcd->saw_irq = 0;
+	pci_restore_state (dev);
+#ifdef	CONFIG_USB_SUSPEND
+	pci_enable_wake (dev, dev->current_state, 0);
+	pci_enable_wake (dev, 4, 0);
+#endif
+
+	retval = hcd->driver->resume (hcd);
+	if (!HC_IS_RUNNING (hcd->state)) {
+		dev_dbg (hcd->self.controller, 
+				"resume fail, retval %d\n", retval);
+		usb_hc_died (hcd);
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_resume);
+
+#endif	/* CONFIG_PM */
+
+
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
new file mode 100644
index 0000000..266e9e0
--- /dev/null
+++ b/drivers/usb/core/hcd.c
@@ -0,0 +1,1840 @@
+/*
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2002
+ * 
+ * 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/utsname.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+#include <linux/usb.h>
+
+#include "usb.h"
+#include "hcd.h"
+#include "hub.h"
+
+
+// #define USB_BANDWIDTH_MESSAGES
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver framework
+ *
+ * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
+ * HCD-specific behaviors/bugs.
+ *
+ * This does error checks, tracks devices and urbs, and delegates to a
+ * "hc_driver" only for code (and data) that really needs to know about
+ * hardware differences.  That includes root hub registers, i/o queues,
+ * and so on ... but as little else as possible.
+ *
+ * Shared code includes most of the "root hub" code (these are emulated,
+ * though each HC's hardware works differently) and PCI glue, plus request
+ * tracking overhead.  The HCD code should only block on spinlocks or on
+ * hardware handshaking; blocking on software events (such as other kernel
+ * threads releasing resources, or completing actions) is all generic.
+ *
+ * Happens the USB 2.0 spec says this would be invisible inside the "USBD",
+ * and includes mostly a "HCDI" (HCD Interface) along with some APIs used
+ * only by the hub driver ... and that neither should be seen or used by
+ * usb client device drivers.
+ *
+ * Contributors of ideas or unattributed patches include: David Brownell,
+ * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...
+ *
+ * HISTORY:
+ * 2002-02-21	Pull in most of the usb_bus support from usb.c; some
+ *		associated cleanup.  "usb_hcd" still != "usb_bus".
+ * 2001-12-12	Initial patch version for Linux 2.5.1 kernel.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* host controllers we manage */
+LIST_HEAD (usb_bus_list);
+EXPORT_SYMBOL_GPL (usb_bus_list);
+
+/* used when allocating bus numbers */
+#define USB_MAXBUS		64
+struct usb_busmap {
+	unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
+};
+static struct usb_busmap busmap;
+
+/* used when updating list of hcds */
+DECLARE_MUTEX (usb_bus_list_lock);	/* exported only for usbfs */
+EXPORT_SYMBOL_GPL (usb_bus_list_lock);
+
+/* used for controlling access to virtual root hubs */
+static DEFINE_SPINLOCK(hcd_root_hub_lock);
+
+/* used when updating hcd data */
+static DEFINE_SPINLOCK(hcd_data_lock);
+
+/* wait queue for synchronous unlinks */
+DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sharable chunks of root hub code.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#define KERNEL_REL	((LINUX_VERSION_CODE >> 16) & 0x0ff)
+#define KERNEL_VER	((LINUX_VERSION_CODE >> 8) & 0x0ff)
+
+/* usb 2.0 root hub device descriptor */
+static const u8 usb2_rh_dev_descriptor [18] = {
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x00, 0x02, /*  __le16 bcdUSB; v2.0 */
+
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x01,       /*  __u8  bDeviceProtocol; [ usb 2.0 single TT ]*/
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+
+	0x00, 0x00, /*  __le16 idVendor; */
+ 	0x00, 0x00, /*  __le16 idProduct; */
+	KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
+
+	0x03,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
+
+/* usb 1.1 root hub device descriptor */
+static const u8 usb11_rh_dev_descriptor [18] = {
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x10, 0x01, /*  __le16 bcdUSB; v1.1 */
+
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x00,       /*  __u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+
+	0x00, 0x00, /*  __le16 idVendor; */
+ 	0x00, 0x00, /*  __le16 idProduct; */
+	KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
+
+	0x03,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Configuration descriptors for our root hubs */
+
+static const u8 fs_rh_config_descriptor [] = {
+
+	/* one configuration */
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19, 0x00, /*  __le16 wTotalLength; */
+	0x01,       /*  __u8  bNumInterfaces; (1) */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0xc0,       /*  __u8  bmAttributes; 
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+      
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x00,       /*  __u8  if_iInterface; */
+     
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+	0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const u8 hs_rh_config_descriptor [] = {
+
+	/* one configuration */
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19, 0x00, /*  __le16 wTotalLength; */
+	0x01,       /*  __u8  bNumInterfaces; (1) */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0xc0,       /*  __u8  bmAttributes; 
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+      
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x00,       /*  __u8  if_iInterface; */
+     
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+	0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * helper routine for returning string descriptors in UTF-16LE
+ * input can actually be ISO-8859-1; ASCII is its 7-bit subset
+ */
+static int ascii2utf (char *s, u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *s++;
+		*utf++ = 0;
+	}
+	if (utfmax > 0) {
+		*utf = *s;
+		++retval;
+	}
+	return retval;
+}
+
+/*
+ * rh_string - provides manufacturer, product and serial strings for root hub
+ * @id: the string ID number (1: serial number, 2: product, 3: vendor)
+ * @hcd: the host controller for this root hub
+ * @type: string describing our driver 
+ * @data: return packet in UTF-16 LE
+ * @len: length of the return packet
+ *
+ * Produces either a manufacturer, product or serial number string for the
+ * virtual root hub device.
+ */
+static int rh_string (
+	int		id,
+	struct usb_hcd	*hcd,
+	u8		*data,
+	int		len
+) {
+	char buf [100];
+
+	// language ids
+	if (id == 0) {
+		buf[0] = 4;    buf[1] = 3;	/* 4 bytes string data */
+		buf[2] = 0x09; buf[3] = 0x04;	/* MSFT-speak for "en-us" */
+		len = min (len, 4);
+		memcpy (data, buf, len);
+		return len;
+
+	// serial number
+	} else if (id == 1) {
+		strlcpy (buf, hcd->self.bus_name, sizeof buf);
+
+	// product description
+	} else if (id == 2) {
+		strlcpy (buf, hcd->product_desc, sizeof buf);
+
+ 	// id 3 == vendor description
+	} else if (id == 3) {
+		snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname,
+			system_utsname.release, hcd->driver->description);
+
+	// unsupported IDs --> "protocol stall"
+	} else
+		return -EPIPE;
+
+	switch (len) {		/* All cases fall through */
+	default:
+		len = 2 + ascii2utf (buf, data + 2, len - 2);
+	case 2:
+		data [1] = 3;	/* type == string */
+	case 1:
+		data [0] = 2 * (strlen (buf) + 1);
+	case 0:
+		;		/* Compiler wants a statement here */
+	}
+	return len;
+}
+
+
+/* Root hub control transfers execute synchronously */
+static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
+{
+	struct usb_ctrlrequest *cmd;
+ 	u16		typeReq, wValue, wIndex, wLength;
+	u8		*ubuf = urb->transfer_buffer;
+	u8		tbuf [sizeof (struct usb_hub_descriptor)];
+	const u8	*bufp = tbuf;
+	int		len = 0;
+	int		patch_wakeup = 0;
+	unsigned long	flags;
+	int		status = 0;
+	int		n;
+
+	cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+	typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
+	wValue   = le16_to_cpu (cmd->wValue);
+	wIndex   = le16_to_cpu (cmd->wIndex);
+	wLength  = le16_to_cpu (cmd->wLength);
+
+	if (wLength > urb->transfer_buffer_length)
+		goto error;
+
+	urb->actual_length = 0;
+	switch (typeReq) {
+
+	/* DEVICE REQUESTS */
+
+	case DeviceRequest | USB_REQ_GET_STATUS:
+		tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
+				| (1 << USB_DEVICE_SELF_POWERED);
+		tbuf [1] = 0;
+		len = 2;
+		break;
+	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+		if (wValue == USB_DEVICE_REMOTE_WAKEUP)
+			hcd->remote_wakeup = 0;
+		else
+			goto error;
+		break;
+	case DeviceOutRequest | USB_REQ_SET_FEATURE:
+		if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP)
+			hcd->remote_wakeup = 1;
+		else
+			goto error;
+		break;
+	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+		tbuf [0] = 1;
+		len = 1;
+			/* FALLTHROUGH */
+	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		switch (wValue & 0xff00) {
+		case USB_DT_DEVICE << 8:
+			if (hcd->driver->flags & HCD_USB2)
+				bufp = usb2_rh_dev_descriptor;
+			else if (hcd->driver->flags & HCD_USB11)
+				bufp = usb11_rh_dev_descriptor;
+			else
+				goto error;
+			len = 18;
+			break;
+		case USB_DT_CONFIG << 8:
+			if (hcd->driver->flags & HCD_USB2) {
+				bufp = hs_rh_config_descriptor;
+				len = sizeof hs_rh_config_descriptor;
+			} else {
+				bufp = fs_rh_config_descriptor;
+				len = sizeof fs_rh_config_descriptor;
+			}
+			if (hcd->can_wakeup)
+				patch_wakeup = 1;
+			break;
+		case USB_DT_STRING << 8:
+			n = rh_string (wValue & 0xff, hcd, ubuf, wLength);
+			if (n < 0)
+				goto error;
+			urb->actual_length = n;
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case DeviceRequest | USB_REQ_GET_INTERFACE:
+		tbuf [0] = 0;
+		len = 1;
+			/* FALLTHROUGH */
+	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+		break;
+	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+		// wValue == urb->dev->devaddr
+		dev_dbg (hcd->self.controller, "root hub device address %d\n",
+			wValue);
+		break;
+
+	/* INTERFACE REQUESTS (no defined feature/status flags) */
+
+	/* ENDPOINT REQUESTS */
+
+	case EndpointRequest | USB_REQ_GET_STATUS:
+		// ENDPOINT_HALT flag
+		tbuf [0] = 0;
+		tbuf [1] = 0;
+		len = 2;
+			/* FALLTHROUGH */
+	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+	case EndpointOutRequest | USB_REQ_SET_FEATURE:
+		dev_dbg (hcd->self.controller, "no endpoint features yet\n");
+		break;
+
+	/* CLASS REQUESTS (and errors) */
+
+	default:
+		/* non-generic request */
+		if (HC_IS_SUSPENDED (hcd->state))
+			status = -EAGAIN;
+		else {
+			switch (typeReq) {
+			case GetHubStatus:
+			case GetPortStatus:
+				len = 4;
+				break;
+			case GetHubDescriptor:
+				len = sizeof (struct usb_hub_descriptor);
+				break;
+			}
+			status = hcd->driver->hub_control (hcd,
+				typeReq, wValue, wIndex,
+				tbuf, wLength);
+		}
+		break;
+error:
+		/* "protocol stall" on error */
+		status = -EPIPE;
+	}
+
+	if (status) {
+		len = 0;
+		if (status != -EPIPE) {
+			dev_dbg (hcd->self.controller,
+				"CTRL: TypeReq=0x%x val=0x%x "
+				"idx=0x%x len=%d ==> %d\n",
+				typeReq, wValue, wIndex,
+				wLength, urb->status);
+		}
+	}
+	if (len) {
+		if (urb->transfer_buffer_length < len)
+			len = urb->transfer_buffer_length;
+		urb->actual_length = len;
+		// always USB_DIR_IN, toward host
+		memcpy (ubuf, bufp, len);
+
+		/* report whether RH hardware supports remote wakeup */
+		if (patch_wakeup &&
+				len > offsetof (struct usb_config_descriptor,
+						bmAttributes))
+			((struct usb_config_descriptor *)ubuf)->bmAttributes
+				|= USB_CONFIG_ATT_WAKEUP;
+	}
+
+	/* any errors get returned through the urb completion */
+	local_irq_save (flags);
+	spin_lock (&urb->lock);
+	if (urb->status == -EINPROGRESS)
+		urb->status = status;
+	spin_unlock (&urb->lock);
+	usb_hcd_giveback_urb (hcd, urb, NULL);
+	local_irq_restore (flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Root Hub interrupt transfers are synthesized with a timer.
+ * Completions are called in_interrupt() but not in_irq().
+ *
+ * Note: some root hubs (including common UHCI based designs) can't
+ * correctly issue port change IRQs.  They're the ones that _need_ a
+ * timer; most other root hubs don't.  Some systems could save a
+ * lot of battery power by eliminating these root hub timer IRQs.
+ */
+
+static void rh_report_status (unsigned long ptr);
+
+static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) 
+{
+	int	len = 1 + (urb->dev->maxchild / 8);
+
+	/* rh_timer protected by hcd_data_lock */
+	if (hcd->rh_timer.data || urb->transfer_buffer_length < len) {
+		dev_dbg (hcd->self.controller,
+				"not queuing rh status urb, stat %d\n",
+				urb->status);
+		return -EINVAL;
+	}
+
+	init_timer (&hcd->rh_timer);
+	hcd->rh_timer.function = rh_report_status;
+	hcd->rh_timer.data = (unsigned long) urb;
+	/* USB 2.0 spec says 256msec; this is close enough */
+	hcd->rh_timer.expires = jiffies + HZ/4;
+	add_timer (&hcd->rh_timer);
+	urb->hcpriv = hcd;	/* nonzero to indicate it's queued */
+	return 0;
+}
+
+/* timer callback */
+
+static void rh_report_status (unsigned long ptr)
+{
+	struct urb	*urb;
+	struct usb_hcd	*hcd;
+	int		length = 0;
+	unsigned long	flags;
+
+	urb = (struct urb *) ptr;
+	local_irq_save (flags);
+	spin_lock (&urb->lock);
+
+	/* do nothing if the urb's been unlinked */
+	if (!urb->dev
+			|| urb->status != -EINPROGRESS
+			|| (hcd = urb->dev->bus->hcpriv) == NULL) {
+		spin_unlock (&urb->lock);
+		local_irq_restore (flags);
+		return;
+	}
+
+	/* complete the status urb, or retrigger the timer */
+	spin_lock (&hcd_data_lock);
+	if (urb->dev->state == USB_STATE_CONFIGURED) {
+		length = hcd->driver->hub_status_data (
+					hcd, urb->transfer_buffer);
+		if (length > 0) {
+			hcd->rh_timer.data = 0;
+			urb->actual_length = length;
+			urb->status = 0;
+			urb->hcpriv = NULL;
+		} else
+			mod_timer (&hcd->rh_timer, jiffies + HZ/4);
+	}
+	spin_unlock (&hcd_data_lock);
+	spin_unlock (&urb->lock);
+
+	/* local irqs are always blocked in completions */
+	if (length > 0)
+		usb_hcd_giveback_urb (hcd, urb, NULL);
+	local_irq_restore (flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
+{
+	if (usb_pipeint (urb->pipe)) {
+		int		retval;
+		unsigned long	flags;
+
+		spin_lock_irqsave (&hcd_data_lock, flags);
+		retval = rh_status_urb (hcd, urb);
+		spin_unlock_irqrestore (&hcd_data_lock, flags);
+		return retval;
+	}
+	if (usb_pipecontrol (urb->pipe))
+		return rh_call_control (hcd, urb);
+	else
+		return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+	unsigned long	flags;
+
+	/* note:  always a synchronous unlink */
+	if ((unsigned long) urb == hcd->rh_timer.data) {
+		del_timer_sync (&hcd->rh_timer);
+		hcd->rh_timer.data = 0;
+
+		local_irq_save (flags);
+		urb->hcpriv = NULL;
+		usb_hcd_giveback_urb (hcd, urb, NULL);
+		local_irq_restore (flags);
+
+	} else if (usb_pipeendpoint(urb->pipe) == 0) {
+		spin_lock_irq(&urb->lock);	/* from usb_kill_urb */
+		++urb->reject;
+		spin_unlock_irq(&urb->lock);
+
+		wait_event(usb_kill_urb_queue,
+				atomic_read(&urb->use_count) == 0);
+
+		spin_lock_irq(&urb->lock);
+		--urb->reject;
+		spin_unlock_irq(&urb->lock);
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+struct usb_bus *usb_bus_get (struct usb_bus *bus)
+{
+	struct class_device *tmp;
+
+	if (!bus)
+		return NULL;
+
+	tmp = class_device_get(&bus->class_dev);
+	if (tmp)        
+		return to_usb_bus(tmp);
+	else
+		return NULL;
+}
+
+/* exported only within usbcore */
+void usb_bus_put (struct usb_bus *bus)
+{
+	if (bus)
+		class_device_put(&bus->class_dev);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void usb_host_release(struct class_device *class_dev)
+{
+	struct usb_bus *bus = to_usb_bus(class_dev);
+
+	if (bus->release)
+		bus->release(bus);
+}
+
+static struct class usb_host_class = {
+	.name		= "usb_host",
+	.release	= &usb_host_release,
+};
+
+int usb_host_init(void)
+{
+	return class_register(&usb_host_class);
+}
+
+void usb_host_cleanup(void)
+{
+	class_unregister(&usb_host_class);
+}
+
+/**
+ * usb_bus_init - shared initialization code
+ * @bus: the bus structure being initialized
+ *
+ * This code is used to initialize a usb_bus structure, memory for which is
+ * separately managed.
+ */
+static void usb_bus_init (struct usb_bus *bus)
+{
+	memset (&bus->devmap, 0, sizeof(struct usb_devmap));
+
+	bus->devnum_next = 1;
+
+	bus->root_hub = NULL;
+	bus->hcpriv = NULL;
+	bus->busnum = -1;
+	bus->bandwidth_allocated = 0;
+	bus->bandwidth_int_reqs  = 0;
+	bus->bandwidth_isoc_reqs = 0;
+
+	INIT_LIST_HEAD (&bus->bus_list);
+
+	class_device_initialize(&bus->class_dev);
+	bus->class_dev.class = &usb_host_class;
+}
+
+/**
+ * usb_alloc_bus - creates a new USB host controller structure
+ * @op: pointer to a struct usb_operations that this bus structure should use
+ * Context: !in_interrupt()
+ *
+ * Creates a USB host controller bus structure with the specified 
+ * usb_operations and initializes all the necessary internal objects.
+ *
+ * If no memory is available, NULL is returned.
+ *
+ * The caller should call usb_put_bus() when it is finished with the structure.
+ */
+struct usb_bus *usb_alloc_bus (struct usb_operations *op)
+{
+	struct usb_bus *bus;
+
+	bus = kmalloc (sizeof *bus, GFP_KERNEL);
+	if (!bus)
+		return NULL;
+	memset(bus, 0, sizeof(struct usb_bus));
+	usb_bus_init (bus);
+	bus->op = op;
+	return bus;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_register_bus - registers the USB host controller with the usb core
+ * @bus: pointer to the bus to register
+ * Context: !in_interrupt()
+ *
+ * Assigns a bus number, and links the controller into usbcore data
+ * structures so that it can be seen by scanning the bus list.
+ */
+static int usb_register_bus(struct usb_bus *bus)
+{
+	int busnum;
+	int retval;
+
+	down (&usb_bus_list_lock);
+	busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+	if (busnum < USB_MAXBUS) {
+		set_bit (busnum, busmap.busmap);
+		bus->busnum = busnum;
+	} else {
+		printk (KERN_ERR "%s: too many buses\n", usbcore_name);
+		up(&usb_bus_list_lock);
+		return -E2BIG;
+	}
+
+	snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum);
+	bus->class_dev.dev = bus->controller;
+	retval = class_device_add(&bus->class_dev);
+	if (retval) {
+		clear_bit(busnum, busmap.busmap);
+		up(&usb_bus_list_lock);
+		return retval;
+	}
+
+	/* Add it to the local list of buses */
+	list_add (&bus->bus_list, &usb_bus_list);
+	up (&usb_bus_list_lock);
+
+	usbfs_add_bus (bus);
+	usbmon_notify_bus_add (bus);
+
+	dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
+	return 0;
+}
+
+/**
+ * usb_deregister_bus - deregisters the USB host controller
+ * @bus: pointer to the bus to deregister
+ * Context: !in_interrupt()
+ *
+ * Recycles the bus number, and unlinks the controller from usbcore data
+ * structures so that it won't be seen by scanning the bus list.
+ */
+static void usb_deregister_bus (struct usb_bus *bus)
+{
+	dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);
+
+	/*
+	 * NOTE: make sure that all the devices are removed by the
+	 * controller code, as well as having it call this when cleaning
+	 * itself up
+	 */
+	down (&usb_bus_list_lock);
+	list_del (&bus->bus_list);
+	up (&usb_bus_list_lock);
+
+	usbmon_notify_bus_remove (bus);
+	usbfs_remove_bus (bus);
+
+	clear_bit (bus->busnum, busmap.busmap);
+
+	class_device_del(&bus->class_dev);
+}
+
+/**
+ * usb_hcd_register_root_hub - called by HCD to register its root hub 
+ * @usb_dev: the usb root hub device to be registered.
+ * @hcd: host controller for this root hub
+ *
+ * The USB host controller calls this function to register the root hub
+ * properly with the USB subsystem.  It sets up the device properly in
+ * the device tree and stores the root_hub pointer in the bus structure,
+ * then calls usb_new_device() to register the usb device.  It also
+ * assigns the root hub's USB address (always 1).
+ */
+int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd)
+{
+	struct device *parent_dev = hcd->self.controller;
+	const int devnum = 1;
+	int retval;
+
+	/* hcd->driver->start() reported can_wakeup, probably with
+	 * assistance from board's boot firmware.
+	 * NOTE:  normal devices won't enable wakeup by default.
+	 */
+	if (hcd->can_wakeup)
+		dev_dbg (parent_dev, "supports USB remote wakeup\n");
+	hcd->remote_wakeup = hcd->can_wakeup;
+
+	usb_dev->devnum = devnum;
+	usb_dev->bus->devnum_next = devnum + 1;
+	memset (&usb_dev->bus->devmap.devicemap, 0,
+			sizeof usb_dev->bus->devmap.devicemap);
+	set_bit (devnum, usb_dev->bus->devmap.devicemap);
+	usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
+
+	down (&usb_bus_list_lock);
+	usb_dev->bus->root_hub = usb_dev;
+
+	usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
+	retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
+	if (retval != sizeof usb_dev->descriptor) {
+		usb_dev->bus->root_hub = NULL;
+		up (&usb_bus_list_lock);
+		dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
+				usb_dev->dev.bus_id, retval);
+		return (retval < 0) ? retval : -EMSGSIZE;
+	}
+
+	usb_lock_device (usb_dev);
+	retval = usb_new_device (usb_dev);
+	usb_unlock_device (usb_dev);
+	if (retval) {
+		usb_dev->bus->root_hub = NULL;
+		dev_err (parent_dev, "can't register root hub for %s, %d\n",
+				usb_dev->dev.bus_id, retval);
+	}
+	up (&usb_bus_list_lock);
+
+	if (retval == 0) {
+		spin_lock_irq (&hcd_root_hub_lock);
+		hcd->rh_registered = 1;
+		spin_unlock_irq (&hcd_root_hub_lock);
+
+		/* Did the HC die before the root hub was registered? */
+		if (hcd->state == HC_STATE_HALT)
+			usb_hc_died (hcd);	/* This time clean up */
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub);
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * 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
+ * @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; only periodic transfers need to be
+ * scheduled in software, this function is only used for such scheduling.
+ */
+long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
+{
+	unsigned long	tmp;
+
+	switch (speed) {
+	case USB_SPEED_LOW: 	/* INTR only */
+		if (is_input) {
+			tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		} else {
+			tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+	case USB_SPEED_FULL:	/* ISOC or INTR */
+		if (isoc) {
+			tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+		} else {
+			tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (9107L + BW_HOST_DELAY + tmp);
+		}
+	case USB_SPEED_HIGH:	/* ISOC or INTR */
+		// FIXME adjust for input vs output
+		if (isoc)
+			tmp = HS_USECS (bytecount);
+		else
+			tmp = HS_USECS_ISO (bytecount);
+		return tmp;
+	default:
+		pr_debug ("%s: bogus device speed!\n", usbcore_name);
+		return -1;
+	}
+}
+EXPORT_SYMBOL (usb_calc_bus_time);
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or -ENOSPC if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ *
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc.  Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ *
+ * Similarly for isochronous transfers...
+ *
+ * Individual HCDs can schedule more directly ... this logic
+ * is not correct for high speed transfers.
+ */
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+	unsigned int	pipe = urb->pipe;
+	long		bustime;
+	int		is_in = usb_pipein (pipe);
+	int		is_iso = usb_pipeisoc (pipe);
+	int		old_alloc = dev->bus->bandwidth_allocated;
+	int		new_alloc;
+
+
+	bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
+			usb_maxpacket (dev, pipe, !is_in)));
+	if (is_iso)
+		bustime /= urb->number_of_packets;
+
+	new_alloc = old_alloc + (int) bustime;
+	if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
+#ifdef	DEBUG
+		char	*mode = 
+#ifdef CONFIG_USB_BANDWIDTH
+			"";
+#else
+			"would have ";
+#endif
+		dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
+			mode, old_alloc, bustime, new_alloc);
+#endif
+#ifdef CONFIG_USB_BANDWIDTH
+		bustime = -ENOSPC;	/* report error */
+#endif
+	}
+
+	return bustime;
+}
+EXPORT_SYMBOL (usb_check_bandwidth);
+
+
+/**
+ * usb_claim_bandwidth - records bandwidth for a periodic transfer
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @bustime: bandwidth consumed, in (average) microseconds per frame
+ * @isoc: true iff the request is isochronous
+ *
+ * Bus bandwidth reservations are recorded purely for diagnostic purposes.
+ * HCDs are expected not to overcommit periodic bandwidth, and to record such
+ * reservations whenever endpoints are added to the periodic schedule.
+ *
+ * FIXME averaging per-frame is suboptimal.  Better to sum over the HCD's
+ * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
+ * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
+ * large its periodic schedule is.
+ */
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+	dev->bus->bandwidth_allocated += bustime;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs++;
+	else
+		dev->bus->bandwidth_int_reqs++;
+	urb->bandwidth = bustime;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+	dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
+		bustime,
+		isoc ? "ISOC" : "INTR",
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+}
+EXPORT_SYMBOL (usb_claim_bandwidth);
+
+
+/**
+ * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @isoc: true iff the request is isochronous
+ *
+ * This records that previously allocated bandwidth has been released.
+ * Bandwidth is released when endpoints are removed from the host controller's
+ * periodic schedule.
+ */
+void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
+{
+	dev->bus->bandwidth_allocated -= urb->bandwidth;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs--;
+	else
+		dev->bus->bandwidth_int_reqs--;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+	dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
+		urb->bandwidth,
+		isoc ? "ISOC" : "INTR",
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+	urb->bandwidth = 0;
+}
+EXPORT_SYMBOL (usb_release_bandwidth);
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic HC operations.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static void urb_unlink (struct urb *urb)
+{
+	unsigned long		flags;
+
+	/* Release any periodic transfer bandwidth */
+	if (urb->bandwidth)
+		usb_release_bandwidth (urb->dev, urb,
+			usb_pipeisoc (urb->pipe));
+
+	/* clear all state linking urb to this dev (and hcd) */
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	list_del_init (&urb->urb_list);
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+	usb_put_dev (urb->dev);
+}
+
+
+/* may be called in any context with a valid urb->dev usecount
+ * caller surrenders "ownership" of urb
+ * expects usb_submit_urb() to have sanity checked and conditioned all
+ * inputs in the urb
+ */
+static int hcd_submit_urb (struct urb *urb, int mem_flags)
+{
+	int			status;
+	struct usb_hcd		*hcd = urb->dev->bus->hcpriv;
+	struct usb_host_endpoint *ep;
+	unsigned long		flags;
+
+	if (!hcd)
+		return -ENODEV;
+
+	usbmon_urb_submit(&hcd->self, urb);
+
+	/*
+	 * Atomically queue the urb,  first to our records, then to the HCD.
+	 * Access to urb->status is controlled by urb->lock ... changes on
+	 * i/o completion (normal or fault) or unlinking.
+	 */
+
+	// FIXME:  verify that quiescing hc works right (RH cleans up)
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
+			[usb_pipeendpoint(urb->pipe)];
+	if (unlikely (!ep))
+		status = -ENOENT;
+	else if (unlikely (urb->reject))
+		status = -EPERM;
+	else switch (hcd->state) {
+	case HC_STATE_RUNNING:
+	case HC_STATE_RESUMING:
+		usb_get_dev (urb->dev);
+		list_add_tail (&urb->urb_list, &ep->urb_list);
+		status = 0;
+		break;
+	default:
+		status = -ESHUTDOWN;
+		break;
+	}
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+	if (status) {
+		INIT_LIST_HEAD (&urb->urb_list);
+		usbmon_urb_submit_error(&hcd->self, urb, status);
+		return status;
+	}
+
+	/* increment urb's reference count as part of giving it to the HCD
+	 * (which now controls it).  HCD guarantees that it either returns
+	 * an error or calls giveback(), but not both.
+	 */
+	urb = usb_get_urb (urb);
+	atomic_inc (&urb->use_count);
+
+	if (urb->dev == hcd->self.root_hub) {
+		/* NOTE:  requirement on hub callers (usbfs and the hub
+		 * driver, for now) that URBs' urb->transfer_buffer be
+		 * valid and usb_buffer_{sync,unmap}() not be needed, since
+		 * they could clobber root hub response data.
+		 */
+		status = rh_urb_enqueue (hcd, urb);
+		goto done;
+	}
+
+	/* lower level hcd code should use *_dma exclusively,
+	 * unless it uses pio or talks to another transport.
+	 */
+	if (hcd->self.controller->dma_mask) {
+		if (usb_pipecontrol (urb->pipe)
+			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+			urb->setup_dma = dma_map_single (
+					hcd->self.controller,
+					urb->setup_packet,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+		if (urb->transfer_buffer_length != 0
+			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+			urb->transfer_dma = dma_map_single (
+					hcd->self.controller,
+					urb->transfer_buffer,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? DMA_FROM_DEVICE
+					    : DMA_TO_DEVICE);
+	}
+
+	status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
+done:
+	if (unlikely (status)) {
+		urb_unlink (urb);
+		atomic_dec (&urb->use_count);
+		if (urb->reject)
+			wake_up (&usb_kill_urb_queue);
+		usb_put_urb (urb);
+		usbmon_urb_submit_error(&hcd->self, urb, status);
+	}
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called in any context */
+static int hcd_get_frame_number (struct usb_device *udev)
+{
+	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;
+	if (!HC_IS_RUNNING (hcd->state))
+		return -ESHUTDOWN;
+	return hcd->driver->get_frame_number (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* this makes the hcd giveback() the urb more quickly, by kicking it
+ * off hardware queues (which may take a while) and returning it as
+ * soon as practical.  we've already set up the urb's return status,
+ * but we can't know if the callback completed already.
+ */
+static int
+unlink1 (struct usb_hcd *hcd, struct urb *urb)
+{
+	int		value;
+
+	if (urb->dev == hcd->self.root_hub)
+		value = usb_rh_urb_dequeue (hcd, urb);
+	else {
+
+		/* The only reason an HCD might fail this call is if
+		 * it has not yet fully queued the urb to begin with.
+		 * Such failures should be harmless. */
+		value = hcd->driver->urb_dequeue (hcd, urb);
+	}
+
+	if (value != 0)
+		dev_dbg (hcd->self.controller, "dequeue %p --> %d\n",
+				urb, value);
+	return value;
+}
+
+/*
+ * called in any context
+ *
+ * caller guarantees urb won't be recycled till both unlink()
+ * and the urb's completion function return
+ */
+static int hcd_unlink_urb (struct urb *urb, int status)
+{
+	struct usb_host_endpoint	*ep;
+	struct usb_hcd			*hcd = NULL;
+	struct device			*sys = NULL;
+	unsigned long			flags;
+	struct list_head		*tmp;
+	int				retval;
+
+	if (!urb)
+		return -EINVAL;
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+	ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out)
+			[usb_pipeendpoint(urb->pipe)];
+	if (!ep)
+		return -ENODEV;
+
+	/*
+	 * we contend for urb->status with the hcd core,
+	 * which changes it while returning the urb.
+	 *
+	 * Caller guaranteed that the urb pointer hasn't been freed, and
+	 * that it was submitted.  But as a rule it can't know whether or
+	 * not it's already been unlinked ... so we respect the reversed
+	 * lock sequence needed for the usb_hcd_giveback_urb() code paths
+	 * (urb lock, then hcd_data_lock) in case some other CPU is now
+	 * unlinking it.
+	 */
+	spin_lock_irqsave (&urb->lock, flags);
+	spin_lock (&hcd_data_lock);
+
+	sys = &urb->dev->dev;
+	hcd = urb->dev->bus->hcpriv;
+	if (hcd == NULL) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	/* running ~= hc unlink handshake works (irq, timer, etc)
+	 * halted ~= no unlink handshake is needed
+	 * suspended, resuming == should never happen
+	 */
+	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT);
+
+	/* insist the urb is still queued */
+	list_for_each(tmp, &ep->urb_list) {
+		if (tmp == &urb->urb_list)
+			break;
+	}
+	if (tmp != &urb->urb_list) {
+		retval = -EIDRM;
+		goto done;
+	}
+
+	/* Any status except -EINPROGRESS means something already started to
+	 * unlink this URB from the hardware.  So there's no more work to do.
+	 */
+	if (urb->status != -EINPROGRESS) {
+		retval = -EBUSY;
+		goto done;
+	}
+
+	/* IRQ setup can easily be broken so that USB controllers
+	 * never get completion IRQs ... maybe even the ones we need to
+	 * finish unlinking the initial failed usb_set_address()
+	 * or device descriptor fetch.
+	 */
+	if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) {
+		dev_warn (hcd->self.controller, "Unlink after no-IRQ?  "
+			"Controller is probably using the wrong IRQ."
+			"\n");
+		hcd->saw_irq = 1;
+	}
+
+	urb->status = status;
+
+	spin_unlock (&hcd_data_lock);
+	spin_unlock_irqrestore (&urb->lock, flags);
+
+	retval = unlink1 (hcd, urb);
+	if (retval == 0)
+		retval = -EINPROGRESS;
+	return retval;
+
+done:
+	spin_unlock (&hcd_data_lock);
+	spin_unlock_irqrestore (&urb->lock, flags);
+	if (retval != -EIDRM && sys && sys->driver)
+		dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* disables the endpoint: cancels any pending urbs, then synchronizes with
+ * the hcd to make sure all endpoint state is gone from hardware. use for
+ * set_configuration, set_interface, driver removal, physical disconnect.
+ *
+ * example:  a qh stored in ep->hcpriv, holding state related to endpoint
+ * type, maxpacket size, toggle, halt status, and scheduling.
+ */
+static void
+hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+{
+	struct usb_hcd		*hcd;
+	struct urb		*urb;
+
+	hcd = udev->bus->hcpriv;
+
+	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT);
+
+	local_irq_disable ();
+
+	/* FIXME move most of this into message.c as part of its
+	 * endpoint disable logic
+	 */
+
+	/* ep is already gone from udev->ep_{in,out}[]; no more submits */
+rescan:
+	spin_lock (&hcd_data_lock);
+	list_for_each_entry (urb, &ep->urb_list, urb_list) {
+		int	tmp;
+
+		/* another cpu may be in hcd, spinning on hcd_data_lock
+		 * to giveback() this urb.  the races here should be
+		 * small, but a full fix needs a new "can't submit"
+		 * urb state.
+		 * FIXME urb->reject should allow that...
+		 */
+		if (urb->status != -EINPROGRESS)
+			continue;
+		usb_get_urb (urb);
+		spin_unlock (&hcd_data_lock);
+
+		spin_lock (&urb->lock);
+		tmp = urb->status;
+		if (tmp == -EINPROGRESS)
+			urb->status = -ESHUTDOWN;
+		spin_unlock (&urb->lock);
+
+		/* kick hcd unless it's already returning this */
+		if (tmp == -EINPROGRESS) {
+			tmp = urb->pipe;
+			unlink1 (hcd, urb);
+			dev_dbg (hcd->self.controller,
+				"shutdown urb %p pipe %08x ep%d%s%s\n",
+				urb, tmp, usb_pipeendpoint (tmp),
+				(tmp & USB_DIR_IN) ? "in" : "out",
+				({ char *s; \
+				 switch (usb_pipetype (tmp)) { \
+				 case PIPE_CONTROL:	s = ""; break; \
+				 case PIPE_BULK:	s = "-bulk"; break; \
+				 case PIPE_INTERRUPT:	s = "-intr"; break; \
+				 default: 		s = "-iso"; break; \
+				}; s;}));
+		}
+		usb_put_urb (urb);
+
+		/* list contents may have changed */
+		goto rescan;
+	}
+	spin_unlock (&hcd_data_lock);
+	local_irq_enable ();
+
+	/* synchronize with the hardware, so old configuration state
+	 * clears out immediately (and will be freed).
+	 */
+	might_sleep ();
+	if (hcd->driver->endpoint_disable)
+		hcd->driver->endpoint_disable (hcd, ep);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_SUSPEND
+
+static int hcd_hub_suspend (struct usb_bus *bus)
+{
+	struct usb_hcd		*hcd;
+
+	hcd = container_of (bus, struct usb_hcd, self);
+	if (hcd->driver->hub_suspend)
+		return hcd->driver->hub_suspend (hcd);
+	return 0;
+}
+
+static int hcd_hub_resume (struct usb_bus *bus)
+{
+	struct usb_hcd		*hcd;
+
+	hcd = container_of (bus, struct usb_hcd, self);
+	if (hcd->driver->hub_resume)
+		return hcd->driver->hub_resume (hcd);
+	return 0;
+}
+
+/**
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
+ * @hcd: host controller for this root hub
+ *
+ * The USB host controller calls this function when its root hub is
+ * suspended (with the remote wakeup feature enabled) and a remote
+ * wakeup request is received.  It queues a request for khubd to
+ * resume the root hub.
+ */
+void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&hcd_root_hub_lock, flags);
+	if (hcd->rh_registered)
+		usb_resume_root_hub (hcd->self.root_hub);
+	spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+}
+
+#else
+void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
+{
+}
+#endif
+EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_OTG
+
+/**
+ * usb_bus_start_enum - start immediate enumeration (for OTG)
+ * @bus: the bus (must use hcd framework)
+ * @port_num: 1-based number of port; usually bus->otg_port
+ * Context: in_interrupt()
+ *
+ * Starts enumeration, with an immediate reset followed later by
+ * khubd identifying and possibly configuring the device.
+ * This is needed by OTG controller drivers, where it helps meet
+ * HNP protocol timing requirements for starting a port reset.
+ */
+int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
+{
+	struct usb_hcd		*hcd;
+	int			status = -EOPNOTSUPP;
+
+	/* NOTE: since HNP can't start by grabbing the bus's address0_sem,
+	 * boards with root hubs hooked up to internal devices (instead of
+	 * just the OTG port) may need more attention to resetting...
+	 */
+	hcd = container_of (bus, struct usb_hcd, self);
+	if (port_num && hcd->driver->start_port_reset)
+		status = hcd->driver->start_port_reset(hcd, port_num);
+
+	/* run khubd shortly after (first) root port reset finishes;
+	 * it may issue others, until at least 50 msecs have passed.
+	 */
+	if (status == 0)
+		mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10));
+	return status;
+}
+EXPORT_SYMBOL (usb_bus_start_enum);
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
+ */
+static struct usb_operations usb_hcd_operations = {
+	.get_frame_number =	hcd_get_frame_number,
+	.submit_urb =		hcd_submit_urb,
+	.unlink_urb =		hcd_unlink_urb,
+	.buffer_alloc =		hcd_buffer_alloc,
+	.buffer_free =		hcd_buffer_free,
+	.disable =		hcd_endpoint_disable,
+#ifdef	CONFIG_USB_SUSPEND
+	.hub_suspend =		hcd_hub_suspend,
+	.hub_resume =		hcd_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_giveback_urb - return URB from HCD to device driver
+ * @hcd: host controller returning the URB
+ * @urb: urb being returned to the USB device driver.
+ * @regs: pt_regs, passed down to the URB completion handler
+ * Context: in_interrupt()
+ *
+ * This hands the URB from HCD to its USB device driver, using its
+ * completion function.  The HCD has freed all per-urb resources
+ * (and is done using urb->hcpriv).  It also released all HCD locks;
+ * the device driver won't cause problems if it frees, modifies,
+ * or resubmits this URB.
+ */
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+{
+	int at_root_hub;
+
+	at_root_hub = (urb->dev == hcd->self.root_hub);
+	urb_unlink (urb);
+
+	/* lower level hcd code should use *_dma exclusively */
+	if (hcd->self.controller->dma_mask && !at_root_hub) {
+		if (usb_pipecontrol (urb->pipe)
+			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+			dma_unmap_single (hcd->self.controller, urb->setup_dma,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+		if (urb->transfer_buffer_length != 0
+			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+			dma_unmap_single (hcd->self.controller, 
+					urb->transfer_dma,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? DMA_FROM_DEVICE
+					    : DMA_TO_DEVICE);
+	}
+
+	usbmon_urb_complete (&hcd->self, urb);
+	/* pass ownership to the completion handler */
+	urb->complete (urb, regs);
+	atomic_dec (&urb->use_count);
+	if (unlikely (urb->reject))
+		wake_up (&usb_kill_urb_queue);
+	usb_put_urb (urb);
+}
+EXPORT_SYMBOL (usb_hcd_giveback_urb);
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_irq - hook IRQs to HCD framework (bus glue)
+ * @irq: the IRQ being raised
+ * @__hcd: pointer to the HCD whose IRQ is being signaled
+ * @r: saved hardware registers
+ *
+ * If the controller isn't HALTed, calls the driver's irq handler.
+ * Checks whether the controller is now dead.
+ */
+irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+{
+	struct usb_hcd		*hcd = __hcd;
+	int			start = hcd->state;
+
+	if (start == HC_STATE_HALT)
+		return IRQ_NONE;
+	if (hcd->driver->irq (hcd, r) == IRQ_NONE)
+		return IRQ_NONE;
+
+	hcd->saw_irq = 1;
+	if (hcd->state != start && hcd->state == HC_STATE_HALT)
+		usb_hc_died (hcd);
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
+ * @hcd: pointer to the HCD representing the controller
+ *
+ * This is called by bus glue to report a USB host controller that died
+ * while operations may still have been pending.  It's called automatically
+ * by the PCI glue, so only glue for non-PCI busses should need to call it. 
+ */
+void usb_hc_died (struct usb_hcd *hcd)
+{
+	unsigned long flags;
+
+	dev_err (hcd->self.controller, "HC died; cleaning up\n");
+
+	spin_lock_irqsave (&hcd_root_hub_lock, flags);
+	if (hcd->rh_registered) {
+
+		/* make khubd clean up old urbs and devices */
+		usb_set_device_state (hcd->self.root_hub,
+				USB_STATE_NOTATTACHED);
+		usb_kick_khubd (hcd->self.root_hub);
+	}
+	spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
+}
+EXPORT_SYMBOL_GPL (usb_hc_died);
+
+/*-------------------------------------------------------------------------*/
+
+static void hcd_release (struct usb_bus *bus)
+{
+	struct usb_hcd *hcd;
+
+	hcd = container_of(bus, struct usb_hcd, self);
+	kfree(hcd);
+}
+
+/**
+ * usb_create_hcd - create and initialize an HCD structure
+ * @driver: HC driver that will use this hcd
+ * @dev: device for this HC, stored in hcd->self.controller
+ * @bus_name: value to store in hcd->self.bus_name
+ * Context: !in_interrupt()
+ *
+ * Allocate a struct usb_hcd, with extra space at the end for the
+ * HC driver's private data.  Initialize the generic members of the
+ * hcd structure.
+ *
+ * If memory is unavailable, returns NULL.
+ */
+struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
+		struct device *dev, char *bus_name)
+{
+	struct usb_hcd *hcd;
+
+	hcd = kcalloc(1, sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
+	if (!hcd) {
+		dev_dbg (dev, "hcd alloc failed\n");
+		return NULL;
+	}
+	dev_set_drvdata(dev, hcd);
+
+	usb_bus_init(&hcd->self);
+	hcd->self.op = &usb_hcd_operations;
+	hcd->self.hcpriv = hcd;
+	hcd->self.release = &hcd_release;
+	hcd->self.controller = dev;
+	hcd->self.bus_name = bus_name;
+
+	init_timer(&hcd->rh_timer);
+
+	hcd->driver = driver;
+	hcd->product_desc = (driver->product_desc) ? driver->product_desc :
+			"USB Host Controller";
+
+	return hcd;
+}
+EXPORT_SYMBOL (usb_create_hcd);
+
+void usb_put_hcd (struct usb_hcd *hcd)
+{
+	dev_set_drvdata(hcd->self.controller, NULL);
+	usb_bus_put(&hcd->self);
+}
+EXPORT_SYMBOL (usb_put_hcd);
+
+/**
+ * usb_add_hcd - finish generic HCD structure initialization and register
+ * @hcd: the usb_hcd structure to initialize
+ * @irqnum: Interrupt line to allocate
+ * @irqflags: Interrupt type flags
+ *
+ * Finish the remaining parts of generic HCD initialization: allocate the
+ * buffers of consistent memory, register the bus, request the IRQ line,
+ * and call the driver's reset() and start() routines.
+ */
+int usb_add_hcd(struct usb_hcd *hcd,
+		unsigned int irqnum, unsigned long irqflags)
+{
+	int	retval;
+
+	dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
+
+	/* till now HC has been in an indeterminate state ... */
+	if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
+		dev_err(hcd->self.controller, "can't reset\n");
+		return retval;
+	}
+
+	if ((retval = hcd_buffer_create(hcd)) != 0) {
+		dev_dbg(hcd->self.controller, "pool alloc failed\n");
+		return retval;
+	}
+
+	if ((retval = usb_register_bus(&hcd->self)) < 0)
+		goto err1;
+
+	if (hcd->driver->irq) {
+		char	buf[8], *bufp = buf;
+
+#ifdef __sparc__
+		bufp = __irq_itoa(irqnum);
+#else
+		sprintf(buf, "%d", irqnum);
+#endif
+
+		snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
+				hcd->driver->description, hcd->self.busnum);
+		if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
+				hcd->irq_descr, hcd)) != 0) {
+			dev_err(hcd->self.controller,
+					"request interrupt %s failed\n", bufp);
+			goto err2;
+		}
+		hcd->irq = irqnum;
+		dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp,
+				(hcd->driver->flags & HCD_MEMORY) ?
+					"io mem" : "io base",
+					(unsigned long long)hcd->rsrc_start);
+	} else {
+		hcd->irq = -1;
+		if (hcd->rsrc_start)
+			dev_info(hcd->self.controller, "%s 0x%08llx\n",
+					(hcd->driver->flags & HCD_MEMORY) ?
+					"io mem" : "io base",
+					(unsigned long long)hcd->rsrc_start);
+	}
+
+	if ((retval = hcd->driver->start(hcd)) < 0) {
+		dev_err(hcd->self.controller, "startup error %d\n", retval);
+		goto err3;
+	}
+
+	return retval;
+
+ err3:
+	if (hcd->irq >= 0)
+		free_irq(irqnum, hcd);
+ err2:
+	usb_deregister_bus(&hcd->self);
+ err1:
+	hcd_buffer_destroy(hcd);
+	return retval;
+} 
+EXPORT_SYMBOL (usb_add_hcd);
+
+/**
+ * usb_remove_hcd - shutdown processing for generic HCDs
+ * @hcd: the usb_hcd structure to remove
+ * Context: !in_interrupt()
+ *
+ * Disconnects the root hub, then reverses the effects of usb_add_hcd(),
+ * invoking the HCD's stop() method.
+ */
+void usb_remove_hcd(struct usb_hcd *hcd)
+{
+	dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
+
+	if (HC_IS_RUNNING (hcd->state))
+		hcd->state = HC_STATE_QUIESCING;
+
+	dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
+	spin_lock_irq (&hcd_root_hub_lock);
+	hcd->rh_registered = 0;
+	spin_unlock_irq (&hcd_root_hub_lock);
+	usb_disconnect(&hcd->self.root_hub);
+
+	hcd->driver->stop(hcd);
+	hcd->state = HC_STATE_HALT;
+
+	if (hcd->irq >= 0)
+		free_irq(hcd->irq, hcd);
+	usb_deregister_bus(&hcd->self);
+	hcd_buffer_destroy(hcd);
+}
+EXPORT_SYMBOL (usb_remove_hcd);
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations *mon_ops;
+
+/*
+ * The registration is unlocked.
+ * We do it this way because we do not want to lock in hot paths.
+ *
+ * Notice that the code is minimally error-proof. Because usbmon needs
+ * symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
+ */
+ 
+int usb_mon_register (struct usb_mon_operations *ops)
+{
+
+	if (mon_ops)
+		return -EBUSY;
+
+	mon_ops = ops;
+	mb();
+	return 0;
+}
+EXPORT_SYMBOL_GPL (usb_mon_register);
+
+void usb_mon_deregister (void)
+{
+
+	if (mon_ops == NULL) {
+		printk(KERN_ERR "USB: monitor was not registered\n");
+		return;
+	}
+	mon_ops = NULL;
+	mb();
+}
+EXPORT_SYMBOL_GPL (usb_mon_deregister);
+
+#endif /* CONFIG_USB_MON */
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
new file mode 100644
index 0000000..6c625b3
--- /dev/null
+++ b/drivers/usb/core/hcd.h
@@ -0,0 +1,476 @@
+/*
+ * 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
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifdef __KERNEL__
+
+/* This file contains declarations of usbcore internals that are mostly
+ * used or exposed by Host Controller Drivers.
+ */
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0			0xf0
+#define USB_PID_OUT			0xe1
+#define USB_PID_ACK			0xd2
+#define USB_PID_DATA0			0xc3
+#define USB_PID_PING			0xb4	/* USB 2.0 */
+#define USB_PID_SOF			0xa5
+#define USB_PID_NYET			0x96	/* USB 2.0 */
+#define USB_PID_DATA2			0x87	/* USB 2.0 */
+#define USB_PID_SPLIT			0x78	/* USB 2.0 */
+#define USB_PID_IN			0x69
+#define USB_PID_NAK			0x5a
+#define USB_PID_DATA1			0x4b
+#define USB_PID_PREAMBLE		0x3c	/* Token mode */
+#define USB_PID_ERR			0x3c	/* USB 2.0: handshake mode */
+#define USB_PID_SETUP			0x2d
+#define USB_PID_STALL			0x1e
+#define USB_PID_MDATA			0x0f	/* USB 2.0 */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver (usb_hcd) framework
+ *
+ * Since "struct usb_bus" is so thin, you can't share much code in it.
+ * This framework is a layer over that, and should be more sharable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_hcd {	/* usb_bus.hcpriv points to this */
+
+	/*
+	 * housekeeping
+	 */
+	struct usb_bus		self;		/* hcd is-a bus */
+
+	const char		*product_desc;	/* product/vendor string */
+	char			irq_descr[24];	/* driver + bus # */
+
+	struct timer_list	rh_timer;	/* drives root hub */
+
+	/*
+	 * hardware info/state
+	 */
+	const struct hc_driver	*driver;	/* hw-specific hooks */
+	unsigned		saw_irq : 1;
+	unsigned		can_wakeup:1;	/* hw supports wakeup? */
+	unsigned		remote_wakeup:1;/* sw should use wakeup? */
+	unsigned		rh_registered:1;/* is root hub registered? */
+
+	int			irq;		/* irq allocated */
+	void __iomem		*regs;		/* device memory/io */
+	u64			rsrc_start;	/* memory/io resource start */
+	u64			rsrc_len;	/* memory/io resource length */
+
+#define HCD_BUFFER_POOLS	4
+	struct dma_pool		*pool [HCD_BUFFER_POOLS];
+
+	int			state;
+#	define	__ACTIVE		0x01
+#	define	__SUSPEND		0x04
+#	define	__TRANSIENT		0x80
+
+#	define	HC_STATE_HALT		0
+#	define	HC_STATE_RUNNING	(__ACTIVE)
+#	define	HC_STATE_QUIESCING	(__SUSPEND|__TRANSIENT|__ACTIVE)
+#	define	HC_STATE_RESUMING	(__SUSPEND|__TRANSIENT)
+#	define	HC_STATE_SUSPENDED	(__SUSPEND)
+
+#define	HC_IS_RUNNING(state) ((state) & __ACTIVE)
+#define	HC_IS_SUSPENDED(state) ((state) & __SUSPEND)
+
+	/* more shared queuing code would be good; it should support
+	 * smarter scheduling, handle transaction translators, etc;
+	 * input size of periodic table to an interrupt scheduler. 
+	 * (ohci 32, uhci 1024, ehci 256/512/1024).
+	 */
+
+	/* The HC driver's private data is stored at the end of
+	 * this structure.
+	 */
+	unsigned long hcd_priv[0]
+			__attribute__ ((aligned (sizeof(unsigned long))));
+};
+
+/* 2.4 does this a bit differently ... */
+static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
+{
+	return &hcd->self;
+}
+
+
+// urb.hcpriv is really hardware-specific
+
+struct hcd_timeout {	/* timeouts we allocate */
+	struct list_head	timeout_list;
+	struct timer_list	timer;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * FIXME usb_operations should vanish or become hc_driver,
+ * when usb_bus and usb_hcd become the same thing.
+ */
+
+struct usb_operations {
+	int (*get_frame_number) (struct usb_device *usb_dev);
+	int (*submit_urb) (struct urb *urb, int mem_flags);
+	int (*unlink_urb) (struct urb *urb, int status);
+
+	/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
+	void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
+			int mem_flags,
+			dma_addr_t *dma);
+	void (*buffer_free)(struct usb_bus *bus, size_t size,
+			void *addr, dma_addr_t dma);
+
+	void (*disable)(struct usb_device *udev,
+			struct usb_host_endpoint *ep);
+
+	/* global suspend/resume of bus */
+	int (*hub_suspend)(struct usb_bus *);
+	int (*hub_resume)(struct usb_bus *);
+};
+
+/* each driver provides one of these, and hardware init support */
+
+struct pt_regs;
+
+struct hc_driver {
+	const char	*description;	/* "ehci-hcd" etc */
+	const char	*product_desc;	/* product/vendor string */
+	size_t		hcd_priv_size;	/* size of private data */
+
+	/* irq handler */
+	irqreturn_t	(*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
+
+	int	flags;
+#define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
+#define	HCD_USB11	0x0010		/* USB 1.1 */
+#define	HCD_USB2	0x0020		/* USB 2.0 */
+
+	/* called to init HCD and root hub */
+	int	(*reset) (struct usb_hcd *hcd);
+	int	(*start) (struct usb_hcd *hcd);
+
+	/* NOTE:  these suspend/resume calls relate to the HC as
+	 * a whole, not just the root hub; they're for bus glue.
+	 */
+	/* called after all devices were suspended */
+	int	(*suspend) (struct usb_hcd *hcd, u32 state);
+
+	/* called before any devices get resumed */
+	int	(*resume) (struct usb_hcd *hcd);
+
+	/* cleanly make HCD stop writing memory and doing I/O */
+	void	(*stop) (struct usb_hcd *hcd);
+
+	/* return current frame number */
+	int	(*get_frame_number) (struct usb_hcd *hcd);
+
+	/* manage i/o requests, device state */
+	int	(*urb_enqueue) (struct usb_hcd *hcd,
+					struct usb_host_endpoint *ep,
+					struct urb *urb,
+					int mem_flags);
+	int	(*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+	/* hw synch, freeing endpoint resources that urb_dequeue can't */
+	void 	(*endpoint_disable)(struct usb_hcd *hcd,
+			struct usb_host_endpoint *ep);
+
+	/* root hub support */
+	int		(*hub_status_data) (struct usb_hcd *hcd, char *buf);
+	int		(*hub_control) (struct usb_hcd *hcd,
+				u16 typeReq, u16 wValue, u16 wIndex,
+				char *buf, u16 wLength);
+	int		(*hub_suspend)(struct usb_hcd *);
+	int		(*hub_resume)(struct usb_hcd *);
+	int		(*start_port_reset)(struct usb_hcd *, unsigned port_num);
+};
+
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+
+extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
+		struct device *dev, char *bus_name);
+extern void usb_put_hcd (struct usb_hcd *hcd);
+extern int usb_add_hcd(struct usb_hcd *hcd,
+		unsigned int irqnum, unsigned long irqflags);
+extern void usb_remove_hcd(struct usb_hcd *hcd);
+
+#ifdef CONFIG_PCI
+struct pci_dev;
+struct pci_device_id;
+extern int usb_hcd_pci_probe (struct pci_dev *dev,
+				const struct pci_device_id *id);
+extern void usb_hcd_pci_remove (struct pci_dev *dev);
+
+#ifdef CONFIG_PM
+extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);
+extern int usb_hcd_pci_resume (struct pci_dev *dev);
+#endif /* CONFIG_PM */
+
+#endif /* CONFIG_PCI */
+
+/* pci-ish (pdev null is ok) buffer alloc/mapping support */
+int hcd_buffer_create (struct usb_hcd *hcd);
+void hcd_buffer_destroy (struct usb_hcd *hcd);
+
+void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
+	int mem_flags, dma_addr_t *dma);
+void hcd_buffer_free (struct usb_bus *bus, size_t size,
+	void *addr, dma_addr_t dma);
+
+/* generic bus glue, needed for host controllers that don't use PCI */
+extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+extern void usb_hc_died (struct usb_hcd *hcd);
+
+/* -------------------------------------------------------------------------- */
+
+/* Enumeration is only for the hub driver, or HCD virtual root hubs */
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
+					struct usb_bus *, unsigned port);
+extern int usb_new_device(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+
+extern int usb_get_configuration(struct usb_device *dev);
+extern void usb_destroy_configuration(struct usb_device *dev);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * HCD Root Hub support
+ */
+
+#include "hub.h"
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+	((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest \
+	((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+
+#define InterfaceRequest \
+	((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+#define EndpointRequest \
+	((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointOutRequest \
+	((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+/* class requests from the USB 2.0 hub spec, table 11-15 */
+/* GetBusState and SetHubDescriptor are optional, omitted */
+#define ClearHubFeature		(0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature	(0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor	(0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus		(0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus		(0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature		(0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature		(0x2300 | USB_REQ_SET_FEATURE)
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic bandwidth allocation constants/support
+ */
+#define FRAME_TIME_USECS	1000L
+#define BitTime(bytecount)  (7 * 8 * bytecount / 6)  /* with integer truncation */
+		/* Trying not to use worst-case bit-stuffing
+                   of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+		/* bytecount = data payload byte count */
+
+#define NS_TO_US(ns)	((ns + 500L) / 1000L)
+			/* convert & round nanoseconds to microseconds */
+
+extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
+		int bustime, int isoc);
+extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
+		int isoc);
+
+/*
+ * Full/low speed bandwidth allocation constants/support.
+ */
+#define BW_HOST_DELAY	1000L		/* nanoseconds */
+#define BW_HUB_LS_SETUP	333L		/* nanoseconds */
+                        /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS         12000L		/* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC	(90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_MAX_USECS_ALLOC	(90L * FRAME_TIME_USECS / 100L)
+
+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 ( ((38 * 8 * 2083)/1000) \
+	+ ((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 *);
+
+extern int usb_hcd_register_root_hub (struct usb_device *usb_dev,
+		struct usb_hcd *hcd);
+
+extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
+
+extern void usb_set_device_state(struct usb_device *udev,
+		enum usb_device_state new_state);
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+extern wait_queue_head_t usb_kill_urb_queue;
+
+extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
+extern void usb_bus_put (struct usb_bus *bus);
+
+extern int usb_find_interface_driver (struct usb_device *dev,
+	struct usb_interface *interface);
+
+#define usb_endpoint_out(ep_dir)	(!((ep_dir) & USB_DIR_IN))
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbfs_add_bus(struct usb_bus *bus);
+extern void usbfs_remove_bus(struct usb_bus *bus);
+extern void usbfs_add_device(struct usb_device *dev);
+extern void usbfs_remove_device(struct usb_device *dev);
+extern void usbfs_update_special (void);
+
+extern int usbfs_init(void);
+extern void usbfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+static inline void usbfs_add_bus(struct usb_bus *bus) {}
+static inline void usbfs_remove_bus(struct usb_bus *bus) {}
+static inline void usbfs_add_device(struct usb_device *dev) {}
+static inline void usbfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_update_special (void) {}
+
+static inline int usbfs_init(void) { return 0; }
+static inline void usbfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+struct usb_mon_operations {
+	void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
+	void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err);
+	void (*urb_complete)(struct usb_bus *bus, struct urb *urb);
+	/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
+	void (*bus_add)(struct usb_bus *bus);
+	void (*bus_remove)(struct usb_bus *bus);
+};
+
+extern struct usb_mon_operations *mon_ops;
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_submit)(bus, urb);
+}
+
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_submit_error)(bus, urb, error);
+}
+
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb)
+{
+	if (bus->monitored)
+		(*mon_ops->urb_complete)(bus, urb);
+}
+ 
+static inline void usbmon_notify_bus_add(struct usb_bus *bus)
+{
+	if (mon_ops)
+		(*mon_ops->bus_add)(bus);
+}
+
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus)
+{
+	if (mon_ops)
+		(*mon_ops->bus_remove)(bus);
+}
+
+int usb_mon_register(struct usb_mon_operations *ops);
+void usb_mon_deregister(void);
+
+#else
+
+static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
+    int error) {}
+static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {}
+static inline void usbmon_notify_bus_add(struct usb_bus *bus) {}
+static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {}
+
+#endif /* CONFIG_USB_MON */
+
+/*-------------------------------------------------------------------------*/
+
+/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
+// bleech -- resurfaced in 2.4.11 or 2.4.12
+#define bitmap 	DeviceRemovable
+
+
+/*-------------------------------------------------------------------------*/
+
+/* random stuff */
+
+#define	RUN_CONTEXT (in_irq () ? "in_irq" \
+		: (in_interrupt () ? "in_interrupt" : "can sleep"))
+
+
+#endif /* __KERNEL__ */
+
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
new file mode 100644
index 0000000..fa0dc4f
--- /dev/null
+++ b/drivers/usb/core/hub.c
@@ -0,0 +1,3057 @@
+/*
+ * USB hub driver.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Gregory P. Smith
+ * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
+ *
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "usb.h"
+#include "hcd.h"
+#include "hub.h"
+
+/* Protect struct usb_device->state and ->children members
+ * Note: Both are also protected by ->serialize, except that ->state can
+ * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
+static DEFINE_SPINLOCK(device_state_lock);
+
+/* khubd's worklist and its lock */
+static DEFINE_SPINLOCK(hub_event_lock);
+static LIST_HEAD(hub_event_list);	/* List of hubs needing servicing */
+
+/* Wakes up khubd */
+static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
+
+static pid_t khubd_pid = 0;			/* PID of khubd */
+static DECLARE_COMPLETION(khubd_exited);
+
+/* cycle leds on hubs that aren't blinking for attention */
+static int blinkenlights = 0;
+module_param (blinkenlights, bool, S_IRUGO);
+MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
+
+/*
+ * As of 2.6.10 we introduce a new USB device initialization scheme which
+ * closely resembles the way Windows works.  Hopefully it will be compatible
+ * with a wider range of devices than the old scheme.  However some previously
+ * working devices may start giving rise to "device not accepting address"
+ * errors; if that happens the user can try the old scheme by adjusting the
+ * following module parameters.
+ *
+ * For maximum flexibility there are two boolean parameters to control the
+ * hub driver's behavior.  On the first initialization attempt, if the
+ * "old_scheme_first" parameter is set then the old scheme will be used,
+ * otherwise the new scheme is used.  If that fails and "use_both_schemes"
+ * is set, then the driver will make another attempt, using the other scheme.
+ */
+static int old_scheme_first = 0;
+module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(old_scheme_first,
+		 "start with the old device initialization scheme");
+
+static int use_both_schemes = 1;
+module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(use_both_schemes,
+		"try the other device initialization scheme if the "
+		"first one fails");
+
+
+#ifdef	DEBUG
+static inline char *portspeed (int portstatus)
+{
+	if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
+    		return "480 Mb/s";
+	else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
+		return "1.5 Mb/s";
+	else
+		return "12 Mb/s";
+}
+#endif
+
+/* Note that hdev or one of its children must be locked! */
+static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
+{
+	return usb_get_intfdata(hdev->actconfig->interface[0]);
+}
+
+/* USB 2.0 spec Section 11.24.4.5 */
+static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
+{
+	int i, ret;
+
+	for (i = 0; i < 3; i++) {
+		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+			USB_DT_HUB << 8, 0, data, size,
+			USB_CTRL_GET_TIMEOUT);
+		if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
+			return ret;
+	}
+	return -EINVAL;
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.1
+ */
+static int clear_hub_feature(struct usb_device *hdev, int feature)
+{
+	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, 1000);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.2
+ */
+static int clear_port_feature(struct usb_device *hdev, int port1, int feature)
+{
+	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
+		NULL, 0, 1000);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.13
+ */
+static int set_port_feature(struct usb_device *hdev, int port1, int feature)
+{
+	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
+		NULL, 0, 1000);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
+ * for info about using port indicators
+ */
+static void set_port_led(
+	struct usb_hub *hub,
+	int port1,
+	int selector
+)
+{
+	int status = set_port_feature(hub->hdev, (selector << 8) | port1,
+			USB_PORT_FEAT_INDICATOR);
+	if (status < 0)
+		dev_dbg (hub->intfdev,
+			"port %d indicator %s status %d\n",
+			port1,
+			({ char *s; switch (selector) {
+			case HUB_LED_AMBER: s = "amber"; break;
+			case HUB_LED_GREEN: s = "green"; break;
+			case HUB_LED_OFF: s = "off"; break;
+			case HUB_LED_AUTO: s = "auto"; break;
+			default: s = "??"; break;
+			}; s; }),
+			status);
+}
+
+#define	LED_CYCLE_PERIOD	((2*HZ)/3)
+
+static void led_work (void *__hub)
+{
+	struct usb_hub		*hub = __hub;
+	struct usb_device	*hdev = hub->hdev;
+	unsigned		i;
+	unsigned		changed = 0;
+	int			cursor = -1;
+
+	if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)
+		return;
+
+	for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+		unsigned	selector, mode;
+
+		/* 30%-50% duty cycle */
+
+		switch (hub->indicator[i]) {
+		/* cycle marker */
+		case INDICATOR_CYCLE:
+			cursor = i;
+			selector = HUB_LED_AUTO;
+			mode = INDICATOR_AUTO;
+			break;
+		/* blinking green = sw attention */
+		case INDICATOR_GREEN_BLINK:
+			selector = HUB_LED_GREEN;
+			mode = INDICATOR_GREEN_BLINK_OFF;
+			break;
+		case INDICATOR_GREEN_BLINK_OFF:
+			selector = HUB_LED_OFF;
+			mode = INDICATOR_GREEN_BLINK;
+			break;
+		/* blinking amber = hw attention */
+		case INDICATOR_AMBER_BLINK:
+			selector = HUB_LED_AMBER;
+			mode = INDICATOR_AMBER_BLINK_OFF;
+			break;
+		case INDICATOR_AMBER_BLINK_OFF:
+			selector = HUB_LED_OFF;
+			mode = INDICATOR_AMBER_BLINK;
+			break;
+		/* blink green/amber = reserved */
+		case INDICATOR_ALT_BLINK:
+			selector = HUB_LED_GREEN;
+			mode = INDICATOR_ALT_BLINK_OFF;
+			break;
+		case INDICATOR_ALT_BLINK_OFF:
+			selector = HUB_LED_AMBER;
+			mode = INDICATOR_ALT_BLINK;
+			break;
+		default:
+			continue;
+		}
+		if (selector != HUB_LED_AUTO)
+			changed = 1;
+		set_port_led(hub, i + 1, selector);
+		hub->indicator[i] = mode;
+	}
+	if (!changed && blinkenlights) {
+		cursor++;
+		cursor %= hub->descriptor->bNbrPorts;
+		set_port_led(hub, cursor + 1, HUB_LED_GREEN);
+		hub->indicator[cursor] = INDICATOR_CYCLE;
+		changed++;
+	}
+	if (changed)
+		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+}
+
+/* use a short timeout for hub/port status fetches */
+#define	USB_STS_TIMEOUT		1000
+#define	USB_STS_RETRIES		5
+
+/*
+ * USB 2.0 spec Section 11.24.2.6
+ */
+static int get_hub_status(struct usb_device *hdev,
+		struct usb_hub_status *data)
+{
+	int i, status = -ETIMEDOUT;
+
+	for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
+			data, sizeof(*data), USB_STS_TIMEOUT);
+	}
+	return status;
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.7
+ */
+static int get_port_status(struct usb_device *hdev, int port1,
+		struct usb_port_status *data)
+{
+	int i, status = -ETIMEDOUT;
+
+	for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
+			data, sizeof(*data), USB_STS_TIMEOUT);
+	}
+	return status;
+}
+
+static void kick_khubd(struct usb_hub *hub)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&hub_event_lock, flags);
+	if (list_empty(&hub->event_list)) {
+		list_add_tail(&hub->event_list, &hub_event_list);
+		wake_up(&khubd_wait);
+	}
+	spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+void usb_kick_khubd(struct usb_device *hdev)
+{
+	kick_khubd(hdev_to_hub(hdev));
+}
+
+
+/* completion function, fires on port status changes and various faults */
+static void hub_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_hub *hub = (struct usb_hub *)urb->context;
+	int status;
+	int i;
+	unsigned long bits;
+
+	switch (urb->status) {
+	case -ENOENT:		/* synchronous unlink */
+	case -ECONNRESET:	/* async unlink */
+	case -ESHUTDOWN:	/* hardware going away */
+		return;
+
+	default:		/* presumably an error */
+		/* Cause a hub reset after 10 consecutive errors */
+		dev_dbg (hub->intfdev, "transfer --> %d\n", urb->status);
+		if ((++hub->nerrors < 10) || hub->error)
+			goto resubmit;
+		hub->error = urb->status;
+		/* FALL THROUGH */
+	
+	/* let khubd handle things */
+	case 0:			/* we got data:  port status changed */
+		bits = 0;
+		for (i = 0; i < urb->actual_length; ++i)
+			bits |= ((unsigned long) ((*hub->buffer)[i]))
+					<< (i*8);
+		hub->event_bits[0] = bits;
+		break;
+	}
+
+	hub->nerrors = 0;
+
+	/* Something happened, let khubd figure it out */
+	kick_khubd(hub);
+
+resubmit:
+	if (hub->quiescing)
+		return;
+
+	if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
+			&& status != -ENODEV && status != -EPERM)
+		dev_err (hub->intfdev, "resubmit --> %d\n", status);
+}
+
+/* USB 2.0 spec Section 11.24.2.3 */
+static inline int
+hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
+{
+	return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			       HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
+			       tt, NULL, 0, 1000);
+}
+
+/*
+ * 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;
+		struct usb_device	*hdev = hub->hdev;
+		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 (hdev, clear->devinfo, clear->tt);
+		spin_lock_irqsave (&hub->tt.lock, flags);
+
+		if (status)
+			dev_err (&hdev->dev,
+				"clear tt %d (%04x) error %d\n",
+				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
+ * @udev: 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 *udev, int pipe)
+{
+	struct usb_tt		*tt = udev->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)) == NULL) {
+		dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
+		/* FIXME recover somehow ... RESET_TT? */
+		return;
+	}
+
+	/* info that CLEAR_TT_BUFFER needs */
+	clear->tt = tt->multi ? udev->ttport : 1;
+	clear->devinfo = usb_pipeendpoint (pipe);
+	clear->devinfo |= udev->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_work (&tt->kevent);
+	spin_unlock_irqrestore (&tt->lock, flags);
+}
+
+static void hub_power_on(struct usb_hub *hub)
+{
+	int port1;
+
+	/* if hub supports power switching, enable power on each port */
+	if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
+		dev_dbg(hub->intfdev, "enabling power on all ports\n");
+		for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
+			set_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_POWER);
+	}
+
+	/* Wait for power to be enabled */
+	msleep(hub->descriptor->bPwrOn2PwrGood * 2);
+}
+
+static void hub_quiesce(struct usb_hub *hub)
+{
+	/* stop khubd and related activity */
+	hub->quiescing = 1;
+	usb_kill_urb(hub->urb);
+	if (hub->has_indicators)
+		cancel_delayed_work(&hub->leds);
+	if (hub->has_indicators || hub->tt.hub)
+		flush_scheduled_work();
+}
+
+static void hub_activate(struct usb_hub *hub)
+{
+	int	status;
+
+	hub->quiescing = 0;
+	hub->activating = 1;
+	status = usb_submit_urb(hub->urb, GFP_NOIO);
+	if (status < 0)
+		dev_err(hub->intfdev, "activate --> %d\n", status);
+	if (hub->has_indicators && blinkenlights)
+		schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+
+	/* scan all ports ASAP */
+	kick_khubd(hub);
+}
+
+static int hub_hub_status(struct usb_hub *hub,
+		u16 *status, u16 *change)
+{
+	int ret;
+
+	ret = get_hub_status(hub->hdev, &hub->status->hub);
+	if (ret < 0)
+		dev_err (hub->intfdev,
+			"%s failed (err = %d)\n", __FUNCTION__, ret);
+	else {
+		*status = le16_to_cpu(hub->status->hub.wHubStatus);
+		*change = le16_to_cpu(hub->status->hub.wHubChange); 
+		ret = 0;
+	}
+	return ret;
+}
+
+static int hub_configure(struct usb_hub *hub,
+	struct usb_endpoint_descriptor *endpoint)
+{
+	struct usb_device *hdev = hub->hdev;
+	struct device *hub_dev = hub->intfdev;
+	u16 hubstatus, hubchange;
+	unsigned int pipe;
+	int maxp, ret;
+	char *message;
+
+	hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
+			&hub->buffer_dma);
+	if (!hub->buffer) {
+		message = "can't allocate hub irq buffer";
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
+	if (!hub->status) {
+		message = "can't kmalloc hub status buffer";
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+	if (!hub->descriptor) {
+		message = "can't kmalloc hub descriptor";
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Request the entire hub descriptor.
+	 * hub->descriptor can handle USB_MAXCHILDREN ports,
+	 * but the hub can/will return fewer bytes here.
+	 */
+	ret = get_hub_descriptor(hdev, hub->descriptor,
+			sizeof(*hub->descriptor));
+	if (ret < 0) {
+		message = "can't read hub descriptor";
+		goto fail;
+	} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
+		message = "hub has too many ports!";
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	hdev->maxchild = hub->descriptor->bNbrPorts;
+	dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
+		(hdev->maxchild == 1) ? "" : "s");
+
+	le16_to_cpus(&hub->descriptor->wHubCharacteristics);
+
+	if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {
+		int	i;
+		char	portstr [USB_MAXCHILDREN + 1];
+
+		for (i = 0; i < hdev->maxchild; i++)
+			portstr[i] = hub->descriptor->DeviceRemovable
+				    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
+				? 'F' : 'R';
+		portstr[hdev->maxchild] = 0;
+		dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
+	} else
+		dev_dbg(hub_dev, "standalone hub\n");
+
+	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
+		case 0x00:
+			dev_dbg(hub_dev, "ganged power switching\n");
+			break;
+		case 0x01:
+			dev_dbg(hub_dev, "individual port power switching\n");
+			break;
+		case 0x02:
+		case 0x03:
+			dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
+			break;
+	}
+
+	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
+		case 0x00:
+			dev_dbg(hub_dev, "global over-current protection\n");
+			break;
+		case 0x08:
+			dev_dbg(hub_dev, "individual port over-current protection\n");
+			break;
+		case 0x10:
+		case 0x18:
+			dev_dbg(hub_dev, "no over-current protection\n");
+                        break;
+	}
+
+	spin_lock_init (&hub->tt.lock);
+	INIT_LIST_HEAD (&hub->tt.clear_list);
+	INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);
+	switch (hdev->descriptor.bDeviceProtocol) {
+		case 0:
+			break;
+		case 1:
+			dev_dbg(hub_dev, "Single TT\n");
+			hub->tt.hub = hdev;
+			break;
+		case 2:
+			ret = usb_set_interface(hdev, 0, 1);
+			if (ret == 0) {
+				dev_dbg(hub_dev, "TT per port\n");
+				hub->tt.multi = 1;
+			} else
+				dev_err(hub_dev, "Using single TT (err %d)\n",
+					ret);
+			hub->tt.hub = hdev;
+			break;
+		default:
+			dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
+				hdev->descriptor.bDeviceProtocol);
+			break;
+	}
+
+	switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
+		case 0x00:
+			if (hdev->descriptor.bDeviceProtocol != 0)
+				dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");
+			break;
+		case 0x20:
+			dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");
+			break;
+		case 0x40:
+			dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");
+			break;
+		case 0x60:
+			dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");
+			break;
+	}
+
+	/* probe() zeroes hub->indicator[] */
+	if (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) {
+		hub->has_indicators = 1;
+		dev_dbg(hub_dev, "Port indicators are supported\n");
+	}
+
+	dev_dbg(hub_dev, "power on to power good time: %dms\n",
+		hub->descriptor->bPwrOn2PwrGood * 2);
+
+	/* power budgeting mostly matters with bus-powered hubs,
+	 * and battery-powered root hubs (may provide just 8 mA).
+	 */
+	ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
+	if (ret < 0) {
+		message = "can't get hub status";
+		goto fail;
+	}
+	cpu_to_le16s(&hubstatus);
+	if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+		dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
+			hub->descriptor->bHubContrCurrent);
+		hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
+					/ 2;
+		dev_dbg(hub_dev, "%dmA bus power budget for children\n",
+			hub->power_budget * 2);
+	}
+
+
+	ret = hub_hub_status(hub, &hubstatus, &hubchange);
+	if (ret < 0) {
+		message = "can't get hub status";
+		goto fail;
+	}
+
+	/* local power status reports aren't always correct */
+	if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)
+		dev_dbg(hub_dev, "local power source is %s\n",
+			(hubstatus & HUB_STATUS_LOCAL_POWER)
+			? "lost (inactive)" : "good");
+
+	if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) == 0)
+		dev_dbg(hub_dev, "%sover-current condition exists\n",
+			(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+
+	/* set up the interrupt endpoint */
+	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
+
+	if (maxp > sizeof(*hub->buffer))
+		maxp = sizeof(*hub->buffer);
+
+	hub->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!hub->urb) {
+		message = "couldn't allocate interrupt urb";
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
+		hub, endpoint->bInterval);
+	hub->urb->transfer_dma = hub->buffer_dma;
+	hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* maybe cycle the hub leds */
+	if (hub->has_indicators && blinkenlights)
+		hub->indicator [0] = INDICATOR_CYCLE;
+
+	hub_power_on(hub);
+	hub_activate(hub);
+	return 0;
+
+fail:
+	dev_err (hub_dev, "config failed, %s (err %d)\n",
+			message, ret);
+	/* hub_disconnect() frees urb and descriptor */
+	return ret;
+}
+
+static unsigned highspeed_hubs;
+
+static void hub_disconnect(struct usb_interface *intf)
+{
+	struct usb_hub *hub = usb_get_intfdata (intf);
+	struct usb_device *hdev;
+
+	if (!hub)
+		return;
+	hdev = hub->hdev;
+
+	if (hdev->speed == USB_SPEED_HIGH)
+		highspeed_hubs--;
+
+	usb_set_intfdata (intf, NULL);
+
+	hub_quiesce(hub);
+	usb_free_urb(hub->urb);
+	hub->urb = NULL;
+
+	spin_lock_irq(&hub_event_lock);
+	list_del_init(&hub->event_list);
+	spin_unlock_irq(&hub_event_lock);
+
+	if (hub->descriptor) {
+		kfree(hub->descriptor);
+		hub->descriptor = NULL;
+	}
+
+	if (hub->status) {
+		kfree(hub->status);
+		hub->status = NULL;
+	}
+
+	if (hub->buffer) {
+		usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
+				hub->buffer_dma);
+		hub->buffer = NULL;
+	}
+
+	/* Free the memory */
+	kfree(hub);
+}
+
+static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_host_interface *desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_device *hdev;
+	struct usb_hub *hub;
+
+	desc = intf->cur_altsetting;
+	hdev = interface_to_usbdev(intf);
+
+	/* Some hubs have a subclass of 1, which AFAICT according to the */
+	/*  specs is not defined, but it works */
+	if ((desc->desc.bInterfaceSubClass != 0) &&
+	    (desc->desc.bInterfaceSubClass != 1)) {
+descriptor_error:
+		dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
+		return -EIO;
+	}
+
+	/* Multiple endpoints? What kind of mutant ninja-hub is this? */
+	if (desc->desc.bNumEndpoints != 1)
+		goto descriptor_error;
+
+	endpoint = &desc->endpoint[0].desc;
+
+	/* Output endpoint? Curiouser and curiouser.. */
+	if (!(endpoint->bEndpointAddress & USB_DIR_IN))
+		goto descriptor_error;
+
+	/* If it's not an interrupt endpoint, we'd better punt! */
+	if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			!= USB_ENDPOINT_XFER_INT)
+		goto descriptor_error;
+
+	/* We found a hub */
+	dev_info (&intf->dev, "USB hub found\n");
+
+	hub = kmalloc(sizeof(*hub), GFP_KERNEL);
+	if (!hub) {
+		dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
+		return -ENOMEM;
+	}
+
+	memset(hub, 0, sizeof(*hub));
+
+	INIT_LIST_HEAD(&hub->event_list);
+	hub->intfdev = &intf->dev;
+	hub->hdev = hdev;
+	INIT_WORK(&hub->leds, led_work, hub);
+
+	usb_set_intfdata (intf, hub);
+
+	if (hdev->speed == USB_SPEED_HIGH)
+		highspeed_hubs++;
+
+	if (hub_configure(hub, endpoint) >= 0)
+		return 0;
+
+	hub_disconnect (intf);
+	return -ENODEV;
+}
+
+static int
+hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
+{
+	struct usb_device *hdev = interface_to_usbdev (intf);
+
+	/* assert ifno == 0 (part of hub spec) */
+	switch (code) {
+	case USBDEVFS_HUB_PORTINFO: {
+		struct usbdevfs_hub_portinfo *info = user_data;
+		int i;
+
+		spin_lock_irq(&device_state_lock);
+		if (hdev->devnum <= 0)
+			info->nports = 0;
+		else {
+			info->nports = hdev->maxchild;
+			for (i = 0; i < info->nports; i++) {
+				if (hdev->children[i] == NULL)
+					info->port[i] = 0;
+				else
+					info->port[i] =
+						hdev->children[i]->devnum;
+			}
+		}
+		spin_unlock_irq(&device_state_lock);
+
+		return info->nports + 1;
+		}
+
+	default:
+		return -ENOSYS;
+	}
+}
+
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_hub *hub)
+{
+	struct usb_device *hdev = hub->hdev;
+	int i;
+
+	for (i = 0; i < hdev->maxchild; ++i) {
+		if (hdev->children[i])
+			usb_disconnect(&hdev->children[i]);
+	}
+	hub_quiesce(hub);
+}
+
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_hub *hub)
+{
+	hub_activate(hub);
+	hub_power_on(hub);
+}
+
+
+/* grab device/port lock, returning index of that port (zero based).
+ * protects the upstream link used by this device from concurrent
+ * tree operations like suspend, resume, reset, and disconnect, which
+ * apply to everything downstream of a given port.
+ */
+static int locktree(struct usb_device *udev)
+{
+	int			t;
+	struct usb_device	*hdev;
+
+	if (!udev)
+		return -ENODEV;
+
+	/* root hub is always the first lock in the series */
+	hdev = udev->parent;
+	if (!hdev) {
+		usb_lock_device(udev);
+		return 0;
+	}
+
+	/* on the path from root to us, lock everything from
+	 * top down, dropping parent locks when not needed
+	 */
+	t = locktree(hdev);
+	if (t < 0)
+		return t;
+	for (t = 0; t < hdev->maxchild; t++) {
+		if (hdev->children[t] == udev) {
+			/* everything is fail-fast once disconnect
+			 * processing starts
+			 */
+			if (udev->state == USB_STATE_NOTATTACHED)
+				break;
+
+			/* when everyone grabs locks top->bottom,
+			 * non-overlapping work may be concurrent
+			 */
+			down(&udev->serialize);
+			up(&hdev->serialize);
+			return t + 1;
+		}
+	}
+	usb_unlock_device(hdev);
+	return -ENODEV;
+}
+
+static void recursively_mark_NOTATTACHED(struct usb_device *udev)
+{
+	int i;
+
+	for (i = 0; i < udev->maxchild; ++i) {
+		if (udev->children[i])
+			recursively_mark_NOTATTACHED(udev->children[i]);
+	}
+	udev->state = USB_STATE_NOTATTACHED;
+}
+
+/**
+ * usb_set_device_state - change a device's current state (usbcore, hcds)
+ * @udev: pointer to device whose state should be changed
+ * @new_state: new state value to be stored
+ *
+ * udev->state is _not_ fully protected by the device lock.  Although
+ * most transitions are made only while holding the lock, the state can
+ * can change to USB_STATE_NOTATTACHED at almost any time.  This
+ * is so that devices can be marked as disconnected as soon as possible,
+ * without having to wait for any semaphores to be released.  As a result,
+ * all changes to any device's state must be protected by the
+ * device_state_lock spinlock.
+ *
+ * Once a device has been added to the device tree, all changes to its state
+ * should be made using this routine.  The state should _not_ be set directly.
+ *
+ * If udev->state is already USB_STATE_NOTATTACHED then no change is made.
+ * Otherwise udev->state is set to new_state, and if new_state is
+ * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set
+ * to USB_STATE_NOTATTACHED.
+ */
+void usb_set_device_state(struct usb_device *udev,
+		enum usb_device_state new_state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&device_state_lock, flags);
+	if (udev->state == USB_STATE_NOTATTACHED)
+		;	/* do nothing */
+	else if (new_state != USB_STATE_NOTATTACHED)
+		udev->state = new_state;
+	else
+		recursively_mark_NOTATTACHED(udev);
+	spin_unlock_irqrestore(&device_state_lock, flags);
+}
+EXPORT_SYMBOL(usb_set_device_state);
+
+
+static void choose_address(struct usb_device *udev)
+{
+	int		devnum;
+	struct usb_bus	*bus = udev->bus;
+
+	/* If khubd ever becomes multithreaded, this will need a lock */
+
+	/* Try to allocate the next devnum beginning at bus->devnum_next. */
+	devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
+			bus->devnum_next);
+	if (devnum >= 128)
+		devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
+
+	bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+
+	if (devnum < 128) {
+		set_bit(devnum, bus->devmap.devicemap);
+		udev->devnum = devnum;
+	}
+}
+
+static void release_address(struct usb_device *udev)
+{
+	if (udev->devnum > 0) {
+		clear_bit(udev->devnum, udev->bus->devmap.devicemap);
+		udev->devnum = -1;
+	}
+}
+
+/**
+ * usb_disconnect - disconnect a device (usbcore-internal)
+ * @pdev: pointer to device being disconnected
+ * Context: !in_interrupt ()
+ *
+ * Something got disconnected. Get rid of it and all of its children.
+ *
+ * If *pdev is a normal device then the parent hub must already be locked.
+ * If *pdev is a root hub then this routine will acquire the
+ * usb_bus_list_lock on behalf of the caller.
+ *
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ */
+void usb_disconnect(struct usb_device **pdev)
+{
+	struct usb_device	*udev = *pdev;
+	int			i;
+
+	if (!udev) {
+		pr_debug ("%s nodev\n", __FUNCTION__);
+		return;
+	}
+
+	/* mark the device as inactive, so any further urb submissions for
+	 * this device (and any of its children) will fail immediately.
+	 * this quiesces everyting except pending urbs.
+	 */
+	usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+
+	/* lock the bus list on behalf of HCDs unregistering their root hubs */
+	if (!udev->parent) {
+		down(&usb_bus_list_lock);
+		usb_lock_device(udev);
+	} else
+		down(&udev->serialize);
+
+	dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+
+	/* Free up all the children before we remove this device */
+	for (i = 0; i < USB_MAXCHILDREN; i++) {
+		if (udev->children[i])
+			usb_disconnect(&udev->children[i]);
+	}
+
+	/* deallocate hcd/hardware state ... nuking all pending urbs and
+	 * cleaning up all state associated with the current configuration
+	 * so that the hardware is now fully quiesced.
+	 */
+	usb_disable_device(udev, 0);
+
+	/* Free the device number, remove the /proc/bus/usb entry and
+	 * the sysfs attributes, and delete the parent's children[]
+	 * (or root_hub) pointer.
+	 */
+	dev_dbg (&udev->dev, "unregistering device\n");
+	release_address(udev);
+	usbfs_remove_device(udev);
+	usb_remove_sysfs_dev_files(udev);
+
+	/* Avoid races with recursively_mark_NOTATTACHED() */
+	spin_lock_irq(&device_state_lock);
+	*pdev = NULL;
+	spin_unlock_irq(&device_state_lock);
+
+	if (!udev->parent) {
+		usb_unlock_device(udev);
+		up(&usb_bus_list_lock);
+	} else
+		up(&udev->serialize);
+
+	device_unregister(&udev->dev);
+}
+
+static int choose_configuration(struct usb_device *udev)
+{
+	int c, i;
+
+	/* NOTE: this should interact with hub power budgeting */
+
+	c = udev->config[0].desc.bConfigurationValue;
+	if (udev->descriptor.bNumConfigurations != 1) {
+		for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
+			struct usb_interface_descriptor	*desc;
+
+			/* heuristic:  Linux is more likely to have class
+			 * drivers, so avoid vendor-specific interfaces.
+			 */
+			desc = &udev->config[i].intf_cache[0]
+					->altsetting->desc;
+			if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
+				continue;
+			/* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
+			 * MSFT needs this to be the first config; never use
+			 * it as the default unless Linux has host-side RNDIS.
+			 * A second config would ideally be CDC-Ethernet, but
+			 * may instead be the "vendor specific" CDC subset
+			 * long used by ARM Linux for sa1100 or pxa255.
+			 */
+			if (desc->bInterfaceClass == USB_CLASS_COMM
+					&& desc->bInterfaceSubClass == 2
+					&& desc->bInterfaceProtocol == 0xff) {
+				c = udev->config[1].desc.bConfigurationValue;
+				continue;
+			}
+			c = udev->config[i].desc.bConfigurationValue;
+			break;
+		}
+		dev_info(&udev->dev,
+			"configuration #%d chosen from %d choices\n",
+			c, udev->descriptor.bNumConfigurations);
+	}
+	return c;
+}
+
+#ifdef DEBUG
+static void show_string(struct usb_device *udev, char *id, char *string)
+{
+	if (!string)
+		return;
+	dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string);
+}
+
+#else
+static inline void show_string(struct usb_device *udev, char *id, char *string)
+{}
+#endif
+
+static void get_string(struct usb_device *udev, char **string, int index)
+{
+	char *buf;
+
+	if (!index)
+		return;
+	buf = kmalloc(256, GFP_KERNEL);
+	if (!buf)
+		return;
+	if (usb_string(udev, index, buf, 256) > 0)
+		*string = buf;
+	else
+		kfree(buf);
+}
+
+
+#ifdef	CONFIG_USB_OTG
+#include "otg_whitelist.h"
+#endif
+
+/**
+ * usb_new_device - perform initial device setup (usbcore-internal)
+ * @udev: newly addressed device (in ADDRESS state)
+ *
+ * This is called with devices which have been enumerated, but not yet
+ * configured.  The device descriptor is available, but not descriptors
+ * for any device configuration.  The caller must have locked udev and
+ * either the parent hub (if udev is a normal device) or else the
+ * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
+ * udev has already been installed, but udev is not yet visible through
+ * sysfs or other filesystem code.
+ *
+ * Returns 0 for success (device is configured and listed, with its
+ * interfaces, in sysfs); else a negative errno value.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Only the hub driver should ever call this; root hub registration
+ * uses it indirectly.
+ */
+int usb_new_device(struct usb_device *udev)
+{
+	int err;
+	int c;
+
+	err = usb_get_configuration(udev);
+	if (err < 0) {
+		dev_err(&udev->dev, "can't read configurations, error %d\n",
+			err);
+		goto fail;
+	}
+
+	/* read the standard strings and cache them if present */
+	get_string(udev, &udev->product, udev->descriptor.iProduct);
+	get_string(udev, &udev->manufacturer, udev->descriptor.iManufacturer);
+	get_string(udev, &udev->serial, udev->descriptor.iSerialNumber);
+
+	/* Tell the world! */
+	dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, "
+			"SerialNumber=%d\n",
+			udev->descriptor.iManufacturer,
+			udev->descriptor.iProduct,
+			udev->descriptor.iSerialNumber);
+	show_string(udev, "Product", udev->product);
+	show_string(udev, "Manufacturer", udev->manufacturer);
+	show_string(udev, "SerialNumber", udev->serial);
+
+#ifdef	CONFIG_USB_OTG
+	/*
+	 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
+	 * to wake us after we've powered off VBUS; and HNP, switching roles
+	 * "host" to "peripheral".  The OTG descriptor helps figure this out.
+	 */
+	if (!udev->bus->is_b_host
+			&& udev->config
+			&& udev->parent == udev->bus->root_hub) {
+		struct usb_otg_descriptor	*desc = 0;
+		struct usb_bus			*bus = udev->bus;
+
+		/* descriptor may appear anywhere in config */
+		if (__usb_get_extra_descriptor (udev->rawdescriptors[0],
+					le16_to_cpu(udev->config[0].desc.wTotalLength),
+					USB_DT_OTG, (void **) &desc) == 0) {
+			if (desc->bmAttributes & USB_OTG_HNP) {
+				unsigned		port1;
+				struct usb_device	*root = udev->parent;
+				
+				for (port1 = 1; port1 <= root->maxchild;
+						port1++) {
+					if (root->children[port1-1] == udev)
+						break;
+				}
+
+				dev_info(&udev->dev,
+					"Dual-Role OTG device on %sHNP port\n",
+					(port1 == bus->otg_port)
+						? "" : "non-");
+
+				/* enable HNP before suspend, it's simpler */
+				if (port1 == bus->otg_port)
+					bus->b_hnp_enable = 1;
+				err = usb_control_msg(udev,
+					usb_sndctrlpipe(udev, 0),
+					USB_REQ_SET_FEATURE, 0,
+					bus->b_hnp_enable
+						? USB_DEVICE_B_HNP_ENABLE
+						: USB_DEVICE_A_ALT_HNP_SUPPORT,
+					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+				if (err < 0) {
+					/* OTG MESSAGE: report errors here,
+					 * customize to match your product.
+					 */
+					dev_info(&udev->dev,
+						"can't set HNP mode; %d\n",
+						err);
+					bus->b_hnp_enable = 0;
+				}
+			}
+		}
+	}
+
+	if (!is_targeted(udev)) {
+
+		/* Maybe it can talk to us, though we can't talk to it.
+		 * (Includes HNP test device.)
+		 */
+		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
+			static int __usb_suspend_device (struct usb_device *,
+						int port1, pm_message_t state);
+			err = __usb_suspend_device(udev,
+					udev->bus->otg_port,
+					PMSG_SUSPEND);
+			if (err < 0)
+				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+		}
+		err = -ENODEV;
+		goto fail;
+	}
+#endif
+
+	/* put device-specific files into sysfs */
+	err = device_add (&udev->dev);
+	if (err) {
+		dev_err(&udev->dev, "can't device_add, error %d\n", err);
+		goto fail;
+	}
+	usb_create_sysfs_dev_files (udev);
+
+	/* choose and set the configuration. that registers the interfaces
+	 * with the driver core, and lets usb device drivers bind to them.
+	 */
+	c = choose_configuration(udev);
+	if (c < 0)
+		dev_warn(&udev->dev,
+				"can't choose an initial configuration\n");
+	else {
+		err = usb_set_configuration(udev, c);
+		if (err) {
+			dev_err(&udev->dev, "can't set config #%d, error %d\n",
+					c, err);
+			usb_remove_sysfs_dev_files(udev);
+			device_del(&udev->dev);
+			goto fail;
+		}
+	}
+
+	/* USB device state == configured ... usable */
+
+	/* add a /proc/bus/usb entry */
+	usbfs_add_device(udev);
+	return 0;
+
+fail:
+	usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+	return err;
+}
+
+
+static int hub_port_status(struct usb_hub *hub, int port1,
+			       u16 *status, u16 *change)
+{
+	int ret;
+
+	ret = get_port_status(hub->hdev, port1, &hub->status->port);
+	if (ret < 0)
+		dev_err (hub->intfdev,
+			"%s failed (err = %d)\n", __FUNCTION__, ret);
+	else {
+		*status = le16_to_cpu(hub->status->port.wPortStatus);
+		*change = le16_to_cpu(hub->status->port.wPortChange); 
+		ret = 0;
+	}
+	return ret;
+}
+
+#define PORT_RESET_TRIES	5
+#define SET_ADDRESS_TRIES	2
+#define GET_DESCRIPTOR_TRIES	2
+#define SET_CONFIG_TRIES	(2 * (use_both_schemes + 1))
+#define USE_NEW_SCHEME(i)	((i) / 2 == old_scheme_first)
+
+#define HUB_ROOT_RESET_TIME	50	/* times are in msec */
+#define HUB_SHORT_RESET_TIME	10
+#define HUB_LONG_RESET_TIME	200
+#define HUB_RESET_TIMEOUT	500
+
+static int hub_port_wait_reset(struct usb_hub *hub, int port1,
+				struct usb_device *udev, unsigned int delay)
+{
+	int delay_time, ret;
+	u16 portstatus;
+	u16 portchange;
+
+	for (delay_time = 0;
+			delay_time < HUB_RESET_TIMEOUT;
+			delay_time += delay) {
+		/* wait to give the device a chance to reset */
+		msleep(delay);
+
+		/* read and decode port status */
+		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		if (ret < 0)
+			return ret;
+
+		/* Device went away? */
+		if (!(portstatus & USB_PORT_STAT_CONNECTION))
+			return -ENOTCONN;
+
+		/* bomb out completely if something weird happened */
+		if ((portchange & USB_PORT_STAT_C_CONNECTION))
+			return -EINVAL;
+
+		/* if we`ve finished resetting, then break out of the loop */
+		if (!(portstatus & USB_PORT_STAT_RESET) &&
+		    (portstatus & USB_PORT_STAT_ENABLE)) {
+			if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+				udev->speed = USB_SPEED_HIGH;
+			else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+				udev->speed = USB_SPEED_LOW;
+			else
+				udev->speed = USB_SPEED_FULL;
+			return 0;
+		}
+
+		/* switch to the long delay after two short delay failures */
+		if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
+			delay = HUB_LONG_RESET_TIME;
+
+		dev_dbg (hub->intfdev,
+			"port %d not reset yet, waiting %dms\n",
+			port1, delay);
+	}
+
+	return -EBUSY;
+}
+
+static int hub_port_reset(struct usb_hub *hub, int port1,
+				struct usb_device *udev, unsigned int delay)
+{
+	int i, status;
+
+	/* Reset the port */
+	for (i = 0; i < PORT_RESET_TRIES; i++) {
+		status = set_port_feature(hub->hdev,
+				port1, USB_PORT_FEAT_RESET);
+		if (status)
+			dev_err(hub->intfdev,
+					"cannot reset port %d (err = %d)\n",
+					port1, status);
+		else {
+			status = hub_port_wait_reset(hub, port1, udev, delay);
+			if (status)
+				dev_dbg(hub->intfdev,
+						"port_wait_reset: err = %d\n",
+						status);
+		}
+
+		/* return on disconnect or reset */
+		switch (status) {
+		case 0:
+			/* TRSTRCY = 10 ms */
+			msleep(10);
+			/* FALL THROUGH */
+		case -ENOTCONN:
+		case -ENODEV:
+			clear_port_feature(hub->hdev,
+				port1, USB_PORT_FEAT_C_RESET);
+			/* FIXME need disconnect() for NOTATTACHED device */
+			usb_set_device_state(udev, status
+					? USB_STATE_NOTATTACHED
+					: USB_STATE_DEFAULT);
+			return status;
+		}
+
+		dev_dbg (hub->intfdev,
+			"port %d not enabled, trying reset again...\n",
+			port1);
+		delay = HUB_LONG_RESET_TIME;
+	}
+
+	dev_err (hub->intfdev,
+		"Cannot enable port %i.  Maybe the USB cable is bad?\n",
+		port1);
+
+	return status;
+}
+
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+	struct usb_device *hdev = hub->hdev;
+	int ret;
+
+	if (hdev->children[port1-1] && set_state) {
+		usb_set_device_state(hdev->children[port1-1],
+				USB_STATE_NOTATTACHED);
+	}
+	ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+	if (ret)
+		dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+			port1, ret);
+
+	return ret;
+}
+
+/*
+ * Disable a port and mark a logical connnect-change event, so that some
+ * time later khubd will disconnect() any existing usb_device on the port
+ * and will re-enumerate if there actually is a device attached.
+ */
+static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
+{
+	dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
+	hub_port_disable(hub, port1, 1);
+
+	/* FIXME let caller ask to power down the port:
+	 *  - some devices won't enumerate without a VBUS power cycle
+	 *  - SRP saves power that way
+	 *  - usb_suspend_device(dev,PM_SUSPEND_DISK)
+	 * That's easy if this hub can switch power per-port, and
+	 * khubd reactivates the port later (timer, SRP, etc).
+	 * Powerdown must be optional, because of reset/DFU.
+	 */
+
+	set_bit(port1, hub->change_bits);
+ 	kick_khubd(hub);
+}
+
+
+#ifdef	CONFIG_USB_SUSPEND
+
+/*
+ * Selective port suspend reduces power; most suspended devices draw
+ * less than 500 uA.  It's also used in OTG, along with remote wakeup.
+ * All devices below the suspended port are also suspended.
+ *
+ * Devices leave suspend state when the host wakes them up.  Some devices
+ * also support "remote wakeup", where the device can activate the USB
+ * tree above them to deliver data, such as a keypress or packet.  In
+ * some cases, this wakes the USB host.
+ */
+static int hub_port_suspend(struct usb_hub *hub, int port1,
+		struct usb_device *udev)
+{
+	int	status;
+
+	// dev_dbg(hub->intfdev, "suspend port %d\n", port1);
+
+	/* enable remote wakeup when appropriate; this lets the device
+	 * wake up the upstream hub (including maybe the root hub).
+	 *
+	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
+	 * we don't explicitly enable it here.
+	 */
+	if (udev->actconfig
+			// && FIXME (remote wakeup enabled on this bus)
+			// ... currently assuming it's always appropriate
+			&& (udev->actconfig->desc.bmAttributes
+				& USB_CONFIG_ATT_WAKEUP) != 0) {
+		status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+				USB_DEVICE_REMOTE_WAKEUP, 0,
+				NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+		if (status)
+			dev_dbg(&udev->dev,
+				"won't remote wakeup, status %d\n",
+				status);
+	}
+
+	/* see 7.1.7.6 */
+	status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
+	if (status) {
+		dev_dbg(hub->intfdev,
+			"can't suspend port %d, status %d\n",
+			port1, status);
+		/* paranoia:  "should not happen" */
+		(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+				USB_DEVICE_REMOTE_WAKEUP, 0,
+				NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	} else {
+		/* device has up to 10 msec to fully suspend */
+		dev_dbg(&udev->dev, "usb suspend\n");
+		usb_set_device_state(udev, USB_STATE_SUSPENDED);
+		msleep(10);
+	}
+	return status;
+}
+
+/*
+ * Devices on USB hub ports have only one "suspend" state, corresponding
+ * to ACPI D2 (PM_SUSPEND_MEM), "may cause the device to lose some context".
+ * State transitions include:
+ *
+ *   - suspend, resume ... when the VBUS power link stays live
+ *   - suspend, disconnect ... VBUS lost
+ *
+ * Once VBUS drop breaks the circuit, the port it's using has to go through
+ * normal re-enumeration procedures, starting with enabling VBUS power.
+ * Other than re-initializing the hub (plug/unplug, except for root hubs),
+ * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
+ * timer, no SRP, no requests through sysfs.
+ */
+static int __usb_suspend_device (struct usb_device *udev, int port1,
+				 pm_message_t state)
+{
+	int	status;
+
+	/* caller owns the udev device lock */
+	if (port1 < 0)
+		return port1;
+
+	if (udev->state == USB_STATE_SUSPENDED
+			|| udev->state == USB_STATE_NOTATTACHED) {
+		return 0;
+	}
+
+	/* suspend interface drivers; if this is a hub, it
+	 * suspends the child devices
+	 */
+	if (udev->actconfig) {
+		int	i;
+
+		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+			struct usb_interface	*intf;
+			struct usb_driver	*driver;
+
+			intf = udev->actconfig->interface[i];
+			if (state <= intf->dev.power.power_state)
+				continue;
+			if (!intf->dev.driver)
+				continue;
+			driver = to_usb_driver(intf->dev.driver);
+
+			if (driver->suspend) {
+				status = driver->suspend(intf, state);
+				if (intf->dev.power.power_state != state
+						|| status)
+					dev_err(&intf->dev,
+						"suspend %d fail, code %d\n",
+						state, status);
+			}
+
+			/* only drivers with suspend() can ever resume();
+			 * and after power loss, even they won't.
+			 * bus_rescan_devices() can rebind drivers later.
+			 *
+			 * FIXME the PM core self-deadlocks when unbinding
+			 * drivers during suspend/resume ... everything grabs
+			 * dpm_sem (not a spinlock, ugh).  we want to unbind,
+			 * since we know every driver's probe/disconnect works
+			 * even for drivers that can't suspend.
+			 */
+			if (!driver->suspend || state > PM_SUSPEND_MEM) {
+#if 1
+				dev_warn(&intf->dev, "resume is unsafe!\n");
+#else
+				down_write(&usb_bus_type.rwsem);
+				device_release_driver(&intf->dev);
+				up_write(&usb_bus_type.rwsem);
+#endif
+			}
+		}
+	}
+
+	/*
+	 * FIXME this needs port power off call paths too, to help force
+	 * USB into the "generic" PM model.  At least for devices on
+	 * ports that aren't using ganged switching (usually root hubs).
+	 *
+	 * NOTE: SRP-capable links should adopt more aggressive poweroff
+	 * policies (when HNP doesn't apply) once we have mechanisms to
+	 * turn power back on!  (Likely not before 2.7...)
+	 */
+	if (state > PM_SUSPEND_MEM) {
+		dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
+	}
+
+	/* "global suspend" of the HC-to-USB interface (root hub), or
+	 * "selective suspend" of just one hub-device link.
+	 */
+	if (!udev->parent) {
+		struct usb_bus	*bus = udev->bus;
+		if (bus && bus->op->hub_suspend) {
+			status = bus->op->hub_suspend (bus);
+			if (status == 0) {
+				dev_dbg(&udev->dev, "usb suspend\n");
+				usb_set_device_state(udev,
+						USB_STATE_SUSPENDED);
+			}
+		} else
+			status = -EOPNOTSUPP;
+	} else
+		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
+				udev);
+
+	if (status == 0)
+		udev->dev.power.power_state = state;
+	return status;
+}
+
+/**
+ * usb_suspend_device - suspend a usb device
+ * @udev: device that's no longer in active use
+ * @state: PMSG_SUSPEND to suspend
+ * Context: must be able to sleep; device not locked
+ *
+ * Suspends a USB device that isn't in active use, conserving power.
+ * Devices may wake out of a suspend, if anything important happens,
+ * using the remote wakeup mechanism.  They may also be taken out of
+ * suspend by the host, using usb_resume_device().  It's also routine
+ * to disconnect devices while they are suspended.
+ *
+ * Suspending OTG devices may trigger HNP, if that's been enabled
+ * between a pair of dual-role devices.  That will change roles, such
+ * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+{
+	int	port1, status;
+
+	port1 = locktree(udev);
+	if (port1 < 0)
+		return port1;
+
+	status = __usb_suspend_device(udev, port1, state);
+	usb_unlock_device(udev);
+	return status;
+}
+
+/*
+ * hardware resume signaling is finished, either because of selective
+ * resume (by host) or remote wakeup (by device) ... now see what changed
+ * in the tree that's rooted at this device.
+ */
+static int finish_port_resume(struct usb_device *udev)
+{
+	int	status;
+	u16	devstatus;
+
+	/* caller owns the udev device lock */
+	dev_dbg(&udev->dev, "usb resume\n");
+
+	/* usb ch9 identifies four variants of SUSPENDED, based on what
+	 * state the device resumes to.  Linux currently won't see the
+	 * first two on the host side; they'd be inside hub_port_init()
+	 * during many timeouts, but khubd can't suspend until later.
+	 */
+	usb_set_device_state(udev, udev->actconfig
+			? USB_STATE_CONFIGURED
+			: USB_STATE_ADDRESS);
+	udev->dev.power.power_state = PMSG_ON;
+
+ 	/* 10.5.4.5 says be sure devices in the tree are still there.
+ 	 * For now let's assume the device didn't go crazy on resume,
+	 * and device drivers will know about any resume quirks.
+	 */
+	status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+	if (status < 0)
+		dev_dbg(&udev->dev,
+			"gone after usb resume? status %d\n",
+			status);
+	else if (udev->actconfig) {
+		unsigned	i;
+
+		le16_to_cpus(&devstatus);
+		if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
+			status = usb_control_msg(udev,
+					usb_sndctrlpipe(udev, 0),
+					USB_REQ_CLEAR_FEATURE,
+						USB_RECIP_DEVICE,
+					USB_DEVICE_REMOTE_WAKEUP, 0,
+					NULL, 0,
+					USB_CTRL_SET_TIMEOUT);
+			if (status) {
+				dev_dbg(&udev->dev, "disable remote "
+					"wakeup, status %d\n", status);
+				status = 0;
+			}
+		}
+
+		/* resume interface drivers; if this is a hub, it
+		 * resumes the child devices
+		 */
+		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+			struct usb_interface	*intf;
+			struct usb_driver	*driver;
+
+			intf = udev->actconfig->interface[i];
+			if (intf->dev.power.power_state == PM_SUSPEND_ON)
+				continue;
+			if (!intf->dev.driver) {
+				/* FIXME maybe force to alt 0 */
+				continue;
+			}
+			driver = to_usb_driver(intf->dev.driver);
+
+			/* bus_rescan_devices() may rebind drivers */
+			if (!driver->resume)
+				continue;
+
+			/* can we do better than just logging errors? */
+			status = driver->resume(intf);
+			if (intf->dev.power.power_state != PM_SUSPEND_ON
+					|| status)
+				dev_dbg(&intf->dev,
+					"resume fail, state %d code %d\n",
+					intf->dev.power.power_state, status);
+		}
+		status = 0;
+
+	} else if (udev->devnum <= 0) {
+		dev_dbg(&udev->dev, "bogus resume!\n");
+		status = -EINVAL;
+	}
+	return status;
+}
+
+static int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+	int	status;
+
+	// dev_dbg(hub->intfdev, "resume port %d\n", port1);
+
+	/* see 7.1.7.7; affects power usage, but not budgeting */
+	status = clear_port_feature(hub->hdev,
+			port1, USB_PORT_FEAT_SUSPEND);
+	if (status) {
+		dev_dbg(hub->intfdev,
+			"can't resume port %d, status %d\n",
+			port1, status);
+	} else {
+		u16		devstatus;
+		u16		portchange;
+
+		/* drive resume for at least 20 msec */
+		if (udev)
+			dev_dbg(&udev->dev, "RESUME\n");
+		msleep(25);
+
+#define LIVE_FLAGS	( USB_PORT_STAT_POWER \
+			| USB_PORT_STAT_ENABLE \
+			| USB_PORT_STAT_CONNECTION)
+
+		/* Virtual root hubs can trigger on GET_PORT_STATUS to
+		 * stop resume signaling.  Then finish the resume
+		 * sequence.
+		 */
+		devstatus = portchange = 0;
+		status = hub_port_status(hub, port1,
+				&devstatus, &portchange);
+		if (status < 0
+				|| (devstatus & LIVE_FLAGS) != LIVE_FLAGS
+				|| (devstatus & USB_PORT_STAT_SUSPEND) != 0
+				) {
+			dev_dbg(hub->intfdev,
+				"port %d status %04x.%04x after resume, %d\n",
+				port1, portchange, devstatus, status);
+		} else {
+			/* TRSMRCY = 10 msec */
+			msleep(10);
+			if (udev)
+				status = finish_port_resume(udev);
+		}
+	}
+	if (status < 0)
+		hub_port_logical_disconnect(hub, port1);
+
+	return status;
+}
+
+static int hub_resume (struct usb_interface *intf);
+
+/**
+ * usb_resume_device - re-activate a suspended usb device
+ * @udev: device to re-activate
+ * Context: must be able to sleep; device not locked
+ *
+ * This will re-activate the suspended device, increasing power usage
+ * while letting drivers communicate again with its endpoints.
+ * USB resume explicitly guarantees that the power session between
+ * the host and the device is the same as it was when the device
+ * suspended.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_resume_device(struct usb_device *udev)
+{
+	int	port1, status;
+
+	port1 = locktree(udev);
+	if (port1 < 0)
+		return port1;
+
+	/* "global resume" of the HC-to-USB interface (root hub), or
+	 * selective resume of one hub-to-device port
+	 */
+	if (!udev->parent) {
+		struct usb_bus	*bus = udev->bus;
+		if (bus && bus->op->hub_resume) {
+			status = bus->op->hub_resume (bus);
+		} else
+			status = -EOPNOTSUPP;
+		if (status == 0) {
+			dev_dbg(&udev->dev, "usb resume\n");
+			/* TRSMRCY = 10 msec */
+			msleep(10);
+			usb_set_device_state (udev, USB_STATE_CONFIGURED);
+			udev->dev.power.power_state = PMSG_ON;
+			status = hub_resume (udev
+					->actconfig->interface[0]);
+		}
+	} else if (udev->state == USB_STATE_SUSPENDED) {
+		// NOTE this fails if parent is also suspended...
+		status = hub_port_resume(hdev_to_hub(udev->parent),
+				port1, udev);
+	} else {
+		status = 0;
+	}
+	if (status < 0) {
+		dev_dbg(&udev->dev, "can't resume, status %d\n",
+			status);
+	}
+
+	usb_unlock_device(udev);
+
+	/* rebind drivers that had no suspend() */
+	if (status == 0) {
+		usb_lock_all_devices();
+		bus_rescan_devices(&usb_bus_type);
+		usb_unlock_all_devices();
+	}
+	return status;
+}
+
+static int remote_wakeup(struct usb_device *udev)
+{
+	int	status = 0;
+
+	/* don't repeat RESUME sequence if this device
+	 * was already woken up by some other task
+	 */
+	down(&udev->serialize);
+	if (udev->state == USB_STATE_SUSPENDED) {
+		dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+		/* TRSMRCY = 10 msec */
+		msleep(10);
+		status = finish_port_resume(udev);
+	}
+	up(&udev->serialize);
+	return status;
+}
+
+static int hub_suspend(struct usb_interface *intf, pm_message_t state)
+{
+	struct usb_hub		*hub = usb_get_intfdata (intf);
+	struct usb_device	*hdev = hub->hdev;
+	unsigned		port1;
+	int			status;
+
+	/* stop khubd and related activity */
+	hub_quiesce(hub);
+
+	/* then suspend every port */
+	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+		struct usb_device	*udev;
+
+		udev = hdev->children [port1-1];
+		if (!udev)
+			continue;
+		down(&udev->serialize);
+		status = __usb_suspend_device(udev, port1, state);
+		up(&udev->serialize);
+		if (status < 0)
+			dev_dbg(&intf->dev, "suspend port %d --> %d\n",
+				port1, status);
+	}
+
+	intf->dev.power.power_state = state;
+	return 0;
+}
+
+static int hub_resume(struct usb_interface *intf)
+{
+	struct usb_device	*hdev = interface_to_usbdev(intf);
+	struct usb_hub		*hub = usb_get_intfdata (intf);
+	unsigned		port1;
+	int			status;
+
+	if (intf->dev.power.power_state == PM_SUSPEND_ON)
+		return 0;
+
+	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+		struct usb_device	*udev;
+		u16			portstat, portchange;
+
+		udev = hdev->children [port1-1];
+		status = hub_port_status(hub, port1, &portstat, &portchange);
+		if (status == 0) {
+			if (portchange & USB_PORT_STAT_C_SUSPEND) {
+				clear_port_feature(hdev, port1,
+					USB_PORT_FEAT_C_SUSPEND);
+				portchange &= ~USB_PORT_STAT_C_SUSPEND;
+			}
+
+			/* let khubd handle disconnects etc */
+			if (portchange)
+				continue;
+		}
+
+		if (!udev || status < 0)
+			continue;
+		down (&udev->serialize);
+		if (portstat & USB_PORT_STAT_SUSPEND)
+			status = hub_port_resume(hub, port1, udev);
+		else {
+			status = finish_port_resume(udev);
+			if (status < 0) {
+				dev_dbg(&intf->dev, "resume port %d --> %d\n",
+					port1, status);
+				hub_port_logical_disconnect(hub, port1);
+			}
+		}
+		up(&udev->serialize);
+	}
+	intf->dev.power.power_state = PMSG_ON;
+
+	hub->resume_root_hub = 0;
+	hub_activate(hub);
+	return 0;
+}
+
+void usb_resume_root_hub(struct usb_device *hdev)
+{
+	struct usb_hub *hub = hdev_to_hub(hdev);
+
+	hub->resume_root_hub = 1;
+	kick_khubd(hub);
+}
+
+#else	/* !CONFIG_USB_SUSPEND */
+
+int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+{
+	return 0;
+}
+
+int usb_resume_device(struct usb_device *udev)
+{
+	return 0;
+}
+
+#define	hub_suspend		NULL
+#define	hub_resume		NULL
+#define	remote_wakeup(x)	0
+
+#endif	/* CONFIG_USB_SUSPEND */
+
+EXPORT_SYMBOL(usb_suspend_device);
+EXPORT_SYMBOL(usb_resume_device);
+
+
+
+/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
+ *
+ * Between connect detection and reset signaling there must be a delay
+ * of 100ms at least for debounce and power-settling.  The corresponding
+ * timer shall restart whenever the downstream port detects a disconnect.
+ * 
+ * Apparently there are some bluetooth and irda-dongles and a number of
+ * low-speed devices for which this debounce period may last over a second.
+ * Not covered by the spec - but easy to deal with.
+ *
+ * This implementation uses a 1500ms total debounce timeout; if the
+ * connection isn't stable by then it returns -ETIMEDOUT.  It checks
+ * every 25ms for transient disconnects.  When the port status has been
+ * unchanged for 100ms it returns the port status.
+ */
+
+#define HUB_DEBOUNCE_TIMEOUT	1500
+#define HUB_DEBOUNCE_STEP	  25
+#define HUB_DEBOUNCE_STABLE	 100
+
+static int hub_port_debounce(struct usb_hub *hub, int port1)
+{
+	int ret;
+	int total_time, stable_time = 0;
+	u16 portchange, portstatus;
+	unsigned connection = 0xffff;
+
+	for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		if (ret < 0)
+			return ret;
+
+		if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
+		     (portstatus & USB_PORT_STAT_CONNECTION) == connection) {
+			stable_time += HUB_DEBOUNCE_STEP;
+			if (stable_time >= HUB_DEBOUNCE_STABLE)
+				break;
+		} else {
+			stable_time = 0;
+			connection = portstatus & USB_PORT_STAT_CONNECTION;
+		}
+
+		if (portchange & USB_PORT_STAT_C_CONNECTION) {
+			clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_CONNECTION);
+		}
+
+		if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+			break;
+		msleep(HUB_DEBOUNCE_STEP);
+	}
+
+	dev_dbg (hub->intfdev,
+		"debounce: port %d: total %dms stable %dms status 0x%x\n",
+		port1, total_time, stable_time, portstatus);
+
+	if (stable_time < HUB_DEBOUNCE_STABLE)
+		return -ETIMEDOUT;
+	return portstatus;
+}
+
+static void ep0_reinit(struct usb_device *udev)
+{
+	usb_disable_endpoint(udev, 0 + USB_DIR_IN);
+	usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
+	udev->ep_in[0] = udev->ep_out[0] = &udev->ep0;
+}
+
+#define usb_sndaddr0pipe()	(PIPE_CONTROL << 30)
+#define usb_rcvaddr0pipe()	((PIPE_CONTROL << 30) | USB_DIR_IN)
+
+static int hub_set_address(struct usb_device *udev)
+{
+	int retval;
+
+	if (udev->devnum == 0)
+		return -EINVAL;
+	if (udev->state == USB_STATE_ADDRESS)
+		return 0;
+	if (udev->state != USB_STATE_DEFAULT)
+		return -EINVAL;
+	retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+		USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,
+		NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (retval == 0) {
+		usb_set_device_state(udev, USB_STATE_ADDRESS);
+		ep0_reinit(udev);
+	}
+	return retval;
+}
+
+/* Reset device, (re)assign address, get device descriptor.
+ * Device connection must be stable, no more debouncing needed.
+ * Returns device in USB_STATE_ADDRESS, except on error.
+ *
+ * If this is called for an already-existing device (as part of
+ * usb_reset_device), the caller must own the device lock.  For a
+ * newly detected device that is not accessible through any global
+ * pointers, it's not necessary to lock the device.
+ */
+static int
+hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
+		int retry_counter)
+{
+	static DECLARE_MUTEX(usb_address0_sem);
+
+	struct usb_device	*hdev = hub->hdev;
+	int			i, j, retval;
+	unsigned		delay = HUB_SHORT_RESET_TIME;
+	enum usb_device_speed	oldspeed = udev->speed;
+
+	/* root hub ports have a slightly longer reset period
+	 * (from USB 2.0 spec, section 7.1.7.5)
+	 */
+	if (!hdev->parent) {
+		delay = HUB_ROOT_RESET_TIME;
+		if (port1 == hdev->bus->otg_port)
+			hdev->bus->b_hnp_enable = 0;
+	}
+
+	/* Some low speed devices have problems with the quick delay, so */
+	/*  be a bit pessimistic with those devices. RHbug #23670 */
+	if (oldspeed == USB_SPEED_LOW)
+		delay = HUB_LONG_RESET_TIME;
+
+	down(&usb_address0_sem);
+
+	/* Reset the device; full speed may morph to high speed */
+	retval = hub_port_reset(hub, port1, udev, delay);
+	if (retval < 0)		/* error or disconnect */
+		goto fail;
+				/* success, speed is known */
+	retval = -ENODEV;
+
+	if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
+		dev_dbg(&udev->dev, "device reset changed speed!\n");
+		goto fail;
+	}
+	oldspeed = udev->speed;
+  
+	/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+	 * it's fixed size except for full speed devices.
+	 */
+	switch (udev->speed) {
+	case USB_SPEED_HIGH:		/* fixed at 64 */
+		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
+		break;
+	case USB_SPEED_FULL:		/* 8, 16, 32, or 64 */
+		/* to determine the ep0 maxpacket size, try to read
+		 * the device descriptor to get bMaxPacketSize0 and
+		 * then correct our initial guess.
+		 */
+		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
+		break;
+	case USB_SPEED_LOW:		/* fixed at 8 */
+		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);
+		break;
+	default:
+		goto fail;
+	}
+ 
+	dev_info (&udev->dev,
+			"%s %s speed USB device using %s and address %d\n",
+			(udev->config) ? "reset" : "new",
+			({ char *speed; switch (udev->speed) {
+			case USB_SPEED_LOW:	speed = "low";	break;
+			case USB_SPEED_FULL:	speed = "full";	break;
+			case USB_SPEED_HIGH:	speed = "high";	break;
+			default: 		speed = "?";	break;
+			}; speed;}),
+			udev->bus->controller->driver->name,
+			udev->devnum);
+
+	/* Set up TT records, if needed  */
+	if (hdev->tt) {
+		udev->tt = hdev->tt;
+		udev->ttport = hdev->ttport;
+	} else if (udev->speed != USB_SPEED_HIGH
+			&& hdev->speed == USB_SPEED_HIGH) {
+		udev->tt = &hub->tt;
+		udev->ttport = port1;
+	}
+ 
+	/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
+	 * Because device hardware and firmware is sometimes buggy in
+	 * this area, and this is how Linux has done it for ages.
+	 * Change it cautiously.
+	 *
+	 * NOTE:  If USE_NEW_SCHEME() is true we will start by issuing
+	 * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
+	 * so it may help with some non-standards-compliant devices.
+	 * Otherwise we start with SET_ADDRESS and then try to read the
+	 * first 8 bytes of the device descriptor to get the ep0 maxpacket
+	 * value.
+	 */
+	for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
+		if (USE_NEW_SCHEME(retry_counter)) {
+			struct usb_device_descriptor *buf;
+			int r = 0;
+
+#define GET_DESCRIPTOR_BUFSIZE	64
+			buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+			if (!buf) {
+				retval = -ENOMEM;
+				continue;
+			}
+
+			/* Use a short timeout the first time through,
+			 * so that recalcitrant full-speed devices with
+			 * 8- or 16-byte ep0-maxpackets won't slow things
+			 * down tremendously by NAKing the unexpectedly
+			 * early status stage.  Also, retry on all errors;
+			 * some devices are flakey.
+			 */
+			for (j = 0; j < 3; ++j) {
+				buf->bMaxPacketSize0 = 0;
+				r = usb_control_msg(udev, usb_rcvaddr0pipe(),
+					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+					USB_DT_DEVICE << 8, 0,
+					buf, GET_DESCRIPTOR_BUFSIZE,
+					(i ? USB_CTRL_GET_TIMEOUT : 1000));
+				switch (buf->bMaxPacketSize0) {
+				case 8: case 16: case 32: case 64:
+					if (buf->bDescriptorType ==
+							USB_DT_DEVICE) {
+						r = 0;
+						break;
+					}
+					/* FALL THROUGH */
+				default:
+					if (r == 0)
+						r = -EPROTO;
+					break;
+				}
+				if (r == 0)
+					break;
+			}
+			udev->descriptor.bMaxPacketSize0 =
+					buf->bMaxPacketSize0;
+			kfree(buf);
+
+			retval = hub_port_reset(hub, port1, udev, delay);
+			if (retval < 0)		/* error or disconnect */
+				goto fail;
+			if (oldspeed != udev->speed) {
+				dev_dbg(&udev->dev,
+					"device reset changed speed!\n");
+				retval = -ENODEV;
+				goto fail;
+			}
+			if (r) {
+				dev_err(&udev->dev, "device descriptor "
+						"read/%s, error %d\n",
+						"64", r);
+				retval = -EMSGSIZE;
+				continue;
+			}
+#undef GET_DESCRIPTOR_BUFSIZE
+		}
+
+		for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
+			retval = hub_set_address(udev);
+			if (retval >= 0)
+				break;
+			msleep(200);
+		}
+		if (retval < 0) {
+			dev_err(&udev->dev,
+				"device not accepting address %d, error %d\n",
+				udev->devnum, retval);
+			goto fail;
+		}
+ 
+		/* cope with hardware quirkiness:
+		 *  - let SET_ADDRESS settle, some device hardware wants it
+		 *  - read ep0 maxpacket even for high and low speed,
+  		 */
+		msleep(10);
+		if (USE_NEW_SCHEME(retry_counter))
+			break;
+
+		retval = usb_get_device_descriptor(udev, 8);
+		if (retval < 8) {
+			dev_err(&udev->dev, "device descriptor "
+					"read/%s, error %d\n",
+					"8", retval);
+			if (retval >= 0)
+				retval = -EMSGSIZE;
+		} else {
+			retval = 0;
+			break;
+		}
+	}
+	if (retval)
+		goto fail;
+
+	i = udev->descriptor.bMaxPacketSize0;
+	if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
+		if (udev->speed != USB_SPEED_FULL ||
+				!(i == 8 || i == 16 || i == 32 || i == 64)) {
+			dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);
+			retval = -EMSGSIZE;
+			goto fail;
+		}
+		dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
+		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
+		ep0_reinit(udev);
+	}
+  
+	retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
+	if (retval < (signed)sizeof(udev->descriptor)) {
+		dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
+			"all", retval);
+		if (retval >= 0)
+			retval = -ENOMSG;
+		goto fail;
+	}
+
+	retval = 0;
+
+fail:
+	if (retval)
+		hub_port_disable(hub, port1, 0);
+	up(&usb_address0_sem);
+	return retval;
+}
+
+static void
+check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
+{
+	struct usb_qualifier_descriptor	*qual;
+	int				status;
+
+	qual = kmalloc (sizeof *qual, SLAB_KERNEL);
+	if (qual == NULL)
+		return;
+
+	status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,
+			qual, sizeof *qual);
+	if (status == sizeof *qual) {
+		dev_info(&udev->dev, "not running at top speed; "
+			"connect to a high speed hub\n");
+		/* hub LEDs are probably harder to miss than syslog */
+		if (hub->has_indicators) {
+			hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;
+			schedule_work (&hub->leds);
+		}
+	}
+	kfree (qual);
+}
+
+static unsigned
+hub_power_remaining (struct usb_hub *hub)
+{
+	struct usb_device *hdev = hub->hdev;
+	int remaining;
+	unsigned i;
+
+	remaining = hub->power_budget;
+	if (!remaining)		/* self-powered */
+		return 0;
+
+	for (i = 0; i < hdev->maxchild; i++) {
+		struct usb_device	*udev = hdev->children[i];
+		int			delta, ceiling;
+
+		if (!udev)
+			continue;
+
+		/* 100mA per-port ceiling, or 8mA for OTG ports */
+		if (i != (udev->bus->otg_port - 1) || hdev->parent)
+			ceiling = 50;
+		else
+			ceiling = 4;
+
+		if (udev->actconfig)
+			delta = udev->actconfig->desc.bMaxPower;
+		else
+			delta = ceiling;
+		// dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);
+		if (delta > ceiling)
+			dev_warn(&udev->dev, "%dmA over %dmA budget!\n",
+				2 * (delta - ceiling), 2 * ceiling);
+		remaining -= delta;
+	}
+	if (remaining < 0) {
+		dev_warn(hub->intfdev,
+			"%dmA over power budget!\n",
+			-2 * remaining);
+		remaining = 0;
+	}
+	return remaining;
+}
+
+/* Handle physical or logical connection change events.
+ * This routine is called when:
+ * 	a port connection-change occurs;
+ *	a port enable-change occurs (often caused by EMI);
+ *	usb_reset_device() encounters changed descriptors (as from
+ *		a firmware download)
+ * caller already locked the hub
+ */
+static void hub_port_connect_change(struct usb_hub *hub, int port1,
+					u16 portstatus, u16 portchange)
+{
+	struct usb_device *hdev = hub->hdev;
+	struct device *hub_dev = hub->intfdev;
+	int status, i;
+ 
+	dev_dbg (hub_dev,
+		"port %d, status %04x, change %04x, %s\n",
+		port1, portstatus, portchange, portspeed (portstatus));
+
+	if (hub->has_indicators) {
+		set_port_led(hub, port1, HUB_LED_AUTO);
+		hub->indicator[port1-1] = INDICATOR_AUTO;
+	}
+ 
+	/* Disconnect any existing devices under this port */
+	if (hdev->children[port1-1])
+		usb_disconnect(&hdev->children[port1-1]);
+	clear_bit(port1, hub->change_bits);
+
+#ifdef	CONFIG_USB_OTG
+	/* during HNP, don't repeat the debounce */
+	if (hdev->bus->is_b_host)
+		portchange &= ~USB_PORT_STAT_C_CONNECTION;
+#endif
+
+	if (portchange & USB_PORT_STAT_C_CONNECTION) {
+		status = hub_port_debounce(hub, port1);
+		if (status < 0) {
+			dev_err (hub_dev,
+				"connect-debounce failed, port %d disabled\n",
+				port1);
+			goto done;
+		}
+		portstatus = status;
+	}
+
+	/* Return now if nothing is connected */
+	if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+
+		/* maybe switch power back on (e.g. root hub was reset) */
+		if ((hub->descriptor->wHubCharacteristics
+					& HUB_CHAR_LPSM) < 2
+				&& !(portstatus & (1 << USB_PORT_FEAT_POWER)))
+			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+ 
+		if (portstatus & USB_PORT_STAT_ENABLE)
+  			goto done;
+		return;
+	}
+
+#ifdef  CONFIG_USB_SUSPEND
+	/* If something is connected, but the port is suspended, wake it up. */
+	if (portstatus & USB_PORT_STAT_SUSPEND) {
+		status = hub_port_resume(hub, port1, NULL);
+		if (status < 0) {
+			dev_dbg(hub_dev,
+				"can't clear suspend on port %d; %d\n",
+				port1, status);
+			goto done;
+		}
+	}
+#endif
+
+	for (i = 0; i < SET_CONFIG_TRIES; i++) {
+		struct usb_device *udev;
+
+		/* reallocate for each attempt, since references
+		 * to the previous one can escape in various ways
+		 */
+		udev = usb_alloc_dev(hdev, hdev->bus, port1);
+		if (!udev) {
+			dev_err (hub_dev,
+				"couldn't allocate port %d usb_device\n",
+				port1);
+			goto done;
+		}
+
+		usb_set_device_state(udev, USB_STATE_POWERED);
+		udev->speed = USB_SPEED_UNKNOWN;
+ 
+		/* set the address */
+		choose_address(udev);
+		if (udev->devnum <= 0) {
+			status = -ENOTCONN;	/* Don't retry */
+			goto loop;
+		}
+
+		/* reset and get descriptor */
+		status = hub_port_init(hub, udev, port1, i);
+		if (status < 0)
+			goto loop;
+
+		/* consecutive bus-powered hubs aren't reliable; they can
+		 * violate the voltage drop budget.  if the new child has
+		 * a "powered" LED, users should notice we didn't enable it
+		 * (without reading syslog), even without per-port LEDs
+		 * on the parent.
+		 */
+		if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
+				&& hub->power_budget) {
+			u16	devstat;
+
+			status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
+					&devstat);
+			if (status < 0) {
+				dev_dbg(&udev->dev, "get status %d ?\n", status);
+				goto loop_disable;
+			}
+			cpu_to_le16s(&devstat);
+			if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+				dev_err(&udev->dev,
+					"can't connect bus-powered hub "
+					"to this port\n");
+				if (hub->has_indicators) {
+					hub->indicator[port1-1] =
+						INDICATOR_AMBER_BLINK;
+					schedule_work (&hub->leds);
+				}
+				status = -ENOTCONN;	/* Don't retry */
+				goto loop_disable;
+			}
+		}
+ 
+		/* check for devices running slower than they could */
+		if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
+				&& udev->speed == USB_SPEED_FULL
+				&& highspeed_hubs != 0)
+			check_highspeed (hub, udev, port1);
+
+		/* Store the parent's children[] pointer.  At this point
+		 * udev becomes globally accessible, although presumably
+		 * no one will look at it until hdev is unlocked.
+		 */
+		down (&udev->serialize);
+		status = 0;
+
+		/* We mustn't add new devices if the parent hub has
+		 * been disconnected; we would race with the
+		 * recursively_mark_NOTATTACHED() routine.
+		 */
+		spin_lock_irq(&device_state_lock);
+		if (hdev->state == USB_STATE_NOTATTACHED)
+			status = -ENOTCONN;
+		else
+			hdev->children[port1-1] = udev;
+		spin_unlock_irq(&device_state_lock);
+
+		/* Run it through the hoops (find a driver, etc) */
+		if (!status) {
+			status = usb_new_device(udev);
+			if (status) {
+				spin_lock_irq(&device_state_lock);
+				hdev->children[port1-1] = NULL;
+				spin_unlock_irq(&device_state_lock);
+			}
+		}
+
+		up (&udev->serialize);
+		if (status)
+			goto loop_disable;
+
+		status = hub_power_remaining(hub);
+		if (status)
+			dev_dbg(hub_dev,
+				"%dmA power budget left\n",
+				2 * status);
+
+		return;
+
+loop_disable:
+		hub_port_disable(hub, port1, 1);
+loop:
+		ep0_reinit(udev);
+		release_address(udev);
+		usb_put_dev(udev);
+		if (status == -ENOTCONN)
+			break;
+	}
+ 
+done:
+	hub_port_disable(hub, port1, 1);
+}
+
+static void hub_events(void)
+{
+	struct list_head *tmp;
+	struct usb_device *hdev;
+	struct usb_interface *intf;
+	struct usb_hub *hub;
+	struct device *hub_dev;
+	u16 hubstatus;
+	u16 hubchange;
+	u16 portstatus;
+	u16 portchange;
+	int i, ret;
+	int connect_change;
+
+	/*
+	 *  We restart the list every time to avoid a deadlock with
+	 * deleting hubs downstream from this one. This should be
+	 * safe since we delete the hub from the event list.
+	 * Not the most efficient, but avoids deadlocks.
+	 */
+	while (1) {
+
+		/* Grab the first entry at the beginning of the list */
+		spin_lock_irq(&hub_event_lock);
+		if (list_empty(&hub_event_list)) {
+			spin_unlock_irq(&hub_event_lock);
+			break;
+		}
+
+		tmp = hub_event_list.next;
+		list_del_init(tmp);
+
+		hub = list_entry(tmp, struct usb_hub, event_list);
+		hdev = hub->hdev;
+		intf = to_usb_interface(hub->intfdev);
+		hub_dev = &intf->dev;
+
+		dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
+				hdev->state, hub->descriptor
+					? hub->descriptor->bNbrPorts
+					: 0,
+				/* NOTE: expects max 15 ports... */
+				(u16) hub->change_bits[0],
+				(u16) hub->event_bits[0]);
+
+		usb_get_intf(intf);
+		i = hub->resume_root_hub;
+		spin_unlock_irq(&hub_event_lock);
+
+		/* Is this is a root hub wanting to be resumed? */
+		if (i)
+			usb_resume_device(hdev);
+
+		/* Lock the device, then check to see if we were
+		 * disconnected while waiting for the lock to succeed. */
+		if (locktree(hdev) < 0) {
+			usb_put_intf(intf);
+			continue;
+		}
+		if (hub != usb_get_intfdata(intf))
+			goto loop;
+
+		/* If the hub has died, clean up after it */
+		if (hdev->state == USB_STATE_NOTATTACHED) {
+			hub_pre_reset(hub);
+			goto loop;
+		}
+
+		/* If this is an inactive or suspended hub, do nothing */
+		if (hub->quiescing)
+			goto loop;
+
+		if (hub->error) {
+			dev_dbg (hub_dev, "resetting for error %d\n",
+				hub->error);
+
+			ret = usb_reset_device(hdev);
+			if (ret) {
+				dev_dbg (hub_dev,
+					"error resetting hub: %d\n", ret);
+				goto loop;
+			}
+
+			hub->nerrors = 0;
+			hub->error = 0;
+		}
+
+		/* deal with port status changes */
+		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
+			if (test_bit(i, hub->busy_bits))
+				continue;
+			connect_change = test_bit(i, hub->change_bits);
+			if (!test_and_clear_bit(i, hub->event_bits) &&
+					!connect_change && !hub->activating)
+				continue;
+
+			ret = hub_port_status(hub, i,
+					&portstatus, &portchange);
+			if (ret < 0)
+				continue;
+
+			if (hub->activating && !hdev->children[i-1] &&
+					(portstatus &
+						USB_PORT_STAT_CONNECTION))
+				connect_change = 1;
+
+			if (portchange & USB_PORT_STAT_C_CONNECTION) {
+				clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_CONNECTION);
+				connect_change = 1;
+			}
+
+			if (portchange & USB_PORT_STAT_C_ENABLE) {
+				if (!connect_change)
+					dev_dbg (hub_dev,
+						"port %d enable change, "
+						"status %08x\n",
+						i, portstatus);
+				clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_ENABLE);
+
+				/*
+				 * EM interference sometimes causes badly
+				 * shielded USB devices to be shutdown by
+				 * the hub, this hack enables them again.
+				 * Works at least with mouse driver. 
+				 */
+				if (!(portstatus & USB_PORT_STAT_ENABLE)
+				    && !connect_change
+				    && hdev->children[i-1]) {
+					dev_err (hub_dev,
+					    "port %i "
+					    "disabled by hub (EMI?), "
+					    "re-enabling...\n",
+						i);
+					connect_change = 1;
+				}
+			}
+
+			if (portchange & USB_PORT_STAT_C_SUSPEND) {
+				clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_SUSPEND);
+				if (hdev->children[i-1]) {
+					ret = remote_wakeup(hdev->
+							children[i-1]);
+					if (ret < 0)
+						connect_change = 1;
+				} else {
+					ret = -ENODEV;
+					hub_port_disable(hub, i, 1);
+				}
+				dev_dbg (hub_dev,
+					"resume on port %d, status %d\n",
+					i, ret);
+			}
+			
+			if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+				dev_err (hub_dev,
+					"over-current change on port %d\n",
+					i);
+				clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_OVER_CURRENT);
+				hub_power_on(hub);
+			}
+
+			if (portchange & USB_PORT_STAT_C_RESET) {
+				dev_dbg (hub_dev,
+					"reset change on port %d\n",
+					i);
+				clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_RESET);
+			}
+
+			if (connect_change)
+				hub_port_connect_change(hub, i,
+						portstatus, portchange);
+		} /* end for i */
+
+		/* deal with hub status changes */
+		if (test_and_clear_bit(0, hub->event_bits) == 0)
+			;	/* do nothing */
+		else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
+			dev_err (hub_dev, "get_hub_status failed\n");
+		else {
+			if (hubchange & HUB_CHANGE_LOCAL_POWER) {
+				dev_dbg (hub_dev, "power change\n");
+				clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
+			}
+			if (hubchange & HUB_CHANGE_OVERCURRENT) {
+				dev_dbg (hub_dev, "overcurrent change\n");
+				msleep(500);	/* Cool down */
+				clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
+                        	hub_power_on(hub);
+			}
+		}
+
+		hub->activating = 0;
+
+loop:
+		usb_unlock_device(hdev);
+		usb_put_intf(intf);
+
+        } /* end while (1) */
+}
+
+static int hub_thread(void *__unused)
+{
+	/*
+	 * This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+
+	daemonize("khubd");
+	allow_signal(SIGKILL);
+
+	/* Send me a signal to get me die (for debugging) */
+	do {
+		hub_events();
+		wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); 
+		try_to_freeze(PF_FREEZE);
+	} while (!signal_pending(current));
+
+	pr_debug ("%s: khubd exiting\n", usbcore_name);
+	complete_and_exit(&khubd_exited, 0);
+}
+
+static struct usb_device_id hub_id_table [] = {
+    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
+      .bDeviceClass = USB_CLASS_HUB},
+    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
+      .bInterfaceClass = USB_CLASS_HUB},
+    { }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hub_id_table);
+
+static struct usb_driver hub_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"hub",
+	.probe =	hub_probe,
+	.disconnect =	hub_disconnect,
+	.suspend =	hub_suspend,
+	.resume =	hub_resume,
+	.ioctl =	hub_ioctl,
+	.id_table =	hub_id_table,
+};
+
+int usb_hub_init(void)
+{
+	pid_t pid;
+
+	if (usb_register(&hub_driver) < 0) {
+		printk(KERN_ERR "%s: can't register hub driver\n",
+			usbcore_name);
+		return -1;
+	}
+
+	pid = kernel_thread(hub_thread, NULL, CLONE_KERNEL);
+	if (pid >= 0) {
+		khubd_pid = pid;
+
+		return 0;
+	}
+
+	/* Fall through if kernel_thread failed */
+	usb_deregister(&hub_driver);
+	printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
+
+	return -1;
+}
+
+void usb_hub_cleanup(void)
+{
+	int ret;
+
+	/* Kill the thread */
+	ret = kill_proc(khubd_pid, SIGKILL, 1);
+
+	wait_for_completion(&khubd_exited);
+
+	/*
+	 * Hub resources are freed for us by usb_deregister. It calls
+	 * usb_driver_purge on every device which in turn calls that
+	 * devices disconnect function if it is using this driver.
+	 * The hub_disconnect function takes care of releasing the
+	 * individual hub resources. -greg
+	 */
+	usb_deregister(&hub_driver);
+} /* usb_hub_cleanup() */
+
+
+static int config_descriptors_changed(struct usb_device *udev)
+{
+	unsigned			index;
+	unsigned			len = 0;
+	struct usb_config_descriptor	*buf;
+
+	for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
+		if (len < le16_to_cpu(udev->config[index].desc.wTotalLength))
+			len = le16_to_cpu(udev->config[index].desc.wTotalLength);
+	}
+	buf = kmalloc (len, SLAB_KERNEL);
+	if (buf == NULL) {
+		dev_err(&udev->dev, "no mem to re-read configs after reset\n");
+		/* assume the worst */
+		return 1;
+	}
+	for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
+		int length;
+		int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
+
+		length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
+				old_length);
+		if (length < old_length) {
+			dev_dbg(&udev->dev, "config index %d, error %d\n",
+					index, length);
+			break;
+		}
+		if (memcmp (buf, udev->rawdescriptors[index], old_length)
+				!= 0) {
+			dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
+				index, buf->bConfigurationValue);
+			break;
+		}
+	}
+	kfree(buf);
+	return index != udev->descriptor.bNumConfigurations;
+}
+
+/**
+ * usb_reset_device - perform a USB port reset to reinitialize a device
+ * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
+ *
+ * WARNING - don't reset any device unless drivers for all of its
+ * interfaces are expecting that reset!  Maybe some driver->reset()
+ * method should eventually help ensure sufficient cooperation.
+ *
+ * Do a port reset, reassign the device's address, and establish its
+ * former operating configuration.  If the reset fails, or the device's
+ * descriptors change from their values before the reset, or the original
+ * configuration and altsettings cannot be restored, a flag will be set
+ * telling khubd to pretend the device has been disconnected and then
+ * re-connected.  All drivers will be unbound, and the device will be
+ * re-enumerated and probed all over again.
+ *
+ * Returns 0 if the reset succeeded, -ENODEV if the device has been
+ * flagged for logical disconnection, or some other negative error code
+ * if the reset wasn't even attempted.
+ *
+ * The caller must own the device lock.  For example, it's safe to use
+ * this from a driver probe() routine after downloading new firmware.
+ * For calls that might not occur during probe(), drivers should lock
+ * the device using usb_lock_device_for_reset().
+ */
+int usb_reset_device(struct usb_device *udev)
+{
+	struct usb_device		*parent_hdev = udev->parent;
+	struct usb_hub			*parent_hub;
+	struct usb_device_descriptor	descriptor = udev->descriptor;
+	struct usb_hub			*hub = NULL;
+	int 				i, ret = 0, port1 = -1;
+
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state == USB_STATE_SUSPENDED) {
+		dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
+				udev->state);
+		return -EINVAL;
+	}
+
+	if (!parent_hdev) {
+		/* this requires hcd-specific logic; see OHCI hc_restart() */
+		dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
+		return -EISDIR;
+	}
+
+	for (i = 0; i < parent_hdev->maxchild; i++)
+		if (parent_hdev->children[i] == udev) {
+			port1 = i + 1;
+			break;
+		}
+
+	if (port1 < 0) {
+		/* If this ever happens, it's very bad */
+		dev_err(&udev->dev, "Can't locate device's port!\n");
+		return -ENOENT;
+	}
+	parent_hub = hdev_to_hub(parent_hdev);
+
+	/* If we're resetting an active hub, take some special actions */
+	if (udev->actconfig &&
+			udev->actconfig->interface[0]->dev.driver ==
+				&hub_driver.driver &&
+			(hub = hdev_to_hub(udev)) != NULL) {
+		hub_pre_reset(hub);
+	}
+
+	set_bit(port1, parent_hub->busy_bits);
+	for (i = 0; i < SET_CONFIG_TRIES; ++i) {
+
+		/* ep0 maxpacket size may change; let the HCD know about it.
+		 * Other endpoints will be handled by re-enumeration. */
+		ep0_reinit(udev);
+		ret = hub_port_init(parent_hub, udev, port1, i);
+		if (ret >= 0)
+			break;
+	}
+	clear_bit(port1, parent_hub->busy_bits);
+	if (ret < 0)
+		goto re_enumerate;
+ 
+	/* Device might have changed firmware (DFU or similar) */
+	if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor)
+			|| config_descriptors_changed (udev)) {
+		dev_info(&udev->dev, "device firmware changed\n");
+		udev->descriptor = descriptor;	/* for disconnect() calls */
+		goto re_enumerate;
+  	}
+  
+	if (!udev->actconfig)
+		goto done;
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_SET_CONFIGURATION, 0,
+			udev->actconfig->desc.bConfigurationValue, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(&udev->dev,
+			"can't restore configuration #%d (error=%d)\n",
+			udev->actconfig->desc.bConfigurationValue, ret);
+		goto re_enumerate;
+  	}
+	usb_set_device_state(udev, USB_STATE_CONFIGURED);
+
+	for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+		struct usb_interface *intf = udev->actconfig->interface[i];
+		struct usb_interface_descriptor *desc;
+
+		/* set_interface resets host side toggle even
+		 * for altsetting zero.  the interface may have no driver.
+		 */
+		desc = &intf->cur_altsetting->desc;
+		ret = usb_set_interface(udev, desc->bInterfaceNumber,
+			desc->bAlternateSetting);
+		if (ret < 0) {
+			dev_err(&udev->dev, "failed to restore interface %d "
+				"altsetting %d (error=%d)\n",
+				desc->bInterfaceNumber,
+				desc->bAlternateSetting,
+				ret);
+			goto re_enumerate;
+		}
+	}
+
+done:
+	if (hub)
+		hub_post_reset(hub);
+	return 0;
+ 
+re_enumerate:
+	hub_port_logical_disconnect(parent_hub, port1);
+	return -ENODEV;
+}
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
new file mode 100644
index 0000000..d114b84
--- /dev/null
+++ b/drivers/usb/core/hub.h
@@ -0,0 +1,238 @@
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+/*
+ * Hub protocol and driver data structures.
+ *
+ * Some of these are known to the "virtual root hub" code
+ * in host controller drivers.
+ */
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h>	/* likely()/unlikely() */
+
+/*
+ * Hub request types
+ */
+
+#define USB_RT_HUB	(USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT	(USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+/*
+ * Hub class requests
+ * See USB 2.0 spec Table 11-16
+ */
+#define HUB_CLEAR_TT_BUFFER	8
+#define HUB_RESET_TT		9
+#define HUB_GET_TT_STATE	10
+#define HUB_STOP_TT		11
+
+/*
+ * Hub Class feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define C_HUB_LOCAL_POWER	0
+#define C_HUB_OVER_CURRENT	1
+
+/*
+ * Port feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define USB_PORT_FEAT_CONNECTION	0
+#define USB_PORT_FEAT_ENABLE		1
+#define USB_PORT_FEAT_SUSPEND		2
+#define USB_PORT_FEAT_OVER_CURRENT	3
+#define USB_PORT_FEAT_RESET		4
+#define USB_PORT_FEAT_POWER		8
+#define USB_PORT_FEAT_LOWSPEED		9
+#define USB_PORT_FEAT_HIGHSPEED		10
+#define USB_PORT_FEAT_C_CONNECTION	16
+#define USB_PORT_FEAT_C_ENABLE		17
+#define USB_PORT_FEAT_C_SUSPEND		18
+#define USB_PORT_FEAT_C_OVER_CURRENT	19
+#define USB_PORT_FEAT_C_RESET		20
+#define USB_PORT_FEAT_TEST              21
+#define USB_PORT_FEAT_INDICATOR         22
+
+/* 
+ * Hub Status and Hub Change results
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ */
+struct usb_port_status {
+	__le16 wPortStatus;
+	__le16 wPortChange;	
+} __attribute__ ((packed));
+
+/* 
+ * wPortStatus bit field
+ * See USB 2.0 spec Table 11-21
+ */
+#define USB_PORT_STAT_CONNECTION	0x0001
+#define USB_PORT_STAT_ENABLE		0x0002
+#define USB_PORT_STAT_SUSPEND		0x0004
+#define USB_PORT_STAT_OVERCURRENT	0x0008
+#define USB_PORT_STAT_RESET		0x0010
+/* bits 5 to 7 are reserved */
+#define USB_PORT_STAT_POWER		0x0100
+#define USB_PORT_STAT_LOW_SPEED		0x0200
+#define USB_PORT_STAT_HIGH_SPEED        0x0400
+#define USB_PORT_STAT_TEST              0x0800
+#define USB_PORT_STAT_INDICATOR         0x1000
+/* bits 13 to 15 are reserved */
+
+/* 
+ * wPortChange bit field
+ * See USB 2.0 spec Table 11-22
+ * Bits 0 to 4 shown, bits 5 to 15 are reserved
+ */
+#define USB_PORT_STAT_C_CONNECTION	0x0001
+#define USB_PORT_STAT_C_ENABLE		0x0002
+#define USB_PORT_STAT_C_SUSPEND		0x0004
+#define USB_PORT_STAT_C_OVERCURRENT	0x0008
+#define USB_PORT_STAT_C_RESET		0x0010
+
+/*
+ * wHubCharacteristics (masks) 
+ * See USB 2.0 spec Table 11-13, offset 3
+ */
+#define HUB_CHAR_LPSM		0x0003 /* D1 .. D0 */
+#define HUB_CHAR_COMPOUND	0x0004 /* D2       */
+#define HUB_CHAR_OCPM		0x0018 /* D4 .. D3 */
+#define HUB_CHAR_TTTT           0x0060 /* D6 .. D5 */
+#define HUB_CHAR_PORTIND        0x0080 /* D7       */
+
+struct usb_hub_status {
+	__le16 wHubStatus;
+	__le16 wHubChange;
+} __attribute__ ((packed));
+
+/*
+ * Hub Status & Hub Change bit masks
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ * Bits 0 and 1 for wHubStatus and wHubChange
+ * Bits 2 to 15 are reserved for both
+ */
+#define HUB_STATUS_LOCAL_POWER	0x0001
+#define HUB_STATUS_OVERCURRENT	0x0002
+#define HUB_CHANGE_LOCAL_POWER	0x0001
+#define HUB_CHANGE_OVERCURRENT	0x0002
+
+
+/* 
+ * Hub descriptor 
+ * See USB 2.0 spec Table 11-13
+ */
+
+#define USB_DT_HUB			(USB_TYPE_CLASS | 0x09)
+#define USB_DT_HUB_NONVAR_SIZE		7
+
+struct usb_hub_descriptor {
+	__u8  bDescLength;
+	__u8  bDescriptorType;
+	__u8  bNbrPorts;
+	__u16 wHubCharacteristics;
+	__u8  bPwrOn2PwrGood;
+	__u8  bHubContrCurrent;
+	    	/* add 1 bit for hub status change; round to bytes */
+	__u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+	__u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+} __attribute__ ((packed));
+
+
+/* port indicator status selectors, tables 11-7 and 11-25 */
+#define HUB_LED_AUTO	0
+#define HUB_LED_AMBER	1
+#define HUB_LED_GREEN	2
+#define HUB_LED_OFF	3
+
+enum hub_led_mode {
+	INDICATOR_AUTO = 0,
+	INDICATOR_CYCLE,
+	/* software blinks for attention:  software, hardware, reserved */
+	INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF,
+	INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF,
+	INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF
+} __attribute__ ((packed));
+
+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 work_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 device		*intfdev;	/* the "interface" device */
+	struct usb_device	*hdev;
+	struct urb		*urb;		/* for interrupt polling pipe */
+
+	/* buffer for urb ... with extra space in case of babble */
+	char			(*buffer)[8];
+	dma_addr_t		buffer_dma;	/* DMA address for buffer */
+	union {
+		struct usb_hub_status	hub;
+		struct usb_port_status	port;
+	}			*status;	/* buffer for status reports */
+
+	int			error;		/* last reported error */
+	int			nerrors;	/* track consecutive errors */
+
+	struct list_head	event_list;	/* hubs w/data or errs ready */
+	unsigned long		event_bits[1];	/* status change bitmask */
+	unsigned long		change_bits[1];	/* ports with logical connect
+							status change */
+	unsigned long		busy_bits[1];	/* ports being reset */
+#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
+#error event_bits[] is too short!
+#endif
+
+	struct usb_hub_descriptor *descriptor;	/* class descriptor */
+	struct usb_tt		tt;		/* Transaction Translator */
+
+	u8			power_budget;	/* in 2mA units; or zero */
+
+	unsigned		quiescing:1;
+	unsigned		activating:1;
+	unsigned		resume_root_hub:1;
+
+	unsigned		has_indicators:1;
+	enum hub_led_mode	indicator[USB_MAXCHILDREN];
+	struct work_struct	leds;
+};
+
+/* use this for low-powered root hubs */
+static inline void
+hub_set_power_budget (struct usb_device *hubdev, unsigned mA)
+{
+	struct usb_hub	*hub;
+
+	hub = (struct usb_hub *)
+		usb_get_intfdata (hubdev->actconfig->interface[0]);
+	hub->power_budget = min(mA,(unsigned)500)/2;
+}
+
+#endif /* __LINUX_HUB_H */
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
new file mode 100644
index 0000000..d913407
--- /dev/null
+++ b/drivers/usb/core/inode.c
@@ -0,0 +1,764 @@
+/*****************************************************************************/
+
+/*
+ *	inode.c  --  Inode/Dentry functions for the USB device file system.
+ *
+ *	Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	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 Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  History:
+ *   0.1  04.01.2000  Created
+ *   0.2  10.12.2001  converted to use the vfs layer better
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/usb.h>
+#include <linux/namei.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/parser.h>
+#include <asm/byteorder.h>
+#include "usb.h"
+
+static struct super_operations usbfs_ops;
+static struct file_operations default_file_operations;
+static struct inode_operations usbfs_dir_inode_operations;
+static struct vfsmount *usbfs_mount;
+static int usbfs_mount_count;	/* = 0 */
+static int ignore_mount = 0;
+
+static struct dentry *devices_usbfs_dentry;
+static int num_buses;	/* = 0 */
+
+static uid_t devuid;	/* = 0 */
+static uid_t busuid;	/* = 0 */
+static uid_t listuid;	/* = 0 */
+static gid_t devgid;	/* = 0 */
+static gid_t busgid;	/* = 0 */
+static gid_t listgid;	/* = 0 */
+static umode_t devmode = S_IWUSR | S_IRUGO;
+static umode_t busmode = S_IXUGO | S_IRUGO;
+static umode_t listmode = S_IRUGO;
+
+enum {
+	Opt_devuid, Opt_devgid, Opt_devmode,
+	Opt_busuid, Opt_busgid, Opt_busmode,
+	Opt_listuid, Opt_listgid, Opt_listmode,
+	Opt_err,
+};
+
+static match_table_t tokens = {
+	{Opt_devuid, "devuid=%u"},
+	{Opt_devgid, "devgid=%u"},
+	{Opt_devmode, "devmode=%o"},
+	{Opt_busuid, "busuid=%u"},
+	{Opt_busgid, "busgid=%u"},
+	{Opt_busmode, "busmode=%o"},
+	{Opt_listuid, "listuid=%u"},
+	{Opt_listgid, "listgid=%u"},
+	{Opt_listmode, "listmode=%o"},
+	{Opt_err, NULL}
+};
+
+static int parse_options(struct super_block *s, char *data)
+{
+	char *p;
+	int option;
+
+	/* (re)set to defaults. */
+	devuid = 0;
+	busuid = 0;
+	listuid = 0;
+	devgid = 0;
+	busgid = 0;
+	listgid = 0;
+	devmode = S_IWUSR | S_IRUGO;
+	busmode = S_IXUGO | S_IRUGO;
+	listmode = S_IRUGO;
+
+	while ((p = strsep(&data, ",")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case Opt_devuid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			devuid = option;
+			break;
+		case Opt_devgid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			devgid = option;
+			break;
+		case Opt_devmode:
+			if (match_octal(&args[0], &option))
+				return -EINVAL;
+			devmode = option & S_IRWXUGO;
+			break;
+		case Opt_busuid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			busuid = option;
+			break;
+		case Opt_busgid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			busgid = option;
+			break;
+		case Opt_busmode:
+			if (match_octal(&args[0], &option))
+				return -EINVAL;
+			busmode = option & S_IRWXUGO;
+			break;
+		case Opt_listuid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			listuid = option;
+			break;
+		case Opt_listgid:
+			if (match_int(&args[0], &option))
+			       return -EINVAL;
+			listgid = option;
+			break;
+		case Opt_listmode:
+			if (match_octal(&args[0], &option))
+				return -EINVAL;
+			listmode = option & S_IRWXUGO;
+			break;
+		default:
+			err("usbfs: unrecognised mount option \"%s\" "
+			    "or missing value\n", p);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void update_special(struct dentry *special)
+{
+	special->d_inode->i_uid = listuid;
+	special->d_inode->i_gid = listgid;
+	special->d_inode->i_mode = S_IFREG | listmode;
+}
+
+static void update_dev(struct dentry *dev)
+{
+	dev->d_inode->i_uid = devuid;
+	dev->d_inode->i_gid = devgid;
+	dev->d_inode->i_mode = S_IFREG | devmode;
+}
+
+static void update_bus(struct dentry *bus)
+{
+	struct dentry *dev = NULL;
+
+	bus->d_inode->i_uid = busuid;
+	bus->d_inode->i_gid = busgid;
+	bus->d_inode->i_mode = S_IFDIR | busmode;
+
+	down(&bus->d_inode->i_sem);
+
+	list_for_each_entry(dev, &bus->d_subdirs, d_child)
+		if (dev->d_inode)
+			update_dev(dev);
+
+	up(&bus->d_inode->i_sem);
+}
+
+static void update_sb(struct super_block *sb)
+{
+	struct dentry *root = sb->s_root;
+	struct dentry *bus = NULL;
+
+	if (!root)
+		return;
+
+	down(&root->d_inode->i_sem);
+
+	list_for_each_entry(bus, &root->d_subdirs, d_child) {
+		if (bus->d_inode) {
+			switch (S_IFMT & bus->d_inode->i_mode) {
+			case S_IFDIR:
+				update_bus(bus);
+				break;
+			case S_IFREG:
+				update_special(bus);
+				break;
+			default:
+				warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode);
+				break;
+			}
+		}
+	}
+
+	up(&root->d_inode->i_sem);
+}
+
+static int remount(struct super_block *sb, int *flags, char *data)
+{
+	/* If this is not a real mount,
+	 * i.e. it's a simple_pin_fs from create_special_files,
+	 * then ignore it.
+	 */
+	if (ignore_mount)
+		return 0;
+
+	if (parse_options(sb, data)) {
+		warn("usbfs: mount parameter error:");
+		return -EINVAL;
+	}
+
+	if (usbfs_mount && usbfs_mount->mnt_sb)
+		update_sb(usbfs_mount->mnt_sb);
+
+	return 0;
+}
+
+static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (inode) {
+		inode->i_mode = mode;
+		inode->i_uid = current->fsuid;
+		inode->i_gid = current->fsgid;
+		inode->i_blksize = PAGE_CACHE_SIZE;
+		inode->i_blocks = 0;
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		switch (mode & S_IFMT) {
+		default:
+			init_special_inode(inode, mode, dev);
+			break;
+		case S_IFREG:
+			inode->i_fop = &default_file_operations;
+			break;
+		case S_IFDIR:
+			inode->i_op = &usbfs_dir_inode_operations;
+			inode->i_fop = &simple_dir_operations;
+
+			/* directory inodes start off with i_nlink == 2 (for "." entry) */
+			inode->i_nlink++;
+			break;
+		}
+	}
+	return inode; 
+}
+
+/* SMP-safe */
+static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
+			dev_t dev)
+{
+	struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
+	int error = -EPERM;
+
+	if (dentry->d_inode)
+		return -EEXIST;
+
+	if (inode) {
+		d_instantiate(dentry, inode);
+		dget(dentry);
+		error = 0;
+	}
+	return error;
+}
+
+static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+	int res;
+
+	mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
+	res = usbfs_mknod (dir, dentry, mode, 0);
+	if (!res)
+		dir->i_nlink++;
+	return res;
+}
+
+static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+	mode = (mode & S_IALLUGO) | S_IFREG;
+	return usbfs_mknod (dir, dentry, mode, 0);
+}
+
+static inline int usbfs_positive (struct dentry *dentry)
+{
+	return dentry->d_inode && !d_unhashed(dentry);
+}
+
+static int usbfs_empty (struct dentry *dentry)
+{
+	struct list_head *list;
+
+	spin_lock(&dcache_lock);
+
+	list_for_each(list, &dentry->d_subdirs) {
+		struct dentry *de = list_entry(list, struct dentry, d_child);
+		if (usbfs_positive(de)) {
+			spin_unlock(&dcache_lock);
+			return 0;
+		}
+	}
+
+	spin_unlock(&dcache_lock);
+	return 1;
+}
+
+static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	down(&inode->i_sem);
+	dentry->d_inode->i_nlink--;
+	dput(dentry);
+	up(&inode->i_sem);
+	d_delete(dentry);
+	return 0;
+}
+
+static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int error = -ENOTEMPTY;
+	struct inode * inode = dentry->d_inode;
+
+	down(&inode->i_sem);
+	dentry_unhash(dentry);
+	if (usbfs_empty(dentry)) {
+		dentry->d_inode->i_nlink -= 2;
+		dput(dentry);
+		inode->i_flags |= S_DEAD;
+		dir->i_nlink--;
+		error = 0;
+	}
+	up(&inode->i_sem);
+	if (!error)
+		d_delete(dentry);
+	dput(dentry);
+	return error;
+}
+
+
+/* default file operations */
+static ssize_t default_read_file (struct file *file, char __user *buf,
+				  size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t default_write_file (struct file *file, const char __user *buf,
+				   size_t count, loff_t *ppos)
+{
+	return count;
+}
+
+static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
+{
+	loff_t retval = -EINVAL;
+
+	down(&file->f_dentry->d_inode->i_sem);
+	switch(orig) {
+	case 0:
+		if (offset > 0) {
+			file->f_pos = offset;
+			retval = file->f_pos;
+		} 
+		break;
+	case 1:
+		if ((offset + file->f_pos) > 0) {
+			file->f_pos += offset;
+			retval = file->f_pos;
+		} 
+		break;
+	default:
+		break;
+	}
+	up(&file->f_dentry->d_inode->i_sem);
+	return retval;
+}
+
+static int default_open (struct inode *inode, struct file *file)
+{
+	if (inode->u.generic_ip)
+		file->private_data = inode->u.generic_ip;
+
+	return 0;
+}
+
+static struct file_operations default_file_operations = {
+	.read =		default_read_file,
+	.write =	default_write_file,
+	.open =		default_open,
+	.llseek =	default_file_lseek,
+};
+
+static struct inode_operations usbfs_dir_inode_operations = {
+	.lookup =	simple_lookup,
+};
+
+static struct super_operations usbfs_ops = {
+	.statfs =	simple_statfs,
+	.drop_inode =	generic_delete_inode,
+	.remount_fs =	remount,
+};
+
+static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *inode;
+	struct dentry *root;
+
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = USBDEVICE_SUPER_MAGIC;
+	sb->s_op = &usbfs_ops;
+	sb->s_time_gran = 1;
+	inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
+
+	if (!inode) {
+		dbg("%s: could not get inode!",__FUNCTION__);
+		return -ENOMEM;
+	}
+
+	root = d_alloc_root(inode);
+	if (!root) {
+		dbg("%s: could not get root dentry!",__FUNCTION__);
+		iput(inode);
+		return -ENOMEM;
+	}
+	sb->s_root = root;
+	return 0;
+}
+
+static struct dentry * get_dentry(struct dentry *parent, const char *name)
+{               
+	struct qstr qstr;
+
+	qstr.name = name;
+	qstr.len = strlen(name);
+	qstr.hash = full_name_hash(name,qstr.len);
+	return lookup_hash(&qstr,parent);
+}               
+
+
+/*
+ * fs_create_by_name - create a file, given a name
+ * @name:	name of file
+ * @mode:	type of file
+ * @parent:	dentry of directory to create it in
+ * @dentry:	resulting dentry of file
+ *
+ * This function handles both regular files and directories.
+ */
+static int fs_create_by_name (const char *name, mode_t mode,
+			      struct dentry *parent, struct dentry **dentry)
+{
+	int error = 0;
+
+	/* If the parent is not specified, we create it in the root.
+	 * We need the root dentry to do this, which is in the super 
+	 * block. A pointer to that is in the struct vfsmount that we
+	 * have around.
+	 */
+	if (!parent ) {
+		if (usbfs_mount && usbfs_mount->mnt_sb) {
+			parent = usbfs_mount->mnt_sb->s_root;
+		}
+	}
+
+	if (!parent) {
+		dbg("Ah! can not find a parent!");
+		return -EFAULT;
+	}
+
+	*dentry = NULL;
+	down(&parent->d_inode->i_sem);
+	*dentry = get_dentry (parent, name);
+	if (!IS_ERR(dentry)) {
+		if ((mode & S_IFMT) == S_IFDIR)
+			error = usbfs_mkdir (parent->d_inode, *dentry, mode);
+		else 
+			error = usbfs_create (parent->d_inode, *dentry, mode);
+	} else
+		error = PTR_ERR(dentry);
+	up(&parent->d_inode->i_sem);
+
+	return error;
+}
+
+static struct dentry *fs_create_file (const char *name, mode_t mode,
+				      struct dentry *parent, void *data,
+				      struct file_operations *fops,
+				      uid_t uid, gid_t gid)
+{
+	struct dentry *dentry;
+	int error;
+
+	dbg("creating file '%s'",name);
+
+	error = fs_create_by_name (name, mode, parent, &dentry);
+	if (error) {
+		dentry = NULL;
+	} else {
+		if (dentry->d_inode) {
+			if (data)
+				dentry->d_inode->u.generic_ip = data;
+			if (fops)
+				dentry->d_inode->i_fop = fops;
+			dentry->d_inode->i_uid = uid;
+			dentry->d_inode->i_gid = gid;
+		}
+	}
+
+	return dentry;
+}
+
+static void fs_remove_file (struct dentry *dentry)
+{
+	struct dentry *parent = dentry->d_parent;
+	
+	if (!parent || !parent->d_inode)
+		return;
+
+	down(&parent->d_inode->i_sem);
+	if (usbfs_positive(dentry)) {
+		if (dentry->d_inode) {
+			if (S_ISDIR(dentry->d_inode->i_mode))
+				usbfs_rmdir(parent->d_inode, dentry);
+			else
+				usbfs_unlink(parent->d_inode, dentry);
+		dput(dentry);
+		}
+	}
+	up(&parent->d_inode->i_sem);
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct super_block *usb_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_single(fs_type, flags, data, usbfs_fill_super);
+}
+
+static struct file_system_type usb_fs_type = {
+	.owner =	THIS_MODULE,
+	.name =		"usbfs",
+	.get_sb =	usb_get_sb,
+	.kill_sb =	kill_litter_super,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int create_special_files (void)
+{
+	struct dentry *parent;
+	int retval;
+
+	/* the simple_pin_fs calls will call remount with no options
+	 * without this flag that would overwrite the real mount options (if any)
+	 */
+	ignore_mount = 1;
+
+	/* create the devices special file */
+	retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count);
+	if (retval) {
+		err ("Unable to get usbfs mount");
+		goto exit;
+	}
+
+	ignore_mount = 0;
+
+	parent = usbfs_mount->mnt_sb->s_root;
+	devices_usbfs_dentry = fs_create_file ("devices",
+					       listmode | S_IFREG, parent,
+					       NULL, &usbfs_devices_fops,
+					       listuid, listgid);
+	if (devices_usbfs_dentry == NULL) {
+		err ("Unable to create devices usbfs file");
+		retval = -ENODEV;
+		goto error_clean_mounts;
+	}
+
+	goto exit;
+	
+error_clean_mounts:
+	simple_release_fs(&usbfs_mount, &usbfs_mount_count);
+exit:
+	return retval;
+}
+
+static void remove_special_files (void)
+{
+	if (devices_usbfs_dentry)
+		fs_remove_file (devices_usbfs_dentry);
+	devices_usbfs_dentry = NULL;
+	simple_release_fs(&usbfs_mount, &usbfs_mount_count);
+}
+
+void usbfs_update_special (void)
+{
+	struct inode *inode;
+
+	if (devices_usbfs_dentry) {
+		inode = devices_usbfs_dentry->d_inode;
+		if (inode)
+			inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	}
+}
+
+void usbfs_add_bus(struct usb_bus *bus)
+{
+	struct dentry *parent;
+	char name[8];
+	int retval;
+
+	/* create the special files if this is the first bus added */
+	if (num_buses == 0) {
+		retval = create_special_files();
+		if (retval)
+			return;
+	}
+	++num_buses;
+
+	sprintf (name, "%03d", bus->busnum);
+
+	parent = usbfs_mount->mnt_sb->s_root;
+	bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
+					    bus, NULL, busuid, busgid);
+	if (bus->usbfs_dentry == NULL) {
+		err ("error creating usbfs bus entry");
+		return;
+	}
+
+	usbfs_update_special();
+	usbfs_conn_disc_event();
+}
+
+void usbfs_remove_bus(struct usb_bus *bus)
+{
+	if (bus->usbfs_dentry) {
+		fs_remove_file (bus->usbfs_dentry);
+		bus->usbfs_dentry = NULL;
+	}
+
+	--num_buses;
+	if (num_buses <= 0) {
+		remove_special_files();
+		num_buses = 0;
+	}
+
+	usbfs_update_special();
+	usbfs_conn_disc_event();
+}
+
+void usbfs_add_device(struct usb_device *dev)
+{
+	char name[8];
+	int i;
+	int i_size;
+
+	sprintf (name, "%03d", dev->devnum);
+	dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
+					    dev->bus->usbfs_dentry, dev,
+					    &usbfs_device_file_operations,
+					    devuid, devgid);
+	if (dev->usbfs_dentry == NULL) {
+		err ("error creating usbfs device entry");
+		return;
+	}
+
+	/* Set the size of the device's file to be
+	 * equal to the size of the device descriptors. */
+	i_size = sizeof (struct usb_device_descriptor);
+	for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
+		struct usb_config_descriptor *config =
+			(struct usb_config_descriptor *)dev->rawdescriptors[i];
+		i_size += le16_to_cpu(config->wTotalLength);
+	}
+	if (dev->usbfs_dentry->d_inode)
+		dev->usbfs_dentry->d_inode->i_size = i_size;
+
+	usbfs_update_special();
+	usbfs_conn_disc_event();
+}
+
+void usbfs_remove_device(struct usb_device *dev)
+{
+	struct dev_state *ds;
+	struct siginfo sinfo;
+
+	if (dev->usbfs_dentry) {
+		fs_remove_file (dev->usbfs_dentry);
+		dev->usbfs_dentry = NULL;
+	}
+	while (!list_empty(&dev->filelist)) {
+		ds = list_entry(dev->filelist.next, struct dev_state, list);
+		wake_up_all(&ds->wait);
+		list_del_init(&ds->list);
+		if (ds->discsignr) {
+			sinfo.si_signo = SIGPIPE;
+			sinfo.si_errno = EPIPE;
+			sinfo.si_code = SI_ASYNCIO;
+			sinfo.si_addr = ds->disccontext;
+			send_sig_info(ds->discsignr, &sinfo, ds->disctask);
+		}
+	}
+	usbfs_update_special();
+	usbfs_conn_disc_event();
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct proc_dir_entry *usbdir = NULL;
+
+int __init usbfs_init(void)
+{
+	int retval;
+
+	retval = usb_register(&usbfs_driver);
+	if (retval)
+		return retval;
+
+	retval = register_filesystem(&usb_fs_type);
+	if (retval) {
+		usb_deregister(&usbfs_driver);
+		return retval;
+	}
+
+	/* create mount point for usbfs */
+	usbdir = proc_mkdir("usb", proc_bus);
+
+	return 0;
+}
+
+void usbfs_cleanup(void)
+{
+	usb_deregister(&usbfs_driver);
+	unregister_filesystem(&usb_fs_type);
+	if (usbdir)
+		remove_proc_entry("usb", proc_bus);
+}
+
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
new file mode 100644
index 0000000..40bdb38
--- /dev/null
+++ b/drivers/usb/core/message.c
@@ -0,0 +1,1480 @@
+/*
+ * message.c - synchronous message handling
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/pci.h>	/* for scatterlist macros */
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <asm/byteorder.h>
+
+#include "hcd.h"	/* for usbcore internals */
+#include "usb.h"
+
+static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
+{
+	complete((struct completion *)urb->context);
+}
+
+
+static void timeout_kill(unsigned long data)
+{
+	struct urb	*urb = (struct urb *) data;
+
+	usb_unlink_urb(urb);
+}
+
+// Starts urb and waits for completion or timeout
+// note that this call is NOT interruptible, while
+// many device driver i/o requests should be interruptible
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+{ 
+	struct completion	done;
+	struct timer_list	timer;
+	int			status;
+
+	init_completion(&done); 	
+	urb->context = &done;
+	urb->transfer_flags |= URB_ASYNC_UNLINK;
+	urb->actual_length = 0;
+	status = usb_submit_urb(urb, GFP_NOIO);
+
+	if (status == 0) {
+		if (timeout > 0) {
+			init_timer(&timer);
+			timer.expires = jiffies + msecs_to_jiffies(timeout);
+			timer.data = (unsigned long)urb;
+			timer.function = timeout_kill;
+			/* grr.  timeout _should_ include submit delays. */
+			add_timer(&timer);
+		}
+		wait_for_completion(&done);
+		status = urb->status;
+		/* note:  HCDs return ETIMEDOUT for other reasons too */
+		if (status == -ECONNRESET) {
+			dev_dbg(&urb->dev->dev,
+				"%s timed out on ep%d%s len=%d/%d\n",
+				current->comm,
+				usb_pipeendpoint(urb->pipe),
+				usb_pipein(urb->pipe) ? "in" : "out",
+				urb->actual_length,
+				urb->transfer_buffer_length
+				);
+			if (urb->actual_length > 0)
+				status = 0;
+			else
+				status = -ETIMEDOUT;
+		}
+		if (timeout > 0)
+			del_timer_sync(&timer);
+	}
+
+	if (actual_length)
+		*actual_length = urb->actual_length;
+	usb_free_urb(urb);
+	return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+static int usb_internal_control_msg(struct usb_device *usb_dev,
+				    unsigned int pipe, 
+				    struct usb_ctrlrequest *cmd,
+				    void *data, int len, int timeout)
+{
+	struct urb *urb;
+	int retv;
+	int length;
+
+	urb = usb_alloc_urb(0, GFP_NOIO);
+	if (!urb)
+		return -ENOMEM;
+  
+	usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
+			     len, usb_api_blocking_completion, NULL);
+
+	retv = usb_start_wait_urb(urb, timeout, &length);
+	if (retv < 0)
+		return retv;
+	else
+		return length;
+}
+
+/**
+ *	usb_control_msg - Builds a control urb, sends it off and waits for completion
+ *	@dev: pointer to the usb device to send the message to
+ *	@pipe: endpoint "pipe" to send the message to
+ *	@request: USB message request value
+ *	@requesttype: USB message request type value
+ *	@value: USB message value
+ *	@index: USB message index value
+ *	@data: pointer to the data to send
+ *	@size: length in bytes of the data to send
+ *	@timeout: time in msecs to wait for the message to complete before
+ *		timing out (if 0 the wait is forever)
+ *	Context: !in_interrupt ()
+ *
+ *	This function sends a simple control message to a specified endpoint
+ *	and waits for the message to complete, or timeout.
+ *	
+ *	If successful, it returns the number of bytes transferred, otherwise a negative error number.
+ *
+ *	Don't use this function from within an interrupt context, like a
+ *	bottom half handler.  If you need an asynchronous message, or need to send
+ *	a message from within interrupt context, use usb_submit_urb()
+ *      If a thread in your driver uses this call, make sure your disconnect()
+ *      method can wait for it to complete.  Since you don't have a handle on
+ *      the URB used, you can't cancel the request.
+ */
+int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+			 __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+	struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+	int ret;
+	
+	if (!dr)
+		return -ENOMEM;
+
+	dr->bRequestType= requesttype;
+	dr->bRequest = request;
+	dr->wValue = cpu_to_le16p(&value);
+	dr->wIndex = cpu_to_le16p(&index);
+	dr->wLength = cpu_to_le16p(&size);
+
+	//dbg("usb_control_msg");	
+
+	ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+	kfree(dr);
+
+	return ret;
+}
+
+
+/**
+ *	usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
+ *	@usb_dev: pointer to the usb device to send the message to
+ *	@pipe: endpoint "pipe" to send the message to
+ *	@data: pointer to the data to send
+ *	@len: length in bytes of the data to send
+ *	@actual_length: pointer to a location to put the actual length transferred in bytes
+ *	@timeout: time in msecs to wait for the message to complete before
+ *		timing out (if 0 the wait is forever)
+ *	Context: !in_interrupt ()
+ *
+ *	This function sends a simple bulk message to a specified endpoint
+ *	and waits for the message to complete, or timeout.
+ *	
+ *	If successful, it returns 0, otherwise a negative error number.
+ *	The number of actual bytes transferred will be stored in the 
+ *	actual_length paramater.
+ *
+ *	Don't use this function from within an interrupt context, like a
+ *	bottom half handler.  If you need an asynchronous message, or need to
+ *	send a message from within interrupt context, use usb_submit_urb()
+ *      If a thread in your driver uses this call, make sure your disconnect()
+ *      method can wait for it to complete.  Since you don't have a handle on
+ *      the URB used, you can't cancel the request.
+ */
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, 
+			void *data, int len, int *actual_length, int timeout)
+{
+	struct urb *urb;
+
+	if (len < 0)
+		return -EINVAL;
+
+	urb=usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+
+	usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
+			  usb_api_blocking_completion, NULL);
+
+	return usb_start_wait_urb(urb, timeout, actual_length);
+}
+
+/*-------------------------------------------------------------------*/
+
+static void sg_clean (struct usb_sg_request *io)
+{
+	if (io->urbs) {
+		while (io->entries--)
+			usb_free_urb (io->urbs [io->entries]);
+		kfree (io->urbs);
+		io->urbs = NULL;
+	}
+	if (io->dev->dev.dma_mask != NULL)
+		usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
+	io->dev = NULL;
+}
+
+static void sg_complete (struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_sg_request	*io = (struct usb_sg_request *) urb->context;
+
+	spin_lock (&io->lock);
+
+	/* In 2.5 we require hcds' endpoint queues not to progress after fault
+	 * reports, until the completion callback (this!) returns.  That lets
+	 * device driver code (like this routine) unlink queued urbs first,
+	 * if it needs to, since the HC won't work on them at all.  So it's
+	 * not possible for page N+1 to overwrite page N, and so on.
+	 *
+	 * That's only for "hard" faults; "soft" faults (unlinks) sometimes
+	 * complete before the HCD can get requests away from hardware,
+	 * though never during cleanup after a hard fault.
+	 */
+	if (io->status
+			&& (io->status != -ECONNRESET
+				|| urb->status != -ECONNRESET)
+			&& urb->actual_length) {
+		dev_err (io->dev->bus->controller,
+			"dev %s ep%d%s scatterlist error %d/%d\n",
+			io->dev->devpath,
+			usb_pipeendpoint (urb->pipe),
+			usb_pipein (urb->pipe) ? "in" : "out",
+			urb->status, io->status);
+		// BUG ();
+	}
+
+	if (io->status == 0 && urb->status && urb->status != -ECONNRESET) {
+		int		i, found, status;
+
+		io->status = urb->status;
+
+		/* the previous urbs, and this one, completed already.
+		 * unlink pending urbs so they won't rx/tx bad data.
+		 * careful: unlink can sometimes be synchronous...
+		 */
+		spin_unlock (&io->lock);
+		for (i = 0, found = 0; i < io->entries; i++) {
+			if (!io->urbs [i] || !io->urbs [i]->dev)
+				continue;
+			if (found) {
+				status = usb_unlink_urb (io->urbs [i]);
+				if (status != -EINPROGRESS && status != -EBUSY)
+					dev_err (&io->dev->dev,
+						"%s, unlink --> %d\n",
+						__FUNCTION__, status);
+			} else if (urb == io->urbs [i])
+				found = 1;
+		}
+		spin_lock (&io->lock);
+	}
+	urb->dev = NULL;
+
+	/* on the last completion, signal usb_sg_wait() */
+	io->bytes += urb->actual_length;
+	io->count--;
+	if (!io->count)
+		complete (&io->complete);
+
+	spin_unlock (&io->lock);
+}
+
+
+/**
+ * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request
+ * @io: request block being initialized.  until usb_sg_wait() returns,
+ *	treat this as a pointer to an opaque block of memory,
+ * @dev: the usb device that will send or receive the data
+ * @pipe: endpoint "pipe" used to transfer the data
+ * @period: polling rate for interrupt endpoints, in frames or
+ * 	(for high speed endpoints) microframes; ignored for bulk
+ * @sg: scatterlist entries
+ * @nents: how many entries in the scatterlist
+ * @length: how many bytes to send from the scatterlist, or zero to
+ * 	send every byte identified in the list.
+ * @mem_flags: SLAB_* flags affecting memory allocations in this call
+ *
+ * Returns zero for success, else a negative errno value.  This initializes a
+ * scatter/gather request, allocating resources such as I/O mappings and urb
+ * memory (except maybe memory used by USB controller drivers).
+ *
+ * The request must be issued using usb_sg_wait(), which waits for the I/O to
+ * complete (or to be canceled) and then cleans up all resources allocated by
+ * usb_sg_init().
+ *
+ * The request may be canceled with usb_sg_cancel(), either before or after
+ * usb_sg_wait() is called.
+ */
+int usb_sg_init (
+	struct usb_sg_request	*io,
+	struct usb_device	*dev,
+	unsigned		pipe, 
+	unsigned		period,
+	struct scatterlist	*sg,
+	int			nents,
+	size_t			length,
+	int			mem_flags
+)
+{
+	int			i;
+	int			urb_flags;
+	int			dma;
+
+	if (!io || !dev || !sg
+			|| usb_pipecontrol (pipe)
+			|| usb_pipeisoc (pipe)
+			|| nents <= 0)
+		return -EINVAL;
+
+	spin_lock_init (&io->lock);
+	io->dev = dev;
+	io->pipe = pipe;
+	io->sg = sg;
+	io->nents = nents;
+
+	/* not all host controllers use DMA (like the mainstream pci ones);
+	 * they can use PIO (sl811) or be software over another transport.
+	 */
+	dma = (dev->dev.dma_mask != NULL);
+	if (dma)
+		io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
+	else
+		io->entries = nents;
+
+	/* initialize all the urbs we'll use */
+	if (io->entries <= 0)
+		return io->entries;
+
+	io->count = io->entries;
+	io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags);
+	if (!io->urbs)
+		goto nomem;
+
+	urb_flags = URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP
+			| URB_NO_INTERRUPT;
+	if (usb_pipein (pipe))
+		urb_flags |= URB_SHORT_NOT_OK;
+
+	for (i = 0; i < io->entries; i++) {
+		unsigned		len;
+
+		io->urbs [i] = usb_alloc_urb (0, mem_flags);
+		if (!io->urbs [i]) {
+			io->entries = i;
+			goto nomem;
+		}
+
+		io->urbs [i]->dev = NULL;
+		io->urbs [i]->pipe = pipe;
+		io->urbs [i]->interval = period;
+		io->urbs [i]->transfer_flags = urb_flags;
+
+		io->urbs [i]->complete = sg_complete;
+		io->urbs [i]->context = io;
+		io->urbs [i]->status = -EINPROGRESS;
+		io->urbs [i]->actual_length = 0;
+
+		if (dma) {
+			/* hc may use _only_ transfer_dma */
+			io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
+			len = sg_dma_len (sg + i);
+		} else {
+			/* hc may use _only_ transfer_buffer */
+			io->urbs [i]->transfer_buffer =
+				page_address (sg [i].page) + sg [i].offset;
+			len = sg [i].length;
+		}
+
+		if (length) {
+			len = min_t (unsigned, len, length);
+			length -= len;
+			if (length == 0)
+				io->entries = i + 1;
+		}
+		io->urbs [i]->transfer_buffer_length = len;
+	}
+	io->urbs [--i]->transfer_flags &= ~URB_NO_INTERRUPT;
+
+	/* transaction state */
+	io->status = 0;
+	io->bytes = 0;
+	init_completion (&io->complete);
+	return 0;
+
+nomem:
+	sg_clean (io);
+	return -ENOMEM;
+}
+
+
+/**
+ * usb_sg_wait - synchronously execute scatter/gather request
+ * @io: request block handle, as initialized with usb_sg_init().
+ * 	some fields become accessible when this call returns.
+ * Context: !in_interrupt ()
+ *
+ * This function blocks until the specified I/O operation completes.  It
+ * leverages the grouping of the related I/O requests to get good transfer
+ * rates, by queueing the requests.  At higher speeds, such queuing can
+ * significantly improve USB throughput.
+ *
+ * There are three kinds of completion for this function.
+ * (1) success, where io->status is zero.  The number of io->bytes
+ *     transferred is as requested.
+ * (2) error, where io->status is a negative errno value.  The number
+ *     of io->bytes transferred before the error is usually less
+ *     than requested, and can be nonzero.
+ * (3) cancelation, a type of error with status -ECONNRESET that
+ *     is initiated by usb_sg_cancel().
+ *
+ * When this function returns, all memory allocated through usb_sg_init() or
+ * this call will have been freed.  The request block parameter may still be
+ * passed to usb_sg_cancel(), or it may be freed.  It could also be
+ * reinitialized and then reused.
+ *
+ * Data Transfer Rates:
+ *
+ * Bulk transfers are valid for full or high speed endpoints.
+ * The best full speed data rate is 19 packets of 64 bytes each
+ * per frame, or 1216 bytes per millisecond.
+ * The best high speed data rate is 13 packets of 512 bytes each
+ * per microframe, or 52 KBytes per millisecond.
+ *
+ * The reason to use interrupt transfers through this API would most likely
+ * be to reserve high speed bandwidth, where up to 24 KBytes per millisecond
+ * could be transferred.  That capability is less useful for low or full
+ * speed interrupt endpoints, which allow at most one packet per millisecond,
+ * of at most 8 or 64 bytes (respectively).
+ */
+void usb_sg_wait (struct usb_sg_request *io)
+{
+	int		i, entries = io->entries;
+
+	/* queue the urbs.  */
+	spin_lock_irq (&io->lock);
+	for (i = 0; i < entries && !io->status; i++) {
+		int	retval;
+
+		io->urbs [i]->dev = io->dev;
+		retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC);
+
+		/* after we submit, let completions or cancelations fire;
+		 * we handshake using io->status.
+		 */
+		spin_unlock_irq (&io->lock);
+		switch (retval) {
+			/* maybe we retrying will recover */
+		case -ENXIO:	// hc didn't queue this one
+		case -EAGAIN:
+		case -ENOMEM:
+			io->urbs[i]->dev = NULL;
+			retval = 0;
+			i--;
+			yield ();
+			break;
+
+			/* no error? continue immediately.
+			 *
+			 * NOTE: to work better with UHCI (4K I/O buffer may
+			 * need 3K of TDs) it may be good to limit how many
+			 * URBs are queued at once; N milliseconds?
+			 */
+		case 0:
+			cpu_relax ();
+			break;
+
+			/* fail any uncompleted urbs */
+		default:
+			io->urbs [i]->dev = NULL;
+			io->urbs [i]->status = retval;
+			dev_dbg (&io->dev->dev, "%s, submit --> %d\n",
+				__FUNCTION__, retval);
+			usb_sg_cancel (io);
+		}
+		spin_lock_irq (&io->lock);
+		if (retval && (io->status == 0 || io->status == -ECONNRESET))
+			io->status = retval;
+	}
+	io->count -= entries - i;
+	if (io->count == 0)
+		complete (&io->complete);
+	spin_unlock_irq (&io->lock);
+
+	/* OK, yes, this could be packaged as non-blocking.
+	 * So could the submit loop above ... but it's easier to
+	 * solve neither problem than to solve both!
+	 */
+	wait_for_completion (&io->complete);
+
+	sg_clean (io);
+}
+
+/**
+ * usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait()
+ * @io: request block, initialized with usb_sg_init()
+ *
+ * This stops a request after it has been started by usb_sg_wait().
+ * It can also prevents one initialized by usb_sg_init() from starting,
+ * so that call just frees resources allocated to the request.
+ */
+void usb_sg_cancel (struct usb_sg_request *io)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave (&io->lock, flags);
+
+	/* shut everything down, if it didn't already */
+	if (!io->status) {
+		int	i;
+
+		io->status = -ECONNRESET;
+		spin_unlock (&io->lock);
+		for (i = 0; i < io->entries; i++) {
+			int	retval;
+
+			if (!io->urbs [i]->dev)
+				continue;
+			retval = usb_unlink_urb (io->urbs [i]);
+			if (retval != -EINPROGRESS && retval != -EBUSY)
+				dev_warn (&io->dev->dev, "%s, unlink --> %d\n",
+					__FUNCTION__, retval);
+		}
+		spin_lock (&io->lock);
+	}
+	spin_unlock_irqrestore (&io->lock, flags);
+}
+
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_get_descriptor - issues a generic GET_DESCRIPTOR request
+ * @dev: the device whose descriptor is being retrieved
+ * @type: the descriptor type (USB_DT_*)
+ * @index: the number of the descriptor
+ * @buf: where to put the descriptor
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ *
+ * Gets a USB descriptor.  Convenience functions exist to simplify
+ * getting some types of descriptors.  Use
+ * usb_get_string() or usb_string() for USB_DT_STRING.
+ * Device (USB_DT_DEVICE) and configuration descriptors (USB_DT_CONFIG)
+ * are part of the device structure.
+ * In addition to a number of USB-standard descriptors, some
+ * devices also use class-specific or vendor-specific descriptors.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
+{
+	int i;
+	int result;
+	
+	memset(buf,0,size);	// Make sure we parse really received data
+
+	for (i = 0; i < 3; ++i) {
+		/* retry on length 0 or stall; some devices are flakey */
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+				(type << 8) + index, 0, buf, size,
+				USB_CTRL_GET_TIMEOUT);
+		if (result == 0 || result == -EPIPE)
+			continue;
+		if (result > 1 && ((u8 *)buf)[1] != type) {
+			result = -EPROTO;
+			continue;
+		}
+		break;
+	}
+	return result;
+}
+
+/**
+ * usb_get_string - gets a string descriptor
+ * @dev: the device whose string descriptor is being retrieved
+ * @langid: code for language chosen (from string descriptor zero)
+ * @index: the number of the descriptor
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ *
+ * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character,
+ * in little-endian byte order).
+ * The usb_string() function will often be a convenient way to turn
+ * these strings into kernel-printable form.
+ *
+ * Strings may be referenced in device, configuration, interface, or other
+ * descriptors, and could also be used in vendor-specific ways.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_string(struct usb_device *dev, unsigned short langid,
+		unsigned char index, void *buf, int size)
+{
+	int i;
+	int result;
+
+	for (i = 0; i < 3; ++i) {
+		/* retry on length 0 or stall; some devices are flakey */
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(USB_DT_STRING << 8) + index, langid, buf, size,
+			USB_CTRL_GET_TIMEOUT);
+		if (!(result == 0 || result == -EPIPE))
+			break;
+	}
+	return result;
+}
+
+static void usb_try_string_workarounds(unsigned char *buf, int *length)
+{
+	int newlength, oldlength = *length;
+
+	for (newlength = 2; newlength + 1 < oldlength; newlength += 2)
+		if (!isprint(buf[newlength]) || buf[newlength + 1])
+			break;
+
+	if (newlength > 2) {
+		buf[0] = newlength;
+		*length = newlength;
+	}
+}
+
+static int usb_string_sub(struct usb_device *dev, unsigned int langid,
+		unsigned int index, unsigned char *buf)
+{
+	int rc;
+
+	/* Try to read the string descriptor by asking for the maximum
+	 * possible number of bytes */
+	rc = usb_get_string(dev, langid, index, buf, 255);
+
+	/* If that failed try to read the descriptor length, then
+	 * ask for just that many bytes */
+	if (rc < 2) {
+		rc = usb_get_string(dev, langid, index, buf, 2);
+		if (rc == 2)
+			rc = usb_get_string(dev, langid, index, buf, buf[0]);
+	}
+
+	if (rc >= 2) {
+		if (!buf[0] && !buf[1])
+			usb_try_string_workarounds(buf, &rc);
+
+		/* There might be extra junk at the end of the descriptor */
+		if (buf[0] < rc)
+			rc = buf[0];
+
+		rc = rc - (rc & 1); /* force a multiple of two */
+	}
+
+	if (rc < 2)
+		rc = (rc < 0 ? rc : -EINVAL);
+
+	return rc;
+}
+
+/**
+ * usb_string - returns ISO 8859-1 version of a string descriptor
+ * @dev: the device whose string descriptor is being retrieved
+ * @index: the number of the descriptor
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ * 
+ * This converts the UTF-16LE encoded strings returned by devices, from
+ * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
+ * that are more usable in most kernel contexts.  Note that all characters
+ * in the chosen descriptor that can't be encoded using ISO-8859-1
+ * are converted to the question mark ("?") character, and this function
+ * chooses strings in the first language supported by the device.
+ *
+ * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
+ * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
+ * and is appropriate for use many uses of English and several other
+ * Western European languages.  (But it doesn't include the "Euro" symbol.)
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns length of the string (>= 0) or usb_control_msg status (< 0).
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+	unsigned char *tbuf;
+	int err;
+	unsigned int u, idx;
+
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+	if (size <= 0 || !buf || !index)
+		return -EINVAL;
+	buf[0] = 0;
+	tbuf = kmalloc(256, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	/* get langid for strings if it's not yet known */
+	if (!dev->have_langid) {
+		err = usb_string_sub(dev, 0, 0, tbuf);
+		if (err < 0) {
+			dev_err (&dev->dev,
+				"string descriptor 0 read error: %d\n",
+				err);
+			goto errout;
+		} else if (err < 4) {
+			dev_err (&dev->dev, "string descriptor 0 too short\n");
+			err = -EINVAL;
+			goto errout;
+		} else {
+			dev->have_langid = -1;
+			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+				/* always use the first langid listed */
+			dev_dbg (&dev->dev, "default language 0x%04x\n",
+				dev->string_langid);
+		}
+	}
+	
+	err = usb_string_sub(dev, dev->string_langid, index, tbuf);
+	if (err < 0)
+		goto errout;
+
+	size--;		/* leave room for trailing NULL char in output buffer */
+	for (idx = 0, u = 2; u < err; u += 2) {
+		if (idx >= size)
+			break;
+		if (tbuf[u+1])			/* high byte */
+			buf[idx++] = '?';  /* non ISO-8859-1 character */
+		else
+			buf[idx++] = tbuf[u];
+	}
+	buf[idx] = 0;
+	err = idx;
+
+	if (tbuf[1] != USB_DT_STRING)
+		dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf);
+
+ errout:
+	kfree(tbuf);
+	return err;
+}
+
+/*
+ * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
+ * @dev: the device whose device descriptor is being updated
+ * @size: how much of the descriptor to read
+ * Context: !in_interrupt ()
+ *
+ * Updates the copy of the device descriptor stored in the device structure,
+ * which dedicates space for this purpose.  Note that several fields are
+ * converted to the host CPU's byte order:  the USB version (bcdUSB), and
+ * vendors product and version fields (idVendor, idProduct, and bcdDevice).
+ * That lets device drivers compare against non-byteswapped constants.
+ *
+ * Not exported, only for use by the core.  If drivers really want to read
+ * the device descriptor directly, they can call usb_get_descriptor() with
+ * type = USB_DT_DEVICE and index = 0.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
+{
+	struct usb_device_descriptor *desc;
+	int ret;
+
+	if (size > sizeof(*desc))
+		return -EINVAL;
+	desc = kmalloc(sizeof(*desc), GFP_NOIO);
+	if (!desc)
+		return -ENOMEM;
+
+	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
+	if (ret >= 0) 
+		memcpy(&dev->descriptor, desc, size);
+	kfree(desc);
+	return ret;
+}
+
+/**
+ * usb_get_status - issues a GET_STATUS call
+ * @dev: the device whose status is being checked
+ * @type: USB_RECIP_*; for device, interface, or endpoint
+ * @target: zero (for device), else interface or endpoint number
+ * @data: pointer to two bytes of bitmap data
+ * Context: !in_interrupt ()
+ *
+ * Returns device, interface, or endpoint status.  Normally only of
+ * interest to see if the device is self powered, or has enabled the
+ * remote wakeup facility; or whether a bulk or interrupt endpoint
+ * is halted ("stalled").
+ *
+ * Bits in these status bitmaps are set using the SET_FEATURE request,
+ * and cleared using the CLEAR_FEATURE request.  The usb_clear_halt()
+ * function should be used to clear halt ("stall") status.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_status(struct usb_device *dev, int type, int target, void *data)
+{
+	int ret;
+	u16 *status = kmalloc(sizeof(*status), GFP_KERNEL);
+
+	if (!status)
+		return -ENOMEM;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status,
+		sizeof(*status), USB_CTRL_GET_TIMEOUT);
+
+	*(u16 *)data = *status;
+	kfree(status);
+	return ret;
+}
+
+/**
+ * usb_clear_halt - tells device to clear endpoint halt/stall condition
+ * @dev: device whose endpoint is halted
+ * @pipe: endpoint "pipe" being cleared
+ * Context: !in_interrupt ()
+ *
+ * This is used to clear halt conditions for bulk and interrupt endpoints,
+ * as reported by URB completion status.  Endpoints that are halted are
+ * sometimes referred to as being "stalled".  Such endpoints are unable
+ * to transmit or receive data until the halt status is cleared.  Any URBs
+ * queued for such an endpoint should normally be unlinked by the driver
+ * before clearing the halt condition, as described in sections 5.7.5
+ * and 5.8.5 of the USB 2.0 spec.
+ *
+ * Note that control and isochronous endpoints don't halt, although control
+ * endpoints report "protocol stall" (for unsupported requests) using the
+ * same status code used to report a true stall.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying usb_control_msg() call.
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
+{
+	int result;
+	int endp = usb_pipeendpoint(pipe);
+	
+	if (usb_pipein (pipe))
+		endp |= USB_DIR_IN;
+
+	/* we don't care if it wasn't halted first. in fact some devices
+	 * (like some ibmcam model 1 units) seem to expect hosts to make
+	 * this request for iso endpoints, which can't halt!
+	 */
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+		USB_ENDPOINT_HALT, endp, NULL, 0,
+		USB_CTRL_SET_TIMEOUT);
+
+	/* don't un-halt or force to DATA0 except on success */
+	if (result < 0)
+		return result;
+
+	/* NOTE:  seems like Microsoft and Apple don't bother verifying
+	 * the clear "took", so some devices could lock up if you check...
+	 * such as the Hagiwara FlashGate DUAL.  So we won't bother.
+	 *
+	 * NOTE:  make sure the logic here doesn't diverge much from
+	 * the copy in usb-storage, for as long as we need two copies.
+	 */
+
+	/* toggle was reset by the clear */
+	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
+
+	return 0;
+}
+
+/**
+ * usb_disable_endpoint -- Disable an endpoint by address
+ * @dev: the device whose endpoint is being disabled
+ * @epaddr: the endpoint's address.  Endpoint number for output,
+ *	endpoint number + USB_DIR_IN for input
+ *
+ * Deallocates hcd/hardware state for this endpoint ... and nukes all
+ * pending urbs.
+ *
+ * If the HCD hasn't registered a disable() function, this sets the
+ * endpoint's maxpacket size to 0 to prevent further submissions.
+ */
+void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
+{
+	unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+	struct usb_host_endpoint *ep;
+
+	if (!dev)
+		return;
+
+	if (usb_endpoint_out(epaddr)) {
+		ep = dev->ep_out[epnum];
+		dev->ep_out[epnum] = NULL;
+	} else {
+		ep = dev->ep_in[epnum];
+		dev->ep_in[epnum] = NULL;
+	}
+	if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
+		dev->bus->op->disable(dev, ep);
+}
+
+/**
+ * usb_disable_interface -- Disable all endpoints for an interface
+ * @dev: the device whose interface is being disabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Disables all the endpoints for the interface's current altsetting.
+ */
+void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
+{
+	struct usb_host_interface *alt = intf->cur_altsetting;
+	int i;
+
+	for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
+		usb_disable_endpoint(dev,
+				alt->endpoint[i].desc.bEndpointAddress);
+	}
+}
+
+/*
+ * usb_disable_device - Disable all the endpoints for a USB device
+ * @dev: the device whose endpoints are being disabled
+ * @skip_ep0: 0 to disable endpoint 0, 1 to skip it.
+ *
+ * Disables all the device's endpoints, potentially including endpoint 0.
+ * Deallocates hcd/hardware state for the endpoints (nuking all or most
+ * pending urbs) and usbcore state for the interfaces, so that usbcore
+ * must usb_set_configuration() before any interfaces could be used.
+ */
+void usb_disable_device(struct usb_device *dev, int skip_ep0)
+{
+	int i;
+
+	dev_dbg(&dev->dev, "%s nuking %s URBs\n", __FUNCTION__,
+			skip_ep0 ? "non-ep0" : "all");
+	for (i = skip_ep0; i < 16; ++i) {
+		usb_disable_endpoint(dev, i);
+		usb_disable_endpoint(dev, i + USB_DIR_IN);
+	}
+	dev->toggle[0] = dev->toggle[1] = 0;
+
+	/* getting rid of interfaces will disconnect
+	 * any drivers bound to them (a key side effect)
+	 */
+	if (dev->actconfig) {
+		for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+			struct usb_interface	*interface;
+
+			/* remove this interface */
+			interface = dev->actconfig->interface[i];
+			dev_dbg (&dev->dev, "unregistering interface %s\n",
+				interface->dev.bus_id);
+			usb_remove_sysfs_intf_files(interface);
+			kfree(interface->cur_altsetting->string);
+			interface->cur_altsetting->string = NULL;
+			device_del (&interface->dev);
+		}
+
+		/* Now that the interfaces are unbound, nobody should
+		 * try to access them.
+		 */
+		for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+			put_device (&dev->actconfig->interface[i]->dev);
+			dev->actconfig->interface[i] = NULL;
+		}
+		dev->actconfig = NULL;
+		if (dev->state == USB_STATE_CONFIGURED)
+			usb_set_device_state(dev, USB_STATE_ADDRESS);
+	}
+}
+
+
+/*
+ * usb_enable_endpoint - Enable an endpoint for USB communications
+ * @dev: the device whose interface is being enabled
+ * @ep: the endpoint
+ *
+ * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
+ * For control endpoints, both the input and output sides are handled.
+ */
+static void
+usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
+{
+	unsigned int epaddr = ep->desc.bEndpointAddress;
+	unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+	int is_control;
+
+	is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+			== USB_ENDPOINT_XFER_CONTROL);
+	if (usb_endpoint_out(epaddr) || is_control) {
+		usb_settoggle(dev, epnum, 1, 0);
+		dev->ep_out[epnum] = ep;
+	}
+	if (!usb_endpoint_out(epaddr) || is_control) {
+		usb_settoggle(dev, epnum, 0, 0);
+		dev->ep_in[epnum] = ep;
+	}
+}
+
+/*
+ * usb_enable_interface - Enable all the endpoints for an interface
+ * @dev: the device whose interface is being enabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Enables all the endpoints for the interface's current altsetting.
+ */
+static void usb_enable_interface(struct usb_device *dev,
+				 struct usb_interface *intf)
+{
+	struct usb_host_interface *alt = intf->cur_altsetting;
+	int i;
+
+	for (i = 0; i < alt->desc.bNumEndpoints; ++i)
+		usb_enable_endpoint(dev, &alt->endpoint[i]);
+}
+
+/**
+ * usb_set_interface - Makes a particular alternate setting be current
+ * @dev: the device whose interface is being updated
+ * @interface: the interface being updated
+ * @alternate: the setting being chosen.
+ * Context: !in_interrupt ()
+ *
+ * This is used to enable data transfers on interfaces that may not
+ * be enabled by default.  Not all devices support such configurability.
+ * Only the driver bound to an interface may change its setting.
+ *
+ * Within any given configuration, each interface may have several
+ * alternative settings.  These are often used to control levels of
+ * bandwidth consumption.  For example, the default setting for a high
+ * speed interrupt endpoint may not send more than 64 bytes per microframe,
+ * while interrupt transfers of up to 3KBytes per microframe are legal.
+ * Also, isochronous endpoints may never be part of an
+ * interface's default setting.  To access such bandwidth, alternate
+ * interface settings must be made current.
+ *
+ * Note that in the Linux USB subsystem, bandwidth associated with
+ * an endpoint in a given alternate setting is not reserved until an URB
+ * is submitted that needs that bandwidth.  Some other operating systems
+ * allocate bandwidth early, when a configuration is chosen.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Also, drivers must not change altsettings while urbs are scheduled for
+ * endpoints in that interface; all such urbs must first be completed
+ * (perhaps forced by unlinking).
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying usb_control_msg() call.
+ */
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+	struct usb_interface *iface;
+	struct usb_host_interface *alt;
+	int ret;
+	int manual = 0;
+
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
+	iface = usb_ifnum_to_if(dev, interface);
+	if (!iface) {
+		dev_dbg(&dev->dev, "selecting invalid interface %d\n",
+			interface);
+		return -EINVAL;
+	}
+
+	alt = usb_altnum_to_altsetting(iface, alternate);
+	if (!alt) {
+		warn("selecting invalid altsetting %d", alternate);
+		return -EINVAL;
+	}
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				   USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
+				   alternate, interface, NULL, 0, 5000);
+
+	/* 9.4.10 says devices don't need this and are free to STALL the
+	 * request if the interface only has one alternate setting.
+	 */
+	if (ret == -EPIPE && iface->num_altsetting == 1) {
+		dev_dbg(&dev->dev,
+			"manual set_interface for iface %d, alt %d\n",
+			interface, alternate);
+		manual = 1;
+	} else if (ret < 0)
+		return ret;
+
+	/* FIXME drivers shouldn't need to replicate/bugfix the logic here
+	 * when they implement async or easily-killable versions of this or
+	 * other "should-be-internal" functions (like clear_halt).
+	 * should hcd+usbcore postprocess control requests?
+	 */
+
+	/* prevent submissions using previous endpoint settings */
+	usb_disable_interface(dev, iface);
+
+	/* 9.1.1.5 says:
+	 *
+	 *	Configuring a device or changing an alternate setting
+	 *	causes all of the status and configuration values
+	 *	associated with endpoints in the affected interfaces to
+	 *	be set to their default values. This includes setting
+	 *	the data toggle of any endpoint using data toggles to
+	 *	the value DATA0.
+	 *
+	 * Some devices take this too literally and don't reset the data
+	 * toggles if the new altsetting is the same as the old one (the
+	 * command isn't "changing" an alternate setting).  We will manually
+	 * reset the toggles when the new and old altsettings are the same.
+	 * Most devices won't need this, but fortunately it doesn't happen
+	 * often.
+	 */
+	if (iface->cur_altsetting == alt)
+		manual = 1;
+	iface->cur_altsetting = alt;
+
+	/* If the interface only has one altsetting and the device didn't
+	 * accept the request (or whenever the old altsetting is the same
+	 * as the new one), we attempt to carry out the equivalent action
+	 * by manually clearing the HALT feature for each endpoint in the
+	 * new altsetting.
+	 */
+	if (manual) {
+		int i;
+
+		for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+			unsigned int epaddr =
+				alt->endpoint[i].desc.bEndpointAddress;
+			unsigned int pipe =
+	__create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr)
+	| (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN);
+
+			usb_clear_halt(dev, pipe);
+		}
+	}
+
+	/* 9.1.1.5: reset toggles for all endpoints in the new altsetting
+	 *
+	 * Note:
+	 * Despite EP0 is always present in all interfaces/AS, the list of
+	 * endpoints from the descriptor does not contain EP0. Due to its
+	 * omnipresence one might expect EP0 being considered "affected" by
+	 * any SetInterface request and hence assume toggles need to be reset.
+	 * However, EP0 toggles are re-synced for every individual transfer
+	 * during the SETUP stage - hence EP0 toggles are "don't care" here.
+	 * (Likewise, EP0 never "halts" on well designed devices.)
+	 */
+	usb_enable_interface(dev, iface);
+
+	return 0;
+}
+
+/**
+ * usb_reset_configuration - lightweight device reset
+ * @dev: the device whose configuration is being reset
+ *
+ * This issues a standard SET_CONFIGURATION request to the device using
+ * the current configuration.  The effect is to reset most USB-related
+ * state in the device, including interface altsettings (reset to zero),
+ * endpoint halts (cleared), and data toggle (only for bulk and interrupt
+ * endpoints).  Other usbcore state is unchanged, including bindings of
+ * usb device drivers to interfaces.
+ *
+ * Because this affects multiple interfaces, avoid using this with composite
+ * (multi-interface) devices.  Instead, the driver for each interface may
+ * use usb_set_interface() on the interfaces it claims.  Resetting the whole
+ * configuration would affect other drivers' interfaces.
+ *
+ * The caller must own the device lock.
+ *
+ * Returns zero on success, else a negative error code.
+ */
+int usb_reset_configuration(struct usb_device *dev)
+{
+	int			i, retval;
+	struct usb_host_config	*config;
+
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
+	/* caller must have locked the device and must own
+	 * the usb bus readlock (so driver bindings are stable);
+	 * calls during probe() are fine
+	 */
+
+	for (i = 1; i < 16; ++i) {
+		usb_disable_endpoint(dev, i);
+		usb_disable_endpoint(dev, i + USB_DIR_IN);
+	}
+
+	config = dev->actconfig;
+	retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			USB_REQ_SET_CONFIGURATION, 0,
+			config->desc.bConfigurationValue, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (retval < 0) {
+		usb_set_device_state(dev, USB_STATE_ADDRESS);
+		return retval;
+	}
+
+	dev->toggle[0] = dev->toggle[1] = 0;
+
+	/* re-init hc/hcd interface/endpoint state */
+	for (i = 0; i < config->desc.bNumInterfaces; i++) {
+		struct usb_interface *intf = config->interface[i];
+		struct usb_host_interface *alt;
+
+		alt = usb_altnum_to_altsetting(intf, 0);
+
+		/* No altsetting 0?  We'll assume the first altsetting.
+		 * We could use a GetInterface call, but if a device is
+		 * so non-compliant that it doesn't have altsetting 0
+		 * then I wouldn't trust its reply anyway.
+		 */
+		if (!alt)
+			alt = &intf->altsetting[0];
+
+		intf->cur_altsetting = alt;
+		usb_enable_interface(dev, intf);
+	}
+	return 0;
+}
+
+static void release_interface(struct device *dev)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_interface_cache *intfc =
+			altsetting_to_usb_interface_cache(intf->altsetting);
+
+	kref_put(&intfc->ref, usb_release_interface_cache);
+	kfree(intf);
+}
+
+/*
+ * usb_set_configuration - Makes a particular device setting be current
+ * @dev: the device whose configuration is being updated
+ * @configuration: the configuration being chosen.
+ * Context: !in_interrupt(), caller owns the device lock
+ *
+ * This is used to enable non-default device modes.  Not all devices
+ * use this kind of configurability; many devices only have one
+ * configuration.
+ *
+ * USB device configurations may affect Linux interoperability,
+ * power consumption and the functionality available.  For example,
+ * the default configuration is limited to using 100mA of bus power,
+ * so that when certain device functionality requires more power,
+ * and the device is bus powered, that functionality should be in some
+ * non-default device configuration.  Other device modes may also be
+ * reflected as configuration options, such as whether two ISDN
+ * channels are available independently; and choosing between open
+ * standard device protocols (like CDC) or proprietary ones.
+ *
+ * Note that USB has an additional level of device configurability,
+ * associated with interfaces.  That configurability is accessed using
+ * usb_set_interface().
+ *
+ * This call is synchronous. The calling context must be able to sleep,
+ * must own the device lock, and must not hold the driver model's USB
+ * bus rwsem; usb device driver probe() methods cannot use this routine.
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying call that failed.  On succesful completion, each interface
+ * in the original device configuration has been destroyed, and each one
+ * in the new configuration has been probed by all relevant usb device
+ * drivers currently known to the kernel.
+ */
+int usb_set_configuration(struct usb_device *dev, int configuration)
+{
+	int i, ret;
+	struct usb_host_config *cp = NULL;
+	struct usb_interface **new_interfaces = NULL;
+	int n, nintf;
+
+	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+		if (dev->config[i].desc.bConfigurationValue == configuration) {
+			cp = &dev->config[i];
+			break;
+		}
+	}
+	if ((!cp && configuration != 0))
+		return -EINVAL;
+
+	/* The USB spec says configuration 0 means unconfigured.
+	 * But if a device includes a configuration numbered 0,
+	 * we will accept it as a correctly configured state.
+	 */
+	if (cp && configuration == 0)
+		dev_warn(&dev->dev, "config 0 descriptor??\n");
+
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
+	/* Allocate memory for new interfaces before doing anything else,
+	 * so that if we run out then nothing will have changed. */
+	n = nintf = 0;
+	if (cp) {
+		nintf = cp->desc.bNumInterfaces;
+		new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
+				GFP_KERNEL);
+		if (!new_interfaces) {
+			dev_err(&dev->dev, "Out of memory");
+			return -ENOMEM;
+		}
+
+		for (; n < nintf; ++n) {
+			new_interfaces[n] = kmalloc(
+					sizeof(struct usb_interface),
+					GFP_KERNEL);
+			if (!new_interfaces[n]) {
+				dev_err(&dev->dev, "Out of memory");
+				ret = -ENOMEM;
+free_interfaces:
+				while (--n >= 0)
+					kfree(new_interfaces[n]);
+				kfree(new_interfaces);
+				return ret;
+			}
+		}
+	}
+
+	/* if it's already configured, clear out old state first.
+	 * getting rid of old interfaces means unbinding their drivers.
+	 */
+	if (dev->state != USB_STATE_ADDRESS)
+		usb_disable_device (dev, 1);	// Skip ep0
+
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)
+		goto free_interfaces;
+
+	dev->actconfig = cp;
+	if (!cp)
+		usb_set_device_state(dev, USB_STATE_ADDRESS);
+	else {
+		usb_set_device_state(dev, USB_STATE_CONFIGURED);
+
+		/* Initialize the new interface structures and the
+		 * hc/hcd/usbcore interface/endpoint state.
+		 */
+		for (i = 0; i < nintf; ++i) {
+			struct usb_interface_cache *intfc;
+			struct usb_interface *intf;
+			struct usb_host_interface *alt;
+
+			cp->interface[i] = intf = new_interfaces[i];
+			memset(intf, 0, sizeof(*intf));
+			intfc = cp->intf_cache[i];
+			intf->altsetting = intfc->altsetting;
+			intf->num_altsetting = intfc->num_altsetting;
+			kref_get(&intfc->ref);
+
+			alt = usb_altnum_to_altsetting(intf, 0);
+
+			/* No altsetting 0?  We'll assume the first altsetting.
+			 * We could use a GetInterface call, but if a device is
+			 * so non-compliant that it doesn't have altsetting 0
+			 * then I wouldn't trust its reply anyway.
+			 */
+			if (!alt)
+				alt = &intf->altsetting[0];
+
+			intf->cur_altsetting = alt;
+			usb_enable_interface(dev, intf);
+			intf->dev.parent = &dev->dev;
+			intf->dev.driver = NULL;
+			intf->dev.bus = &usb_bus_type;
+			intf->dev.dma_mask = dev->dev.dma_mask;
+			intf->dev.release = release_interface;
+			device_initialize (&intf->dev);
+			sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
+				 dev->bus->busnum, dev->devpath,
+				 configuration,
+				 alt->desc.bInterfaceNumber);
+		}
+		kfree(new_interfaces);
+
+		if ((cp->desc.iConfiguration) &&
+		    (cp->string == NULL)) {
+			cp->string = kmalloc(256, GFP_KERNEL);
+			if (cp->string)
+				usb_string(dev, cp->desc.iConfiguration, cp->string, 256);
+		}
+
+		/* Now that all the interfaces are set up, register them
+		 * to trigger binding of drivers to interfaces.  probe()
+		 * routines may install different altsettings and may
+		 * claim() any interfaces not yet bound.  Many class drivers
+		 * need that: CDC, audio, video, etc.
+		 */
+		for (i = 0; i < nintf; ++i) {
+			struct usb_interface *intf = cp->interface[i];
+			struct usb_interface_descriptor *desc;
+
+			desc = &intf->altsetting [0].desc;
+			dev_dbg (&dev->dev,
+				"adding %s (config #%d, interface %d)\n",
+				intf->dev.bus_id, configuration,
+				desc->bInterfaceNumber);
+			ret = device_add (&intf->dev);
+			if (ret != 0) {
+				dev_err(&dev->dev,
+					"device_add(%s) --> %d\n",
+					intf->dev.bus_id,
+					ret);
+				continue;
+			}
+			if ((intf->cur_altsetting->desc.iInterface) &&
+			    (intf->cur_altsetting->string == NULL)) {
+				intf->cur_altsetting->string = kmalloc(256, GFP_KERNEL);
+				if (intf->cur_altsetting->string)
+					usb_string(dev, intf->cur_altsetting->desc.iInterface,
+						   intf->cur_altsetting->string, 256);
+			}
+			usb_create_sysfs_intf_files (intf);
+		}
+	}
+
+	return ret;
+}
+
+// synchronous request completion model
+EXPORT_SYMBOL(usb_control_msg);
+EXPORT_SYMBOL(usb_bulk_msg);
+
+EXPORT_SYMBOL(usb_sg_init);
+EXPORT_SYMBOL(usb_sg_cancel);
+EXPORT_SYMBOL(usb_sg_wait);
+
+// synchronous control message convenience routines
+EXPORT_SYMBOL(usb_get_descriptor);
+EXPORT_SYMBOL(usb_get_status);
+EXPORT_SYMBOL(usb_get_string);
+EXPORT_SYMBOL(usb_string);
+
+// synchronous calls that also maintain usbcore state
+EXPORT_SYMBOL(usb_clear_halt);
+EXPORT_SYMBOL(usb_reset_configuration);
+EXPORT_SYMBOL(usb_set_interface);
+
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
new file mode 100644
index 0000000..627a5a2
--- /dev/null
+++ b/drivers/usb/core/otg_whitelist.h
@@ -0,0 +1,112 @@
+/*
+ * drivers/usb/core/otg_whitelist.h
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This OTG Whitelist is the OTG "Targeted Peripheral List".  It should
+ * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
+ *
+ * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
+ */ 
+
+static struct usb_device_id whitelist_table [] = {
+
+/* hubs are optional in OTG, but very handy ... */
+{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
+{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
+
+#ifdef	CONFIG_USB_PRINTER		/* ignoring nonstatic linkage! */
+/* FIXME actually, printers are NOT supposed to use device classes;
+ * they're supposed to use interface classes...
+ */
+{ USB_DEVICE_INFO(7, 1, 1) },
+{ USB_DEVICE_INFO(7, 1, 2) },
+{ USB_DEVICE_INFO(7, 1, 3) },
+#endif
+
+#ifdef	CONFIG_USB_CDCETHER
+/* Linux-USB CDC Ethernet gadget */
+{ USB_DEVICE(0x0525, 0xa4a1), },
+/* Linux-USB CDC Ethernet + RNDIS gadget */
+{ USB_DEVICE(0x0525, 0xa4a2), },
+#endif
+
+#if	defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE)
+/* gadget zero, for testing */
+{ USB_DEVICE(0x0525, 0xa4a0), },
+#endif
+
+{ }	/* Terminating entry */
+};
+
+static int is_targeted(struct usb_device *dev)
+{
+	struct usb_device_id	*id = whitelist_table;
+
+	/* possible in developer configs only! */
+	if (!dev->bus->otg_port)
+		return 1;
+
+	/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
+	if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && 
+	     le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
+		return 0;
+
+	/* NOTE: can't use usb_match_id() since interface caches
+	 * aren't set up yet. this is cut/paste from that code.
+	 */
+	for (id = whitelist_table; id->match_flags; id++) {
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+		    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+		    id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
+			continue;
+
+		/* No need to test id->bcdDevice_lo != 0, since 0 is never
+		   greater than any unsigned number. */
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+		    (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+		    (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+		    (id->bDeviceClass != dev->descriptor.bDeviceClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+		    (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+		    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+			continue;
+
+		return 1;
+	}
+
+	/* add other match criteria here ... */
+
+
+	/* OTG MESSAGE: report errors here, customize to match your product */
+	dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
+		le16_to_cpu(dev->descriptor.idVendor),
+		le16_to_cpu(dev->descriptor.idProduct));
+#ifdef	CONFIG_USB_OTG_WHITELIST
+	return 0;
+#else
+	return 1;
+#endif
+}
+
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
new file mode 100644
index 0000000..ec9b3bd
--- /dev/null
+++ b/drivers/usb/core/sysfs.c
@@ -0,0 +1,318 @@
+/*
+ * drivers/usb/core/sysfs.c
+ *
+ * (C) Copyright 2002 David Brownell
+ * (C) Copyright 2002,2004 Greg Kroah-Hartman
+ * (C) Copyright 2002,2004 IBM Corp.
+ *
+ * All of the sysfs file attributes for usb devices and interfaces.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb.h"
+
+/* Active configuration fields */
+#define usb_actconfig_show(field, multiplier, format_string)		\
+static ssize_t  show_##field (struct device *dev, char *buf)		\
+{									\
+	struct usb_device *udev;					\
+	struct usb_host_config *actconfig;				\
+									\
+	udev = to_usb_device (dev);					\
+	actconfig = udev->actconfig;					\
+	if (actconfig)							\
+		return sprintf (buf, format_string,			\
+				actconfig->desc.field * multiplier);	\
+	else								\
+		return 0;						\
+}									\
+
+#define usb_actconfig_attr(field, multiplier, format_string)		\
+usb_actconfig_show(field, multiplier, format_string)			\
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_actconfig_attr (bNumInterfaces, 1, "%2d\n")
+usb_actconfig_attr (bmAttributes, 1, "%2x\n")
+usb_actconfig_attr (bMaxPower, 2, "%3dmA\n")
+
+static ssize_t show_configuration_string(struct device *dev, char *buf)
+{
+	struct usb_device *udev;
+	struct usb_host_config *actconfig;
+	int len;
+
+	udev = to_usb_device (dev);
+	actconfig = udev->actconfig;
+	if ((!actconfig) || (!actconfig->string))
+		return 0;
+	len = sprintf(buf, actconfig->string, PAGE_SIZE);
+	if (len < 0)
+		return 0;
+	buf[len] = '\n';
+	buf[len+1] = 0;
+	return len+1;
+}
+static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
+
+/* configuration value is always present, and r/w */
+usb_actconfig_show(bConfigurationValue, 1, "%u\n");
+
+static ssize_t
+set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
+{
+	struct usb_device	*udev = udev = to_usb_device (dev);
+	int			config, value;
+
+	if (sscanf (buf, "%u", &config) != 1 || config > 255)
+		return -EINVAL;
+	usb_lock_device(udev);
+	value = usb_set_configuration (udev, config);
+	usb_unlock_device(udev);
+	return (value < 0) ? value : count;
+}
+
+static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR, 
+		show_bConfigurationValue, set_bConfigurationValue);
+
+/* String fields */
+#define usb_string_attr(name)						\
+static ssize_t  show_##name(struct device *dev, char *buf)		\
+{									\
+	struct usb_device *udev;					\
+	int len;							\
+									\
+	udev = to_usb_device (dev);					\
+	len = snprintf(buf, 256, "%s", udev->name);			\
+	if (len < 0)							\
+		return 0;						\
+	buf[len] = '\n';						\
+	buf[len+1] = 0;							\
+	return len+1;							\
+}									\
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+usb_string_attr(product);
+usb_string_attr(manufacturer);
+usb_string_attr(serial);
+
+static ssize_t
+show_speed (struct device *dev, char *buf)
+{
+	struct usb_device *udev;
+	char *speed;
+
+	udev = to_usb_device (dev);
+
+	switch (udev->speed) {
+	case USB_SPEED_LOW:
+		speed = "1.5";
+		break;
+	case USB_SPEED_UNKNOWN:
+	case USB_SPEED_FULL:
+		speed = "12";
+		break;
+	case USB_SPEED_HIGH:
+		speed = "480";
+		break;
+	default:
+		speed = "unknown";
+	}
+	return sprintf (buf, "%s\n", speed);
+}
+static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
+
+static ssize_t
+show_devnum (struct device *dev, char *buf)
+{
+	struct usb_device *udev;
+
+	udev = to_usb_device (dev);
+	return sprintf (buf, "%d\n", udev->devnum);
+}
+static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL);
+
+static ssize_t
+show_version (struct device *dev, char *buf)
+{
+	struct usb_device *udev;
+	u16 bcdUSB;
+
+	udev = to_usb_device(dev);
+	bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
+	return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t
+show_maxchild (struct device *dev, char *buf)
+{
+	struct usb_device *udev;
+
+	udev = to_usb_device (dev);
+	return sprintf (buf, "%d\n", udev->maxchild);
+}
+static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
+
+/* Descriptor fields */
+#define usb_descriptor_attr_le16(field, format_string)			\
+static ssize_t								\
+show_##field (struct device *dev, char *buf)				\
+{									\
+	struct usb_device *udev;					\
+									\
+	udev = to_usb_device (dev);					\
+	return sprintf (buf, format_string, 				\
+			le16_to_cpu(udev->descriptor.field));		\
+}									\
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_descriptor_attr_le16(idVendor, "%04x\n")
+usb_descriptor_attr_le16(idProduct, "%04x\n")
+usb_descriptor_attr_le16(bcdDevice, "%04x\n")
+
+#define usb_descriptor_attr(field, format_string)			\
+static ssize_t								\
+show_##field (struct device *dev, char *buf)				\
+{									\
+	struct usb_device *udev;					\
+									\
+	udev = to_usb_device (dev);					\
+	return sprintf (buf, format_string, udev->descriptor.field);	\
+}									\
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_descriptor_attr (bDeviceClass, "%02x\n")
+usb_descriptor_attr (bDeviceSubClass, "%02x\n")
+usb_descriptor_attr (bDeviceProtocol, "%02x\n")
+usb_descriptor_attr (bNumConfigurations, "%d\n")
+
+static struct attribute *dev_attrs[] = {
+	/* current configuration's attributes */
+	&dev_attr_bNumInterfaces.attr,
+	&dev_attr_bConfigurationValue.attr,
+	&dev_attr_bmAttributes.attr,
+	&dev_attr_bMaxPower.attr,
+	/* device attributes */
+	&dev_attr_idVendor.attr,
+	&dev_attr_idProduct.attr,
+	&dev_attr_bcdDevice.attr,
+	&dev_attr_bDeviceClass.attr,
+	&dev_attr_bDeviceSubClass.attr,
+	&dev_attr_bDeviceProtocol.attr,
+	&dev_attr_bNumConfigurations.attr,
+	&dev_attr_speed.attr,
+	&dev_attr_devnum.attr,
+	&dev_attr_version.attr,
+	&dev_attr_maxchild.attr,
+	NULL,
+};
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+
+void usb_create_sysfs_dev_files (struct usb_device *udev)
+{
+	struct device *dev = &udev->dev;
+
+	sysfs_create_group(&dev->kobj, &dev_attr_grp);
+
+	if (udev->manufacturer)
+		device_create_file (dev, &dev_attr_manufacturer);
+	if (udev->product)
+		device_create_file (dev, &dev_attr_product);
+	if (udev->serial)
+		device_create_file (dev, &dev_attr_serial);
+	device_create_file (dev, &dev_attr_configuration);
+}
+
+void usb_remove_sysfs_dev_files (struct usb_device *udev)
+{
+	struct device *dev = &udev->dev;
+
+	sysfs_remove_group(&dev->kobj, &dev_attr_grp);
+
+	if (udev->descriptor.iManufacturer)
+		device_remove_file(dev, &dev_attr_manufacturer);
+	if (udev->descriptor.iProduct)
+		device_remove_file(dev, &dev_attr_product);
+	if (udev->descriptor.iSerialNumber)
+		device_remove_file(dev, &dev_attr_serial);
+	device_remove_file (dev, &dev_attr_configuration);
+}
+
+/* Interface fields */
+#define usb_intf_attr(field, format_string)				\
+static ssize_t								\
+show_##field (struct device *dev, char *buf)				\
+{									\
+	struct usb_interface *intf = to_usb_interface (dev);		\
+									\
+	return sprintf (buf, format_string, intf->cur_altsetting->desc.field); \
+}									\
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_intf_attr (bInterfaceNumber, "%02x\n")
+usb_intf_attr (bAlternateSetting, "%2d\n")
+usb_intf_attr (bNumEndpoints, "%02x\n")
+usb_intf_attr (bInterfaceClass, "%02x\n")
+usb_intf_attr (bInterfaceSubClass, "%02x\n")
+usb_intf_attr (bInterfaceProtocol, "%02x\n")
+
+static ssize_t show_interface_string(struct device *dev, char *buf)
+{
+	struct usb_interface *intf;
+	struct usb_device *udev;
+	int len;
+
+	intf = to_usb_interface (dev);
+	udev = interface_to_usbdev (intf);
+	len = snprintf(buf, 256, "%s", intf->cur_altsetting->string);
+	if (len < 0)
+		return 0;
+	buf[len] = '\n';
+	buf[len+1] = 0;
+	return len+1;
+}
+static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
+
+static struct attribute *intf_attrs[] = {
+	&dev_attr_bInterfaceNumber.attr,
+	&dev_attr_bAlternateSetting.attr,
+	&dev_attr_bNumEndpoints.attr,
+	&dev_attr_bInterfaceClass.attr,
+	&dev_attr_bInterfaceSubClass.attr,
+	&dev_attr_bInterfaceProtocol.attr,
+	NULL,
+};
+static struct attribute_group intf_attr_grp = {
+	.attrs = intf_attrs,
+};
+
+void usb_create_sysfs_intf_files (struct usb_interface *intf)
+{
+	sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+
+	if (intf->cur_altsetting->string)
+		device_create_file(&intf->dev, &dev_attr_interface);
+		
+}
+
+void usb_remove_sysfs_intf_files (struct usb_interface *intf)
+{
+	sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+
+	if (intf->cur_altsetting->string)
+		device_remove_file(&intf->dev, &dev_attr_interface);
+
+}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
new file mode 100644
index 0000000..dc838f8
--- /dev/null
+++ b/drivers/usb/core/urb.c
@@ -0,0 +1,511 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+#include "hcd.h"
+
+#define to_urb(d) container_of(d, struct urb, kref)
+
+static void urb_destroy(struct kref *kref)
+{
+	struct urb *urb = to_urb(kref);
+	kfree(urb);
+}
+
+/**
+ * usb_init_urb - initializes a urb so that it can be used by a USB driver
+ * @urb: pointer to the urb to initialize
+ *
+ * Initializes a urb so that the USB subsystem can use it properly.
+ *
+ * If a urb is created with a call to usb_alloc_urb() it is not
+ * necessary to call this function.  Only use this if you allocate the
+ * space for a struct urb on your own.  If you call this function, be
+ * careful when freeing the memory for your urb that it is no longer in
+ * use by the USB core.
+ *
+ * Only use this function if you _really_ understand what you are doing.
+ */
+void usb_init_urb(struct urb *urb)
+{
+	if (urb) {
+		memset(urb, 0, sizeof(*urb));
+		kref_init(&urb->kref);
+		spin_lock_init(&urb->lock);
+	}
+}
+
+/**
+ * usb_alloc_urb - creates a new urb for a USB driver to use
+ * @iso_packets: number of iso packets for this urb
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list of
+ *	valid options for this.
+ *
+ * Creates an urb for the USB driver to use, initializes a few internal
+ * structures, incrementes the usage counter, and returns a pointer to it.
+ *
+ * If no memory is available, NULL is returned.
+ *
+ * If the driver want to use this urb for interrupt, control, or bulk
+ * endpoints, pass '0' as the number of iso packets.
+ *
+ * The driver must call usb_free_urb() when it is finished with the urb.
+ */
+struct urb *usb_alloc_urb(int iso_packets, int mem_flags)
+{
+	struct urb *urb;
+
+	urb = (struct urb *)kmalloc(sizeof(struct urb) + 
+		iso_packets * sizeof(struct usb_iso_packet_descriptor),
+		mem_flags);
+	if (!urb) {
+		err("alloc_urb: kmalloc failed");
+		return NULL;
+	}
+	usb_init_urb(urb);
+	return urb;
+}
+
+/**
+ * usb_free_urb - frees the memory used by a urb when all users of it are finished
+ * @urb: pointer to the urb to free, may be NULL
+ *
+ * Must be called when a user of a urb is finished with it.  When the last user
+ * of the urb calls this function, the memory of the urb is freed.
+ *
+ * Note: The transfer buffer associated with the urb is not freed, that must be
+ * done elsewhere.
+ */
+void usb_free_urb(struct urb *urb)
+{
+	if (urb)
+		kref_put(&urb->kref, urb_destroy);
+}
+
+/**
+ * usb_get_urb - increments the reference count of the urb
+ * @urb: pointer to the urb to modify, may be NULL
+ *
+ * This must be  called whenever a urb is transferred from a device driver to a
+ * host controller driver.  This allows proper reference counting to happen
+ * for urbs.
+ *
+ * A pointer to the urb with the incremented reference counter is returned.
+ */
+struct urb * usb_get_urb(struct urb *urb)
+{
+	if (urb)
+		kref_get(&urb->kref);
+	return urb;
+}
+		
+		
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_submit_urb - issue an asynchronous transfer request for an endpoint
+ * @urb: pointer to the urb describing the request
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list
+ *	of valid options for this.
+ *
+ * This submits a transfer request, and transfers control of the URB
+ * describing that request to the USB subsystem.  Request completion will
+ * be indicated later, asynchronously, by calling the completion handler.
+ * The three types of completion are success, error, and unlink
+ * (a software-induced fault, also called "request cancelation").  
+ *
+ * URBs may be submitted in interrupt context.
+ *
+ * The caller must have correctly initialized the URB before submitting
+ * it.  Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
+ * available to ensure that most fields are correctly initialized, for
+ * the particular kind of transfer, although they will not initialize
+ * any transfer flags.
+ *
+ * Successful submissions return 0; otherwise this routine returns a
+ * negative error number.  If the submission is successful, the complete()
+ * callback from the URB will be called exactly once, when the USB core and
+ * Host Controller Driver (HCD) are finished with the URB.  When the completion
+ * function is called, control of the URB is returned to the device
+ * driver which issued the request.  The completion handler may then
+ * immediately free or reuse that URB.
+ *
+ * With few exceptions, USB device drivers should never access URB fields
+ * provided by usbcore or the HCD until its complete() is called.
+ * The exceptions relate to periodic transfer scheduling.  For both
+ * interrupt and isochronous urbs, as part of successful URB submission
+ * urb->interval is modified to reflect the actual transfer period used
+ * (normally some power of two units).  And for isochronous urbs,
+ * urb->start_frame is modified to reflect when the URB's transfers were
+ * scheduled to start.  Not all isochronous transfer scheduling policies
+ * will work, but most host controller drivers should easily handle ISO
+ * queues going from now until 10-200 msec into the future.
+ *
+ * For control endpoints, the synchronous usb_control_msg() call is
+ * often used (in non-interrupt context) instead of this call.
+ * That is often used through convenience wrappers, for the requests
+ * that are standardized in the USB 2.0 specification.  For bulk
+ * endpoints, a synchronous usb_bulk_msg() call is available.
+ *
+ * Request Queuing:
+ *
+ * URBs may be submitted to endpoints before previous ones complete, to
+ * minimize the impact of interrupt latencies and system overhead on data
+ * throughput.  With that queuing policy, an endpoint's queue would never
+ * be empty.  This is required for continuous isochronous data streams,
+ * and may also be required for some kinds of interrupt transfers. Such
+ * queuing also maximizes bandwidth utilization by letting USB controllers
+ * start work on later requests before driver software has finished the
+ * completion processing for earlier (successful) requests.
+ *
+ * As of Linux 2.6, all USB endpoint transfer queues support depths greater
+ * than one.  This was previously a HCD-specific behavior, except for ISO
+ * transfers.  Non-isochronous endpoint queues are inactive during cleanup
+ * after faults (transfer errors or cancelation).
+ *
+ * Reserved Bandwidth Transfers:
+ *
+ * Periodic transfers (interrupt or isochronous) are performed repeatedly,
+ * using the interval specified in the urb.  Submitting the first urb to
+ * the endpoint reserves the bandwidth necessary to make those transfers.
+ * If the USB subsystem can't allocate sufficient bandwidth to perform
+ * the periodic request, submitting such a periodic request should fail.
+ *
+ * Device drivers must explicitly request that repetition, by ensuring that
+ * some URB is always on the endpoint's queue (except possibly for short
+ * periods during completion callacks).  When there is no longer an urb
+ * queued, the endpoint's bandwidth reservation is canceled.  This means
+ * drivers can use their completion handlers to ensure they keep bandwidth
+ * they need, by reinitializing and resubmitting the just-completed urb
+ * until the driver longer needs that periodic bandwidth.
+ *
+ * Memory Flags:
+ *
+ * The general rules for how to decide which mem_flags to use
+ * are the same as for kmalloc.  There are four
+ * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
+ * GFP_ATOMIC.
+ *
+ * GFP_NOFS is not ever used, as it has not been implemented yet.
+ *
+ * GFP_ATOMIC is used when
+ *   (a) you are inside a completion handler, an interrupt, bottom half,
+ *       tasklet or timer, or
+ *   (b) you are holding a spinlock or rwlock (does not apply to
+ *       semaphores), or
+ *   (c) current->state != TASK_RUNNING, this is the case only after
+ *       you've changed it.
+ * 
+ * GFP_NOIO is used in the block io path and error handling of storage
+ * devices.
+ *
+ * All other situations use GFP_KERNEL.
+ *
+ * Some more specific rules for mem_flags can be inferred, such as
+ *  (1) start_xmit, timeout, and receive methods of network drivers must
+ *      use GFP_ATOMIC (they are called with a spinlock held);
+ *  (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
+ *      called with a spinlock held);
+ *  (3) If you use a kernel thread with a network driver you must use
+ *      GFP_NOIO, unless (b) or (c) apply;
+ *  (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
+ *      apply or your are in a storage driver's block io path;
+ *  (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
+ *  (6) changing firmware on a running storage or net device uses
+ *      GFP_NOIO, unless b) or c) apply
+ *
+ */
+int usb_submit_urb(struct urb *urb, int mem_flags)
+{
+	int			pipe, temp, max;
+	struct usb_device	*dev;
+	struct usb_operations	*op;
+	int			is_out;
+
+	if (!urb || urb->hcpriv || !urb->complete)
+		return -EINVAL;
+	if (!(dev = urb->dev) ||
+	    (dev->state < USB_STATE_DEFAULT) ||
+	    (!dev->bus) || (dev->devnum <= 0))
+		return -ENODEV;
+	if (dev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+	if (!(op = dev->bus->op) || !op->submit_urb)
+		return -ENODEV;
+
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->bandwidth = 0;
+
+	/* Lots of sanity checks, so HCDs can rely on clean data
+	 * and don't need to duplicate tests
+	 */
+	pipe = urb->pipe;
+	temp = usb_pipetype (pipe);
+	is_out = usb_pipeout (pipe);
+
+	if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
+		return -ENODEV;
+
+	/* FIXME there should be a sharable lock protecting us against
+	 * config/altsetting changes and disconnects, kicking in here.
+	 * (here == before maxpacket, and eventually endpoint type,
+	 * checks get made.)
+	 */
+
+	max = usb_maxpacket (dev, pipe, is_out);
+	if (max <= 0) {
+		dev_dbg(&dev->dev,
+			"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
+			usb_pipeendpoint (pipe), is_out ? "out" : "in",
+			__FUNCTION__, max);
+		return -EMSGSIZE;
+	}
+
+	/* periodic transfers limit size per frame/uframe,
+	 * but drivers only control those sizes for ISO.
+	 * while we're checking, initialize return status.
+	 */
+	if (temp == PIPE_ISOCHRONOUS) {
+		int	n, len;
+
+		/* "high bandwidth" mode, 1-3 packets/uframe? */
+		if (dev->speed == USB_SPEED_HIGH) {
+			int	mult = 1 + ((max >> 11) & 0x03);
+			max &= 0x07ff;
+			max *= mult;
+		}
+
+		if (urb->number_of_packets <= 0)		    
+			return -EINVAL;
+		for (n = 0; n < urb->number_of_packets; n++) {
+			len = urb->iso_frame_desc [n].length;
+			if (len < 0 || len > max) 
+				return -EMSGSIZE;
+			urb->iso_frame_desc [n].status = -EXDEV;
+			urb->iso_frame_desc [n].actual_length = 0;
+		}
+	}
+
+	/* the I/O buffer must be mapped/unmapped, except when length=0 */
+	if (urb->transfer_buffer_length < 0)
+		return -EMSGSIZE;
+
+#ifdef DEBUG
+	/* stuff that drivers shouldn't do, but which shouldn't
+	 * cause problems in HCDs if they get it wrong.
+	 */
+	{
+	unsigned int	orig_flags = urb->transfer_flags;
+	unsigned int	allowed;
+
+	/* enforce simple/standard policy */
+	allowed = URB_ASYNC_UNLINK;	// affects later unlinks
+	allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
+	allowed |= URB_NO_INTERRUPT;
+	switch (temp) {
+	case PIPE_BULK:
+		if (is_out)
+			allowed |= URB_ZERO_PACKET;
+		/* FALLTHROUGH */
+	case PIPE_CONTROL:
+		allowed |= URB_NO_FSBR;	/* only affects UHCI */
+		/* FALLTHROUGH */
+	default:			/* all non-iso endpoints */
+		if (!is_out)
+			allowed |= URB_SHORT_NOT_OK;
+		break;
+	case PIPE_ISOCHRONOUS:
+		allowed |= URB_ISO_ASAP;
+		break;
+	}
+	urb->transfer_flags &= allowed;
+
+	/* fail if submitter gave bogus flags */
+	if (urb->transfer_flags != orig_flags) {
+		err ("BOGUS urb flags, %x --> %x",
+			orig_flags, urb->transfer_flags);
+		return -EINVAL;
+	}
+	}
+#endif
+	/*
+	 * Force periodic transfer intervals to be legal values that are
+	 * a power of two (so HCDs don't need to).
+	 *
+	 * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC
+	 * supports different values... this uses EHCI/UHCI defaults (and
+	 * EHCI can use smaller non-default values).
+	 */
+	switch (temp) {
+	case PIPE_ISOCHRONOUS:
+	case PIPE_INTERRUPT:
+		/* too small? */
+		if (urb->interval <= 0)
+			return -EINVAL;
+		/* too big? */
+		switch (dev->speed) {
+		case USB_SPEED_HIGH:	/* units are microframes */
+			// NOTE usb handles 2^15
+			if (urb->interval > (1024 * 8))
+				urb->interval = 1024 * 8;
+			temp = 1024 * 8;
+			break;
+		case USB_SPEED_FULL:	/* units are frames/msec */
+		case USB_SPEED_LOW:
+			if (temp == PIPE_INTERRUPT) {
+				if (urb->interval > 255)
+					return -EINVAL;
+				// NOTE ohci only handles up to 32
+				temp = 128;
+			} else {
+				if (urb->interval > 1024)
+					urb->interval = 1024;
+				// NOTE usb and ohci handle up to 2^15
+				temp = 1024;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+		/* power of two? */
+		while (temp > urb->interval)
+			temp >>= 1;
+		urb->interval = temp;
+	}
+
+	return op->submit_urb (urb, mem_flags);
+}
+
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_unlink_urb - abort/cancel a transfer request for an endpoint
+ * @urb: pointer to urb describing a previously submitted request,
+ *	may be NULL
+ *
+ * This routine cancels an in-progress request.  URBs complete only
+ * once per submission, and may be canceled only once per submission.
+ * Successful cancelation means the requests's completion handler will
+ * be called with a status code indicating that the request has been
+ * canceled (rather than any other code) and will quickly be removed
+ * from host controller data structures.
+ *
+ * In the past, clearing the URB_ASYNC_UNLINK transfer flag for the
+ * URB indicated that the request was synchronous.  This usage is now
+ * deprecated; if the flag is clear the call will be forwarded to
+ * usb_kill_urb() and the return value will be 0.  In the future, drivers
+ * should call usb_kill_urb() directly for synchronous unlinking.
+ *
+ * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
+ * request is asynchronous.  Success is indicated by returning -EINPROGRESS,
+ * at which time the URB will normally have been unlinked but not yet
+ * given back to the device driver.  When it is called, the completion
+ * function will see urb->status == -ECONNRESET.  Failure is indicated
+ * by any other return value.  Unlinking will fail when the URB is not
+ * currently "linked" (i.e., it was never submitted, or it was unlinked
+ * before, or the hardware is already finished with it), even if the
+ * completion handler has not yet run.
+ *
+ * Unlinking and Endpoint Queues:
+ *
+ * Host Controller Drivers (HCDs) place all the URBs for a particular
+ * endpoint in a queue.  Normally the queue advances as the controller
+ * hardware processes each request.  But when an URB terminates with any
+ * fault (such as an error, or being unlinked) its queue stops, at least
+ * until that URB's completion routine returns.  It is guaranteed that
+ * the queue will not restart until all its unlinked URBs have been fully
+ * retired, with their completion routines run, even if that's not until
+ * some time after the original completion handler returns.
+ *
+ * This means that USB device drivers can safely build deep queues for
+ * large or complex transfers, and clean them up reliably after any sort
+ * of aborted transfer by unlinking all pending URBs at the first fault.
+ *
+ * Note that an URB terminating early because a short packet was received
+ * will count as an error if and only if the URB_SHORT_NOT_OK flag is set.
+ * Also, that all unlinks performed in any URB completion handler must
+ * be asynchronous.
+ *
+ * Queues for isochronous endpoints are treated differently, because they
+ * advance at fixed rates.  Such queues do not stop when an URB is unlinked.
+ * An unlinked URB may leave a gap in the stream of packets.  It is undefined
+ * whether such gaps can be filled in.
+ *
+ * When a control URB terminates with an error, it is likely that the
+ * status stage of the transfer will not take place, even if it is merely
+ * a soft error resulting from a short-packet with URB_SHORT_NOT_OK set.
+ */
+int usb_unlink_urb(struct urb *urb)
+{
+	if (!urb)
+		return -EINVAL;
+	if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+#ifdef CONFIG_DEBUG_KERNEL
+		if (printk_ratelimit()) {
+			printk(KERN_NOTICE "usb_unlink_urb() is deprecated for "
+				"synchronous unlinks.  Use usb_kill_urb() instead.\n");
+			WARN_ON(1);
+		}
+#endif
+		usb_kill_urb(urb);
+		return 0;
+	}
+	if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+		return -ENODEV;
+	return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+}
+
+/**
+ * usb_kill_urb - cancel a transfer request and wait for it to finish
+ * @urb: pointer to URB describing a previously submitted request,
+ *	may be NULL
+ *
+ * This routine cancels an in-progress request.  It is guaranteed that
+ * upon return all completion handlers will have finished and the URB
+ * will be totally idle and available for reuse.  These features make
+ * this an ideal way to stop I/O in a disconnect() callback or close()
+ * function.  If the request has not already finished or been unlinked
+ * the completion handler will see urb->status == -ENOENT.
+ *
+ * While the routine is running, attempts to resubmit the URB will fail
+ * with error -EPERM.  Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * This routine may not be used in an interrupt context (such as a bottom
+ * half or a completion handler), or when holding a spinlock, or in other
+ * situations where the caller can't schedule().
+ */
+void usb_kill_urb(struct urb *urb)
+{
+	if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+		return;
+	spin_lock_irq(&urb->lock);
+	++urb->reject;
+	spin_unlock_irq(&urb->lock);
+
+	urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
+
+	spin_lock_irq(&urb->lock);
+	--urb->reject;
+	spin_unlock_irq(&urb->lock);
+}
+
+EXPORT_SYMBOL(usb_init_urb);
+EXPORT_SYMBOL(usb_alloc_urb);
+EXPORT_SYMBOL(usb_free_urb);
+EXPORT_SYMBOL(usb_get_urb);
+EXPORT_SYMBOL(usb_submit_urb);
+EXPORT_SYMBOL(usb_unlink_urb);
+EXPORT_SYMBOL(usb_kill_urb);
+
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
new file mode 100644
index 0000000..f0534ee
--- /dev/null
+++ b/drivers/usb/core/usb.c
@@ -0,0 +1,1573 @@
+/*
+ * drivers/usb/usb.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2004
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ *     (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ * NOTE! This is not actually a driver at all, rather this is
+ * just a collection of helper routines that implement the
+ * generic USB things that the real drivers can use..
+ *
+ * Think of this as a "USB library" rather than anything else.
+ * It should be considered a slave, with no callbacks. Callbacks
+ * are evil.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>  /* for in_interrupt() */
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/rwsem.h>
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+#include "hcd.h"
+#include "usb.h"
+
+extern int  usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+extern int usb_major_init(void);
+extern void usb_major_cleanup(void);
+extern int usb_host_init(void);
+extern void usb_host_cleanup(void);
+
+
+const char *usbcore_name = "usbcore";
+
+static int nousb;	/* Disable USB when built into kernel image */
+			/* Not honored on modular build */
+
+static DECLARE_RWSEM(usb_all_devices_rwsem);
+
+
+static int generic_probe (struct device *dev)
+{
+	return 0;
+}
+static int generic_remove (struct device *dev)
+{
+	return 0;
+}
+
+static struct device_driver usb_generic_driver = {
+	.owner = THIS_MODULE,
+	.name =	"usb",
+	.bus = &usb_bus_type,
+	.probe = generic_probe,
+	.remove = generic_remove,
+};
+
+static int usb_generic_driver_data;
+
+/* called from driver core with usb_bus_type.subsys writelock */
+static int usb_probe_interface(struct device *dev)
+{
+	struct usb_interface * intf = to_usb_interface(dev);
+	struct usb_driver * driver = to_usb_driver(dev->driver);
+	const struct usb_device_id *id;
+	int error = -ENODEV;
+
+	dev_dbg(dev, "%s\n", __FUNCTION__);
+
+	if (!driver->probe)
+		return error;
+	/* FIXME we'd much prefer to just resume it ... */
+	if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+
+	id = usb_match_id (intf, driver->id_table);
+	if (id) {
+		dev_dbg (dev, "%s - got id\n", __FUNCTION__);
+		intf->condition = USB_INTERFACE_BINDING;
+		error = driver->probe (intf, id);
+		intf->condition = error ? USB_INTERFACE_UNBOUND :
+				USB_INTERFACE_BOUND;
+	}
+
+	return error;
+}
+
+/* called from driver core with usb_bus_type.subsys writelock */
+static int usb_unbind_interface(struct device *dev)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+	intf->condition = USB_INTERFACE_UNBINDING;
+
+	/* release all urbs for this interface */
+	usb_disable_interface(interface_to_usbdev(intf), intf);
+
+	if (driver && driver->disconnect)
+		driver->disconnect(intf);
+
+	/* reset other interface state */
+	usb_set_interface(interface_to_usbdev(intf),
+			intf->altsetting[0].desc.bInterfaceNumber,
+			0);
+	usb_set_intfdata(intf, NULL);
+	intf->condition = USB_INTERFACE_UNBOUND;
+
+	return 0;
+}
+
+/**
+ * usb_register - register a USB driver
+ * @new_driver: USB operations for the driver
+ *
+ * Registers a USB driver with the USB core.  The list of unattached
+ * interfaces will be rescanned whenever a new driver is added, allowing
+ * the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ * 
+ * NOTE: if you want your driver to use the USB major number, you must call
+ * usb_register_dev() to enable that functionality.  This function no longer
+ * takes care of that.
+ */
+int usb_register(struct usb_driver *new_driver)
+{
+	int retval = 0;
+
+	if (nousb)
+		return -ENODEV;
+
+	new_driver->driver.name = (char *)new_driver->name;
+	new_driver->driver.bus = &usb_bus_type;
+	new_driver->driver.probe = usb_probe_interface;
+	new_driver->driver.remove = usb_unbind_interface;
+	new_driver->driver.owner = new_driver->owner;
+
+	usb_lock_all_devices();
+	retval = driver_register(&new_driver->driver);
+	usb_unlock_all_devices();
+
+	if (!retval) {
+		pr_info("%s: registered new driver %s\n",
+			usbcore_name, new_driver->name);
+		usbfs_update_special();
+	} else {
+		printk(KERN_ERR "%s: error %d registering driver %s\n",
+			usbcore_name, retval, new_driver->name);
+	}
+
+	return retval;
+}
+
+/**
+ * usb_deregister - unregister a USB driver
+ * @driver: USB operations of the driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ * 
+ * NOTE: If you called usb_register_dev(), you still need to call
+ * usb_deregister_dev() to clean up your driver's allocated minor numbers,
+ * this * call will no longer do it for you.
+ */
+void usb_deregister(struct usb_driver *driver)
+{
+	pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+
+	usb_lock_all_devices();
+	driver_unregister (&driver->driver);
+	usb_unlock_all_devices();
+
+	usbfs_update_special();
+}
+
+/**
+ * usb_ifnum_to_if - get the interface object with a given interface number
+ * @dev: the device whose current configuration is considered
+ * @ifnum: the desired interface
+ *
+ * This walks the device descriptor for the currently active configuration
+ * and returns a pointer to the interface with that particular interface
+ * number, or null.
+ *
+ * Note that configuration descriptors are not required to assign interface
+ * numbers sequentially, so that it would be incorrect to assume that
+ * the first interface in that descriptor corresponds to interface zero.
+ * This routine helps device drivers avoid such mistakes.
+ * However, you should make sure that you do the right thing with any
+ * alternate settings available for this interfaces.
+ *
+ * Don't call this function unless you are bound to one of the interfaces
+ * on this device or you have locked the device!
+ */
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+	struct usb_host_config *config = dev->actconfig;
+	int i;
+
+	if (!config)
+		return NULL;
+	for (i = 0; i < config->desc.bNumInterfaces; i++)
+		if (config->interface[i]->altsetting[0]
+				.desc.bInterfaceNumber == ifnum)
+			return config->interface[i];
+
+	return NULL;
+}
+
+/**
+ * usb_altnum_to_altsetting - get the altsetting structure with a given
+ *	alternate setting number.
+ * @intf: the interface containing the altsetting in question
+ * @altnum: the desired alternate setting number
+ *
+ * This searches the altsetting array of the specified interface for
+ * an entry with the correct bAlternateSetting value and returns a pointer
+ * to that entry, or null.
+ *
+ * Note that altsettings need not be stored sequentially by number, so
+ * it would be incorrect to assume that the first altsetting entry in
+ * the array corresponds to altsetting zero.  This routine helps device
+ * drivers avoid such mistakes.
+ *
+ * Don't call this function unless you are bound to the intf interface
+ * or you have locked the device!
+ */
+struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
+		unsigned int altnum)
+{
+	int i;
+
+	for (i = 0; i < intf->num_altsetting; i++) {
+		if (intf->altsetting[i].desc.bAlternateSetting == altnum)
+			return &intf->altsetting[i];
+	}
+	return NULL;
+}
+
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound; must be in the
+ *	usb device's active configuration
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method.
+ *
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+int usb_driver_claim_interface(struct usb_driver *driver,
+				struct usb_interface *iface, void* priv)
+{
+	struct device *dev = &iface->dev;
+
+	if (dev->driver)
+		return -EBUSY;
+
+	dev->driver = &driver->driver;
+	usb_set_intfdata(iface, priv);
+	iface->condition = USB_INTERFACE_BOUND;
+
+	/* if interface was already added, bind now; else let
+	 * the future device_add() bind it, bypassing probe()
+	 */
+	if (!list_empty (&dev->bus_list))
+		device_bind_driver(dev);
+
+	return 0;
+}
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called.  In typical cases this
+ * also causes the driver disconnect() method to be called.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver disconnect() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+void usb_driver_release_interface(struct usb_driver *driver,
+					struct usb_interface *iface)
+{
+	struct device *dev = &iface->dev;
+
+	/* this should never happen, don't release something that's not ours */
+	if (!dev->driver || dev->driver != &driver->driver)
+		return;
+
+	/* don't disconnect from disconnect(), or before dev_add() */
+	if (!list_empty (&dev->driver_list) && !list_empty (&dev->bus_list))
+		device_release_driver(dev);
+
+	dev->driver = NULL;
+	usb_set_intfdata(iface, NULL);
+	iface->condition = USB_INTERFACE_UNBOUND;
+}
+
+/**
+ * usb_match_id - find first usb_device_id matching device or interface
+ * @interface: the interface of interest
+ * @id: array of usb_device_id structures, terminated by zero entry
+ *
+ * usb_match_id searches an array of usb_device_id's and returns
+ * the first one matching the device or interface, or null.
+ * This is used when binding (or rebinding) a driver to an interface.
+ * Most USB device drivers will use this indirectly, through the usb core,
+ * but some layered driver frameworks use it directly.
+ * These device tables are exported with MODULE_DEVICE_TABLE, through
+ * modutils and "modules.usbmap", to support the driver loading
+ * functionality of USB hotplugging.
+ *
+ * What Matches:
+ *
+ * The "match_flags" element in a usb_device_id controls which
+ * members are used.  If the corresponding bit is set, the
+ * value in the device_id must match its corresponding member
+ * in the device or interface descriptor, or else the device_id
+ * does not match.
+ *
+ * "driver_info" is normally used only by device drivers,
+ * but you can create a wildcard "matches anything" usb_device_id
+ * as a driver's "modules.usbmap" entry if you provide an id with
+ * only a nonzero "driver_info" field.  If you do this, the USB device
+ * driver's probe() routine should use additional intelligence to
+ * decide whether to bind to the specified interface.
+ * 
+ * What Makes Good usb_device_id Tables:
+ *
+ * The match algorithm is very simple, so that intelligence in
+ * driver selection must come from smart driver id records.
+ * Unless you have good reasons to use another selection policy,
+ * provide match elements only in related groups, and order match
+ * specifiers from specific to general.  Use the macros provided
+ * for that purpose if you can.
+ *
+ * The most specific match specifiers use device descriptor
+ * data.  These are commonly used with product-specific matches;
+ * the USB_DEVICE macro lets you provide vendor and product IDs,
+ * and you can also match against ranges of product revisions.
+ * These are widely used for devices with application or vendor
+ * specific bDeviceClass values.
+ *
+ * Matches based on device class/subclass/protocol specifications
+ * are slightly more general; use the USB_DEVICE_INFO macro, or
+ * its siblings.  These are used with single-function devices
+ * where bDeviceClass doesn't specify that each interface has
+ * its own class. 
+ *
+ * Matches based on interface class/subclass/protocol are the
+ * most general; they let drivers bind to any interface on a
+ * multiple-function device.  Use the USB_INTERFACE_INFO
+ * macro, or its siblings, to match class-per-interface style 
+ * devices (as recorded in bDeviceClass).
+ *  
+ * Within those groups, remember that not all combinations are
+ * meaningful.  For example, don't give a product version range
+ * without vendor and product IDs; or specify a protocol without
+ * its associated class and subclass.
+ */   
+const struct usb_device_id *
+usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_host_interface *intf;
+	struct usb_device *dev;
+
+	/* proc_connectinfo in devio.c may call us with id == NULL. */
+	if (id == NULL)
+		return NULL;
+
+	intf = interface->cur_altsetting;
+	dev = interface_to_usbdev(interface);
+
+	/* It is important to check that id->driver_info is nonzero,
+	   since an entry that is all zeroes except for a nonzero
+	   id->driver_info is the way to create an entry that
+	   indicates that the driver want to examine every
+	   device and interface. */
+	for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
+	       id->driver_info; id++) {
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+		    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+		    id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
+			continue;
+
+		/* No need to test id->bcdDevice_lo != 0, since 0 is never
+		   greater than any unsigned number. */
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+		    (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+		    (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+		    (id->bDeviceClass != dev->descriptor.bDeviceClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+		    (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+		    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+		    (id->bInterfaceClass != intf->desc.bInterfaceClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+		    (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
+			continue;
+
+		if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+		    (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
+			continue;
+
+		return id;
+	}
+
+	return NULL;
+}
+
+/**
+ * usb_find_interface - find usb_interface pointer for driver and device
+ * @drv: the driver whose current configuration is considered
+ * @minor: the minor number of the desired device
+ *
+ * This walks the driver device list and returns a pointer to the interface 
+ * with the matching minor.  Note, this only works for devices that share the
+ * USB major number.
+ */
+struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
+{
+	struct list_head *entry;
+	struct device *dev;
+	struct usb_interface *intf;
+
+	list_for_each(entry, &drv->driver.devices) {
+		dev = container_of(entry, struct device, driver_list);
+
+		/* can't look at usb devices, only interfaces */
+		if (dev->driver == &usb_generic_driver)
+			continue;
+
+		intf = to_usb_interface(dev);
+		if (intf->minor == -1)
+			continue;
+		if (intf->minor == minor)
+			return intf;
+	}
+
+	/* no device found that matches */
+	return NULL;	
+}
+
+static int usb_device_match (struct device *dev, struct device_driver *drv)
+{
+	struct usb_interface *intf;
+	struct usb_driver *usb_drv;
+	const struct usb_device_id *id;
+
+	/* check for generic driver, which we don't match any device with */
+	if (drv == &usb_generic_driver)
+		return 0;
+
+	intf = to_usb_interface(dev);
+	usb_drv = to_usb_driver(drv);
+	
+	id = usb_match_id (intf, usb_drv->id_table);
+	if (id)
+		return 1;
+
+	return 0;
+}
+
+
+#ifdef	CONFIG_HOTPLUG
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ *
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, and more.  Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_hotplug (struct device *dev, char **envp, int num_envp,
+			char *buffer, int buffer_size)
+{
+	struct usb_interface *intf;
+	struct usb_device *usb_dev;
+	int i = 0;
+	int length = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	/* driver is often null here; dev_dbg() would oops */
+	pr_debug ("usb %s: hotplug\n", dev->bus_id);
+
+	/* Must check driver_data here, as on remove driver is always NULL */
+	if ((dev->driver == &usb_generic_driver) || 
+	    (dev->driver_data == &usb_generic_driver_data))
+		return 0;
+
+	intf = to_usb_interface(dev);
+	usb_dev = interface_to_usbdev (intf);
+	
+	if (usb_dev->devnum < 0) {
+		pr_debug ("usb %s: already deleted?\n", dev->bus_id);
+		return -ENODEV;
+	}
+	if (!usb_dev->bus) {
+		pr_debug ("usb %s: bus removed?\n", dev->bus_id);
+		return -ENODEV;
+	}
+
+#ifdef	CONFIG_USB_DEVICEFS
+	/* If this is available, userspace programs can directly read
+	 * all the device descriptors we don't tell them about.  Or
+	 * even act as usermode drivers.
+	 *
+	 * FIXME reduce hardwired intelligence here
+	 */
+	if (add_hotplug_env_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"DEVICE=/proc/bus/usb/%03d/%03d",
+				usb_dev->bus->busnum, usb_dev->devnum))
+		return -ENOMEM;
+#endif
+
+	/* per-device configurations are common */
+	if (add_hotplug_env_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"PRODUCT=%x/%x/%x",
+				le16_to_cpu(usb_dev->descriptor.idVendor),
+				le16_to_cpu(usb_dev->descriptor.idProduct),
+				le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+		return -ENOMEM;
+
+	/* class-based driver binding models */
+	if (add_hotplug_env_var(envp, num_envp, &i,
+				buffer, buffer_size, &length,
+				"TYPE=%d/%d/%d",
+				usb_dev->descriptor.bDeviceClass,
+				usb_dev->descriptor.bDeviceSubClass,
+				usb_dev->descriptor.bDeviceProtocol))
+		return -ENOMEM;
+
+	if (usb_dev->descriptor.bDeviceClass == 0) {
+		struct usb_host_interface *alt = intf->cur_altsetting;
+
+		/* 2.4 only exposed interface zero.  in 2.5, hotplug
+		 * agents are called for all interfaces, and can use
+		 * $DEVPATH/bInterfaceNumber if necessary.
+		 */
+		if (add_hotplug_env_var(envp, num_envp, &i,
+					buffer, buffer_size, &length,
+					"INTERFACE=%d/%d/%d",
+					alt->desc.bInterfaceClass,
+					alt->desc.bInterfaceSubClass,
+					alt->desc.bInterfaceProtocol))
+			return -ENOMEM;
+
+		if (add_hotplug_env_var(envp, num_envp, &i,
+					buffer, buffer_size, &length,
+					"MODALIAS=usb:v%04Xp%04Xdl%04Xdh%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+					le16_to_cpu(usb_dev->descriptor.idVendor),
+					le16_to_cpu(usb_dev->descriptor.idProduct),
+					le16_to_cpu(usb_dev->descriptor.bcdDevice),
+					le16_to_cpu(usb_dev->descriptor.bcdDevice),
+					usb_dev->descriptor.bDeviceClass,
+					usb_dev->descriptor.bDeviceSubClass,
+					usb_dev->descriptor.bDeviceProtocol,
+					alt->desc.bInterfaceClass,
+					alt->desc.bInterfaceSubClass,
+					alt->desc.bInterfaceProtocol))
+			return -ENOMEM;
+ 	} else {
+		if (add_hotplug_env_var(envp, num_envp, &i,
+					buffer, buffer_size, &length,
+					"MODALIAS=usb:v%04Xp%04Xdl%04Xdh%04Xdc%02Xdsc%02Xdp%02Xic*isc*ip*",
+					le16_to_cpu(usb_dev->descriptor.idVendor),
+					le16_to_cpu(usb_dev->descriptor.idProduct),
+					le16_to_cpu(usb_dev->descriptor.bcdDevice),
+					le16_to_cpu(usb_dev->descriptor.bcdDevice),
+					usb_dev->descriptor.bDeviceClass,
+					usb_dev->descriptor.bDeviceSubClass,
+					usb_dev->descriptor.bDeviceProtocol))
+			return -ENOMEM;
+	}
+
+	envp[i] = NULL;
+
+	return 0;
+}
+
+#else
+
+static int usb_hotplug (struct device *dev, char **envp,
+			int num_envp, char *buffer, int buffer_size)
+{
+	return -ENODEV;
+}
+
+#endif	/* CONFIG_HOTPLUG */
+
+/**
+ * usb_release_dev - free a usb device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this usb device are
+ * done.
+ */
+static void usb_release_dev(struct device *dev)
+{
+	struct usb_device *udev;
+
+	udev = to_usb_device(dev);
+
+	usb_destroy_configuration(udev);
+	usb_bus_put(udev->bus);
+	kfree(udev->product);
+	kfree(udev->manufacturer);
+	kfree(udev->serial);
+	kfree(udev);
+}
+
+/**
+ * usb_alloc_dev - usb device constructor (usbcore-internal)
+ * @parent: hub to which device is connected; null to allocate a root hub
+ * @bus: bus used to access the device
+ * @port1: one-based index of port; ignored for root hubs
+ * Context: !in_interrupt ()
+ *
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ *
+ * This call may not be used in a non-sleeping context.
+ */
+struct usb_device *
+usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
+{
+	struct usb_device *dev;
+
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	memset(dev, 0, sizeof(*dev));
+
+	bus = usb_bus_get(bus);
+	if (!bus) {
+		kfree(dev);
+		return NULL;
+	}
+
+	device_initialize(&dev->dev);
+	dev->dev.bus = &usb_bus_type;
+	dev->dev.dma_mask = bus->controller->dma_mask;
+	dev->dev.driver_data = &usb_generic_driver_data;
+	dev->dev.driver = &usb_generic_driver;
+	dev->dev.release = usb_release_dev;
+	dev->state = USB_STATE_ATTACHED;
+
+	INIT_LIST_HEAD(&dev->ep0.urb_list);
+	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
+	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
+	/* ep0 maxpacket comes later, from device descriptor */
+	dev->ep_in[0] = dev->ep_out[0] = &dev->ep0;
+
+	/* Save readable and stable topology id, distinguishing devices
+	 * by location for diagnostics, tools, driver model, etc.  The
+	 * string is a path along hub ports, from the root.  Each device's
+	 * dev->devpath will be stable until USB is re-cabled, and hubs
+	 * are often labeled with these port numbers.  The bus_id isn't
+	 * as stable:  bus->busnum changes easily from modprobe order,
+	 * cardbus or pci hotplugging, and so on.
+	 */
+	if (unlikely (!parent)) {
+		dev->devpath [0] = '0';
+
+		dev->dev.parent = bus->controller;
+		sprintf (&dev->dev.bus_id[0], "usb%d", bus->busnum);
+	} else {
+		/* match any labeling on the hubs; it's one-based */
+		if (parent->devpath [0] == '0')
+			snprintf (dev->devpath, sizeof dev->devpath,
+				"%d", port1);
+		else
+			snprintf (dev->devpath, sizeof dev->devpath,
+				"%s.%d", parent->devpath, port1);
+
+		dev->dev.parent = &parent->dev;
+		sprintf (&dev->dev.bus_id[0], "%d-%s",
+			bus->busnum, dev->devpath);
+
+		/* hub driver sets up TT records */
+	}
+
+	dev->bus = bus;
+	dev->parent = parent;
+	INIT_LIST_HEAD(&dev->filelist);
+
+	init_MUTEX(&dev->serialize);
+
+	return dev;
+}
+
+/**
+ * usb_get_dev - increments the reference count of the usb device structure
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them by calling usb_put_dev(), in their disconnect() methods.
+ *
+ * A pointer to the device with the incremented reference counter is returned.
+ */
+struct usb_device *usb_get_dev(struct usb_device *dev)
+{
+	if (dev)
+		get_device(&dev->dev);
+	return dev;
+}
+
+/**
+ * usb_put_dev - release a use of the usb device structure
+ * @dev: device that's been disconnected
+ *
+ * Must be called when a user of a device is finished with it.  When the last
+ * user of the device calls this function, the memory of the device is freed.
+ */
+void usb_put_dev(struct usb_device *dev)
+{
+	if (dev)
+		put_device(&dev->dev);
+}
+
+/**
+ * usb_get_intf - increments the reference count of the usb interface structure
+ * @intf: the interface being referenced
+ *
+ * Each live reference to a interface must be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them by calling usb_put_intf(), in their disconnect() methods.
+ *
+ * A pointer to the interface with the incremented reference counter is
+ * returned.
+ */
+struct usb_interface *usb_get_intf(struct usb_interface *intf)
+{
+	if (intf)
+		get_device(&intf->dev);
+	return intf;
+}
+
+/**
+ * usb_put_intf - release a use of the usb interface structure
+ * @intf: interface that's been decremented
+ *
+ * Must be called when a user of an interface is finished with it.  When the
+ * last user of the interface calls this function, the memory of the interface
+ * is freed.
+ */
+void usb_put_intf(struct usb_interface *intf)
+{
+	if (intf)
+		put_device(&intf->dev);
+}
+
+
+/*			USB device locking
+ *
+ * Although locking USB devices should be straightforward, it is
+ * complicated by the way the driver-model core works.  When a new USB
+ * driver is registered or unregistered, the core will automatically
+ * probe or disconnect all matching interfaces on all USB devices while
+ * holding the USB subsystem writelock.  There's no good way for us to
+ * tell which devices will be used or to lock them beforehand; our only
+ * option is to effectively lock all the USB devices.
+ *
+ * We do that by using a private rw-semaphore, usb_all_devices_rwsem.
+ * When locking an individual device you must first acquire the rwsem's
+ * readlock.  When a driver is registered or unregistered the writelock
+ * must be held.  These actions are encapsulated in the subroutines
+ * below, so all a driver needs to do is call usb_lock_device() and
+ * usb_unlock_device().
+ *
+ * Complications arise when several devices are to be locked at the same
+ * time.  Only hub-aware drivers that are part of usbcore ever have to
+ * do this; nobody else needs to worry about it.  The problem is that
+ * usb_lock_device() must not be called to lock a second device since it
+ * would acquire the rwsem's readlock reentrantly, leading to deadlock if
+ * another thread was waiting for the writelock.  The solution is simple:
+ *
+ *	When locking more than one device, call usb_lock_device()
+ *	to lock the first one.  Lock the others by calling
+ *	down(&udev->serialize) directly.
+ *
+ *	When unlocking multiple devices, use up(&udev->serialize)
+ *	to unlock all but the last one.  Unlock the last one by
+ *	calling usb_unlock_device().
+ *
+ *	When locking both a device and its parent, always lock the
+ *	the parent first.
+ */
+
+/**
+ * usb_lock_device - acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Use this routine when you don't hold any other device locks;
+ * to acquire nested inner locks call down(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_lock_device(struct usb_device *udev)
+{
+	down_read(&usb_all_devices_rwsem);
+	down(&udev->serialize);
+}
+
+/**
+ * usb_trylock_device - attempt to acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Don't use this routine if you already hold a device lock;
+ * use down_trylock(&udev->serialize) instead.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ *
+ * Returns 1 if successful, 0 if contention.
+ */
+int usb_trylock_device(struct usb_device *udev)
+{
+	if (!down_read_trylock(&usb_all_devices_rwsem))
+		return 0;
+	if (down_trylock(&udev->serialize)) {
+		up_read(&usb_all_devices_rwsem);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * usb_lock_device_for_reset - cautiously acquire the lock for a
+ *	usb device structure
+ * @udev: device that's being locked
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Attempts to acquire the device lock, but fails if the device is
+ * NOTATTACHED or SUSPENDED, or if iface is specified and the interface
+ * is neither BINDING nor BOUND.  Rather than sleeping to wait for the
+ * lock, the routine polls repeatedly.  This is to prevent deadlock with
+ * disconnect; in some drivers (such as usb-storage) the disconnect()
+ * callback will block waiting for a device reset to complete.
+ *
+ * Returns a negative error code for failure, otherwise 1 or 0 to indicate
+ * that the device will or will not have to be unlocked.  (0 can be
+ * returned when an interface is given and is BINDING, because in that
+ * case the driver already owns the device lock.)
+ */
+int usb_lock_device_for_reset(struct usb_device *udev,
+		struct usb_interface *iface)
+{
+	if (udev->state == USB_STATE_NOTATTACHED)
+		return -ENODEV;
+	if (udev->state == USB_STATE_SUSPENDED)
+		return -EHOSTUNREACH;
+	if (iface) {
+		switch (iface->condition) {
+		  case USB_INTERFACE_BINDING:
+			return 0;
+		  case USB_INTERFACE_BOUND:
+			break;
+		  default:
+			return -EINTR;
+		}
+	}
+
+	while (!usb_trylock_device(udev)) {
+		msleep(15);
+		if (udev->state == USB_STATE_NOTATTACHED)
+			return -ENODEV;
+		if (udev->state == USB_STATE_SUSPENDED)
+			return -EHOSTUNREACH;
+		if (iface && iface->condition != USB_INTERFACE_BOUND)
+			return -EINTR;
+	}
+	return 1;
+}
+
+/**
+ * usb_unlock_device - release the lock for a usb device structure
+ * @udev: device that's being unlocked
+ *
+ * Use this routine when releasing the only device lock you hold;
+ * to release inner nested locks call up(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_unlock_device(struct usb_device *udev)
+{
+	up(&udev->serialize);
+	up_read(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_lock_all_devices - acquire the lock for all usb device structures
+ *
+ * This is necessary when registering a new driver or probing a bus,
+ * since the driver-model core may try to use any usb_device.
+ */
+void usb_lock_all_devices(void)
+{
+	down_write(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_unlock_all_devices - release the lock for all usb device structures
+ */
+void usb_unlock_all_devices(void)
+{
+	up_write(&usb_all_devices_rwsem);
+}
+
+
+static struct usb_device *match_device(struct usb_device *dev,
+				       u16 vendor_id, u16 product_id)
+{
+	struct usb_device *ret_dev = NULL;
+	int child;
+
+	dev_dbg(&dev->dev, "check for vendor %04x, product %04x ...\n",
+	    le16_to_cpu(dev->descriptor.idVendor),
+	    le16_to_cpu(dev->descriptor.idProduct));
+
+	/* see if this device matches */
+	if ((vendor_id == le16_to_cpu(dev->descriptor.idVendor)) &&
+	    (product_id == le16_to_cpu(dev->descriptor.idProduct))) {
+		dev_dbg (&dev->dev, "matched this device!\n");
+		ret_dev = usb_get_dev(dev);
+		goto exit;
+	}
+
+	/* look through all of the children of this device */
+	for (child = 0; child < dev->maxchild; ++child) {
+		if (dev->children[child]) {
+			down(&dev->children[child]->serialize);
+			ret_dev = match_device(dev->children[child],
+					       vendor_id, product_id);
+			up(&dev->children[child]->serialize);
+			if (ret_dev)
+				goto exit;
+		}
+	}
+exit:
+	return ret_dev;
+}
+
+/**
+ * usb_find_device - find a specific usb device in the system
+ * @vendor_id: the vendor id of the device to find
+ * @product_id: the product id of the device to find
+ *
+ * Returns a pointer to a struct usb_device if such a specified usb
+ * device is present in the system currently.  The usage count of the
+ * device will be incremented if a device is found.  Make sure to call
+ * usb_put_dev() when the caller is finished with the device.
+ *
+ * If a device with the specified vendor and product id is not found,
+ * NULL is returned.
+ */
+struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
+{
+	struct list_head *buslist;
+	struct usb_bus *bus;
+	struct usb_device *dev = NULL;
+	
+	down(&usb_bus_list_lock);
+	for (buslist = usb_bus_list.next;
+	     buslist != &usb_bus_list; 
+	     buslist = buslist->next) {
+		bus = container_of(buslist, struct usb_bus, bus_list);
+		if (!bus->root_hub)
+			continue;
+		usb_lock_device(bus->root_hub);
+		dev = match_device(bus->root_hub, vendor_id, product_id);
+		usb_unlock_device(bus->root_hub);
+		if (dev)
+			goto exit;
+	}
+exit:
+	up(&usb_bus_list_lock);
+	return dev;
+}
+
+/**
+ * usb_get_current_frame_number - return current bus frame number
+ * @dev: the device whose bus is being queried
+ *
+ * Returns the current frame number for the USB host controller
+ * used with the given USB device.  This can be used when scheduling
+ * isochronous requests.
+ *
+ * Note that different kinds of host controller have different
+ * "scheduling horizons".  While one type might support scheduling only
+ * 32 frames into the future, others could support scheduling up to
+ * 1024 frames into the future.
+ */
+int usb_get_current_frame_number(struct usb_device *dev)
+{
+	return dev->bus->op->get_frame_number (dev);
+}
+
+/*-------------------------------------------------------------------*/
+/*
+ * __usb_get_extra_descriptor() finds a descriptor of specific type in the
+ * extra field of the interface and endpoint descriptor structs.
+ */
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size,
+	unsigned char type, void **ptr)
+{
+	struct usb_descriptor_header *header;
+
+	while (size >= sizeof(struct usb_descriptor_header)) {
+		header = (struct usb_descriptor_header *)buffer;
+
+		if (header->bLength < 2) {
+			printk(KERN_ERR
+				"%s: bogus descriptor, type %d length %d\n",
+				usbcore_name,
+				header->bDescriptorType, 
+				header->bLength);
+			return -1;
+		}
+
+		if (header->bDescriptorType == type) {
+			*ptr = header;
+			return 0;
+		}
+
+		buffer += header->bLength;
+		size -= header->bLength;
+	}
+	return -1;
+}
+
+/**
+ * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ *
+ * Return value is either null (indicating no buffer could be allocated), or
+ * the cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device.  Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
+ *
+ * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags
+ * to avoid behaviors like using "DMA bounce buffers", or tying down I/O
+ * mapping hardware for long idle periods.  The implementation varies between
+ * platforms, depending on details of how DMA will work to this device.
+ * Using these buffers also helps prevent cacheline sharing problems on
+ * architectures where CPU caches are not DMA-coherent.
+ *
+ * When the buffer is no longer used, free it with usb_buffer_free().
+ */
+void *usb_buffer_alloc (
+	struct usb_device *dev,
+	size_t size,
+	int mem_flags,
+	dma_addr_t *dma
+)
+{
+	if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+		return NULL;
+	return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+}
+
+/**
+ * usb_buffer_free - free memory allocated with usb_buffer_alloc()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dma: DMA address of buffer
+ *
+ * This reclaims an I/O buffer, letting it be reused.  The memory must have
+ * been allocated using usb_buffer_alloc(), and the parameters must match
+ * those provided in that allocation request. 
+ */
+void usb_buffer_free (
+	struct usb_device *dev,
+	size_t size,
+	void *addr,
+	dma_addr_t dma
+)
+{
+	if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
+	    	return;
+	dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+}
+
+/**
+ * usb_buffer_map - create DMA mapping(s) for an urb
+ * @urb: urb whose transfer_buffer/setup_packet will be mapped
+ *
+ * Return value is either null (indicating no buffer could be mapped), or
+ * the parameter.  URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are
+ * added to urb->transfer_flags if the operation succeeds.  If the device
+ * is connected to this system through a non-DMA controller, this operation
+ * always succeeds.
+ *
+ * This call would normally be used for an urb which is reused, perhaps
+ * as the target of a large periodic transfer, with usb_buffer_dmasync()
+ * calls to synchronize memory and dma state.
+ *
+ * Reverse the effect of this call with usb_buffer_unmap().
+ */
+#if 0
+struct urb *usb_buffer_map (struct urb *urb)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!urb
+			|| !urb->dev
+			|| !(bus = urb->dev->bus)
+			|| !(controller = bus->controller))
+		return NULL;
+
+	if (controller->dma_mask) {
+		urb->transfer_dma = dma_map_single (controller,
+			urb->transfer_buffer, urb->transfer_buffer_length,
+			usb_pipein (urb->pipe)
+				? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		if (usb_pipecontrol (urb->pipe))
+			urb->setup_dma = dma_map_single (controller,
+					urb->setup_packet,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+	// FIXME generic api broken like pci, can't report errors
+	// if (urb->transfer_dma == DMA_ADDR_INVALID) return 0;
+	} else
+		urb->transfer_dma = ~0;
+	urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
+				| URB_NO_SETUP_DMA_MAP);
+	return urb;
+}
+#endif  /*  0  */
+
+/* XXX DISABLED, no users currently.  If you wish to re-enable this
+ * XXX please determine whether the sync is to transfer ownership of
+ * XXX the buffer from device to cpu or vice verse, and thusly use the
+ * XXX appropriate _for_{cpu,device}() method.  -DaveM
+ */
+#if 0
+
+/**
+ * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
+ * @urb: urb whose transfer_buffer/setup_packet will be synchronized
+ */
+void usb_buffer_dmasync (struct urb *urb)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!urb
+			|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+			|| !urb->dev
+			|| !(bus = urb->dev->bus)
+			|| !(controller = bus->controller))
+		return;
+
+	if (controller->dma_mask) {
+		dma_sync_single (controller,
+			urb->transfer_dma, urb->transfer_buffer_length,
+			usb_pipein (urb->pipe)
+				? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		if (usb_pipecontrol (urb->pipe))
+			dma_sync_single (controller,
+					urb->setup_dma,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+	}
+}
+#endif
+
+/**
+ * usb_buffer_unmap - free DMA mapping(s) for an urb
+ * @urb: urb whose transfer_buffer will be unmapped
+ *
+ * Reverses the effect of usb_buffer_map().
+ */
+#if 0
+void usb_buffer_unmap (struct urb *urb)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!urb
+			|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+			|| !urb->dev
+			|| !(bus = urb->dev->bus)
+			|| !(controller = bus->controller))
+		return;
+
+	if (controller->dma_mask) {
+		dma_unmap_single (controller,
+			urb->transfer_dma, urb->transfer_buffer_length,
+			usb_pipein (urb->pipe)
+				? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		if (usb_pipecontrol (urb->pipe))
+			dma_unmap_single (controller,
+					urb->setup_dma,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+	}
+	urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP
+				| URB_NO_SETUP_DMA_MAP);
+}
+#endif  /*  0  */
+
+/**
+ * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to map
+ * @nents: the number of entries in the scatterlist
+ *
+ * Return value is either < 0 (indicating no buffers could be mapped), or
+ * the number of DMA mapping array entries in the scatterlist.
+ *
+ * The caller is responsible for placing the resulting DMA addresses from
+ * the scatterlist into URB transfer buffer pointers, and for setting the
+ * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
+ *
+ * Top I/O rates come from queuing URBs, instead of waiting for each one
+ * to complete before starting the next I/O.   This is particularly easy
+ * to do with scatterlists.  Just allocate and submit one URB for each DMA
+ * mapping entry returned, stopping on the first error or when all succeed.
+ * Better yet, use the usb_sg_*() calls, which do that (and more) for you.
+ *
+ * This call would normally be used when translating scatterlist requests,
+ * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
+ * may be able to coalesce mappings for improved I/O efficiency.
+ *
+ * Reverse the effect of this call with usb_buffer_unmap_sg().
+ */
+int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
+		struct scatterlist *sg, int nents)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!dev
+			|| usb_pipecontrol (pipe)
+			|| !(bus = dev->bus)
+			|| !(controller = bus->controller)
+			|| !controller->dma_mask)
+		return -1;
+
+	// FIXME generic api broken like pci, can't report errors
+	return dma_map_sg (controller, sg, nents,
+			usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+/* XXX DISABLED, no users currently.  If you wish to re-enable this
+ * XXX please determine whether the sync is to transfer ownership of
+ * XXX the buffer from device to cpu or vice verse, and thusly use the
+ * XXX appropriate _for_{cpu,device}() method.  -DaveM
+ */
+#if 0
+
+/**
+ * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to synchronize
+ * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ *
+ * Use this when you are re-using a scatterlist's data buffers for
+ * another USB request.
+ */
+void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
+		struct scatterlist *sg, int n_hw_ents)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!dev
+			|| !(bus = dev->bus)
+			|| !(controller = bus->controller)
+			|| !controller->dma_mask)
+		return;
+
+	dma_sync_sg (controller, sg, n_hw_ents,
+			usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+#endif
+
+/**
+ * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to unmap
+ * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ *
+ * Reverses the effect of usb_buffer_map_sg().
+ */
+void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
+		struct scatterlist *sg, int n_hw_ents)
+{
+	struct usb_bus		*bus;
+	struct device		*controller;
+
+	if (!dev
+			|| !(bus = dev->bus)
+			|| !(controller = bus->controller)
+			|| !controller->dma_mask)
+		return;
+
+	dma_unmap_sg (controller, sg, n_hw_ents,
+			usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+static int usb_generic_suspend(struct device *dev, u32 state)
+{
+	struct usb_interface *intf;
+	struct usb_driver *driver;
+
+	if (dev->driver == &usb_generic_driver)
+		return usb_suspend_device (to_usb_device(dev), state);
+
+	if ((dev->driver == NULL) ||
+	    (dev->driver_data == &usb_generic_driver_data))
+		return 0;
+
+	intf = to_usb_interface(dev);
+	driver = to_usb_driver(dev->driver);
+
+	/* there's only one USB suspend state */
+	if (intf->dev.power.power_state)
+		return 0;
+
+	if (driver->suspend)
+		return driver->suspend(intf, state);
+	return 0;
+}
+
+static int usb_generic_resume(struct device *dev)
+{
+	struct usb_interface *intf;
+	struct usb_driver *driver;
+
+	/* devices resume through their hub */
+	if (dev->driver == &usb_generic_driver)
+		return usb_resume_device (to_usb_device(dev));
+
+	if ((dev->driver == NULL) ||
+	    (dev->driver_data == &usb_generic_driver_data))
+		return 0;
+
+	intf = to_usb_interface(dev);
+	driver = to_usb_driver(dev->driver);
+
+	if (driver->resume)
+		return driver->resume(intf);
+	return 0;
+}
+
+struct bus_type usb_bus_type = {
+	.name =		"usb",
+	.match =	usb_device_match,
+	.hotplug =	usb_hotplug,
+	.suspend =	usb_generic_suspend,
+	.resume =	usb_generic_resume,
+};
+
+#ifndef MODULE
+
+static int __init usb_setup_disable(char *str)
+{
+	nousb = 1;
+	return 1;
+}
+
+/* format to disable USB on kernel command line is: nousb */
+__setup("nousb", usb_setup_disable);
+
+#endif
+
+/*
+ * for external read access to <nousb>
+ */
+int usb_disabled(void)
+{
+	return nousb;
+}
+
+/*
+ * Init
+ */
+static int __init usb_init(void)
+{
+	int retval;
+	if (nousb) {
+		pr_info ("%s: USB support disabled\n", usbcore_name);
+		return 0;
+	}
+
+	retval = bus_register(&usb_bus_type);
+	if (retval) 
+		goto out;
+	retval = usb_host_init();
+	if (retval)
+		goto host_init_failed;
+	retval = usb_major_init();
+	if (retval)
+		goto major_init_failed;
+	retval = usbfs_init();
+	if (retval)
+		goto fs_init_failed;
+	retval = usb_hub_init();
+	if (retval)
+		goto hub_init_failed;
+
+	retval = driver_register(&usb_generic_driver);
+	if (!retval)
+		goto out;
+
+	usb_hub_cleanup();
+hub_init_failed:
+	usbfs_cleanup();
+fs_init_failed:
+	usb_major_cleanup();	
+major_init_failed:
+	usb_host_cleanup();
+host_init_failed:
+	bus_unregister(&usb_bus_type);
+out:
+	return retval;
+}
+
+/*
+ * Cleanup
+ */
+static void __exit usb_exit(void)
+{
+	/* This will matter if shutdown/reboot does exitcalls. */
+	if (nousb)
+		return;
+
+	driver_unregister(&usb_generic_driver);
+	usb_major_cleanup();
+	usbfs_cleanup();
+	usb_hub_cleanup();
+	usb_host_cleanup();
+	bus_unregister(&usb_bus_type);
+}
+
+subsys_initcall(usb_init);
+module_exit(usb_exit);
+
+/*
+ * USB may be built into the kernel or be built as modules.
+ * These symbols are exported for device (or host controller)
+ * driver modules to use.
+ */
+
+EXPORT_SYMBOL(usb_register);
+EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL(usb_disabled);
+
+EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL(usb_put_dev);
+EXPORT_SYMBOL(usb_get_dev);
+EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
+
+EXPORT_SYMBOL(usb_lock_device);
+EXPORT_SYMBOL(usb_trylock_device);
+EXPORT_SYMBOL(usb_lock_device_for_reset);
+EXPORT_SYMBOL(usb_unlock_device);
+
+EXPORT_SYMBOL(usb_driver_claim_interface);
+EXPORT_SYMBOL(usb_driver_release_interface);
+EXPORT_SYMBOL(usb_match_id);
+EXPORT_SYMBOL(usb_find_interface);
+EXPORT_SYMBOL(usb_ifnum_to_if);
+EXPORT_SYMBOL(usb_altnum_to_altsetting);
+
+EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(__usb_get_extra_descriptor);
+
+EXPORT_SYMBOL(usb_find_device);
+EXPORT_SYMBOL(usb_get_current_frame_number);
+
+EXPORT_SYMBOL (usb_buffer_alloc);
+EXPORT_SYMBOL (usb_buffer_free);
+
+#if 0
+EXPORT_SYMBOL (usb_buffer_map);
+EXPORT_SYMBOL (usb_buffer_dmasync);
+EXPORT_SYMBOL (usb_buffer_unmap);
+#endif
+
+EXPORT_SYMBOL (usb_buffer_map_sg);
+#if 0
+EXPORT_SYMBOL (usb_buffer_dmasync_sg);
+#endif
+EXPORT_SYMBOL (usb_buffer_unmap_sg);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
new file mode 100644
index 0000000..4c33eee
--- /dev/null
+++ b/drivers/usb/core/usb.h
@@ -0,0 +1,46 @@
+/* Functions local to drivers/usb/core/ */
+
+extern void usb_create_sysfs_dev_files (struct usb_device *dev);
+extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
+extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
+extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
+
+extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
+extern void usb_disable_interface (struct usb_device *dev,
+		struct usb_interface *intf);
+extern void usb_release_interface_cache(struct kref *ref);
+extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+
+extern int usb_get_device_descriptor(struct usb_device *dev,
+		unsigned int size);
+extern int usb_set_configuration(struct usb_device *dev, int configuration);
+
+extern void usb_lock_all_devices(void);
+extern void usb_unlock_all_devices(void);
+
+extern void usb_kick_khubd(struct usb_device *dev);
+extern void usb_resume_root_hub(struct usb_device *dev);
+
+/* for labeling diagnostics */
+extern const char *usbcore_name;
+
+/* usbfs stuff */
+extern struct usb_driver usbfs_driver;
+extern struct file_operations usbfs_devices_fops;
+extern struct file_operations usbfs_device_file_operations;
+extern void usbfs_conn_disc_event(void);
+
+struct dev_state {
+	struct list_head list;      /* state list */
+	struct usb_device *dev;
+	struct file *file;
+	spinlock_t lock;            /* protects the async urb lists */
+	struct list_head async_pending;
+	struct list_head async_completed;
+	wait_queue_head_t wait;     /* wake up if a request completed */
+	unsigned int discsignr;
+	struct task_struct *disctask;
+	void __user *disccontext;
+	unsigned long ifclaimed;
+};
+