aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Kconfig99
-rw-r--r--drivers/usb/core/Makefile16
-rw-r--r--drivers/usb/core/buffer.c154
-rw-r--r--drivers/usb/core/config.c534
-rw-r--r--drivers/usb/core/devices.c677
-rw-r--r--drivers/usb/core/devio.c1483
-rw-r--r--drivers/usb/core/file.c227
-rw-r--r--drivers/usb/core/hcd-pci.c358
-rw-r--r--drivers/usb/core/hcd.c1840
-rw-r--r--drivers/usb/core/hcd.h476
-rw-r--r--drivers/usb/core/hub.c3057
-rw-r--r--drivers/usb/core/hub.h238
-rw-r--r--drivers/usb/core/inode.c764
-rw-r--r--drivers/usb/core/message.c1480
-rw-r--r--drivers/usb/core/otg_whitelist.h112
-rw-r--r--drivers/usb/core/sysfs.c318
-rw-r--r--drivers/usb/core/urb.c511
-rw-r--r--drivers/usb/core/usb.c1573
-rw-r--r--drivers/usb/core/usb.h46
19 files changed, 13963 insertions, 0 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
new file mode 100644
index 00000000000..1a9ff618494
--- /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 00000000000..9e8c377b816
--- /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 00000000000..b7827df21f4
--- /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 00000000000..0b092bdf98f
--- /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 00000000000..8b61bcd742c
--- /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 00000000000..a047bc39298
--- /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 00000000000..80ce9644d0e
--- /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 00000000000..b9a3dae0703
--- /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 00000000000..266e9e06a9f
--- /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 00000000000..6c625b35fa0
--- /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 00000000000..fa0dc4f6de4
--- /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 00000000000..d114b847d56
--- /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 00000000000..d913407bcdc
--- /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 00000000000..40bdb38e7bc
--- /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 00000000000..627a5a2fc9c
--- /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 00000000000..ec9b3bde8ae
--- /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 00000000000..dc838f81742
--- /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 00000000000..f0534ee0649
--- /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 00000000000..4c33eee5200
--- /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;
+};
+