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-