diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/core | |
download | op-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip op-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 99 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 16 | ||||
-rw-r--r-- | drivers/usb/core/buffer.c | 154 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 534 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 677 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 1483 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 227 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 358 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 1840 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 476 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 3057 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 238 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 764 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 1480 | ||||
-rw-r--r-- | drivers/usb/core/otg_whitelist.h | 112 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 318 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 511 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1573 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 46 |
19 files changed, 13963 insertions, 0 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig new file mode 100644 index 0000000..1a9ff61 --- /dev/null +++ b/drivers/usb/core/Kconfig @@ -0,0 +1,99 @@ +# +# USB Core configuration +# +config USB_DEBUG + bool "USB verbose debug messages" + depends on USB + help + Say Y here if you want the USB core & hub drivers to produce a bunch + of debug messages to the system log. Select this if you are having a + problem with USB support and want to see more of what is going on. + +comment "Miscellaneous USB options" + depends on USB + +config USB_DEVICEFS + bool "USB device filesystem" + depends on USB + ---help--- + If you say Y here (and to "/proc file system support" in the "File + systems" section, above), you will get a file /proc/bus/usb/devices + which lists the devices currently connected to your USB bus or + busses, and for every connected device a file named + "/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the + device number; the latter files can be used by user space programs + to talk directly to the device. These files are "virtual", meaning + they are generated on the fly and not stored on the hard drive. + + You may need to mount the usbfs file system to see the files, use + mount -t usbfs none /proc/bus/usb + + For the format of the various /proc/bus/usb/ files, please read + <file:Documentation/usb/proc_usb_info.txt>. + + Please note that this code is completely unrelated to devfs, the + "/dev file system support". + + Most users want to say Y here. + +config USB_BANDWIDTH + bool "Enforce USB bandwidth allocation (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + If you say Y here, the USB subsystem enforces USB bandwidth + allocation and will prevent some device opens from succeeding + if they would cause USB bandwidth usage to go above 90% of + the bus bandwidth. + + If you say N here, these conditions will cause warning messages + about USB bandwidth usage to be logged and some devices or + drivers may not work correctly. + +config USB_DYNAMIC_MINORS + bool "Dynamic USB minor allocation (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + If you say Y here, the USB subsystem will use dynamic minor + allocation for any device that uses the USB major number. + This means that you can have more than 16 of a single type + of device (like USB printers). + + If you are unsure about this, say N here. + +config USB_SUSPEND + bool "USB suspend/resume (EXPERIMENTAL)" + depends on USB && PM && EXPERIMENTAL + help + If you say Y here, you can use driver calls or the sysfs + "power/state" file to suspend or resume individual USB + peripherals. There are many related features, such as + remote wakeup and driver-specific suspend processing, that + may not yet work as expected. + + If you are unsure about this, say N here. + + +config USB_OTG + bool + depends on USB && EXPERIMENTAL + select USB_SUSPEND + default n + + +config USB_OTG_WHITELIST + bool "Rely on OTG Targeted Peripherals List" + depends on USB_OTG + default y + help + If you say Y here, the "otg_whitelist.h" file will be used as a + product whitelist, so USB peripherals not listed there will be + rejected during enumeration. This behavior is required by the + USB OTG specification for all devices not on your product's + "Targeted Peripherals List". + + Otherwise, peripherals not listed there will only generate a + warning and enumeration will continue. That's more like what + normal Linux-USB hosts do (other than the warning), and is + convenient for many stages of product development. + + diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile new file mode 100644 index 0000000..9e8c377 --- /dev/null +++ b/drivers/usb/core/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for USB Core files and filesystem +# + +usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ + config.o file.o buffer.o sysfs.o + +ifeq ($(CONFIG_PCI),y) + usbcore-objs += hcd-pci.o +endif + +ifeq ($(CONFIG_USB_DEVICEFS),y) + usbcore-objs += devio.o inode.o devices.o +endif + +obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c new file mode 100644 index 0000000..b7827df --- /dev/null +++ b/drivers/usb/core/buffer.c @@ -0,0 +1,154 @@ +/* + * DMA memory management for framework level HCD code (hc_driver) + * + * This implementation plugs in through generic "usb_bus" level methods, + * and should work with all USB controllers, regardles of bus type. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/mm.h> +#include <asm/io.h> +#include <asm/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> + + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/usb.h> +#include "hcd.h" + + +/* + * DMA-Coherent Buffers + */ + +/* FIXME tune these based on pool statistics ... */ +static const size_t pool_max [HCD_BUFFER_POOLS] = { + /* platforms without dma-friendly caches might need to + * prevent cacheline sharing... + */ + 32, + 128, + 512, + PAGE_SIZE / 2 + /* bigger --> allocate pages */ +}; + + +/* SETUP primitives */ + +/** + * hcd_buffer_create - initialize buffer pools + * @hcd: the bus whose buffer pools are to be initialized + * Context: !in_interrupt() + * + * Call this as part of initializing a host controller that uses the dma + * memory allocators. It initializes some pools of dma-coherent memory that + * will be shared by all drivers using that controller, or returns a negative + * errno value on error. + * + * Call hcd_buffer_destroy() to clean up after using those pools. + */ +int hcd_buffer_create (struct usb_hcd *hcd) +{ + char name [16]; + int i, size; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (!(size = pool_max [i])) + continue; + snprintf (name, sizeof name, "buffer-%d", size); + hcd->pool [i] = dma_pool_create (name, hcd->self.controller, + size, size, 0); + if (!hcd->pool [i]) { + hcd_buffer_destroy (hcd); + return -ENOMEM; + } + } + return 0; +} + + +/** + * hcd_buffer_destroy - deallocate buffer pools + * @hcd: the bus whose buffer pools are to be destroyed + * Context: !in_interrupt() + * + * This frees the buffer pools created by hcd_buffer_create(). + */ +void hcd_buffer_destroy (struct usb_hcd *hcd) +{ + int i; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + struct dma_pool *pool = hcd->pool [i]; + if (pool) { + dma_pool_destroy (pool); + hcd->pool[i] = NULL; + } + } +} + + +/* sometimes alloc/free could use kmalloc with SLAB_DMA, for + * better sharing and to leverage mm/slab.c intelligence. + */ + +void *hcd_buffer_alloc ( + struct usb_bus *bus, + size_t size, + int mem_flags, + dma_addr_t *dma +) +{ + struct usb_hcd *hcd = bus->hcpriv; + int i; + + /* some USB hosts just use PIO */ + if (!bus->controller->dma_mask) { + *dma = ~(dma_addr_t) 0; + return kmalloc (size, mem_flags); + } + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (size <= pool_max [i]) + return dma_pool_alloc (hcd->pool [i], mem_flags, dma); + } + return dma_alloc_coherent (hcd->self.controller, size, dma, 0); +} + +void hcd_buffer_free ( + struct usb_bus *bus, + size_t size, + void *addr, + dma_addr_t dma +) +{ + struct usb_hcd *hcd = bus->hcpriv; + int i; + + if (!addr) + return; + + if (!bus->controller->dma_mask) { + kfree (addr); + return; + } + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (size <= pool_max [i]) { + dma_pool_free (hcd->pool [i], addr, dma); + return; + } + } + dma_free_coherent (hcd->self.controller, size, addr, dma); +} diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c new file mode 100644 index 0000000..0b092bd --- /dev/null +++ b/drivers/usb/core/config.c @@ -0,0 +1,534 @@ +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#endif + +#include <linux/usb.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <asm/byteorder.h> + + +#define USB_MAXALTSETTING 128 /* Hard limit */ +#define USB_MAXENDPOINTS 30 /* Hard limit */ + +#define USB_MAXCONFIG 8 /* Arbitrary limit */ + + +static inline const char *plural(int n) +{ + return (n == 1 ? "" : "s"); +} + +static int find_next_descriptor(unsigned char *buffer, int size, + int dt1, int dt2, int *num_skipped) +{ + struct usb_descriptor_header *h; + int n = 0; + unsigned char *buffer0 = buffer; + + /* Find the next descriptor of type dt1 or dt2 */ + while (size > 0) { + h = (struct usb_descriptor_header *) buffer; + if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2) + break; + buffer += h->bLength; + size -= h->bLength; + ++n; + } + + /* Store the number of descriptors skipped and return the + * number of bytes skipped */ + if (num_skipped) + *num_skipped = n; + return buffer - buffer0; +} + +static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, + int asnum, struct usb_host_interface *ifp, int num_ep, + unsigned char *buffer, int size) +{ + unsigned char *buffer0 = buffer; + struct usb_endpoint_descriptor *d; + struct usb_host_endpoint *endpoint; + int n, i; + + d = (struct usb_endpoint_descriptor *) buffer; + buffer += d->bLength; + size -= d->bLength; + + if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) + n = USB_DT_ENDPOINT_AUDIO_SIZE; + else if (d->bLength >= USB_DT_ENDPOINT_SIZE) + n = USB_DT_ENDPOINT_SIZE; + else { + dev_warn(ddev, "config %d interface %d altsetting %d has an " + "invalid endpoint descriptor of length %d, skipping\n", + cfgno, inum, asnum, d->bLength); + goto skip_to_next_endpoint_or_interface_descriptor; + } + + i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK; + if (i >= 16 || i == 0) { + dev_warn(ddev, "config %d interface %d altsetting %d has an " + "invalid endpoint with address 0x%X, skipping\n", + cfgno, inum, asnum, d->bEndpointAddress); + goto skip_to_next_endpoint_or_interface_descriptor; + } + + /* Only store as many endpoints as we have room for */ + if (ifp->desc.bNumEndpoints >= num_ep) + goto skip_to_next_endpoint_or_interface_descriptor; + + endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; + ++ifp->desc.bNumEndpoints; + + memcpy(&endpoint->desc, d, n); + INIT_LIST_HEAD(&endpoint->urb_list); + + /* Skip over any Class Specific or Vendor Specific descriptors; + * find the next endpoint or interface descriptor */ + endpoint->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &n); + endpoint->extralen = i; + if (n > 0) + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "endpoint"); + return buffer - buffer0 + i; + +skip_to_next_endpoint_or_interface_descriptor: + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, NULL); + return buffer - buffer0 + i; +} + +void usb_release_interface_cache(struct kref *ref) +{ + struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref); + int j; + + for (j = 0; j < intfc->num_altsetting; j++) + kfree(intfc->altsetting[j].endpoint); + kfree(intfc); +} + +static int usb_parse_interface(struct device *ddev, int cfgno, + struct usb_host_config *config, unsigned char *buffer, int size, + u8 inums[], u8 nalts[]) +{ + unsigned char *buffer0 = buffer; + struct usb_interface_descriptor *d; + int inum, asnum; + struct usb_interface_cache *intfc; + struct usb_host_interface *alt; + int i, n; + int len, retval; + int num_ep, num_ep_orig; + + d = (struct usb_interface_descriptor *) buffer; + buffer += d->bLength; + size -= d->bLength; + + if (d->bLength < USB_DT_INTERFACE_SIZE) + goto skip_to_next_interface_descriptor; + + /* Which interface entry is this? */ + intfc = NULL; + inum = d->bInterfaceNumber; + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + if (inums[i] == inum) { + intfc = config->intf_cache[i]; + break; + } + } + if (!intfc || intfc->num_altsetting >= nalts[i]) + goto skip_to_next_interface_descriptor; + + /* Check for duplicate altsetting entries */ + asnum = d->bAlternateSetting; + for ((i = 0, alt = &intfc->altsetting[0]); + i < intfc->num_altsetting; + (++i, ++alt)) { + if (alt->desc.bAlternateSetting == asnum) { + dev_warn(ddev, "Duplicate descriptor for config %d " + "interface %d altsetting %d, skipping\n", + cfgno, inum, asnum); + goto skip_to_next_interface_descriptor; + } + } + + ++intfc->num_altsetting; + memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE); + + /* Skip over any Class Specific or Vendor Specific descriptors; + * find the first endpoint or interface descriptor */ + alt->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, + USB_DT_INTERFACE, &n); + alt->extralen = i; + if (n > 0) + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "interface"); + buffer += i; + size -= i; + + /* Allocate space for the right(?) number of endpoints */ + num_ep = num_ep_orig = alt->desc.bNumEndpoints; + alt->desc.bNumEndpoints = 0; // Use as a counter + if (num_ep > USB_MAXENDPOINTS) { + dev_warn(ddev, "too many endpoints for config %d interface %d " + "altsetting %d: %d, using maximum allowed: %d\n", + cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS); + num_ep = USB_MAXENDPOINTS; + } + + len = sizeof(struct usb_host_endpoint) * num_ep; + alt->endpoint = kmalloc(len, GFP_KERNEL); + if (!alt->endpoint) + return -ENOMEM; + memset(alt->endpoint, 0, len); + + /* Parse all the endpoint descriptors */ + n = 0; + while (size > 0) { + if (((struct usb_descriptor_header *) buffer)->bDescriptorType + == USB_DT_INTERFACE) + break; + retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt, + num_ep, buffer, size); + if (retval < 0) + return retval; + ++n; + + buffer += retval; + size -= retval; + } + + if (n != num_ep_orig) + dev_warn(ddev, "config %d interface %d altsetting %d has %d " + "endpoint descriptor%s, different from the interface " + "descriptor's value: %d\n", + cfgno, inum, asnum, n, plural(n), num_ep_orig); + return buffer - buffer0; + +skip_to_next_interface_descriptor: + i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, + USB_DT_INTERFACE, NULL); + return buffer - buffer0 + i; +} + +static int usb_parse_configuration(struct device *ddev, int cfgidx, + struct usb_host_config *config, unsigned char *buffer, int size) +{ + unsigned char *buffer0 = buffer; + int cfgno; + int nintf, nintf_orig; + int i, j, n; + struct usb_interface_cache *intfc; + unsigned char *buffer2; + int size2; + struct usb_descriptor_header *header; + int len, retval; + u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; + + memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); + if (config->desc.bDescriptorType != USB_DT_CONFIG || + config->desc.bLength < USB_DT_CONFIG_SIZE) { + dev_err(ddev, "invalid descriptor for config index %d: " + "type = 0x%X, length = %d\n", cfgidx, + config->desc.bDescriptorType, config->desc.bLength); + return -EINVAL; + } + cfgno = config->desc.bConfigurationValue; + + buffer += config->desc.bLength; + size -= config->desc.bLength; + + nintf = nintf_orig = config->desc.bNumInterfaces; + if (nintf > USB_MAXINTERFACES) { + dev_warn(ddev, "config %d has too many interfaces: %d, " + "using maximum allowed: %d\n", + cfgno, nintf, USB_MAXINTERFACES); + nintf = USB_MAXINTERFACES; + } + + /* Go through the descriptors, checking their length and counting the + * number of altsettings for each interface */ + n = 0; + for ((buffer2 = buffer, size2 = size); + size2 > 0; + (buffer2 += header->bLength, size2 -= header->bLength)) { + + if (size2 < sizeof(struct usb_descriptor_header)) { + dev_warn(ddev, "config %d descriptor has %d excess " + "byte%s, ignoring\n", + cfgno, size2, plural(size2)); + break; + } + + header = (struct usb_descriptor_header *) buffer2; + if ((header->bLength > size2) || (header->bLength < 2)) { + dev_warn(ddev, "config %d has an invalid descriptor " + "of length %d, skipping remainder of the config\n", + cfgno, header->bLength); + break; + } + + if (header->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *d; + int inum; + + d = (struct usb_interface_descriptor *) header; + if (d->bLength < USB_DT_INTERFACE_SIZE) { + dev_warn(ddev, "config %d has an invalid " + "interface descriptor of length %d, " + "skipping\n", cfgno, d->bLength); + continue; + } + + inum = d->bInterfaceNumber; + if (inum >= nintf_orig) + dev_warn(ddev, "config %d has an invalid " + "interface number: %d but max is %d\n", + cfgno, inum, nintf_orig - 1); + + /* Have we already encountered this interface? + * Count its altsettings */ + for (i = 0; i < n; ++i) { + if (inums[i] == inum) + break; + } + if (i < n) { + if (nalts[i] < 255) + ++nalts[i]; + } else if (n < USB_MAXINTERFACES) { + inums[n] = inum; + nalts[n] = 1; + ++n; + } + + } else if (header->bDescriptorType == USB_DT_DEVICE || + header->bDescriptorType == USB_DT_CONFIG) + dev_warn(ddev, "config %d contains an unexpected " + "descriptor of type 0x%X, skipping\n", + cfgno, header->bDescriptorType); + + } /* for ((buffer2 = buffer, size2 = size); ...) */ + size = buffer2 - buffer; + config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0); + + if (n != nintf) + dev_warn(ddev, "config %d has %d interface%s, different from " + "the descriptor's value: %d\n", + cfgno, n, plural(n), nintf_orig); + else if (n == 0) + dev_warn(ddev, "config %d has no interfaces?\n", cfgno); + config->desc.bNumInterfaces = nintf = n; + + /* Check for missing interface numbers */ + for (i = 0; i < nintf; ++i) { + for (j = 0; j < nintf; ++j) { + if (inums[j] == i) + break; + } + if (j >= nintf) + dev_warn(ddev, "config %d has no interface number " + "%d\n", cfgno, i); + } + + /* Allocate the usb_interface_caches and altsetting arrays */ + for (i = 0; i < nintf; ++i) { + j = nalts[i]; + if (j > USB_MAXALTSETTING) { + dev_warn(ddev, "too many alternate settings for " + "config %d interface %d: %d, " + "using maximum allowed: %d\n", + cfgno, inums[i], j, USB_MAXALTSETTING); + nalts[i] = j = USB_MAXALTSETTING; + } + + len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; + config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL); + if (!intfc) + return -ENOMEM; + memset(intfc, 0, len); + kref_init(&intfc->ref); + } + + /* Skip over any Class Specific or Vendor Specific descriptors; + * find the first interface descriptor */ + config->extra = buffer; + i = find_next_descriptor(buffer, size, USB_DT_INTERFACE, + USB_DT_INTERFACE, &n); + config->extralen = i; + if (n > 0) + dev_dbg(ddev, "skipped %d descriptor%s after %s\n", + n, plural(n), "configuration"); + buffer += i; + size -= i; + + /* Parse all the interface/altsetting descriptors */ + while (size > 0) { + retval = usb_parse_interface(ddev, cfgno, config, + buffer, size, inums, nalts); + if (retval < 0) + return retval; + + buffer += retval; + size -= retval; + } + + /* Check for missing altsettings */ + for (i = 0; i < nintf; ++i) { + intfc = config->intf_cache[i]; + for (j = 0; j < intfc->num_altsetting; ++j) { + for (n = 0; n < intfc->num_altsetting; ++n) { + if (intfc->altsetting[n].desc. + bAlternateSetting == j) + break; + } + if (n >= intfc->num_altsetting) + dev_warn(ddev, "config %d interface %d has no " + "altsetting %d\n", cfgno, inums[i], j); + } + } + + return 0; +} + +// hub-only!! ... and only exported for reset/reinit path. +// otherwise used internally on disconnect/destroy path +void usb_destroy_configuration(struct usb_device *dev) +{ + int c, i; + + if (!dev->config) + return; + + if (dev->rawdescriptors) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + kfree(dev->rawdescriptors[i]); + + kfree(dev->rawdescriptors); + dev->rawdescriptors = NULL; + } + + for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { + struct usb_host_config *cf = &dev->config[c]; + + kfree(cf->string); + cf->string = NULL; + + for (i = 0; i < cf->desc.bNumInterfaces; i++) { + if (cf->intf_cache[i]) + kref_put(&cf->intf_cache[i]->ref, + usb_release_interface_cache); + } + } + kfree(dev->config); + dev->config = NULL; +} + + +// hub-only!! ... and only in reset path, or usb_new_device() +// (used by real hubs and virtual root hubs) +int usb_get_configuration(struct usb_device *dev) +{ + struct device *ddev = &dev->dev; + int ncfg = dev->descriptor.bNumConfigurations; + int result = -ENOMEM; + unsigned int cfgno, length; + unsigned char *buffer; + unsigned char *bigbuffer; + struct usb_config_descriptor *desc; + + if (ncfg > USB_MAXCONFIG) { + dev_warn(ddev, "too many configurations: %d, " + "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG); + dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG; + } + + if (ncfg < 1) { + dev_err(ddev, "no configurations\n"); + return -EINVAL; + } + + length = ncfg * sizeof(struct usb_host_config); + dev->config = kmalloc(length, GFP_KERNEL); + if (!dev->config) + goto err2; + memset(dev->config, 0, length); + + length = ncfg * sizeof(char *); + dev->rawdescriptors = kmalloc(length, GFP_KERNEL); + if (!dev->rawdescriptors) + goto err2; + memset(dev->rawdescriptors, 0, length); + + buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); + if (!buffer) + goto err2; + desc = (struct usb_config_descriptor *)buffer; + + for (cfgno = 0; cfgno < ncfg; cfgno++) { + /* We grab just the first descriptor so we know how long + * the whole configuration is */ + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, + buffer, USB_DT_CONFIG_SIZE); + if (result < 0) { + dev_err(ddev, "unable to read config index %d " + "descriptor/%s\n", cfgno, "start"); + goto err; + } else if (result < 4) { + dev_err(ddev, "config index %d descriptor too short " + "(expected %i, got %i)\n", cfgno, + USB_DT_CONFIG_SIZE, result); + result = -EINVAL; + goto err; + } + length = max((int) le16_to_cpu(desc->wTotalLength), + USB_DT_CONFIG_SIZE); + + /* Now that we know the length, get the whole thing */ + bigbuffer = kmalloc(length, GFP_KERNEL); + if (!bigbuffer) { + result = -ENOMEM; + goto err; + } + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, + bigbuffer, length); + if (result < 0) { + dev_err(ddev, "unable to read config index %d " + "descriptor/%s\n", cfgno, "all"); + kfree(bigbuffer); + goto err; + } + if (result < length) { + dev_warn(ddev, "config index %d descriptor too short " + "(expected %i, got %i)\n", cfgno, length, result); + length = result; + } + + dev->rawdescriptors[cfgno] = bigbuffer; + + result = usb_parse_configuration(&dev->dev, cfgno, + &dev->config[cfgno], bigbuffer, length); + if (result < 0) { + ++cfgno; + goto err; + } + } + result = 0; + +err: + kfree(buffer); + dev->descriptor.bNumConfigurations = cfgno; +err2: + if (result == -ENOMEM) + dev_err(ddev, "out of memory\n"); + return result; +} diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c new file mode 100644 index 0000000..8b61bcd --- /dev/null +++ b/drivers/usb/core/devices.c @@ -0,0 +1,677 @@ +/* + * devices.c + * (C) Copyright 1999 Randy Dunlap. + * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ************************************************************* + * + * <mountpoint>/devices contains USB topology, device, config, class, + * interface, & endpoint data. + * + * I considered using /proc/bus/usb/devices/device# for each device + * as it is attached or detached, but I didn't like this for some + * reason -- maybe it's just too deep of a directory structure. + * I also don't like looking in multiple places to gather and view + * the data. Having only one file for ./devices also prevents race + * conditions that could arise if a program was reading device info + * for devices that are being removed (unplugged). (That is, the + * program may find a directory for devnum_12 then try to open it, + * but it was just unplugged, so the directory is now deleted. + * But programs would just have to be prepared for situations like + * this in any plug-and-play environment.) + * + * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch> + * Converted the whole proc stuff to real + * read methods. Now not the whole device list needs to fit + * into one page, only the device list for one bus. + * Added a poll method to /proc/bus/usb/devices, to wake + * up an eventual usbd + * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch> + * Turned into its own filesystem + * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk> + * Converted file reading routine to dump to buffer once + * per device, not per bus + * + * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ + */ + +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/usb.h> +#include <linux/smp_lock.h> +#include <linux/usbdevice_fs.h> +#include <asm/uaccess.h> + +#include "hcd.h" + +#define MAX_TOPO_LEVEL 6 + +/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ +#define ALLOW_SERIAL_NUMBER + +static char *format_topo = +/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ +"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; + +static char *format_string_manufacturer = +/* S: Manufacturer=xxxx */ + "S: Manufacturer=%.100s\n"; + +static char *format_string_product = +/* S: Product=xxxx */ + "S: Product=%.100s\n"; + +#ifdef ALLOW_SERIAL_NUMBER +static char *format_string_serialnumber = +/* S: SerialNumber=xxxx */ + "S: SerialNumber=%.100s\n"; +#endif + +static char *format_bandwidth = +/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ + "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; + +static char *format_device1 = +/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ + "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; + +static char *format_device2 = +/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ + "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; + +static char *format_config = +/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ + "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; + +static char *format_iface = +/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ + "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; + +static char *format_endpt = +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; + + +/* + * Need access to the driver and USB bus lists. + * extern struct list_head usb_bus_list; + * However, these will come from functions that return ptrs to each of them. + */ + +static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq); +static unsigned int conndiscevcnt = 0; + +/* this struct stores the poll state for <mountpoint>/devices pollers */ +struct usb_device_status { + unsigned int lastev; +}; + +struct class_info { + int class; + char *class_name; +}; + +static const struct class_info clas_info[] = +{ /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, + {USB_CLASS_HID, "HID"}, + {USB_CLASS_HUB, "hub"}, + {USB_CLASS_PHYSICAL, "PID"}, + {USB_CLASS_PRINTER, "print"}, + {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_CDC_DATA, "data"}, + {USB_CLASS_APP_SPEC, "app."}, + {USB_CLASS_VENDOR_SPEC, "vend."}, + {USB_CLASS_STILL_IMAGE, "still"}, + {USB_CLASS_CSCID, "scard"}, + {USB_CLASS_CONTENT_SEC, "c-sec"}, + {-1, "unk."} /* leave as last */ +}; + +/*****************************************************************/ + +void usbfs_conn_disc_event(void) +{ + conndiscevcnt++; + wake_up(&deviceconndiscwq); +} + +static const char *class_decode(const int class) +{ + int ix; + + for (ix = 0; clas_info[ix].class != -1; ix++) + if (clas_info[ix].class == class) + break; + return (clas_info[ix].class_name); +} + +static char *usb_dump_endpoint_descriptor ( + int speed, + char *start, + char *end, + const struct usb_endpoint_descriptor *desc +) +{ + char dir, unit, *type; + unsigned interval, in, bandwidth = 1; + + if (start > end) + return start; + in = (desc->bEndpointAddress & USB_DIR_IN); + dir = in ? 'I' : 'O'; + if (speed == USB_SPEED_HIGH) { + switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) { + case 1 << 11: bandwidth = 2; break; + case 2 << 11: bandwidth = 3; break; + } + } + + /* this isn't checking for illegal values */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + type = "Ctrl"; + if (speed == USB_SPEED_HIGH) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + dir = 'B'; /* ctrl is bidirectional */ + break; + case USB_ENDPOINT_XFER_ISOC: + type = "Isoc"; + interval = 1 << (desc->bInterval - 1); + break; + case USB_ENDPOINT_XFER_BULK: + type = "Bulk"; + if (speed == USB_SPEED_HIGH && !in) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + break; + case USB_ENDPOINT_XFER_INT: + type = "Int."; + if (speed == USB_SPEED_HIGH) { + interval = 1 << (desc->bInterval - 1); + } else + interval = desc->bInterval; + break; + default: /* "can't happen" */ + return start; + } + interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000; + if (interval % 1000) + unit = 'u'; + else { + unit = 'm'; + interval /= 1000; + } + + start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, + desc->bmAttributes, type, + (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * bandwidth, + interval, unit); + return start; +} + +static char *usb_dump_interface_descriptor(char *start, char *end, + const struct usb_interface_cache *intfc, + const struct usb_interface *iface, + int setno) +{ + const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc; + char *driver_name = ""; + + if (start > end) + return start; + down_read(&usb_bus_type.subsys.rwsem); + if (iface) + driver_name = (iface->dev.driver + ? iface->dev.driver->name + : "(none)"); + start += sprintf(start, format_iface, + desc->bInterfaceNumber, + desc->bAlternateSetting, + desc->bNumEndpoints, + desc->bInterfaceClass, + class_decode(desc->bInterfaceClass), + desc->bInterfaceSubClass, + desc->bInterfaceProtocol, + driver_name); + up_read(&usb_bus_type.subsys.rwsem); + return start; +} + +static char *usb_dump_interface( + int speed, + char *start, + char *end, + const struct usb_interface_cache *intfc, + const struct usb_interface *iface, + int setno +) { + const struct usb_host_interface *desc = &intfc->altsetting[setno]; + int i; + + start = usb_dump_interface_descriptor(start, end, intfc, iface, setno); + for (i = 0; i < desc->desc.bNumEndpoints; i++) { + if (start > end) + return start; + start = usb_dump_endpoint_descriptor(speed, + start, end, &desc->endpoint[i].desc); + } + return start; +} + +/* TBD: + * 0. TBDs + * 1. marking active interface altsettings (code lists all, but should mark + * which ones are active, if any) + */ + +static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active) +{ + if (start > end) + return start; + start += sprintf(start, format_config, + active ? '*' : ' ', /* mark active/actual/current cfg. */ + desc->bNumInterfaces, + desc->bConfigurationValue, + desc->bmAttributes, + desc->bMaxPower * 2); + return start; +} + +static char *usb_dump_config ( + int speed, + char *start, + char *end, + const struct usb_host_config *config, + int active +) +{ + int i, j; + struct usb_interface_cache *intfc; + struct usb_interface *interface; + + if (start > end) + return start; + if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ + return start + sprintf(start, "(null Cfg. desc.)\n"); + start = usb_dump_config_descriptor(start, end, &config->desc, active); + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intfc = config->intf_cache[i]; + interface = config->interface[i]; + for (j = 0; j < intfc->num_altsetting; j++) { + if (start > end) + return start; + start = usb_dump_interface(speed, + start, end, intfc, interface, j); + } + } + return start; +} + +/* + * Dump the different USB descriptors. + */ +static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc) +{ + u16 bcdUSB = le16_to_cpu(desc->bcdUSB); + u16 bcdDevice = le16_to_cpu(desc->bcdDevice); + + if (start > end) + return start; + start += sprintf (start, format_device1, + bcdUSB >> 8, bcdUSB & 0xff, + desc->bDeviceClass, + class_decode (desc->bDeviceClass), + desc->bDeviceSubClass, + desc->bDeviceProtocol, + desc->bMaxPacketSize0, + desc->bNumConfigurations); + if (start > end) + return start; + start += sprintf(start, format_device2, + le16_to_cpu(desc->idVendor), + le16_to_cpu(desc->idProduct), + bcdDevice >> 8, bcdDevice & 0xff); + return start; +} + +/* + * Dump the different strings that this device holds. + */ +static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev) +{ + if (start > end) + return start; + if (dev->manufacturer) + start += sprintf(start, format_string_manufacturer, dev->manufacturer); + if (start > end) + goto out; + if (dev->product) + start += sprintf(start, format_string_product, dev->product); + if (start > end) + goto out; +#ifdef ALLOW_SERIAL_NUMBER + if (dev->serial) + start += sprintf(start, format_string_serialnumber, dev->serial); +#endif + out: + return start; +} + +static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) +{ + int i; + + if (start > end) + return start; + + start = usb_dump_device_descriptor(start, end, &dev->descriptor); + + if (start > end) + return start; + + start = usb_dump_device_strings (start, end, dev); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (start > end) + return start; + start = usb_dump_config(dev->speed, + start, end, dev->config + i, + /* active ? */ + (dev->config + i) == dev->actconfig); + } + return start; +} + + +#ifdef PROC_EXTRA /* TBD: may want to add this code later */ + +static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc) +{ + int leng = USB_DT_HUB_NONVAR_SIZE; + unsigned char *ptr = (unsigned char *)desc; + + if (start > end) + return start; + start += sprintf(start, "Interface:"); + while (leng && start <= end) { + start += sprintf(start, " %02x", *ptr); + ptr++; leng--; + } + *start++ = '\n'; + return start; +} + +static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index) +{ + if (start > end) + return start; + start += sprintf(start, "Interface:"); + if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index]) + start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]); + return start; +} + +#endif /* PROC_EXTRA */ + +/*****************************************************************/ + +/* This is a recursive function. Parameters: + * buffer - the user-space buffer to write data into + * nbytes - the maximum number of bytes to write + * skip_bytes - the number of bytes to skip before writing anything + * file_offset - the offset into the devices file on completion + * The caller must own the device lock. + */ +static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, + struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) +{ + int chix; + int ret, cnt = 0; + int parent_devnum = 0; + char *pages_start, *data_end, *speed; + unsigned int length; + ssize_t total_written = 0; + + /* don't bother with anything else if we're not writing any data */ + if (*nbytes <= 0) + return 0; + + if (level > MAX_TOPO_LEVEL) + return 0; + /* allocate 2^1 pages = 8K (on i386); should be more than enough for one device */ + if (!(pages_start = (char*) __get_free_pages(GFP_KERNEL,1))) + return -ENOMEM; + + if (usbdev->parent && usbdev->parent->devnum != -1) + parent_devnum = usbdev->parent->devnum; + /* + * So the root hub's parent is 0 and any device that is + * plugged into the root hub has a parent of 0. + */ + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; break; + case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ + case USB_SPEED_FULL: + speed = "12 "; break; + case USB_SPEED_HIGH: + speed = "480"; break; + default: + speed = "?? "; + } + data_end = pages_start + sprintf(pages_start, format_topo, + bus->busnum, level, parent_devnum, + index, count, usbdev->devnum, + speed, usbdev->maxchild); + /* + * level = topology-tier level; + * parent_devnum = parent device number; + * index = parent's connector number; + * count = device count at this level + */ + /* If this is the root hub, display the bandwidth information */ + if (level == 0) { + int max; + + /* high speed reserves 80%, full/low reserves 90% */ + if (usbdev->speed == USB_SPEED_HIGH) + max = 800; + else + max = FRAME_TIME_MAX_USECS_ALLOC; + + /* report "average" periodic allocation over a microsecond. + * the schedules are actually bursty, HCDs need to deal with + * that and just compute/report this average. + */ + data_end += sprintf(data_end, format_bandwidth, + bus->bandwidth_allocated, max, + (100 * bus->bandwidth_allocated + max / 2) + / max, + bus->bandwidth_int_reqs, + bus->bandwidth_isoc_reqs); + + } + data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev); + + if (data_end > (pages_start + (2 * PAGE_SIZE) - 256)) + data_end += sprintf(data_end, "(truncated)\n"); + + length = data_end - pages_start; + /* if we can start copying some data to the user */ + if (length > *skip_bytes) { + length -= *skip_bytes; + if (length > *nbytes) + length = *nbytes; + if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { + free_pages((unsigned long)pages_start, 1); + return -EFAULT; + } + *nbytes -= length; + *file_offset += length; + total_written += length; + *buffer += length; + *skip_bytes = 0; + } else + *skip_bytes -= length; + + free_pages((unsigned long)pages_start, 1); + + /* Now look at all of this device's children. */ + for (chix = 0; chix < usbdev->maxchild; chix++) { + struct usb_device *childdev = usbdev->children[chix]; + + if (childdev) { + down(&childdev->serialize); + ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev, + bus, level + 1, chix, ++cnt); + up(&childdev->serialize); + if (ret == -EFAULT) + return total_written; + total_written += ret; + } + } + return total_written; +} + +static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +{ + struct usb_bus *bus; + ssize_t ret, total_written = 0; + loff_t skip_bytes = *ppos; + + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + + down (&usb_bus_list_lock); + /* print devices for all busses */ + list_for_each_entry(bus, &usb_bus_list, bus_list) { + /* recurse through all children of the root hub */ + if (!bus->root_hub) + continue; + usb_lock_device(bus->root_hub); + ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); + usb_unlock_device(bus->root_hub); + if (ret < 0) { + up(&usb_bus_list_lock); + return ret; + } + total_written += ret; + } + up (&usb_bus_list_lock); + return total_written; +} + +/* Kernel lock for "lastev" protection */ +static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_device_status *st = (struct usb_device_status *)file->private_data; + unsigned int mask = 0; + + lock_kernel(); + if (!st) { + st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); + if (!st) { + unlock_kernel(); + return POLLIN; + } + + /* we may have dropped BKL - need to check for having lost the race */ + if (file->private_data) { + kfree(st); + st = file->private_data; + goto lost_race; + } + + /* + * need to prevent the module from being unloaded, since + * proc_unregister does not call the release method and + * we would have a memory leak + */ + st->lastev = conndiscevcnt; + file->private_data = st; + mask = POLLIN; + } +lost_race: + if (file->f_mode & FMODE_READ) + poll_wait(file, &deviceconndiscwq, wait); + if (st->lastev != conndiscevcnt) + mask |= POLLIN; + st->lastev = conndiscevcnt; + unlock_kernel(); + return mask; +} + +static int usb_device_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int usb_device_release(struct inode *inode, struct file *file) +{ + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + return 0; +} + +static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) +{ + loff_t ret; + + lock_kernel(); + + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + break; + case 2: + default: + ret = -EINVAL; + } + + unlock_kernel(); + return ret; +} + +struct file_operations usbfs_devices_fops = { + .llseek = usb_device_lseek, + .read = usb_device_read, + .poll = usb_device_poll, + .open = usb_device_open, + .release = usb_device_release, +}; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c new file mode 100644 index 0000000..a047bc3 --- /dev/null +++ b/drivers/usb/core/devio.c @@ -0,0 +1,1483 @@ +/*****************************************************************************/ + +/* + * devio.c -- User space communication with USB devices. + * + * Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ + * + * This file implements the usbfs/x/y files, where + * x is the bus number and y the device number. + * + * It allows user space programs/"drivers" to communicate directly + * with USB devices without intervening kernel driver. + * + * Revision history + * 22.12.1999 0.1 Initial release (split from proc_usb.c) + * 04.01.2000 0.2 Turned into its own filesystem + */ + +/*****************************************************************************/ + +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/signal.h> +#include <linux/poll.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usbdevice_fs.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> +#include <linux/moduleparam.h> + +#include "hcd.h" /* for usbcore internals */ +#include "usb.h" + +struct async { + struct list_head asynclist; + struct dev_state *ps; + struct task_struct *task; + unsigned int signr; + unsigned int ifnum; + void __user *userbuffer; + void __user *userurb; + struct urb *urb; +}; + +static int usbfs_snoop = 0; +module_param (usbfs_snoop, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic"); + +#define snoop(dev, format, arg...) \ + do { \ + if (usbfs_snoop) \ + dev_info( dev , format , ## arg); \ + } while (0) + + +#define MAX_USBFS_BUFFER_SIZE 16384 + +static inline int connected (struct usb_device *dev) +{ + return dev->state != USB_STATE_NOTATTACHED; +} + +static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) +{ + loff_t ret; + + lock_kernel(); + + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + break; + case 2: + default: + ret = -EINVAL; + } + + unlock_kernel(); + return ret; +} + +static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; + ssize_t ret = 0; + unsigned len; + loff_t pos; + int i; + + pos = *ppos; + usb_lock_device(dev); + if (!connected(dev)) { + ret = -ENODEV; + goto err; + } else if (pos < 0) { + ret = -EINVAL; + goto err; + } + + if (pos < sizeof(struct usb_device_descriptor)) { + struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto err; + } + memcpy(desc, &dev->descriptor, sizeof(dev->descriptor)); + le16_to_cpus(&desc->bcdUSB); + le16_to_cpus(&desc->idVendor); + le16_to_cpus(&desc->idProduct); + le16_to_cpus(&desc->bcdDevice); + + len = sizeof(struct usb_device_descriptor) - pos; + if (len > nbytes) + len = nbytes; + if (copy_to_user(buf, ((char *)desc) + pos, len)) { + kfree(desc); + ret = -EFAULT; + goto err; + } + kfree(desc); + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos = sizeof(struct usb_device_descriptor); + for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) { + struct usb_config_descriptor *config = + (struct usb_config_descriptor *)dev->rawdescriptors[i]; + unsigned int length = le16_to_cpu(config->wTotalLength); + + if (*ppos < pos + length) { + + /* The descriptor may claim to be longer than it + * really is. Here is the actual allocated length. */ + unsigned alloclen = + le16_to_cpu(dev->config[i].desc.wTotalLength); + + len = length - (*ppos - pos); + if (len > nbytes) + len = nbytes; + + /* Simply don't write (skip over) unallocated parts */ + if (alloclen > (*ppos - pos)) { + alloclen -= (*ppos - pos); + if (copy_to_user(buf, + dev->rawdescriptors[i] + (*ppos - pos), + min(len, alloclen))) { + ret = -EFAULT; + goto err; + } + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos += length; + } + +err: + usb_unlock_device(dev); + return ret; +} + +/* + * async list handling + */ + +static struct async *alloc_async(unsigned int numisoframes) +{ + unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor); + struct async *as = kmalloc(assize, GFP_KERNEL); + if (!as) + return NULL; + memset(as, 0, assize); + as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); + if (!as->urb) { + kfree(as); + return NULL; + } + return as; +} + +static void free_async(struct async *as) +{ + if (as->urb->transfer_buffer) + kfree(as->urb->transfer_buffer); + if (as->urb->setup_packet) + kfree(as->urb->setup_packet); + usb_free_urb(as->urb); + kfree(as); +} + +static inline void async_newpending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_add_tail(&as->asynclist, &ps->async_pending); + spin_unlock_irqrestore(&ps->lock, flags); +} + +static inline void async_removepending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_del_init(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); +} + +static inline struct async *async_getcompleted(struct dev_state *ps) +{ + unsigned long flags; + struct async *as = NULL; + + spin_lock_irqsave(&ps->lock, flags); + if (!list_empty(&ps->async_completed)) { + as = list_entry(ps->async_completed.next, struct async, asynclist); + list_del_init(&as->asynclist); + } + spin_unlock_irqrestore(&ps->lock, flags); + return as; +} + +static inline struct async *async_getpending(struct dev_state *ps, void __user *userurb) +{ + unsigned long flags; + struct async *as; + + spin_lock_irqsave(&ps->lock, flags); + list_for_each_entry(as, &ps->async_pending, asynclist) + if (as->userurb == userurb) { + list_del_init(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + return as; + } + spin_unlock_irqrestore(&ps->lock, flags); + return NULL; +} + +static void async_completed(struct urb *urb, struct pt_regs *regs) +{ + struct async *as = (struct async *)urb->context; + struct dev_state *ps = as->ps; + struct siginfo sinfo; + + spin_lock(&ps->lock); + list_move_tail(&as->asynclist, &ps->async_completed); + spin_unlock(&ps->lock); + if (as->signr) { + sinfo.si_signo = as->signr; + sinfo.si_errno = as->urb->status; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = as->userurb; + send_sig_info(as->signr, &sinfo, as->task); + } + wake_up(&ps->wait); +} + +static void destroy_async (struct dev_state *ps, struct list_head *list) +{ + struct async *as; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + while (!list_empty(list)) { + as = list_entry(list->next, struct async, asynclist); + list_del_init(&as->asynclist); + + /* drop the spinlock so the completion handler can run */ + spin_unlock_irqrestore(&ps->lock, flags); + usb_kill_urb(as->urb); + spin_lock_irqsave(&ps->lock, flags); + } + spin_unlock_irqrestore(&ps->lock, flags); + as = async_getcompleted(ps); + while (as) { + free_async(as); + as = async_getcompleted(ps); + } +} + +static void destroy_async_on_interface (struct dev_state *ps, unsigned int ifnum) +{ + struct list_head *p, *q, hitlist; + unsigned long flags; + + INIT_LIST_HEAD(&hitlist); + spin_lock_irqsave(&ps->lock, flags); + list_for_each_safe(p, q, &ps->async_pending) + if (ifnum == list_entry(p, struct async, asynclist)->ifnum) + list_move_tail(p, &hitlist); + spin_unlock_irqrestore(&ps->lock, flags); + destroy_async(ps, &hitlist); +} + +static inline void destroy_all_async(struct dev_state *ps) +{ + destroy_async(ps, &ps->async_pending); +} + +/* + * interface claims are made only at the request of user level code, + * which can also release them (explicitly or by closing files). + * they're also undone when devices disconnect. + */ + +static int driver_probe (struct usb_interface *intf, + const struct usb_device_id *id) +{ + return -ENODEV; +} + +static void driver_disconnect(struct usb_interface *intf) +{ + struct dev_state *ps = usb_get_intfdata (intf); + unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber; + + if (!ps) + return; + + /* NOTE: this relies on usbcore having canceled and completed + * all pending I/O requests; 2.6 does that. + */ + + if (likely(ifnum < 8*sizeof(ps->ifclaimed))) + clear_bit(ifnum, &ps->ifclaimed); + else + warn("interface number %u out of range", ifnum); + + usb_set_intfdata (intf, NULL); + + /* force async requests to complete */ + destroy_async_on_interface(ps, ifnum); +} + +struct usb_driver usbfs_driver = { + .owner = THIS_MODULE, + .name = "usbfs", + .probe = driver_probe, + .disconnect = driver_disconnect, +}; + +static int claimintf(struct dev_state *ps, unsigned int ifnum) +{ + struct usb_device *dev = ps->dev; + struct usb_interface *intf; + int err; + + if (ifnum >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + /* already claimed */ + if (test_bit(ifnum, &ps->ifclaimed)) + return 0; + + /* lock against other changes to driver bindings */ + down_write(&usb_bus_type.subsys.rwsem); + intf = usb_ifnum_to_if(dev, ifnum); + if (!intf) + err = -ENOENT; + else + err = usb_driver_claim_interface(&usbfs_driver, intf, ps); + up_write(&usb_bus_type.subsys.rwsem); + if (err == 0) + set_bit(ifnum, &ps->ifclaimed); + return err; +} + +static int releaseintf(struct dev_state *ps, unsigned int ifnum) +{ + struct usb_device *dev; + struct usb_interface *intf; + int err; + + err = -EINVAL; + if (ifnum >= 8*sizeof(ps->ifclaimed)) + return err; + dev = ps->dev; + /* lock against other changes to driver bindings */ + down_write(&usb_bus_type.subsys.rwsem); + intf = usb_ifnum_to_if(dev, ifnum); + if (!intf) + err = -ENOENT; + else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { + usb_driver_release_interface(&usbfs_driver, intf); + err = 0; + } + up_write(&usb_bus_type.subsys.rwsem); + return err; +} + +static int checkintf(struct dev_state *ps, unsigned int ifnum) +{ + if (ps->dev->state != USB_STATE_CONFIGURED) + return -EHOSTUNREACH; + if (ifnum >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + if (test_bit(ifnum, &ps->ifclaimed)) + return 0; + /* if not yet claimed, claim it for the driver */ + dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim interface %u before use\n", + current->pid, current->comm, ifnum); + return claimintf(ps, ifnum); +} + +static int findintfep(struct usb_device *dev, unsigned int ep) +{ + unsigned int i, j, e; + struct usb_interface *intf; + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *endpt; + + if (ep & ~(USB_DIR_IN|0xf)) + return -EINVAL; + if (!dev->actconfig) + return -ESRCH; + for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { + intf = dev->actconfig->interface[i]; + for (j = 0; j < intf->num_altsetting; j++) { + alts = &intf->altsetting[j]; + for (e = 0; e < alts->desc.bNumEndpoints; e++) { + endpt = &alts->endpoint[e].desc; + if (endpt->bEndpointAddress == ep) + return alts->desc.bInterfaceNumber; + } + } + } + return -ENOENT; +} + +static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsigned int index) +{ + int ret = 0; + + if (ps->dev->state != USB_STATE_CONFIGURED) + return -EHOSTUNREACH; + if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) + return 0; + + index &= 0xff; + switch (requesttype & USB_RECIP_MASK) { + case USB_RECIP_ENDPOINT: + if ((ret = findintfep(ps->dev, index)) >= 0) + ret = checkintf(ps, ret); + break; + + case USB_RECIP_INTERFACE: + ret = checkintf(ps, index); + break; + } + return ret; +} + +/* + * file operations + */ +static int usbdev_open(struct inode *inode, struct file *file) +{ + struct usb_device *dev; + struct dev_state *ps; + int ret; + + /* + * no locking necessary here, as chrdev_open has the kernel lock + * (still acquire the kernel lock for safety) + */ + ret = -ENOMEM; + if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) + goto out_nolock; + + lock_kernel(); + ret = -ENOENT; + dev = usb_get_dev(inode->u.generic_ip); + if (!dev) { + kfree(ps); + goto out; + } + ret = 0; + ps->dev = dev; + ps->file = file; + spin_lock_init(&ps->lock); + INIT_LIST_HEAD(&ps->async_pending); + INIT_LIST_HEAD(&ps->async_completed); + init_waitqueue_head(&ps->wait); + ps->discsignr = 0; + ps->disctask = current; + ps->disccontext = NULL; + ps->ifclaimed = 0; + wmb(); + list_add_tail(&ps->list, &dev->filelist); + file->private_data = ps; + out: + unlock_kernel(); + out_nolock: + return ret; +} + +static int usbdev_release(struct inode *inode, struct file *file) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; + unsigned int ifnum; + + usb_lock_device(dev); + list_del_init(&ps->list); + for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); + ifnum++) { + if (test_bit(ifnum, &ps->ifclaimed)) + releaseintf(ps, ifnum); + } + destroy_all_async(ps); + usb_unlock_device(dev); + usb_put_dev(dev); + ps->dev = NULL; + kfree(ps); + return 0; +} + +static int proc_control(struct dev_state *ps, void __user *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_ctrltransfer ctrl; + unsigned int tmo; + unsigned char *tbuf; + int i, j, ret; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + if ((ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex))) + return ret; + if (ctrl.wLength > PAGE_SIZE) + return -EINVAL; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + tmo = ctrl.timeout; + if (ctrl.bRequestType & 0x80) { + if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.wLength)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", + ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); + + usb_unlock_device(dev); + i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, + ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); + usb_lock_device(dev); + if ((i > 0) && ctrl.wLength) { + if (usbfs_snoop) { + dev_info(&dev->dev, "control read: data "); + for (j = 0; j < ctrl.wLength; ++j) + printk ("%02x ", (unsigned char)(tbuf)[j]); + printk("\n"); + } + if (copy_to_user(ctrl.data, tbuf, ctrl.wLength)) { + free_page((unsigned long)tbuf); + return -EFAULT; + } + } + } else { + if (ctrl.wLength) { + if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) { + free_page((unsigned long)tbuf); + return -EFAULT; + } + } + snoop(&dev->dev, "control write: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", + ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); + if (usbfs_snoop) { + dev_info(&dev->dev, "control write: data: "); + for (j = 0; j < ctrl.wLength; ++j) + printk ("%02x ", (unsigned char)(tbuf)[j]); + printk("\n"); + } + usb_unlock_device(dev); + i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, + ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); + usb_lock_device(dev); + } + free_page((unsigned long)tbuf); + if (i<0 && i != -EPIPE) { + dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL " + "failed cmd %s rqt %u rq %u len %u ret %d\n", + current->comm, ctrl.bRequestType, ctrl.bRequest, + ctrl.wLength, i); + } + return i; +} + +static int proc_bulk(struct dev_state *ps, void __user *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_bulktransfer bulk; + unsigned int tmo, len1, pipe; + int len2; + unsigned char *tbuf; + int i, ret; + + if (copy_from_user(&bulk, arg, sizeof(bulk))) + return -EFAULT; + if ((ret = findintfep(ps->dev, bulk.ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + if (bulk.ep & USB_DIR_IN) + pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f); + else + pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f); + if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) + return -EINVAL; + len1 = bulk.len; + if (len1 > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; + if (!(tbuf = kmalloc(len1, GFP_KERNEL))) + return -ENOMEM; + tmo = bulk.timeout; + if (bulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { + kfree(tbuf); + return -EINVAL; + } + usb_unlock_device(dev); + i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + usb_lock_device(dev); + if (!i && len2) { + if (copy_to_user(bulk.data, tbuf, len2)) { + kfree(tbuf); + return -EFAULT; + } + } + } else { + if (len1) { + if (copy_from_user(tbuf, bulk.data, len1)) { + kfree(tbuf); + return -EFAULT; + } + } + usb_unlock_device(dev); + i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + usb_lock_device(dev); + } + kfree(tbuf); + if (i < 0) + return i; + return len2; +} + +static int proc_resetep(struct dev_state *ps, void __user *arg) +{ + unsigned int ep; + int ret; + + if (get_user(ep, (unsigned int __user *)arg)) + return -EFAULT; + if ((ret = findintfep(ps->dev, ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0); + return 0; +} + +static int proc_clearhalt(struct dev_state *ps, void __user *arg) +{ + unsigned int ep; + int pipe; + int ret; + + if (get_user(ep, (unsigned int __user *)arg)) + return -EFAULT; + if ((ret = findintfep(ps->dev, ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + if (ep & USB_DIR_IN) + pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f); + else + pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f); + + return usb_clear_halt(ps->dev, pipe); +} + + +static int proc_getdriver(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_getdriver gd; + struct usb_interface *intf; + int ret; + + if (copy_from_user(&gd, arg, sizeof(gd))) + return -EFAULT; + down_read(&usb_bus_type.subsys.rwsem); + intf = usb_ifnum_to_if(ps->dev, gd.interface); + if (!intf || !intf->dev.driver) + ret = -ENODATA; + else { + strncpy(gd.driver, intf->dev.driver->name, + sizeof(gd.driver)); + ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0); + } + up_read(&usb_bus_type.subsys.rwsem); + return ret; +} + +static int proc_connectinfo(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_connectinfo ci; + + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->speed == USB_SPEED_LOW; + if (copy_to_user(arg, &ci, sizeof(ci))) + return -EFAULT; + return 0; +} + +static int proc_resetdevice(struct dev_state *ps) +{ + return usb_reset_device(ps->dev); + +} + +static int proc_setintf(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_setinterface setintf; + int ret; + + if (copy_from_user(&setintf, arg, sizeof(setintf))) + return -EFAULT; + if ((ret = checkintf(ps, setintf.interface))) + return ret; + return usb_set_interface(ps->dev, setintf.interface, + setintf.altsetting); +} + +static int proc_setconfig(struct dev_state *ps, void __user *arg) +{ + unsigned int u; + int status = 0; + struct usb_host_config *actconfig; + + if (get_user(u, (unsigned int __user *)arg)) + return -EFAULT; + + actconfig = ps->dev->actconfig; + + /* Don't touch the device if any interfaces are claimed. + * It could interfere with other drivers' operations, and if + * an interface is claimed by usbfs it could easily deadlock. + */ + if (actconfig) { + int i; + + for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { + if (usb_interface_claimed(actconfig->interface[i])) { + dev_warn (&ps->dev->dev, + "usbfs: interface %d claimed " + "while '%s' sets config #%d\n", + actconfig->interface[i] + ->cur_altsetting + ->desc.bInterfaceNumber, + current->comm, u); +#if 0 /* FIXME: enable in 2.6.10 or so */ + status = -EBUSY; + break; +#endif + } + } + } + + /* SET_CONFIGURATION is often abused as a "cheap" driver reset, + * so avoid usb_set_configuration()'s kick to sysfs + */ + if (status == 0) { + if (actconfig && actconfig->desc.bConfigurationValue == u) + status = usb_reset_configuration(ps->dev); + else + status = usb_set_configuration(ps->dev, u); + } + + return status; +} + + +static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, + struct usbdevfs_iso_packet_desc __user *iso_frame_desc, + void __user *arg) +{ + struct usbdevfs_iso_packet_desc *isopkt = NULL; + struct usb_host_endpoint *ep; + struct async *as; + struct usb_ctrlrequest *dr = NULL; + unsigned int u, totlen, isofrmlen; + int ret, interval = 0, ifnum = -1; + + if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| + URB_NO_FSBR|URB_ZERO_PACKET)) + return -EINVAL; + if (!uurb->buffer) + return -EINVAL; + if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || uurb->signr > SIGRTMAX)) + return -EINVAL; + if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { + if ((ifnum = findintfep(ps->dev, uurb->endpoint)) < 0) + return ifnum; + if ((ret = checkintf(ps, ifnum))) + return ret; + } + if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) + ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; + else + ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; + if (!ep) + return -ENOENT; + switch(uurb->type) { + case USBDEVFS_URB_TYPE_CONTROL: + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_CONTROL) + return -EINVAL; + /* min 8 byte setup packet, max arbitrary */ + if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE) + return -EINVAL; + if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) + return -ENOMEM; + if (copy_from_user(dr, uurb->buffer, 8)) { + kfree(dr); + return -EFAULT; + } + if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { + kfree(dr); + return -EINVAL; + } + if ((ret = check_ctrlrecip(ps, dr->bRequestType, le16_to_cpup(&dr->wIndex)))) { + kfree(dr); + return ret; + } + uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); + uurb->number_of_packets = 0; + uurb->buffer_length = le16_to_cpup(&dr->wLength); + uurb->buffer += 8; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) { + kfree(dr); + return -EFAULT; + } + break; + + case USBDEVFS_URB_TYPE_BULK: + switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_ISOC: + return -EINVAL; + /* allow single-shot interrupt transfers, at bogus rates */ + } + uurb->number_of_packets = 0; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) + return -EFAULT; + break; + + case USBDEVFS_URB_TYPE_ISO: + /* arbitrary limit */ + if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128) + return -EINVAL; + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + interval = 1 << min (15, ep->desc.bInterval - 1); + isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets; + if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) + return -ENOMEM; + if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { + kfree(isopkt); + return -EFAULT; + } + for (totlen = u = 0; u < uurb->number_of_packets; u++) { + if (isopkt[u].length > 1023) { + kfree(isopkt); + return -EINVAL; + } + totlen += isopkt[u].length; + } + if (totlen > 32768) { + kfree(isopkt); + return -EINVAL; + } + uurb->buffer_length = totlen; + break; + + case USBDEVFS_URB_TYPE_INTERRUPT: + uurb->number_of_packets = 0; + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) + return -EINVAL; + if (ps->dev->speed == USB_SPEED_HIGH) + interval = 1 << min (15, ep->desc.bInterval - 1); + else + interval = ep->desc.bInterval; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) + return -EINVAL; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) + return -EFAULT; + break; + + default: + return -EINVAL; + } + if (!(as = alloc_async(uurb->number_of_packets))) { + if (isopkt) + kfree(isopkt); + if (dr) + kfree(dr); + return -ENOMEM; + } + if (!(as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL))) { + if (isopkt) + kfree(isopkt); + if (dr) + kfree(dr); + free_async(as); + return -ENOMEM; + } + as->urb->dev = ps->dev; + as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN); + as->urb->transfer_flags = uurb->flags; + as->urb->transfer_buffer_length = uurb->buffer_length; + as->urb->setup_packet = (unsigned char*)dr; + as->urb->start_frame = uurb->start_frame; + as->urb->number_of_packets = uurb->number_of_packets; + as->urb->interval = interval; + as->urb->context = as; + as->urb->complete = async_completed; + for (totlen = u = 0; u < uurb->number_of_packets; u++) { + as->urb->iso_frame_desc[u].offset = totlen; + as->urb->iso_frame_desc[u].length = isopkt[u].length; + totlen += isopkt[u].length; + } + if (isopkt) + kfree(isopkt); + as->ps = ps; + as->userurb = arg; + if (uurb->endpoint & USB_DIR_IN) + as->userbuffer = uurb->buffer; + else + as->userbuffer = NULL; + as->signr = uurb->signr; + as->ifnum = ifnum; + as->task = current; + if (!(uurb->endpoint & USB_DIR_IN)) { + if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { + free_async(as); + return -EFAULT; + } + } + async_newpending(as); + if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { + dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); + async_removepending(as); + free_async(as); + return ret; + } + return 0; +} + +static int proc_submiturb(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (copy_from_user(&uurb, arg, sizeof(uurb))) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg); +} + +static int proc_unlinkurb(struct dev_state *ps, void __user *arg) +{ + struct async *as; + + as = async_getpending(ps, arg); + if (!as) + return -EINVAL; + usb_kill_urb(as->urb); + return 0; +} + +static int processcompl(struct async *as, void __user * __user *arg) +{ + struct urb *urb = as->urb; + struct usbdevfs_urb __user *userurb = as->userurb; + void __user *addr = as->userurb; + unsigned int i; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) + return -EFAULT; + if (put_user(urb->status, &userurb->status)) + return -EFAULT; + if (put_user(urb->actual_length, &userurb->actual_length)) + return -EFAULT; + if (put_user(urb->error_count, &userurb->error_count)) + return -EFAULT; + + if (!(usb_pipeisoc(urb->pipe))) + return 0; + for (i = 0; i < urb->number_of_packets; i++) { + if (put_user(urb->iso_frame_desc[i].actual_length, + &userurb->iso_frame_desc[i].actual_length)) + return -EFAULT; + if (put_user(urb->iso_frame_desc[i].status, + &userurb->iso_frame_desc[i].status)) + return -EFAULT; + } + + free_async(as); + + if (put_user(addr, (void __user * __user *)arg)) + return -EFAULT; + return 0; +} + +static struct async* reap_as(struct dev_state *ps) +{ + DECLARE_WAITQUEUE(wait, current); + struct async *as = NULL; + struct usb_device *dev = ps->dev; + + add_wait_queue(&ps->wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if ((as = async_getcompleted(ps))) + break; + if (signal_pending(current)) + break; + usb_unlock_device(dev); + schedule(); + usb_lock_device(dev); + } + remove_wait_queue(&ps->wait, &wait); + set_current_state(TASK_RUNNING); + return as; +} + +static int proc_reapurb(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl(as, (void __user * __user *)arg); + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) +{ + struct async *as; + + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + return processcompl(as, (void __user * __user *)arg); +} + +#ifdef CONFIG_COMPAT + +static int get_urb32(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 __user *uurb) +{ + __u32 uptr; + if (get_user(kurb->type, &uurb->type) || + __get_user(kurb->endpoint, &uurb->endpoint) || + __get_user(kurb->status, &uurb->status) || + __get_user(kurb->flags, &uurb->flags) || + __get_user(kurb->buffer_length, &uurb->buffer_length) || + __get_user(kurb->actual_length, &uurb->actual_length) || + __get_user(kurb->start_frame, &uurb->start_frame) || + __get_user(kurb->number_of_packets, &uurb->number_of_packets) || + __get_user(kurb->error_count, &uurb->error_count) || + __get_user(kurb->signr, &uurb->signr)) + return -EFAULT; + + if (__get_user(uptr, &uurb->buffer)) + return -EFAULT; + kurb->buffer = compat_ptr(uptr); + if (__get_user(uptr, &uurb->buffer)) + return -EFAULT; + kurb->usercontext = compat_ptr(uptr); + + return 0; +} + +static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg)) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb __user *)arg)->iso_frame_desc, arg); +} + +static int processcompl_compat(struct async *as, void __user * __user *arg) +{ + struct urb *urb = as->urb; + struct usbdevfs_urb32 __user *userurb = as->userurb; + void __user *addr = as->userurb; + unsigned int i; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) + return -EFAULT; + if (put_user(urb->status, &userurb->status)) + return -EFAULT; + if (put_user(urb->actual_length, &userurb->actual_length)) + return -EFAULT; + if (put_user(urb->error_count, &userurb->error_count)) + return -EFAULT; + + if (!(usb_pipeisoc(urb->pipe))) + return 0; + for (i = 0; i < urb->number_of_packets; i++) { + if (put_user(urb->iso_frame_desc[i].actual_length, + &userurb->iso_frame_desc[i].actual_length)) + return -EFAULT; + if (put_user(urb->iso_frame_desc[i].status, + &userurb->iso_frame_desc[i].status)) + return -EFAULT; + } + + free_async(as); + if (put_user((u32)(u64)addr, (u32 __user *)arg)) + return -EFAULT; + return 0; +} + +static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl_compat(as, (void __user * __user *)arg); + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as; + + printk("reapurbnblock\n"); + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + printk("reap got as %p\n", as); + return processcompl_compat(as, (void __user * __user *)arg); +} + +#endif + +static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_disconnectsignal ds; + + if (copy_from_user(&ds, arg, sizeof(ds))) + return -EFAULT; + if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) + return -EINVAL; + ps->discsignr = ds.signr; + ps->disccontext = ds.context; + return 0; +} + +static int proc_claiminterface(struct dev_state *ps, void __user *arg) +{ + unsigned int ifnum; + + if (get_user(ifnum, (unsigned int __user *)arg)) + return -EFAULT; + return claimintf(ps, ifnum); +} + +static int proc_releaseinterface(struct dev_state *ps, void __user *arg) +{ + unsigned int ifnum; + int ret; + + if (get_user(ifnum, (unsigned int __user *)arg)) + return -EFAULT; + if ((ret = releaseintf(ps, ifnum)) < 0) + return ret; + destroy_async_on_interface (ps, ifnum); + return 0; +} + +static int proc_ioctl (struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_ioctl ctrl; + int size; + void *buf = NULL; + int retval = 0; + struct usb_interface *intf = NULL; + struct usb_driver *driver = NULL; + int i; + + /* get input parameters and alloc buffer */ + if (copy_from_user(&ctrl, arg, sizeof (ctrl))) + return -EFAULT; + if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { + if ((buf = kmalloc (size, GFP_KERNEL)) == NULL) + return -ENOMEM; + if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) { + if (copy_from_user (buf, ctrl.data, size)) { + kfree (buf); + return -EFAULT; + } + } else { + memset (buf, 0, size); + } + } + + if (!connected(ps->dev)) { + if (buf) + kfree(buf); + return -ENODEV; + } + + if (ps->dev->state != USB_STATE_CONFIGURED) + retval = -EHOSTUNREACH; + else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + retval = -EINVAL; + else switch (ctrl.ioctl_code) { + + /* disconnect kernel driver from interface */ + case USBDEVFS_DISCONNECT: + + /* don't allow the user to unbind the hub driver from + * a hub with children to manage */ + for (i = 0; i < ps->dev->maxchild; ++i) { + if (ps->dev->children[i]) + retval = -EBUSY; + } + if (retval) + break; + + down_write(&usb_bus_type.subsys.rwsem); + if (intf->dev.driver) { + driver = to_usb_driver(intf->dev.driver); + dev_dbg (&intf->dev, "disconnect by usbfs\n"); + usb_driver_release_interface(driver, intf); + } else + retval = -ENODATA; + up_write(&usb_bus_type.subsys.rwsem); + break; + + /* let kernel drivers try to (re)bind to the interface */ + case USBDEVFS_CONNECT: + usb_unlock_device(ps->dev); + usb_lock_all_devices(); + bus_rescan_devices(intf->dev.bus); + usb_unlock_all_devices(); + usb_lock_device(ps->dev); + break; + + /* talk directly to the interface's driver */ + default: + down_read(&usb_bus_type.subsys.rwsem); + if (intf->dev.driver) + driver = to_usb_driver(intf->dev.driver); + if (driver == NULL || driver->ioctl == NULL) { + retval = -ENOTTY; + } else { + retval = driver->ioctl (intf, ctrl.ioctl_code, buf); + if (retval == -ENOIOCTLCMD) + retval = -ENOTTY; + } + up_read(&usb_bus_type.subsys.rwsem); + } + + /* cleanup and return */ + if (retval >= 0 + && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 + && size > 0 + && copy_to_user (ctrl.data, buf, size) != 0) + retval = -EFAULT; + if (buf != NULL) + kfree (buf); + return retval; +} + +/* + * NOTE: All requests here that have interface numbers as parameters + * are assuming that somehow the configuration has been prevented from + * changing. But there's no mechanism to ensure that... + */ +static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + struct usb_device *dev = ps->dev; + void __user *p = (void __user *)arg; + int ret = -ENOTTY; + + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + usb_lock_device(dev); + if (!connected(dev)) { + usb_unlock_device(dev); + return -ENODEV; + } + + switch (cmd) { + case USBDEVFS_CONTROL: + snoop(&dev->dev, "%s: CONTROL\n", __FUNCTION__); + ret = proc_control(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_BULK: + snoop(&dev->dev, "%s: BULK\n", __FUNCTION__); + ret = proc_bulk(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESETEP: + snoop(&dev->dev, "%s: RESETEP\n", __FUNCTION__); + ret = proc_resetep(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESET: + snoop(&dev->dev, "%s: RESET\n", __FUNCTION__); + ret = proc_resetdevice(ps); + break; + + case USBDEVFS_CLEAR_HALT: + snoop(&dev->dev, "%s: CLEAR_HALT\n", __FUNCTION__); + ret = proc_clearhalt(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_GETDRIVER: + snoop(&dev->dev, "%s: GETDRIVER\n", __FUNCTION__); + ret = proc_getdriver(ps, p); + break; + + case USBDEVFS_CONNECTINFO: + snoop(&dev->dev, "%s: CONNECTINFO\n", __FUNCTION__); + ret = proc_connectinfo(ps, p); + break; + + case USBDEVFS_SETINTERFACE: + snoop(&dev->dev, "%s: SETINTERFACE\n", __FUNCTION__); + ret = proc_setintf(ps, p); + break; + + case USBDEVFS_SETCONFIGURATION: + snoop(&dev->dev, "%s: SETCONFIGURATION\n", __FUNCTION__); + ret = proc_setconfig(ps, p); + break; + + case USBDEVFS_SUBMITURB: + snoop(&dev->dev, "%s: SUBMITURB\n", __FUNCTION__); + ret = proc_submiturb(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + +#ifdef CONFIG_COMPAT + + case USBDEVFS_SUBMITURB32: + snoop(&dev->dev, "%s: SUBMITURB32\n", __FUNCTION__); + ret = proc_submiturb_compat(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_REAPURB32: + snoop(&dev->dev, "%s: REAPURB32\n", __FUNCTION__); + ret = proc_reapurb_compat(ps, p); + break; + + case USBDEVFS_REAPURBNDELAY32: + snoop(&dev->dev, "%s: REAPURBDELAY32\n", __FUNCTION__); + ret = proc_reapurbnonblock_compat(ps, p); + break; + +#endif + + case USBDEVFS_DISCARDURB: + snoop(&dev->dev, "%s: DISCARDURB\n", __FUNCTION__); + ret = proc_unlinkurb(ps, p); + break; + + case USBDEVFS_REAPURB: + snoop(&dev->dev, "%s: REAPURB\n", __FUNCTION__); + ret = proc_reapurb(ps, p); + break; + + case USBDEVFS_REAPURBNDELAY: + snoop(&dev->dev, "%s: REAPURBDELAY\n", __FUNCTION__); + ret = proc_reapurbnonblock(ps, p); + break; + + case USBDEVFS_DISCSIGNAL: + snoop(&dev->dev, "%s: DISCSIGNAL\n", __FUNCTION__); + ret = proc_disconnectsignal(ps, p); + break; + + case USBDEVFS_CLAIMINTERFACE: + snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __FUNCTION__); + ret = proc_claiminterface(ps, p); + break; + + case USBDEVFS_RELEASEINTERFACE: + snoop(&dev->dev, "%s: RELEASEINTERFACE\n", __FUNCTION__); + ret = proc_releaseinterface(ps, p); + break; + + case USBDEVFS_IOCTL: + snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__); + ret = proc_ioctl(ps, p); + break; + } + usb_unlock_device(dev); + if (ret >= 0) + inode->i_atime = CURRENT_TIME; + return ret; +} + +/* No kernel lock - fine */ +static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + unsigned int mask = 0; + + poll_wait(file, &ps->wait, wait); + if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed)) + mask |= POLLOUT | POLLWRNORM; + if (!connected(ps->dev)) + mask |= POLLERR | POLLHUP; + return mask; +} + +struct file_operations usbfs_device_file_operations = { + .llseek = usbdev_lseek, + .read = usbdev_read, + .poll = usbdev_poll, + .ioctl = usbdev_ioctl, + .open = usbdev_open, + .release = usbdev_release, +}; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c new file mode 100644 index 0000000..80ce964 --- /dev/null +++ b/drivers/usb/core/file.c @@ -0,0 +1,227 @@ +/* + * drivers/usb/file.c + * + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, + more docs, etc) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * (C) Copyright Greg Kroah-Hartman 2002-2003 + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/spinlock.h> +#include <linux/errno.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/usb.h> + +#define MAX_USB_MINORS 256 +static struct file_operations *usb_minors[MAX_USB_MINORS]; +static DEFINE_SPINLOCK(minor_lock); + +static int usb_open(struct inode * inode, struct file * file) +{ + int minor = iminor(inode); + struct file_operations *c; + int err = -ENODEV; + struct file_operations *old_fops, *new_fops = NULL; + + spin_lock (&minor_lock); + c = usb_minors[minor]; + + if (!c || !(new_fops = fops_get(c))) { + spin_unlock(&minor_lock); + return err; + } + spin_unlock(&minor_lock); + + old_fops = file->f_op; + file->f_op = new_fops; + /* Curiouser and curiouser... NULL ->open() as "no device" ? */ + if (file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; +} + +static struct file_operations usb_fops = { + .owner = THIS_MODULE, + .open = usb_open, +}; + +static struct class_simple *usb_class; + +int usb_major_init(void) +{ + int error; + + error = register_chrdev(USB_MAJOR, "usb", &usb_fops); + if (error) { + err("unable to get major %d for usb devices", USB_MAJOR); + goto out; + } + + usb_class = class_simple_create(THIS_MODULE, "usb"); + if (IS_ERR(usb_class)) { + err("class_simple_create failed for usb devices"); + unregister_chrdev(USB_MAJOR, "usb"); + goto out; + } + + devfs_mk_dir("usb"); + +out: + return error; +} + +void usb_major_cleanup(void) +{ + class_simple_destroy(usb_class); + devfs_remove("usb"); + unregister_chrdev(USB_MAJOR, "usb"); +} + +/** + * usb_register_dev - register a USB device, and ask for a minor number + * @intf: pointer to the usb_interface that is being registered + * @class_driver: pointer to the usb_class_driver for this device + * + * This should be called by all USB drivers that use the USB major number. + * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be + * dynamically allocated out of the list of available ones. If it is not + * enabled, the minor number will be based on the next available free minor, + * starting at the class_driver->minor_base. + * + * This function also creates the devfs file for the usb device, if devfs + * is enabled, and creates a usb class device in the sysfs tree. + * + * usb_deregister_dev() must be called when the driver is done with + * the minor numbers given out by this function. + * + * Returns -EINVAL if something bad happens with trying to register a + * device, and 0 on success. + */ +int usb_register_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver) +{ + int retval = -EINVAL; + int minor_base = class_driver->minor_base; + int minor = 0; + char name[BUS_ID_SIZE]; + char *temp; + +#ifdef CONFIG_USB_DYNAMIC_MINORS + /* + * We don't care what the device tries to start at, we want to start + * at zero to pack the devices into the smallest available space with + * no holes in the minor range. + */ + minor_base = 0; +#endif + intf->minor = -1; + + dbg ("looking for a minor, starting at %d", minor_base); + + if (class_driver->fops == NULL) + goto exit; + + spin_lock (&minor_lock); + for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { + if (usb_minors[minor]) + continue; + + usb_minors[minor] = class_driver->fops; + + retval = 0; + break; + } + spin_unlock (&minor_lock); + + if (retval) + goto exit; + + intf->minor = minor; + + /* handle the devfs registration */ + snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base); + devfs_mk_cdev(MKDEV(USB_MAJOR, minor), class_driver->mode, name); + + /* create a usb class device for this usb interface */ + temp = strrchr(name, '/'); + if (temp && (temp[1] != 0x00)) + ++temp; + else + temp = name; + intf->class_dev = class_simple_device_add(usb_class, MKDEV(USB_MAJOR, minor), &intf->dev, "%s", temp); + if (IS_ERR(intf->class_dev)) { + spin_lock (&minor_lock); + usb_minors[intf->minor] = NULL; + spin_unlock (&minor_lock); + devfs_remove (name); + retval = PTR_ERR(intf->class_dev); + } +exit: + return retval; +} +EXPORT_SYMBOL(usb_register_dev); + +/** + * usb_deregister_dev - deregister a USB device's dynamic minor. + * @intf: pointer to the usb_interface that is being deregistered + * @class_driver: pointer to the usb_class_driver for this device + * + * Used in conjunction with usb_register_dev(). This function is called + * when the USB driver is finished with the minor numbers gotten from a + * call to usb_register_dev() (usually when the device is disconnected + * from the system.) + * + * This function also cleans up the devfs file for the usb device, if devfs + * is enabled, and removes the usb class device from the sysfs tree. + * + * This should be called by all drivers that use the USB major number. + */ +void usb_deregister_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver) +{ + int minor_base = class_driver->minor_base; + char name[BUS_ID_SIZE]; + +#ifdef CONFIG_USB_DYNAMIC_MINORS + minor_base = 0; +#endif + + if (intf->minor == -1) + return; + + dbg ("removing %d minor", intf->minor); + + spin_lock (&minor_lock); + usb_minors[intf->minor] = NULL; + spin_unlock (&minor_lock); + + snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); + devfs_remove (name); + class_simple_device_remove(MKDEV(USB_MAJOR, intf->minor)); + intf->class_dev = NULL; + intf->minor = -1; +} +EXPORT_SYMBOL(usb_deregister_dev); + + diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c new file mode 100644 index 0000000..b9a3dae --- /dev/null +++ b/drivers/usb/core/hcd-pci.c @@ -0,0 +1,358 @@ +/* + * (C) Copyright David Brownell 2000-2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/usb.h> +#include "hcd.h" + + +/* PCI-based HCs are normal, but custom bus glue should be ok */ + + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * usb_hcd_pci_probe - initialize PCI-based HCDs + * @dev: USB Host Controller being probed + * @id: pci hotplug id connecting controller to HCD framework + * Context: !in_interrupt() + * + * Allocates basic PCI resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + * Store this function in the HCD's struct pci_driver as probe(). + */ +int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) +{ + struct hc_driver *driver; + struct usb_hcd *hcd; + int retval; + + if (usb_disabled()) + return -ENODEV; + + if (!id || !(driver = (struct hc_driver *) id->driver_data)) + return -EINVAL; + + if (pci_enable_device (dev) < 0) + return -ENODEV; + dev->current_state = 0; + dev->dev.power.power_state = 0; + + if (!dev->irq) { + dev_err (&dev->dev, + "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", + pci_name(dev)); + retval = -ENODEV; + goto err1; + } + + hcd = usb_create_hcd (driver, &dev->dev, pci_name(dev)); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + if (driver->flags & HCD_MEMORY) { // EHCI, OHCI + hcd->rsrc_start = pci_resource_start (dev, 0); + hcd->rsrc_len = pci_resource_len (dev, 0); + if (!request_mem_region (hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg (&dev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = ioremap_nocache (hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg (&dev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + } else { // UHCI + int region; + + for (region = 0; region < PCI_ROM_RESOURCE; region++) { + if (!(pci_resource_flags (dev, region) & + IORESOURCE_IO)) + continue; + + hcd->rsrc_start = pci_resource_start (dev, region); + hcd->rsrc_len = pci_resource_len (dev, region); + if (request_region (hcd->rsrc_start, hcd->rsrc_len, + driver->description)) + break; + } + if (region == PCI_ROM_RESOURCE) { + dev_dbg (&dev->dev, "no i/o regions available\n"); + retval = -EBUSY; + goto err1; + } + } + +#ifdef CONFIG_PCI_NAMES + hcd->product_desc = dev->pretty_name; +#endif + + pci_set_master (dev); + + retval = usb_add_hcd (hcd, dev->irq, SA_SHIRQ); + if (retval != 0) + goto err4; + return retval; + + err4: + if (driver->flags & HCD_MEMORY) { + iounmap (hcd->regs); + err3: + release_mem_region (hcd->rsrc_start, hcd->rsrc_len); + } else + release_region (hcd->rsrc_start, hcd->rsrc_len); + err2: + usb_put_hcd (hcd); + err1: + pci_disable_device (dev); + dev_err (&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); + return retval; +} +EXPORT_SYMBOL (usb_hcd_pci_probe); + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_pci_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + * Store this function in the HCD's struct pci_driver as remove(). + */ +void usb_hcd_pci_remove (struct pci_dev *dev) +{ + struct usb_hcd *hcd; + + hcd = pci_get_drvdata(dev); + if (!hcd) + return; + + usb_remove_hcd (hcd); + if (hcd->driver->flags & HCD_MEMORY) { + iounmap (hcd->regs); + release_mem_region (hcd->rsrc_start, hcd->rsrc_len); + } else { + release_region (hcd->rsrc_start, hcd->rsrc_len); + } + usb_put_hcd (hcd); + pci_disable_device(dev); +} +EXPORT_SYMBOL (usb_hcd_pci_remove); + + +#ifdef CONFIG_PM + +static char __attribute_used__ *pci_state(u32 state) +{ + switch (state) { + case 0: return "D0"; + case 1: return "D1"; + case 2: return "D2"; + case 3: return "D3hot"; + case 4: return "D3cold"; + } + return NULL; +} + +/** + * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD + * @dev: USB Host Controller being suspended + * @state: state that the controller is going into + * + * Store this function in the HCD's struct pci_driver as suspend(). + */ +int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) +{ + struct usb_hcd *hcd; + int retval = 0; + int has_pci_pm; + + hcd = pci_get_drvdata(dev); + + /* even when the PCI layer rejects some of the PCI calls + * below, HCs can try global suspend and reduce DMA traffic. + * PM-sensitive HCDs may already have done this. + */ + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (state > 4) + state = 4; + + switch (hcd->state) { + + /* entry if root hub wasn't yet suspended ... from sysfs, + * without autosuspend, or if USB_SUSPEND isn't configured. + */ + case HC_STATE_RUNNING: + hcd->state = HC_STATE_QUIESCING; + retval = hcd->driver->suspend (hcd, state); + if (retval) { + dev_dbg (hcd->self.controller, + "suspend fail, retval %d\n", + retval); + break; + } + hcd->state = HC_STATE_SUSPENDED; + /* FALLTHROUGH */ + + /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the + * controller and/or root hub will already have been suspended, + * but it won't be ready for a PCI resume call. + * + * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will + * have been called, otherwise root hub timers still run ... + */ + case HC_STATE_SUSPENDED: + if (state <= dev->current_state) + break; + + /* no DMA or IRQs except in D0 */ + if (!dev->current_state) { + pci_save_state (dev); + pci_disable_device (dev); + free_irq (hcd->irq, hcd); + } + + if (!has_pci_pm) { + dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); + break; + } + + /* POLICY: ignore D1/D2/D3hot differences; + * we know D3hot will always work. + */ + retval = pci_set_power_state (dev, state); + if (retval < 0 && state < 3) { + retval = pci_set_power_state (dev, 3); + if (retval == 0) + state = 3; + } + if (retval == 0) { + dev_dbg (hcd->self.controller, "--> PCI %s\n", + pci_state(dev->current_state)); +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, state, hcd->remote_wakeup); + pci_enable_wake (dev, 4, hcd->remote_wakeup); +#endif + } else if (retval < 0) { + dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n", + pci_state(state), retval); + (void) usb_hcd_pci_resume (dev); + break; + } + break; + default: + dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", + hcd->state); + retval = -EINVAL; + break; + } + + /* update power_state **ONLY** to make sysfs happier */ + if (retval == 0) + dev->dev.power.power_state = state; + return retval; +} +EXPORT_SYMBOL (usb_hcd_pci_suspend); + +/** + * usb_hcd_pci_resume - power management resume of a PCI-based HCD + * @dev: USB Host Controller being resumed + * + * Store this function in the HCD's struct pci_driver as resume(). + */ +int usb_hcd_pci_resume (struct pci_dev *dev) +{ + struct usb_hcd *hcd; + int retval; + int has_pci_pm; + + hcd = pci_get_drvdata(dev); + if (hcd->state != HC_STATE_SUSPENDED) { + dev_dbg (hcd->self.controller, + "can't resume, not suspended!\n"); + return 0; + } + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + + /* D3cold resume isn't usually reported this way... */ + dev_dbg(hcd->self.controller, "resume from PCI %s%s\n", + pci_state(dev->current_state), + has_pci_pm ? "" : " (legacy)"); + + hcd->state = HC_STATE_RESUMING; + + if (has_pci_pm) + pci_set_power_state (dev, 0); + dev->dev.power.power_state = 0; + retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, + hcd->driver->description, hcd); + if (retval < 0) { + dev_err (hcd->self.controller, + "can't restore IRQ after resume!\n"); + return retval; + } + hcd->saw_irq = 0; + pci_restore_state (dev); +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, dev->current_state, 0); + pci_enable_wake (dev, 4, 0); +#endif + + retval = hcd->driver->resume (hcd); + if (!HC_IS_RUNNING (hcd->state)) { + dev_dbg (hcd->self.controller, + "resume fail, retval %d\n", retval); + usb_hc_died (hcd); + } + + return retval; +} +EXPORT_SYMBOL (usb_hcd_pci_resume); + +#endif /* CONFIG_PM */ + + diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c new file mode 100644 index 0000000..266e9e0 --- /dev/null +++ b/drivers/usb/core/hcd.c @@ -0,0 +1,1840 @@ +/* + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#endif + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/completion.h> +#include <linux/utsname.h> +#include <linux/mm.h> +#include <asm/io.h> +#include <asm/scatterlist.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <asm/irq.h> +#include <asm/byteorder.h> + +#include <linux/usb.h> + +#include "usb.h" +#include "hcd.h" +#include "hub.h" + + +// #define USB_BANDWIDTH_MESSAGES + +/*-------------------------------------------------------------------------*/ + +/* + * USB Host Controller Driver framework + * + * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing + * HCD-specific behaviors/bugs. + * + * This does error checks, tracks devices and urbs, and delegates to a + * "hc_driver" only for code (and data) that really needs to know about + * hardware differences. That includes root hub registers, i/o queues, + * and so on ... but as little else as possible. + * + * Shared code includes most of the "root hub" code (these are emulated, + * though each HC's hardware works differently) and PCI glue, plus request + * tracking overhead. The HCD code should only block on spinlocks or on + * hardware handshaking; blocking on software events (such as other kernel + * threads releasing resources, or completing actions) is all generic. + * + * Happens the USB 2.0 spec says this would be invisible inside the "USBD", + * and includes mostly a "HCDI" (HCD Interface) along with some APIs used + * only by the hub driver ... and that neither should be seen or used by + * usb client device drivers. + * + * Contributors of ideas or unattributed patches include: David Brownell, + * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ... + * + * HISTORY: + * 2002-02-21 Pull in most of the usb_bus support from usb.c; some + * associated cleanup. "usb_hcd" still != "usb_bus". + * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. + */ + +/*-------------------------------------------------------------------------*/ + +/* host controllers we manage */ +LIST_HEAD (usb_bus_list); +EXPORT_SYMBOL_GPL (usb_bus_list); + +/* used when allocating bus numbers */ +#define USB_MAXBUS 64 +struct usb_busmap { + unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))]; +}; +static struct usb_busmap busmap; + +/* used when updating list of hcds */ +DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ +EXPORT_SYMBOL_GPL (usb_bus_list_lock); + +/* used for controlling access to virtual root hubs */ +static DEFINE_SPINLOCK(hcd_root_hub_lock); + +/* used when updating hcd data */ +static DEFINE_SPINLOCK(hcd_data_lock); + +/* wait queue for synchronous unlinks */ +DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); + +/*-------------------------------------------------------------------------*/ + +/* + * Sharable chunks of root hub code. + */ + +/*-------------------------------------------------------------------------*/ + +#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff) +#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff) + +/* usb 2.0 root hub device descriptor */ +static const u8 usb2_rh_dev_descriptor [18] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, 0x02, /* __le16 bcdUSB; v2.0 */ + + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + + 0x00, 0x00, /* __le16 idVendor; */ + 0x00, 0x00, /* __le16 idProduct; */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */ + +/* usb 1.1 root hub device descriptor */ +static const u8 usb11_rh_dev_descriptor [18] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, 0x01, /* __le16 bcdUSB; v1.1 */ + + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + + 0x00, 0x00, /* __le16 idVendor; */ + 0x00, 0x00, /* __le16 idProduct; */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/*-------------------------------------------------------------------------*/ + +/* Configuration descriptors for our root hubs */ + +static const u8 fs_rh_config_descriptor [] = { + + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __le16 wTotalLength; */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* __u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const u8 hs_rh_config_descriptor [] = { + + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __le16 wTotalLength; */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0xc0, /* __u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* __u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ +}; + +/*-------------------------------------------------------------------------*/ + +/* + * helper routine for returning string descriptors in UTF-16LE + * input can actually be ISO-8859-1; ASCII is its 7-bit subset + */ +static int ascii2utf (char *s, u8 *utf, int utfmax) +{ + int retval; + + for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *s++; + *utf++ = 0; + } + if (utfmax > 0) { + *utf = *s; + ++retval; + } + return retval; +} + +/* + * rh_string - provides manufacturer, product and serial strings for root hub + * @id: the string ID number (1: serial number, 2: product, 3: vendor) + * @hcd: the host controller for this root hub + * @type: string describing our driver + * @data: return packet in UTF-16 LE + * @len: length of the return packet + * + * Produces either a manufacturer, product or serial number string for the + * virtual root hub device. + */ +static int rh_string ( + int id, + struct usb_hcd *hcd, + u8 *data, + int len +) { + char buf [100]; + + // language ids + if (id == 0) { + buf[0] = 4; buf[1] = 3; /* 4 bytes string data */ + buf[2] = 0x09; buf[3] = 0x04; /* MSFT-speak for "en-us" */ + len = min (len, 4); + memcpy (data, buf, len); + return len; + + // serial number + } else if (id == 1) { + strlcpy (buf, hcd->self.bus_name, sizeof buf); + + // product description + } else if (id == 2) { + strlcpy (buf, hcd->product_desc, sizeof buf); + + // id 3 == vendor description + } else if (id == 3) { + snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname, + system_utsname.release, hcd->driver->description); + + // unsupported IDs --> "protocol stall" + } else + return -EPIPE; + + switch (len) { /* All cases fall through */ + default: + len = 2 + ascii2utf (buf, data + 2, len - 2); + case 2: + data [1] = 3; /* type == string */ + case 1: + data [0] = 2 * (strlen (buf) + 1); + case 0: + ; /* Compiler wants a statement here */ + } + return len; +} + + +/* Root hub control transfers execute synchronously */ +static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) +{ + struct usb_ctrlrequest *cmd; + u16 typeReq, wValue, wIndex, wLength; + u8 *ubuf = urb->transfer_buffer; + u8 tbuf [sizeof (struct usb_hub_descriptor)]; + const u8 *bufp = tbuf; + int len = 0; + int patch_wakeup = 0; + unsigned long flags; + int status = 0; + int n; + + cmd = (struct usb_ctrlrequest *) urb->setup_packet; + typeReq = (cmd->bRequestType << 8) | cmd->bRequest; + wValue = le16_to_cpu (cmd->wValue); + wIndex = le16_to_cpu (cmd->wIndex); + wLength = le16_to_cpu (cmd->wLength); + + if (wLength > urb->transfer_buffer_length) + goto error; + + urb->actual_length = 0; + switch (typeReq) { + + /* DEVICE REQUESTS */ + + case DeviceRequest | USB_REQ_GET_STATUS: + tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) + | (1 << USB_DEVICE_SELF_POWERED); + tbuf [1] = 0; + len = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (wValue == USB_DEVICE_REMOTE_WAKEUP) + hcd->remote_wakeup = 0; + else + goto error; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP) + hcd->remote_wakeup = 1; + else + goto error; + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + tbuf [0] = 1; + len = 1; + /* FALLTHROUGH */ + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch (wValue & 0xff00) { + case USB_DT_DEVICE << 8: + if (hcd->driver->flags & HCD_USB2) + bufp = usb2_rh_dev_descriptor; + else if (hcd->driver->flags & HCD_USB11) + bufp = usb11_rh_dev_descriptor; + else + goto error; + len = 18; + break; + case USB_DT_CONFIG << 8: + if (hcd->driver->flags & HCD_USB2) { + bufp = hs_rh_config_descriptor; + len = sizeof hs_rh_config_descriptor; + } else { + bufp = fs_rh_config_descriptor; + len = sizeof fs_rh_config_descriptor; + } + if (hcd->can_wakeup) + patch_wakeup = 1; + break; + case USB_DT_STRING << 8: + n = rh_string (wValue & 0xff, hcd, ubuf, wLength); + if (n < 0) + goto error; + urb->actual_length = n; + break; + default: + goto error; + } + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + tbuf [0] = 0; + len = 1; + /* FALLTHROUGH */ + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + // wValue == urb->dev->devaddr + dev_dbg (hcd->self.controller, "root hub device address %d\n", + wValue); + break; + + /* INTERFACE REQUESTS (no defined feature/status flags) */ + + /* ENDPOINT REQUESTS */ + + case EndpointRequest | USB_REQ_GET_STATUS: + // ENDPOINT_HALT flag + tbuf [0] = 0; + tbuf [1] = 0; + len = 2; + /* FALLTHROUGH */ + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + case EndpointOutRequest | USB_REQ_SET_FEATURE: + dev_dbg (hcd->self.controller, "no endpoint features yet\n"); + break; + + /* CLASS REQUESTS (and errors) */ + + default: + /* non-generic request */ + if (HC_IS_SUSPENDED (hcd->state)) + status = -EAGAIN; + else { + switch (typeReq) { + case GetHubStatus: + case GetPortStatus: + len = 4; + break; + case GetHubDescriptor: + len = sizeof (struct usb_hub_descriptor); + break; + } + status = hcd->driver->hub_control (hcd, + typeReq, wValue, wIndex, + tbuf, wLength); + } + break; +error: + /* "protocol stall" on error */ + status = -EPIPE; + } + + if (status) { + len = 0; + if (status != -EPIPE) { + dev_dbg (hcd->self.controller, + "CTRL: TypeReq=0x%x val=0x%x " + "idx=0x%x len=%d ==> %d\n", + typeReq, wValue, wIndex, + wLength, urb->status); + } + } + if (len) { + if (urb->transfer_buffer_length < len) + len = urb->transfer_buffer_length; + urb->actual_length = len; + // always USB_DIR_IN, toward host + memcpy (ubuf, bufp, len); + + /* report whether RH hardware supports remote wakeup */ + if (patch_wakeup && + len > offsetof (struct usb_config_descriptor, + bmAttributes)) + ((struct usb_config_descriptor *)ubuf)->bmAttributes + |= USB_CONFIG_ATT_WAKEUP; + } + + /* any errors get returned through the urb completion */ + local_irq_save (flags); + spin_lock (&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock (&urb->lock); + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_restore (flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * Root Hub interrupt transfers are synthesized with a timer. + * Completions are called in_interrupt() but not in_irq(). + * + * Note: some root hubs (including common UHCI based designs) can't + * correctly issue port change IRQs. They're the ones that _need_ a + * timer; most other root hubs don't. Some systems could save a + * lot of battery power by eliminating these root hub timer IRQs. + */ + +static void rh_report_status (unsigned long ptr); + +static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) +{ + int len = 1 + (urb->dev->maxchild / 8); + + /* rh_timer protected by hcd_data_lock */ + if (hcd->rh_timer.data || urb->transfer_buffer_length < len) { + dev_dbg (hcd->self.controller, + "not queuing rh status urb, stat %d\n", + urb->status); + return -EINVAL; + } + + init_timer (&hcd->rh_timer); + hcd->rh_timer.function = rh_report_status; + hcd->rh_timer.data = (unsigned long) urb; + /* USB 2.0 spec says 256msec; this is close enough */ + hcd->rh_timer.expires = jiffies + HZ/4; + add_timer (&hcd->rh_timer); + urb->hcpriv = hcd; /* nonzero to indicate it's queued */ + return 0; +} + +/* timer callback */ + +static void rh_report_status (unsigned long ptr) +{ + struct urb *urb; + struct usb_hcd *hcd; + int length = 0; + unsigned long flags; + + urb = (struct urb *) ptr; + local_irq_save (flags); + spin_lock (&urb->lock); + + /* do nothing if the urb's been unlinked */ + if (!urb->dev + || urb->status != -EINPROGRESS + || (hcd = urb->dev->bus->hcpriv) == NULL) { + spin_unlock (&urb->lock); + local_irq_restore (flags); + return; + } + + /* complete the status urb, or retrigger the timer */ + spin_lock (&hcd_data_lock); + if (urb->dev->state == USB_STATE_CONFIGURED) { + length = hcd->driver->hub_status_data ( + hcd, urb->transfer_buffer); + if (length > 0) { + hcd->rh_timer.data = 0; + urb->actual_length = length; + urb->status = 0; + urb->hcpriv = NULL; + } else + mod_timer (&hcd->rh_timer, jiffies + HZ/4); + } + spin_unlock (&hcd_data_lock); + spin_unlock (&urb->lock); + + /* local irqs are always blocked in completions */ + if (length > 0) + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_restore (flags); +} + +/*-------------------------------------------------------------------------*/ + +static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) +{ + if (usb_pipeint (urb->pipe)) { + int retval; + unsigned long flags; + + spin_lock_irqsave (&hcd_data_lock, flags); + retval = rh_status_urb (hcd, urb); + spin_unlock_irqrestore (&hcd_data_lock, flags); + return retval; + } + if (usb_pipecontrol (urb->pipe)) + return rh_call_control (hcd, urb); + else + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + +static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +{ + unsigned long flags; + + /* note: always a synchronous unlink */ + if ((unsigned long) urb == hcd->rh_timer.data) { + del_timer_sync (&hcd->rh_timer); + hcd->rh_timer.data = 0; + + local_irq_save (flags); + urb->hcpriv = NULL; + usb_hcd_giveback_urb (hcd, urb, NULL); + local_irq_restore (flags); + + } else if (usb_pipeendpoint(urb->pipe) == 0) { + spin_lock_irq(&urb->lock); /* from usb_kill_urb */ + ++urb->reject; + spin_unlock_irq(&urb->lock); + + wait_event(usb_kill_urb_queue, + atomic_read(&urb->use_count) == 0); + + spin_lock_irq(&urb->lock); + --urb->reject; + spin_unlock_irq(&urb->lock); + } else + return -EINVAL; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* exported only within usbcore */ +struct usb_bus *usb_bus_get (struct usb_bus *bus) +{ + struct class_device *tmp; + + if (!bus) + return NULL; + + tmp = class_device_get(&bus->class_dev); + if (tmp) + return to_usb_bus(tmp); + else + return NULL; +} + +/* exported only within usbcore */ +void usb_bus_put (struct usb_bus *bus) +{ + if (bus) + class_device_put(&bus->class_dev); +} + +/*-------------------------------------------------------------------------*/ + +static void usb_host_release(struct class_device *class_dev) +{ + struct usb_bus *bus = to_usb_bus(class_dev); + + if (bus->release) + bus->release(bus); +} + +static struct class usb_host_class = { + .name = "usb_host", + .release = &usb_host_release, +}; + +int usb_host_init(void) +{ + return class_register(&usb_host_class); +} + +void usb_host_cleanup(void) +{ + class_unregister(&usb_host_class); +} + +/** + * usb_bus_init - shared initialization code + * @bus: the bus structure being initialized + * + * This code is used to initialize a usb_bus structure, memory for which is + * separately managed. + */ +static void usb_bus_init (struct usb_bus *bus) +{ + memset (&bus->devmap, 0, sizeof(struct usb_devmap)); + + bus->devnum_next = 1; + + bus->root_hub = NULL; + bus->hcpriv = NULL; + bus->busnum = -1; + bus->bandwidth_allocated = 0; + bus->bandwidth_int_reqs = 0; + bus->bandwidth_isoc_reqs = 0; + + INIT_LIST_HEAD (&bus->bus_list); + + class_device_initialize(&bus->class_dev); + bus->class_dev.class = &usb_host_class; +} + +/** + * usb_alloc_bus - creates a new USB host controller structure + * @op: pointer to a struct usb_operations that this bus structure should use + * Context: !in_interrupt() + * + * Creates a USB host controller bus structure with the specified + * usb_operations and initializes all the necessary internal objects. + * + * If no memory is available, NULL is returned. + * + * The caller should call usb_put_bus() when it is finished with the structure. + */ +struct usb_bus *usb_alloc_bus (struct usb_operations *op) +{ + struct usb_bus *bus; + + bus = kmalloc (sizeof *bus, GFP_KERNEL); + if (!bus) + return NULL; + memset(bus, 0, sizeof(struct usb_bus)); + usb_bus_init (bus); + bus->op = op; + return bus; +} + +/*-------------------------------------------------------------------------*/ + +/** + * usb_register_bus - registers the USB host controller with the usb core + * @bus: pointer to the bus to register + * Context: !in_interrupt() + * + * Assigns a bus number, and links the controller into usbcore data + * structures so that it can be seen by scanning the bus list. + */ +static int usb_register_bus(struct usb_bus *bus) +{ + int busnum; + int retval; + + down (&usb_bus_list_lock); + busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); + if (busnum < USB_MAXBUS) { + set_bit (busnum, busmap.busmap); + bus->busnum = busnum; + } else { + printk (KERN_ERR "%s: too many buses\n", usbcore_name); + up(&usb_bus_list_lock); + return -E2BIG; + } + + snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum); + bus->class_dev.dev = bus->controller; + retval = class_device_add(&bus->class_dev); + if (retval) { + clear_bit(busnum, busmap.busmap); + up(&usb_bus_list_lock); + return retval; + } + + /* Add it to the local list of buses */ + list_add (&bus->bus_list, &usb_bus_list); + up (&usb_bus_list_lock); + + usbfs_add_bus (bus); + usbmon_notify_bus_add (bus); + + dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum); + return 0; +} + +/** + * usb_deregister_bus - deregisters the USB host controller + * @bus: pointer to the bus to deregister + * Context: !in_interrupt() + * + * Recycles the bus number, and unlinks the controller from usbcore data + * structures so that it won't be seen by scanning the bus list. + */ +static void usb_deregister_bus (struct usb_bus *bus) +{ + dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum); + + /* + * NOTE: make sure that all the devices are removed by the + * controller code, as well as having it call this when cleaning + * itself up + */ + down (&usb_bus_list_lock); + list_del (&bus->bus_list); + up (&usb_bus_list_lock); + + usbmon_notify_bus_remove (bus); + usbfs_remove_bus (bus); + + clear_bit (bus->busnum, busmap.busmap); + + class_device_del(&bus->class_dev); +} + +/** + * usb_hcd_register_root_hub - called by HCD to register its root hub + * @usb_dev: the usb root hub device to be registered. + * @hcd: host controller for this root hub + * + * The USB host controller calls this function to register the root hub + * properly with the USB subsystem. It sets up the device properly in + * the device tree and stores the root_hub pointer in the bus structure, + * then calls usb_new_device() to register the usb device. It also + * assigns the root hub's USB address (always 1). + */ +int usb_hcd_register_root_hub (struct usb_device *usb_dev, struct usb_hcd *hcd) +{ + struct device *parent_dev = hcd->self.controller; + const int devnum = 1; + int retval; + + /* hcd->driver->start() reported can_wakeup, probably with + * assistance from board's boot firmware. + * NOTE: normal devices won't enable wakeup by default. + */ + if (hcd->can_wakeup) + dev_dbg (parent_dev, "supports USB remote wakeup\n"); + hcd->remote_wakeup = hcd->can_wakeup; + + usb_dev->devnum = devnum; + usb_dev->bus->devnum_next = devnum + 1; + memset (&usb_dev->bus->devmap.devicemap, 0, + sizeof usb_dev->bus->devmap.devicemap); + set_bit (devnum, usb_dev->bus->devmap.devicemap); + usb_set_device_state(usb_dev, USB_STATE_ADDRESS); + + down (&usb_bus_list_lock); + usb_dev->bus->root_hub = usb_dev; + + usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); + retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); + if (retval != sizeof usb_dev->descriptor) { + usb_dev->bus->root_hub = NULL; + up (&usb_bus_list_lock); + dev_dbg (parent_dev, "can't read %s device descriptor %d\n", + usb_dev->dev.bus_id, retval); + return (retval < 0) ? retval : -EMSGSIZE; + } + + usb_lock_device (usb_dev); + retval = usb_new_device (usb_dev); + usb_unlock_device (usb_dev); + if (retval) { + usb_dev->bus->root_hub = NULL; + dev_err (parent_dev, "can't register root hub for %s, %d\n", + usb_dev->dev.bus_id, retval); + } + up (&usb_bus_list_lock); + + if (retval == 0) { + spin_lock_irq (&hcd_root_hub_lock); + hcd->rh_registered = 1; + spin_unlock_irq (&hcd_root_hub_lock); + + /* Did the HC die before the root hub was registered? */ + if (hcd->state == HC_STATE_HALT) + usb_hc_died (hcd); /* This time clean up */ + } + + return retval; +} +EXPORT_SYMBOL_GPL(usb_hcd_register_root_hub); + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_calc_bus_time - approximate periodic transaction time in nanoseconds + * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH} + * @is_input: true iff the transaction sends data to the host + * @isoc: true for isochronous transactions, false for interrupt ones + * @bytecount: how many bytes in the transaction. + * + * Returns approximate bus time in nanoseconds for a periodic transaction. + * See USB 2.0 spec section 5.11.3; only periodic transfers need to be + * scheduled in software, this function is only used for such scheduling. + */ +long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) +{ + unsigned long tmp; + + switch (speed) { + case USB_SPEED_LOW: /* INTR only */ + if (is_input) { + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } else { + tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + case USB_SPEED_FULL: /* ISOC or INTR */ + if (isoc) { + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); + } else { + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (9107L + BW_HOST_DELAY + tmp); + } + case USB_SPEED_HIGH: /* ISOC or INTR */ + // FIXME adjust for input vs output + if (isoc) + tmp = HS_USECS (bytecount); + else + tmp = HS_USECS_ISO (bytecount); + return tmp; + default: + pr_debug ("%s: bogus device speed!\n", usbcore_name); + return -1; + } +} +EXPORT_SYMBOL (usb_calc_bus_time); + +/* + * usb_check_bandwidth(): + * + * old_alloc is from host_controller->bandwidth_allocated in microseconds; + * bustime is from calc_bus_time(), but converted to microseconds. + * + * returns <bustime in us> if successful, + * or -ENOSPC if bandwidth request fails. + * + * FIXME: + * This initial implementation does not use Endpoint.bInterval + * in managing bandwidth allocation. + * It probably needs to be expanded to use Endpoint.bInterval. + * This can be done as a later enhancement (correction). + * + * This will also probably require some kind of + * frame allocation tracking...meaning, for example, + * that if multiple drivers request interrupts every 10 USB frames, + * they don't all have to be allocated at + * frame numbers N, N+10, N+20, etc. Some of them could be at + * N+11, N+21, N+31, etc., and others at + * N+12, N+22, N+32, etc. + * + * Similarly for isochronous transfers... + * + * Individual HCDs can schedule more directly ... this logic + * is not correct for high speed transfers. + */ +int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) +{ + unsigned int pipe = urb->pipe; + long bustime; + int is_in = usb_pipein (pipe); + int is_iso = usb_pipeisoc (pipe); + int old_alloc = dev->bus->bandwidth_allocated; + int new_alloc; + + + bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso, + usb_maxpacket (dev, pipe, !is_in))); + if (is_iso) + bustime /= urb->number_of_packets; + + new_alloc = old_alloc + (int) bustime; + if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) { +#ifdef DEBUG + char *mode = +#ifdef CONFIG_USB_BANDWIDTH + ""; +#else + "would have "; +#endif + dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n", + mode, old_alloc, bustime, new_alloc); +#endif +#ifdef CONFIG_USB_BANDWIDTH + bustime = -ENOSPC; /* report error */ +#endif + } + + return bustime; +} +EXPORT_SYMBOL (usb_check_bandwidth); + + +/** + * usb_claim_bandwidth - records bandwidth for a periodic transfer + * @dev: source/target of request + * @urb: request (urb->dev == dev) + * @bustime: bandwidth consumed, in (average) microseconds per frame + * @isoc: true iff the request is isochronous + * + * Bus bandwidth reservations are recorded purely for diagnostic purposes. + * HCDs are expected not to overcommit periodic bandwidth, and to record such + * reservations whenever endpoints are added to the periodic schedule. + * + * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's + * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable + * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how + * large its periodic schedule is. + */ +void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) +{ + dev->bus->bandwidth_allocated += bustime; + if (isoc) + dev->bus->bandwidth_isoc_reqs++; + else + dev->bus->bandwidth_int_reqs++; + urb->bandwidth = bustime; + +#ifdef USB_BANDWIDTH_MESSAGES + dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n", + bustime, + isoc ? "ISOC" : "INTR", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +#endif +} +EXPORT_SYMBOL (usb_claim_bandwidth); + + +/** + * usb_release_bandwidth - reverses effect of usb_claim_bandwidth() + * @dev: source/target of request + * @urb: request (urb->dev == dev) + * @isoc: true iff the request is isochronous + * + * This records that previously allocated bandwidth has been released. + * Bandwidth is released when endpoints are removed from the host controller's + * periodic schedule. + */ +void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc) +{ + dev->bus->bandwidth_allocated -= urb->bandwidth; + if (isoc) + dev->bus->bandwidth_isoc_reqs--; + else + dev->bus->bandwidth_int_reqs--; + +#ifdef USB_BANDWIDTH_MESSAGES + dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n", + urb->bandwidth, + isoc ? "ISOC" : "INTR", + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +#endif + urb->bandwidth = 0; +} +EXPORT_SYMBOL (usb_release_bandwidth); + + +/*-------------------------------------------------------------------------*/ + +/* + * Generic HC operations. + */ + +/*-------------------------------------------------------------------------*/ + +static void urb_unlink (struct urb *urb) +{ + unsigned long flags; + + /* Release any periodic transfer bandwidth */ + if (urb->bandwidth) + usb_release_bandwidth (urb->dev, urb, + usb_pipeisoc (urb->pipe)); + + /* clear all state linking urb to this dev (and hcd) */ + + spin_lock_irqsave (&hcd_data_lock, flags); + list_del_init (&urb->urb_list); + spin_unlock_irqrestore (&hcd_data_lock, flags); + usb_put_dev (urb->dev); +} + + +/* may be called in any context with a valid urb->dev usecount + * caller surrenders "ownership" of urb + * expects usb_submit_urb() to have sanity checked and conditioned all + * inputs in the urb + */ +static int hcd_submit_urb (struct urb *urb, int mem_flags) +{ + int status; + struct usb_hcd *hcd = urb->dev->bus->hcpriv; + struct usb_host_endpoint *ep; + unsigned long flags; + + if (!hcd) + return -ENODEV; + + usbmon_urb_submit(&hcd->self, urb); + + /* + * Atomically queue the urb, first to our records, then to the HCD. + * Access to urb->status is controlled by urb->lock ... changes on + * i/o completion (normal or fault) or unlinking. + */ + + // FIXME: verify that quiescing hc works right (RH cleans up) + + spin_lock_irqsave (&hcd_data_lock, flags); + ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (unlikely (!ep)) + status = -ENOENT; + else if (unlikely (urb->reject)) + status = -EPERM; + else switch (hcd->state) { + case HC_STATE_RUNNING: + case HC_STATE_RESUMING: + usb_get_dev (urb->dev); + list_add_tail (&urb->urb_list, &ep->urb_list); + status = 0; + break; + default: + status = -ESHUTDOWN; + break; + } + spin_unlock_irqrestore (&hcd_data_lock, flags); + if (status) { + INIT_LIST_HEAD (&urb->urb_list); + usbmon_urb_submit_error(&hcd->self, urb, status); + return status; + } + + /* increment urb's reference count as part of giving it to the HCD + * (which now controls it). HCD guarantees that it either returns + * an error or calls giveback(), but not both. + */ + urb = usb_get_urb (urb); + atomic_inc (&urb->use_count); + + if (urb->dev == hcd->self.root_hub) { + /* NOTE: requirement on hub callers (usbfs and the hub + * driver, for now) that URBs' urb->transfer_buffer be + * valid and usb_buffer_{sync,unmap}() not be needed, since + * they could clobber root hub response data. + */ + status = rh_urb_enqueue (hcd, urb); + goto done; + } + + /* lower level hcd code should use *_dma exclusively, + * unless it uses pio or talks to another transport. + */ + if (hcd->self.controller->dma_mask) { + if (usb_pipecontrol (urb->pipe) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + urb->setup_dma = dma_map_single ( + hcd->self.controller, + urb->setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + urb->transfer_dma = dma_map_single ( + hcd->self.controller, + urb->transfer_buffer, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + } + + status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags); +done: + if (unlikely (status)) { + urb_unlink (urb); + atomic_dec (&urb->use_count); + if (urb->reject) + wake_up (&usb_kill_urb_queue); + usb_put_urb (urb); + usbmon_urb_submit_error(&hcd->self, urb, status); + } + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* called in any context */ +static int hcd_get_frame_number (struct usb_device *udev) +{ + struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv; + if (!HC_IS_RUNNING (hcd->state)) + return -ESHUTDOWN; + return hcd->driver->get_frame_number (hcd); +} + +/*-------------------------------------------------------------------------*/ + +/* this makes the hcd giveback() the urb more quickly, by kicking it + * off hardware queues (which may take a while) and returning it as + * soon as practical. we've already set up the urb's return status, + * but we can't know if the callback completed already. + */ +static int +unlink1 (struct usb_hcd *hcd, struct urb *urb) +{ + int value; + + if (urb->dev == hcd->self.root_hub) + value = usb_rh_urb_dequeue (hcd, urb); + else { + + /* The only reason an HCD might fail this call is if + * it has not yet fully queued the urb to begin with. + * Such failures should be harmless. */ + value = hcd->driver->urb_dequeue (hcd, urb); + } + + if (value != 0) + dev_dbg (hcd->self.controller, "dequeue %p --> %d\n", + urb, value); + return value; +} + +/* + * called in any context + * + * caller guarantees urb won't be recycled till both unlink() + * and the urb's completion function return + */ +static int hcd_unlink_urb (struct urb *urb, int status) +{ + struct usb_host_endpoint *ep; + struct usb_hcd *hcd = NULL; + struct device *sys = NULL; + unsigned long flags; + struct list_head *tmp; + int retval; + + if (!urb) + return -EINVAL; + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) + return -ENODEV; + + /* + * we contend for urb->status with the hcd core, + * which changes it while returning the urb. + * + * Caller guaranteed that the urb pointer hasn't been freed, and + * that it was submitted. But as a rule it can't know whether or + * not it's already been unlinked ... so we respect the reversed + * lock sequence needed for the usb_hcd_giveback_urb() code paths + * (urb lock, then hcd_data_lock) in case some other CPU is now + * unlinking it. + */ + spin_lock_irqsave (&urb->lock, flags); + spin_lock (&hcd_data_lock); + + sys = &urb->dev->dev; + hcd = urb->dev->bus->hcpriv; + if (hcd == NULL) { + retval = -ENODEV; + goto done; + } + + /* running ~= hc unlink handshake works (irq, timer, etc) + * halted ~= no unlink handshake is needed + * suspended, resuming == should never happen + */ + WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); + + /* insist the urb is still queued */ + list_for_each(tmp, &ep->urb_list) { + if (tmp == &urb->urb_list) + break; + } + if (tmp != &urb->urb_list) { + retval = -EIDRM; + goto done; + } + + /* Any status except -EINPROGRESS means something already started to + * unlink this URB from the hardware. So there's no more work to do. + */ + if (urb->status != -EINPROGRESS) { + retval = -EBUSY; + goto done; + } + + /* IRQ setup can easily be broken so that USB controllers + * never get completion IRQs ... maybe even the ones we need to + * finish unlinking the initial failed usb_set_address() + * or device descriptor fetch. + */ + if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { + dev_warn (hcd->self.controller, "Unlink after no-IRQ? " + "Controller is probably using the wrong IRQ." + "\n"); + hcd->saw_irq = 1; + } + + urb->status = status; + + spin_unlock (&hcd_data_lock); + spin_unlock_irqrestore (&urb->lock, flags); + + retval = unlink1 (hcd, urb); + if (retval == 0) + retval = -EINPROGRESS; + return retval; + +done: + spin_unlock (&hcd_data_lock); + spin_unlock_irqrestore (&urb->lock, flags); + if (retval != -EIDRM && sys && sys->driver) + dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval); + return retval; +} + +/*-------------------------------------------------------------------------*/ + +/* disables the endpoint: cancels any pending urbs, then synchronizes with + * the hcd to make sure all endpoint state is gone from hardware. use for + * set_configuration, set_interface, driver removal, physical disconnect. + * + * example: a qh stored in ep->hcpriv, holding state related to endpoint + * type, maxpacket size, toggle, halt status, and scheduling. + */ +static void +hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) +{ + struct usb_hcd *hcd; + struct urb *urb; + + hcd = udev->bus->hcpriv; + + WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); + + local_irq_disable (); + + /* FIXME move most of this into message.c as part of its + * endpoint disable logic + */ + + /* ep is already gone from udev->ep_{in,out}[]; no more submits */ +rescan: + spin_lock (&hcd_data_lock); + list_for_each_entry (urb, &ep->urb_list, urb_list) { + int tmp; + + /* another cpu may be in hcd, spinning on hcd_data_lock + * to giveback() this urb. the races here should be + * small, but a full fix needs a new "can't submit" + * urb state. + * FIXME urb->reject should allow that... + */ + if (urb->status != -EINPROGRESS) + continue; + usb_get_urb (urb); + spin_unlock (&hcd_data_lock); + + spin_lock (&urb->lock); + tmp = urb->status; + if (tmp == -EINPROGRESS) + urb->status = -ESHUTDOWN; + spin_unlock (&urb->lock); + + /* kick hcd unless it's already returning this */ + if (tmp == -EINPROGRESS) { + tmp = urb->pipe; + unlink1 (hcd, urb); + dev_dbg (hcd->self.controller, + "shutdown urb %p pipe %08x ep%d%s%s\n", + urb, tmp, usb_pipeendpoint (tmp), + (tmp & USB_DIR_IN) ? "in" : "out", + ({ char *s; \ + switch (usb_pipetype (tmp)) { \ + case PIPE_CONTROL: s = ""; break; \ + case PIPE_BULK: s = "-bulk"; break; \ + case PIPE_INTERRUPT: s = "-intr"; break; \ + default: s = "-iso"; break; \ + }; s;})); + } + usb_put_urb (urb); + + /* list contents may have changed */ + goto rescan; + } + spin_unlock (&hcd_data_lock); + local_irq_enable (); + + /* synchronize with the hardware, so old configuration state + * clears out immediately (and will be freed). + */ + might_sleep (); + if (hcd->driver->endpoint_disable) + hcd->driver->endpoint_disable (hcd, ep); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_SUSPEND + +static int hcd_hub_suspend (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_suspend) + return hcd->driver->hub_suspend (hcd); + return 0; +} + +static int hcd_hub_resume (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of (bus, struct usb_hcd, self); + if (hcd->driver->hub_resume) + return hcd->driver->hub_resume (hcd); + return 0; +} + +/** + * usb_hcd_resume_root_hub - called by HCD to resume its root hub + * @hcd: host controller for this root hub + * + * The USB host controller calls this function when its root hub is + * suspended (with the remote wakeup feature enabled) and a remote + * wakeup request is received. It queues a request for khubd to + * resume the root hub. + */ +void usb_hcd_resume_root_hub (struct usb_hcd *hcd) +{ + unsigned long flags; + + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (hcd->rh_registered) + usb_resume_root_hub (hcd->self.root_hub); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); +} + +#else +void usb_hcd_resume_root_hub (struct usb_hcd *hcd) +{ +} +#endif +EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OTG + +/** + * usb_bus_start_enum - start immediate enumeration (for OTG) + * @bus: the bus (must use hcd framework) + * @port_num: 1-based number of port; usually bus->otg_port + * Context: in_interrupt() + * + * Starts enumeration, with an immediate reset followed later by + * khubd identifying and possibly configuring the device. + * This is needed by OTG controller drivers, where it helps meet + * HNP protocol timing requirements for starting a port reset. + */ +int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) +{ + struct usb_hcd *hcd; + int status = -EOPNOTSUPP; + + /* NOTE: since HNP can't start by grabbing the bus's address0_sem, + * boards with root hubs hooked up to internal devices (instead of + * just the OTG port) may need more attention to resetting... + */ + hcd = container_of (bus, struct usb_hcd, self); + if (port_num && hcd->driver->start_port_reset) + status = hcd->driver->start_port_reset(hcd, port_num); + + /* run khubd shortly after (first) root port reset finishes; + * it may issue others, until at least 50 msecs have passed. + */ + if (status == 0) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10)); + return status; +} +EXPORT_SYMBOL (usb_bus_start_enum); + +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue) + */ +static struct usb_operations usb_hcd_operations = { + .get_frame_number = hcd_get_frame_number, + .submit_urb = hcd_submit_urb, + .unlink_urb = hcd_unlink_urb, + .buffer_alloc = hcd_buffer_alloc, + .buffer_free = hcd_buffer_free, + .disable = hcd_endpoint_disable, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = hcd_hub_suspend, + .hub_resume = hcd_hub_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +/** + * usb_hcd_giveback_urb - return URB from HCD to device driver + * @hcd: host controller returning the URB + * @urb: urb being returned to the USB device driver. + * @regs: pt_regs, passed down to the URB completion handler + * Context: in_interrupt() + * + * This hands the URB from HCD to its USB device driver, using its + * completion function. The HCD has freed all per-urb resources + * (and is done using urb->hcpriv). It also released all HCD locks; + * the device driver won't cause problems if it frees, modifies, + * or resubmits this URB. + */ +void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) +{ + int at_root_hub; + + at_root_hub = (urb->dev == hcd->self.root_hub); + urb_unlink (urb); + + /* lower level hcd code should use *_dma exclusively */ + if (hcd->self.controller->dma_mask && !at_root_hub) { + if (usb_pipecontrol (urb->pipe) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + dma_unmap_single (hcd->self.controller, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + dma_unmap_single (hcd->self.controller, + urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + } + + usbmon_urb_complete (&hcd->self, urb); + /* pass ownership to the completion handler */ + urb->complete (urb, regs); + atomic_dec (&urb->use_count); + if (unlikely (urb->reject)) + wake_up (&usb_kill_urb_queue); + usb_put_urb (urb); +} +EXPORT_SYMBOL (usb_hcd_giveback_urb); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_hcd_irq - hook IRQs to HCD framework (bus glue) + * @irq: the IRQ being raised + * @__hcd: pointer to the HCD whose IRQ is being signaled + * @r: saved hardware registers + * + * If the controller isn't HALTed, calls the driver's irq handler. + * Checks whether the controller is now dead. + */ +irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) +{ + struct usb_hcd *hcd = __hcd; + int start = hcd->state; + + if (start == HC_STATE_HALT) + return IRQ_NONE; + if (hcd->driver->irq (hcd, r) == IRQ_NONE) + return IRQ_NONE; + + hcd->saw_irq = 1; + if (hcd->state != start && hcd->state == HC_STATE_HALT) + usb_hc_died (hcd); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/** + * usb_hc_died - report abnormal shutdown of a host controller (bus glue) + * @hcd: pointer to the HCD representing the controller + * + * This is called by bus glue to report a USB host controller that died + * while operations may still have been pending. It's called automatically + * by the PCI glue, so only glue for non-PCI busses should need to call it. + */ +void usb_hc_died (struct usb_hcd *hcd) +{ + unsigned long flags; + + dev_err (hcd->self.controller, "HC died; cleaning up\n"); + + spin_lock_irqsave (&hcd_root_hub_lock, flags); + if (hcd->rh_registered) { + + /* make khubd clean up old urbs and devices */ + usb_set_device_state (hcd->self.root_hub, + USB_STATE_NOTATTACHED); + usb_kick_khubd (hcd->self.root_hub); + } + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); +} +EXPORT_SYMBOL_GPL (usb_hc_died); + +/*-------------------------------------------------------------------------*/ + +static void hcd_release (struct usb_bus *bus) +{ + struct usb_hcd *hcd; + + hcd = container_of(bus, struct usb_hcd, self); + kfree(hcd); +} + +/** + * usb_create_hcd - create and initialize an HCD structure + * @driver: HC driver that will use this hcd + * @dev: device for this HC, stored in hcd->self.controller + * @bus_name: value to store in hcd->self.bus_name + * Context: !in_interrupt() + * + * Allocate a struct usb_hcd, with extra space at the end for the + * HC driver's private data. Initialize the generic members of the + * hcd structure. + * + * If memory is unavailable, returns NULL. + */ +struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, + struct device *dev, char *bus_name) +{ + struct usb_hcd *hcd; + + hcd = kcalloc(1, sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); + if (!hcd) { + dev_dbg (dev, "hcd alloc failed\n"); + return NULL; + } + dev_set_drvdata(dev, hcd); + + usb_bus_init(&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = hcd; + hcd->self.release = &hcd_release; + hcd->self.controller = dev; + hcd->self.bus_name = bus_name; + + init_timer(&hcd->rh_timer); + + hcd->driver = driver; + hcd->product_desc = (driver->product_desc) ? driver->product_desc : + "USB Host Controller"; + + return hcd; +} +EXPORT_SYMBOL (usb_create_hcd); + +void usb_put_hcd (struct usb_hcd *hcd) +{ + dev_set_drvdata(hcd->self.controller, NULL); + usb_bus_put(&hcd->self); +} +EXPORT_SYMBOL (usb_put_hcd); + +/** + * usb_add_hcd - finish generic HCD structure initialization and register + * @hcd: the usb_hcd structure to initialize + * @irqnum: Interrupt line to allocate + * @irqflags: Interrupt type flags + * + * Finish the remaining parts of generic HCD initialization: allocate the + * buffers of consistent memory, register the bus, request the IRQ line, + * and call the driver's reset() and start() routines. + */ +int usb_add_hcd(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags) +{ + int retval; + + dev_info(hcd->self.controller, "%s\n", hcd->product_desc); + + /* till now HC has been in an indeterminate state ... */ + if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { + dev_err(hcd->self.controller, "can't reset\n"); + return retval; + } + + if ((retval = hcd_buffer_create(hcd)) != 0) { + dev_dbg(hcd->self.controller, "pool alloc failed\n"); + return retval; + } + + if ((retval = usb_register_bus(&hcd->self)) < 0) + goto err1; + + if (hcd->driver->irq) { + char buf[8], *bufp = buf; + +#ifdef __sparc__ + bufp = __irq_itoa(irqnum); +#else + sprintf(buf, "%d", irqnum); +#endif + + snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", + hcd->driver->description, hcd->self.busnum); + if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags, + hcd->irq_descr, hcd)) != 0) { + dev_err(hcd->self.controller, + "request interrupt %s failed\n", bufp); + goto err2; + } + hcd->irq = irqnum; + dev_info(hcd->self.controller, "irq %s, %s 0x%08llx\n", bufp, + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } else { + hcd->irq = -1; + if (hcd->rsrc_start) + dev_info(hcd->self.controller, "%s 0x%08llx\n", + (hcd->driver->flags & HCD_MEMORY) ? + "io mem" : "io base", + (unsigned long long)hcd->rsrc_start); + } + + if ((retval = hcd->driver->start(hcd)) < 0) { + dev_err(hcd->self.controller, "startup error %d\n", retval); + goto err3; + } + + return retval; + + err3: + if (hcd->irq >= 0) + free_irq(irqnum, hcd); + err2: + usb_deregister_bus(&hcd->self); + err1: + hcd_buffer_destroy(hcd); + return retval; +} +EXPORT_SYMBOL (usb_add_hcd); + +/** + * usb_remove_hcd - shutdown processing for generic HCDs + * @hcd: the usb_hcd structure to remove + * Context: !in_interrupt() + * + * Disconnects the root hub, then reverses the effects of usb_add_hcd(), + * invoking the HCD's stop() method. + */ +void usb_remove_hcd(struct usb_hcd *hcd) +{ + dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); + + if (HC_IS_RUNNING (hcd->state)) + hcd->state = HC_STATE_QUIESCING; + + dev_dbg(hcd->self.controller, "roothub graceful disconnect\n"); + spin_lock_irq (&hcd_root_hub_lock); + hcd->rh_registered = 0; + spin_unlock_irq (&hcd_root_hub_lock); + usb_disconnect(&hcd->self.root_hub); + + hcd->driver->stop(hcd); + hcd->state = HC_STATE_HALT; + + if (hcd->irq >= 0) + free_irq(hcd->irq, hcd); + usb_deregister_bus(&hcd->self); + hcd_buffer_destroy(hcd); +} +EXPORT_SYMBOL (usb_remove_hcd); + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + +struct usb_mon_operations *mon_ops; + +/* + * The registration is unlocked. + * We do it this way because we do not want to lock in hot paths. + * + * Notice that the code is minimally error-proof. Because usbmon needs + * symbols from usbcore, usbcore gets referenced and cannot be unloaded first. + */ + +int usb_mon_register (struct usb_mon_operations *ops) +{ + + if (mon_ops) + return -EBUSY; + + mon_ops = ops; + mb(); + return 0; +} +EXPORT_SYMBOL_GPL (usb_mon_register); + +void usb_mon_deregister (void) +{ + + if (mon_ops == NULL) { + printk(KERN_ERR "USB: monitor was not registered\n"); + return; + } + mon_ops = NULL; + mb(); +} +EXPORT_SYMBOL_GPL (usb_mon_deregister); + +#endif /* CONFIG_USB_MON */ diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h new file mode 100644 index 0000000..6c625b3 --- /dev/null +++ b/drivers/usb/core/hcd.h @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#ifdef __KERNEL__ + +/* This file contains declarations of usbcore internals that are mostly + * used or exposed by Host Controller Drivers. + */ + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +/*-------------------------------------------------------------------------*/ + +/* + * USB Host Controller Driver (usb_hcd) framework + * + * Since "struct usb_bus" is so thin, you can't share much code in it. + * This framework is a layer over that, and should be more sharable. + */ + +/*-------------------------------------------------------------------------*/ + +struct usb_hcd { /* usb_bus.hcpriv points to this */ + + /* + * housekeeping + */ + struct usb_bus self; /* hcd is-a bus */ + + const char *product_desc; /* product/vendor string */ + char irq_descr[24]; /* driver + bus # */ + + struct timer_list rh_timer; /* drives root hub */ + + /* + * hardware info/state + */ + const struct hc_driver *driver; /* hw-specific hooks */ + unsigned saw_irq : 1; + unsigned can_wakeup:1; /* hw supports wakeup? */ + unsigned remote_wakeup:1;/* sw should use wakeup? */ + unsigned rh_registered:1;/* is root hub registered? */ + + int irq; /* irq allocated */ + void __iomem *regs; /* device memory/io */ + u64 rsrc_start; /* memory/io resource start */ + u64 rsrc_len; /* memory/io resource length */ + +#define HCD_BUFFER_POOLS 4 + struct dma_pool *pool [HCD_BUFFER_POOLS]; + + int state; +# define __ACTIVE 0x01 +# define __SUSPEND 0x04 +# define __TRANSIENT 0x80 + +# define HC_STATE_HALT 0 +# define HC_STATE_RUNNING (__ACTIVE) +# define HC_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) +# define HC_STATE_RESUMING (__SUSPEND|__TRANSIENT) +# define HC_STATE_SUSPENDED (__SUSPEND) + +#define HC_IS_RUNNING(state) ((state) & __ACTIVE) +#define HC_IS_SUSPENDED(state) ((state) & __SUSPEND) + + /* more shared queuing code would be good; it should support + * smarter scheduling, handle transaction translators, etc; + * input size of periodic table to an interrupt scheduler. + * (ohci 32, uhci 1024, ehci 256/512/1024). + */ + + /* The HC driver's private data is stored at the end of + * this structure. + */ + unsigned long hcd_priv[0] + __attribute__ ((aligned (sizeof(unsigned long)))); +}; + +/* 2.4 does this a bit differently ... */ +static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) +{ + return &hcd->self; +} + + +// urb.hcpriv is really hardware-specific + +struct hcd_timeout { /* timeouts we allocate */ + struct list_head timeout_list; + struct timer_list timer; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * FIXME usb_operations should vanish or become hc_driver, + * when usb_bus and usb_hcd become the same thing. + */ + +struct usb_operations { + int (*get_frame_number) (struct usb_device *usb_dev); + int (*submit_urb) (struct urb *urb, int mem_flags); + int (*unlink_urb) (struct urb *urb, int status); + + /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ + void *(*buffer_alloc)(struct usb_bus *bus, size_t size, + int mem_flags, + dma_addr_t *dma); + void (*buffer_free)(struct usb_bus *bus, size_t size, + void *addr, dma_addr_t dma); + + void (*disable)(struct usb_device *udev, + struct usb_host_endpoint *ep); + + /* global suspend/resume of bus */ + int (*hub_suspend)(struct usb_bus *); + int (*hub_resume)(struct usb_bus *); +}; + +/* each driver provides one of these, and hardware init support */ + +struct pt_regs; + +struct hc_driver { + const char *description; /* "ehci-hcd" etc */ + const char *product_desc; /* product/vendor string */ + size_t hcd_priv_size; /* size of private data */ + + /* irq handler */ + irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs); + + int flags; +#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */ +#define HCD_USB11 0x0010 /* USB 1.1 */ +#define HCD_USB2 0x0020 /* USB 2.0 */ + + /* called to init HCD and root hub */ + int (*reset) (struct usb_hcd *hcd); + int (*start) (struct usb_hcd *hcd); + + /* NOTE: these suspend/resume calls relate to the HC as + * a whole, not just the root hub; they're for bus glue. + */ + /* called after all devices were suspended */ + int (*suspend) (struct usb_hcd *hcd, u32 state); + + /* called before any devices get resumed */ + int (*resume) (struct usb_hcd *hcd); + + /* cleanly make HCD stop writing memory and doing I/O */ + void (*stop) (struct usb_hcd *hcd); + + /* return current frame number */ + int (*get_frame_number) (struct usb_hcd *hcd); + + /* manage i/o requests, device state */ + int (*urb_enqueue) (struct usb_hcd *hcd, + struct usb_host_endpoint *ep, + struct urb *urb, + int mem_flags); + int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb); + + /* hw synch, freeing endpoint resources that urb_dequeue can't */ + void (*endpoint_disable)(struct usb_hcd *hcd, + struct usb_host_endpoint *ep); + + /* root hub support */ + int (*hub_status_data) (struct usb_hcd *hcd, char *buf); + int (*hub_control) (struct usb_hcd *hcd, + u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength); + int (*hub_suspend)(struct usb_hcd *); + int (*hub_resume)(struct usb_hcd *); + int (*start_port_reset)(struct usb_hcd *, unsigned port_num); +}; + +extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); + +extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, + struct device *dev, char *bus_name); +extern void usb_put_hcd (struct usb_hcd *hcd); +extern int usb_add_hcd(struct usb_hcd *hcd, + unsigned int irqnum, unsigned long irqflags); +extern void usb_remove_hcd(struct usb_hcd *hcd); + +#ifdef CONFIG_PCI +struct pci_dev; +struct pci_device_id; +extern int usb_hcd_pci_probe (struct pci_dev *dev, + const struct pci_device_id *id); +extern void usb_hcd_pci_remove (struct pci_dev *dev); + +#ifdef CONFIG_PM +extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state); +extern int usb_hcd_pci_resume (struct pci_dev *dev); +#endif /* CONFIG_PM */ + +#endif /* CONFIG_PCI */ + +/* pci-ish (pdev null is ok) buffer alloc/mapping support */ +int hcd_buffer_create (struct usb_hcd *hcd); +void hcd_buffer_destroy (struct usb_hcd *hcd); + +void *hcd_buffer_alloc (struct usb_bus *bus, size_t size, + int mem_flags, dma_addr_t *dma); +void hcd_buffer_free (struct usb_bus *bus, size_t size, + void *addr, dma_addr_t dma); + +/* generic bus glue, needed for host controllers that don't use PCI */ +extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); +extern void usb_hc_died (struct usb_hcd *hcd); + +/* -------------------------------------------------------------------------- */ + +/* Enumeration is only for the hub driver, or HCD virtual root hubs */ +extern struct usb_device *usb_alloc_dev(struct usb_device *parent, + struct usb_bus *, unsigned port); +extern int usb_new_device(struct usb_device *dev); +extern void usb_disconnect(struct usb_device **); + +extern int usb_get_configuration(struct usb_device *dev); +extern void usb_destroy_configuration(struct usb_device *dev); + +/*-------------------------------------------------------------------------*/ + +/* + * HCD Root Hub support + */ + +#include "hub.h" + +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ +#define DeviceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define DeviceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) + +#define InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + +#define EndpointRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + +/* class requests from the USB 2.0 hub spec, table 11-15 */ +/* GetBusState and SetHubDescriptor are optional, omitted */ +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) + + +/*-------------------------------------------------------------------------*/ + +/* + * Generic bandwidth allocation constants/support + */ +#define FRAME_TIME_USECS 1000L +#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */ + /* Trying not to use worst-case bit-stuffing + of (7/6 * 8 * bytecount) = 9.33 * bytecount */ + /* bytecount = data payload byte count */ + +#define NS_TO_US(ns) ((ns + 500L) / 1000L) + /* convert & round nanoseconds to microseconds */ + +extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, + int bustime, int isoc); +extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, + int isoc); + +/* + * Full/low speed bandwidth allocation constants/support. + */ +#define BW_HOST_DELAY 1000L /* nanoseconds */ +#define BW_HUB_LS_SETUP 333L /* nanoseconds */ + /* 4 full-speed bit times (est.) */ + +#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */ +#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) +#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) + +extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); + +/* + * Ceiling microseconds (typical) for that many bytes at high speed + * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed + * to preallocate bandwidth) + */ +#define USB2_HOST_DELAY 5 /* nsec, guess */ +#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \ + + ((2083UL * (3167 + BitTime (bytes)))/1000) \ + + USB2_HOST_DELAY) +#define HS_USECS_ISO(bytes) NS_TO_US ( ((38 * 8 * 2083)/1000) \ + + ((2083UL * (3167 + BitTime (bytes)))/1000) \ + + USB2_HOST_DELAY) + +extern long usb_calc_bus_time (int speed, int is_input, + int isoc, int bytecount); + +/*-------------------------------------------------------------------------*/ + +extern struct usb_bus *usb_alloc_bus (struct usb_operations *); + +extern int usb_hcd_register_root_hub (struct usb_device *usb_dev, + struct usb_hcd *hcd); + +extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); + +extern void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state); + +/*-------------------------------------------------------------------------*/ + +/* exported only within usbcore */ + +extern struct list_head usb_bus_list; +extern struct semaphore usb_bus_list_lock; +extern wait_queue_head_t usb_kill_urb_queue; + +extern struct usb_bus *usb_bus_get (struct usb_bus *bus); +extern void usb_bus_put (struct usb_bus *bus); + +extern int usb_find_interface_driver (struct usb_device *dev, + struct usb_interface *interface); + +#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) + +/* + * USB device fs stuff + */ + +#ifdef CONFIG_USB_DEVICEFS + +/* + * these are expected to be called from the USB core/hub thread + * with the kernel lock held + */ +extern void usbfs_add_bus(struct usb_bus *bus); +extern void usbfs_remove_bus(struct usb_bus *bus); +extern void usbfs_add_device(struct usb_device *dev); +extern void usbfs_remove_device(struct usb_device *dev); +extern void usbfs_update_special (void); + +extern int usbfs_init(void); +extern void usbfs_cleanup(void); + +#else /* CONFIG_USB_DEVICEFS */ + +static inline void usbfs_add_bus(struct usb_bus *bus) {} +static inline void usbfs_remove_bus(struct usb_bus *bus) {} +static inline void usbfs_add_device(struct usb_device *dev) {} +static inline void usbfs_remove_device(struct usb_device *dev) {} +static inline void usbfs_update_special (void) {} + +static inline int usbfs_init(void) { return 0; } +static inline void usbfs_cleanup(void) { } + +#endif /* CONFIG_USB_DEVICEFS */ + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + +struct usb_mon_operations { + void (*urb_submit)(struct usb_bus *bus, struct urb *urb); + void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err); + void (*urb_complete)(struct usb_bus *bus, struct urb *urb); + /* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */ + void (*bus_add)(struct usb_bus *bus); + void (*bus_remove)(struct usb_bus *bus); +}; + +extern struct usb_mon_operations *mon_ops; + +static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) +{ + if (bus->monitored) + (*mon_ops->urb_submit)(bus, urb); +} + +static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, + int error) +{ + if (bus->monitored) + (*mon_ops->urb_submit_error)(bus, urb, error); +} + +static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) +{ + if (bus->monitored) + (*mon_ops->urb_complete)(bus, urb); +} + +static inline void usbmon_notify_bus_add(struct usb_bus *bus) +{ + if (mon_ops) + (*mon_ops->bus_add)(bus); +} + +static inline void usbmon_notify_bus_remove(struct usb_bus *bus) +{ + if (mon_ops) + (*mon_ops->bus_remove)(bus); +} + +int usb_mon_register(struct usb_mon_operations *ops); +void usb_mon_deregister(void); + +#else + +static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {} +static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, + int error) {} +static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {} +static inline void usbmon_notify_bus_add(struct usb_bus *bus) {} +static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {} + +#endif /* CONFIG_USB_MON */ + +/*-------------------------------------------------------------------------*/ + +/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */ +// bleech -- resurfaced in 2.4.11 or 2.4.12 +#define bitmap DeviceRemovable + + +/*-------------------------------------------------------------------------*/ + +/* random stuff */ + +#define RUN_CONTEXT (in_irq () ? "in_irq" \ + : (in_interrupt () ? "in_interrupt" : "can sleep")) + + +#endif /* __KERNEL__ */ + diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c new file mode 100644 index 0000000..fa0dc4f --- /dev/null +++ b/drivers/usb/core/hub.c @@ -0,0 +1,3057 @@ +/* + * USB hub driver. + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Gregory P. Smith + * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au) + * + */ + +#include <linux/config.h> +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/completion.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/usb.h> +#include <linux/usbdevice_fs.h> + +#include <asm/semaphore.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> + +#include "usb.h" +#include "hcd.h" +#include "hub.h" + +/* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->serialize, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ +static DEFINE_SPINLOCK(device_state_lock); + +/* khubd's worklist and its lock */ +static DEFINE_SPINLOCK(hub_event_lock); +static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ + +/* Wakes up khubd */ +static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); + +static pid_t khubd_pid = 0; /* PID of khubd */ +static DECLARE_COMPLETION(khubd_exited); + +/* cycle leds on hubs that aren't blinking for attention */ +static int blinkenlights = 0; +module_param (blinkenlights, bool, S_IRUGO); +MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs"); + +/* + * As of 2.6.10 we introduce a new USB device initialization scheme which + * closely resembles the way Windows works. Hopefully it will be compatible + * with a wider range of devices than the old scheme. However some previously + * working devices may start giving rise to "device not accepting address" + * errors; if that happens the user can try the old scheme by adjusting the + * following module parameters. + * + * For maximum flexibility there are two boolean parameters to control the + * hub driver's behavior. On the first initialization attempt, if the + * "old_scheme_first" parameter is set then the old scheme will be used, + * otherwise the new scheme is used. If that fails and "use_both_schemes" + * is set, then the driver will make another attempt, using the other scheme. + */ +static int old_scheme_first = 0; +module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(old_scheme_first, + "start with the old device initialization scheme"); + +static int use_both_schemes = 1; +module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(use_both_schemes, + "try the other device initialization scheme if the " + "first one fails"); + + +#ifdef DEBUG +static inline char *portspeed (int portstatus) +{ + if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + return "480 Mb/s"; + else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} +#endif + +/* Note that hdev or one of its children must be locked! */ +static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev) +{ + return usb_get_intfdata(hdev->actconfig->interface[0]); +} + +/* USB 2.0 spec Section 11.24.4.5 */ +static int get_hub_descriptor(struct usb_device *hdev, void *data, int size) +{ + int i, ret; + + for (i = 0; i < 3; i++) { + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, + USB_CTRL_GET_TIMEOUT); + if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) + return ret; + } + return -EINVAL; +} + +/* + * USB 2.0 spec Section 11.24.2.1 + */ +static int clear_hub_feature(struct usb_device *hdev, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, 1000); +} + +/* + * USB 2.0 spec Section 11.24.2.2 + */ +static int clear_port_feature(struct usb_device *hdev, int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +/* + * USB 2.0 spec Section 11.24.2.13 + */ +static int set_port_feature(struct usb_device *hdev, int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +/* + * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7 + * for info about using port indicators + */ +static void set_port_led( + struct usb_hub *hub, + int port1, + int selector +) +{ + int status = set_port_feature(hub->hdev, (selector << 8) | port1, + USB_PORT_FEAT_INDICATOR); + if (status < 0) + dev_dbg (hub->intfdev, + "port %d indicator %s status %d\n", + port1, + ({ char *s; switch (selector) { + case HUB_LED_AMBER: s = "amber"; break; + case HUB_LED_GREEN: s = "green"; break; + case HUB_LED_OFF: s = "off"; break; + case HUB_LED_AUTO: s = "auto"; break; + default: s = "??"; break; + }; s; }), + status); +} + +#define LED_CYCLE_PERIOD ((2*HZ)/3) + +static void led_work (void *__hub) +{ + struct usb_hub *hub = __hub; + struct usb_device *hdev = hub->hdev; + unsigned i; + unsigned changed = 0; + int cursor = -1; + + if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing) + return; + + for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + unsigned selector, mode; + + /* 30%-50% duty cycle */ + + switch (hub->indicator[i]) { + /* cycle marker */ + case INDICATOR_CYCLE: + cursor = i; + selector = HUB_LED_AUTO; + mode = INDICATOR_AUTO; + break; + /* blinking green = sw attention */ + case INDICATOR_GREEN_BLINK: + selector = HUB_LED_GREEN; + mode = INDICATOR_GREEN_BLINK_OFF; + break; + case INDICATOR_GREEN_BLINK_OFF: + selector = HUB_LED_OFF; + mode = INDICATOR_GREEN_BLINK; + break; + /* blinking amber = hw attention */ + case INDICATOR_AMBER_BLINK: + selector = HUB_LED_AMBER; + mode = INDICATOR_AMBER_BLINK_OFF; + break; + case INDICATOR_AMBER_BLINK_OFF: + selector = HUB_LED_OFF; + mode = INDICATOR_AMBER_BLINK; + break; + /* blink green/amber = reserved */ + case INDICATOR_ALT_BLINK: + selector = HUB_LED_GREEN; + mode = INDICATOR_ALT_BLINK_OFF; + break; + case INDICATOR_ALT_BLINK_OFF: + selector = HUB_LED_AMBER; + mode = INDICATOR_ALT_BLINK; + break; + default: + continue; + } + if (selector != HUB_LED_AUTO) + changed = 1; + set_port_led(hub, i + 1, selector); + hub->indicator[i] = mode; + } + if (!changed && blinkenlights) { + cursor++; + cursor %= hub->descriptor->bNbrPorts; + set_port_led(hub, cursor + 1, HUB_LED_GREEN); + hub->indicator[cursor] = INDICATOR_CYCLE; + changed++; + } + if (changed) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); +} + +/* use a short timeout for hub/port status fetches */ +#define USB_STS_TIMEOUT 1000 +#define USB_STS_RETRIES 5 + +/* + * USB 2.0 spec Section 11.24.2.6 + */ +static int get_hub_status(struct usb_device *hdev, + struct usb_hub_status *data) +{ + int i, status = -ETIMEDOUT; + + for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) { + status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(*data), USB_STS_TIMEOUT); + } + return status; +} + +/* + * USB 2.0 spec Section 11.24.2.7 + */ +static int get_port_status(struct usb_device *hdev, int port1, + struct usb_port_status *data) +{ + int i, status = -ETIMEDOUT; + + for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) { + status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1, + data, sizeof(*data), USB_STS_TIMEOUT); + } + return status; +} + +static void kick_khubd(struct usb_hub *hub) +{ + unsigned long flags; + + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); +} + +void usb_kick_khubd(struct usb_device *hdev) +{ + kick_khubd(hdev_to_hub(hdev)); +} + + +/* completion function, fires on port status changes and various faults */ +static void hub_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_hub *hub = (struct usb_hub *)urb->context; + int status; + int i; + unsigned long bits; + + switch (urb->status) { + case -ENOENT: /* synchronous unlink */ + case -ECONNRESET: /* async unlink */ + case -ESHUTDOWN: /* hardware going away */ + return; + + default: /* presumably an error */ + /* Cause a hub reset after 10 consecutive errors */ + dev_dbg (hub->intfdev, "transfer --> %d\n", urb->status); + if ((++hub->nerrors < 10) || hub->error) + goto resubmit; + hub->error = urb->status; + /* FALL THROUGH */ + + /* let khubd handle things */ + case 0: /* we got data: port status changed */ + bits = 0; + for (i = 0; i < urb->actual_length; ++i) + bits |= ((unsigned long) ((*hub->buffer)[i])) + << (i*8); + hub->event_bits[0] = bits; + break; + } + + hub->nerrors = 0; + + /* Something happened, let khubd figure it out */ + kick_khubd(hub); + +resubmit: + if (hub->quiescing) + return; + + if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0 + && status != -ENODEV && status != -EPERM) + dev_err (hub->intfdev, "resubmit --> %d\n", status); +} + +/* USB 2.0 spec Section 11.24.2.3 */ +static inline int +hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) +{ + return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo, + tt, NULL, 0, 1000); +} + +/* + * enumeration blocks khubd for a long time. we use keventd instead, since + * long blocking there is the exception, not the rule. accordingly, HCDs + * talking to TTs must queue control transfers (not just bulk and iso), so + * both can talk to the same hub concurrently. + */ +static void hub_tt_kevent (void *arg) +{ + struct usb_hub *hub = arg; + unsigned long flags; + + spin_lock_irqsave (&hub->tt.lock, flags); + while (!list_empty (&hub->tt.clear_list)) { + struct list_head *temp; + struct usb_tt_clear *clear; + struct usb_device *hdev = hub->hdev; + int status; + + temp = hub->tt.clear_list.next; + clear = list_entry (temp, struct usb_tt_clear, clear_list); + list_del (&clear->clear_list); + + /* drop lock so HCD can concurrently report other TT errors */ + spin_unlock_irqrestore (&hub->tt.lock, flags); + status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); + spin_lock_irqsave (&hub->tt.lock, flags); + + if (status) + dev_err (&hdev->dev, + "clear tt %d (%04x) error %d\n", + clear->tt, clear->devinfo, status); + kfree (clear); + } + spin_unlock_irqrestore (&hub->tt.lock, flags); +} + +/** + * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub + * @udev: the device whose split transaction failed + * @pipe: identifies the endpoint of the failed transaction + * + * High speed HCDs use this to tell the hub driver that some split control or + * bulk transaction failed in a way that requires clearing internal state of + * a transaction translator. This is normally detected (and reported) from + * interrupt context. + * + * It may not be possible for that hub to handle additional full (or low) + * speed transactions until that state is fully cleared out. + */ +void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) +{ + struct usb_tt *tt = udev->tt; + unsigned long flags; + struct usb_tt_clear *clear; + + /* we've got to cope with an arbitrary number of pending TT clears, + * since each TT has "at least two" buffers that can need it (and + * there can be many TTs per hub). even if they're uncommon. + */ + if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == NULL) { + dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); + /* FIXME recover somehow ... RESET_TT? */ + return; + } + + /* info that CLEAR_TT_BUFFER needs */ + clear->tt = tt->multi ? udev->ttport : 1; + clear->devinfo = usb_pipeendpoint (pipe); + clear->devinfo |= udev->devnum << 4; + clear->devinfo |= usb_pipecontrol (pipe) + ? (USB_ENDPOINT_XFER_CONTROL << 11) + : (USB_ENDPOINT_XFER_BULK << 11); + if (usb_pipein (pipe)) + clear->devinfo |= 1 << 15; + + /* tell keventd to clear state for this TT */ + spin_lock_irqsave (&tt->lock, flags); + list_add_tail (&clear->clear_list, &tt->clear_list); + schedule_work (&tt->kevent); + spin_unlock_irqrestore (&tt->lock, flags); +} + +static void hub_power_on(struct usb_hub *hub) +{ + int port1; + + /* if hub supports power switching, enable power on each port */ + if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) { + dev_dbg(hub->intfdev, "enabling power on all ports\n"); + for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) + set_port_feature(hub->hdev, port1, + USB_PORT_FEAT_POWER); + } + + /* Wait for power to be enabled */ + msleep(hub->descriptor->bPwrOn2PwrGood * 2); +} + +static void hub_quiesce(struct usb_hub *hub) +{ + /* stop khubd and related activity */ + hub->quiescing = 1; + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work(&hub->leds); + if (hub->has_indicators || hub->tt.hub) + flush_scheduled_work(); +} + +static void hub_activate(struct usb_hub *hub) +{ + int status; + + hub->quiescing = 0; + hub->activating = 1; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(hub->intfdev, "activate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + + /* scan all ports ASAP */ + kick_khubd(hub); +} + +static int hub_hub_status(struct usb_hub *hub, + u16 *status, u16 *change) +{ + int ret; + + ret = get_hub_status(hub->hdev, &hub->status->hub); + if (ret < 0) + dev_err (hub->intfdev, + "%s failed (err = %d)\n", __FUNCTION__, ret); + else { + *status = le16_to_cpu(hub->status->hub.wHubStatus); + *change = le16_to_cpu(hub->status->hub.wHubChange); + ret = 0; + } + return ret; +} + +static int hub_configure(struct usb_hub *hub, + struct usb_endpoint_descriptor *endpoint) +{ + struct usb_device *hdev = hub->hdev; + struct device *hub_dev = hub->intfdev; + u16 hubstatus, hubchange; + unsigned int pipe; + int maxp, ret; + char *message; + + hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, + &hub->buffer_dma); + if (!hub->buffer) { + message = "can't allocate hub irq buffer"; + ret = -ENOMEM; + goto fail; + } + + hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL); + if (!hub->status) { + message = "can't kmalloc hub status buffer"; + ret = -ENOMEM; + goto fail; + } + + hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); + if (!hub->descriptor) { + message = "can't kmalloc hub descriptor"; + ret = -ENOMEM; + goto fail; + } + + /* Request the entire hub descriptor. + * hub->descriptor can handle USB_MAXCHILDREN ports, + * but the hub can/will return fewer bytes here. + */ + ret = get_hub_descriptor(hdev, hub->descriptor, + sizeof(*hub->descriptor)); + if (ret < 0) { + message = "can't read hub descriptor"; + goto fail; + } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) { + message = "hub has too many ports!"; + ret = -ENODEV; + goto fail; + } + + hdev->maxchild = hub->descriptor->bNbrPorts; + dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, + (hdev->maxchild == 1) ? "" : "s"); + + le16_to_cpus(&hub->descriptor->wHubCharacteristics); + + if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) { + int i; + char portstr [USB_MAXCHILDREN + 1]; + + for (i = 0; i < hdev->maxchild; i++) + portstr[i] = hub->descriptor->DeviceRemovable + [((i + 1) / 8)] & (1 << ((i + 1) % 8)) + ? 'F' : 'R'; + portstr[hdev->maxchild] = 0; + dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); + } else + dev_dbg(hub_dev, "standalone hub\n"); + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { + case 0x00: + dev_dbg(hub_dev, "ganged power switching\n"); + break; + case 0x01: + dev_dbg(hub_dev, "individual port power switching\n"); + break; + case 0x02: + case 0x03: + dev_dbg(hub_dev, "no power switching (usb 1.0)\n"); + break; + } + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { + case 0x00: + dev_dbg(hub_dev, "global over-current protection\n"); + break; + case 0x08: + dev_dbg(hub_dev, "individual port over-current protection\n"); + break; + case 0x10: + case 0x18: + dev_dbg(hub_dev, "no over-current protection\n"); + break; + } + + spin_lock_init (&hub->tt.lock); + INIT_LIST_HEAD (&hub->tt.clear_list); + INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub); + switch (hdev->descriptor.bDeviceProtocol) { + case 0: + break; + case 1: + dev_dbg(hub_dev, "Single TT\n"); + hub->tt.hub = hdev; + break; + case 2: + ret = usb_set_interface(hdev, 0, 1); + if (ret == 0) { + dev_dbg(hub_dev, "TT per port\n"); + hub->tt.multi = 1; + } else + dev_err(hub_dev, "Using single TT (err %d)\n", + ret); + hub->tt.hub = hdev; + break; + default: + dev_dbg(hub_dev, "Unrecognized hub protocol %d\n", + hdev->descriptor.bDeviceProtocol); + break; + } + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) { + case 0x00: + if (hdev->descriptor.bDeviceProtocol != 0) + dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n"); + break; + case 0x20: + dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n"); + break; + case 0x40: + dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n"); + break; + case 0x60: + dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n"); + break; + } + + /* probe() zeroes hub->indicator[] */ + if (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) { + hub->has_indicators = 1; + dev_dbg(hub_dev, "Port indicators are supported\n"); + } + + dev_dbg(hub_dev, "power on to power good time: %dms\n", + hub->descriptor->bPwrOn2PwrGood * 2); + + /* power budgeting mostly matters with bus-powered hubs, + * and battery-powered root hubs (may provide just 8 mA). + */ + ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); + if (ret < 0) { + message = "can't get hub status"; + goto fail; + } + cpu_to_le16s(&hubstatus); + if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", + hub->descriptor->bHubContrCurrent); + hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) + / 2; + dev_dbg(hub_dev, "%dmA bus power budget for children\n", + hub->power_budget * 2); + } + + + ret = hub_hub_status(hub, &hubstatus, &hubchange); + if (ret < 0) { + message = "can't get hub status"; + goto fail; + } + + /* local power status reports aren't always correct */ + if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER) + dev_dbg(hub_dev, "local power source is %s\n", + (hubstatus & HUB_STATUS_LOCAL_POWER) + ? "lost (inactive)" : "good"); + + if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) == 0) + dev_dbg(hub_dev, "%sover-current condition exists\n", + (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); + + /* set up the interrupt endpoint */ + pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + + if (maxp > sizeof(*hub->buffer)) + maxp = sizeof(*hub->buffer); + + hub->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!hub->urb) { + message = "couldn't allocate interrupt urb"; + ret = -ENOMEM; + goto fail; + } + + usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + hub->urb->transfer_dma = hub->buffer_dma; + hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* maybe cycle the hub leds */ + if (hub->has_indicators && blinkenlights) + hub->indicator [0] = INDICATOR_CYCLE; + + hub_power_on(hub); + hub_activate(hub); + return 0; + +fail: + dev_err (hub_dev, "config failed, %s (err %d)\n", + message, ret); + /* hub_disconnect() frees urb and descriptor */ + return ret; +} + +static unsigned highspeed_hubs; + +static void hub_disconnect(struct usb_interface *intf) +{ + struct usb_hub *hub = usb_get_intfdata (intf); + struct usb_device *hdev; + + if (!hub) + return; + hdev = hub->hdev; + + if (hdev->speed == USB_SPEED_HIGH) + highspeed_hubs--; + + usb_set_intfdata (intf, NULL); + + hub_quiesce(hub); + usb_free_urb(hub->urb); + hub->urb = NULL; + + spin_lock_irq(&hub_event_lock); + list_del_init(&hub->event_list); + spin_unlock_irq(&hub_event_lock); + + if (hub->descriptor) { + kfree(hub->descriptor); + hub->descriptor = NULL; + } + + if (hub->status) { + kfree(hub->status); + hub->status = NULL; + } + + if (hub->buffer) { + usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer, + hub->buffer_dma); + hub->buffer = NULL; + } + + /* Free the memory */ + kfree(hub); +} + +static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_host_interface *desc; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *hdev; + struct usb_hub *hub; + + desc = intf->cur_altsetting; + hdev = interface_to_usbdev(intf); + + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ + if ((desc->desc.bInterfaceSubClass != 0) && + (desc->desc.bInterfaceSubClass != 1)) { +descriptor_error: + dev_err (&intf->dev, "bad descriptor, ignoring hub\n"); + return -EIO; + } + + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if (desc->desc.bNumEndpoints != 1) + goto descriptor_error; + + endpoint = &desc->endpoint[0].desc; + + /* Output endpoint? Curiouser and curiouser.. */ + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) + goto descriptor_error; + + /* If it's not an interrupt endpoint, we'd better punt! */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) + goto descriptor_error; + + /* We found a hub */ + dev_info (&intf->dev, "USB hub found\n"); + + hub = kmalloc(sizeof(*hub), GFP_KERNEL); + if (!hub) { + dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n"); + return -ENOMEM; + } + + memset(hub, 0, sizeof(*hub)); + + INIT_LIST_HEAD(&hub->event_list); + hub->intfdev = &intf->dev; + hub->hdev = hdev; + INIT_WORK(&hub->leds, led_work, hub); + + usb_set_intfdata (intf, hub); + + if (hdev->speed == USB_SPEED_HIGH) + highspeed_hubs++; + + if (hub_configure(hub, endpoint) >= 0) + return 0; + + hub_disconnect (intf); + return -ENODEV; +} + +static int +hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) +{ + struct usb_device *hdev = interface_to_usbdev (intf); + + /* assert ifno == 0 (part of hub spec) */ + switch (code) { + case USBDEVFS_HUB_PORTINFO: { + struct usbdevfs_hub_portinfo *info = user_data; + int i; + + spin_lock_irq(&device_state_lock); + if (hdev->devnum <= 0) + info->nports = 0; + else { + info->nports = hdev->maxchild; + for (i = 0; i < info->nports; i++) { + if (hdev->children[i] == NULL) + info->port[i] = 0; + else + info->port[i] = + hdev->children[i]->devnum; + } + } + spin_unlock_irq(&device_state_lock); + + return info->nports + 1; + } + + default: + return -ENOSYS; + } +} + +/* caller has locked the hub device */ +static void hub_pre_reset(struct usb_hub *hub) +{ + struct usb_device *hdev = hub->hdev; + int i; + + for (i = 0; i < hdev->maxchild; ++i) { + if (hdev->children[i]) + usb_disconnect(&hdev->children[i]); + } + hub_quiesce(hub); +} + +/* caller has locked the hub device */ +static void hub_post_reset(struct usb_hub *hub) +{ + hub_activate(hub); + hub_power_on(hub); +} + + +/* grab device/port lock, returning index of that port (zero based). + * protects the upstream link used by this device from concurrent + * tree operations like suspend, resume, reset, and disconnect, which + * apply to everything downstream of a given port. + */ +static int locktree(struct usb_device *udev) +{ + int t; + struct usb_device *hdev; + + if (!udev) + return -ENODEV; + + /* root hub is always the first lock in the series */ + hdev = udev->parent; + if (!hdev) { + usb_lock_device(udev); + return 0; + } + + /* on the path from root to us, lock everything from + * top down, dropping parent locks when not needed + */ + t = locktree(hdev); + if (t < 0) + return t; + for (t = 0; t < hdev->maxchild; t++) { + if (hdev->children[t] == udev) { + /* everything is fail-fast once disconnect + * processing starts + */ + if (udev->state == USB_STATE_NOTATTACHED) + break; + + /* when everyone grabs locks top->bottom, + * non-overlapping work may be concurrent + */ + down(&udev->serialize); + up(&hdev->serialize); + return t + 1; + } + } + usb_unlock_device(hdev); + return -ENODEV; +} + +static void recursively_mark_NOTATTACHED(struct usb_device *udev) +{ + int i; + + for (i = 0; i < udev->maxchild; ++i) { + if (udev->children[i]) + recursively_mark_NOTATTACHED(udev->children[i]); + } + udev->state = USB_STATE_NOTATTACHED; +} + +/** + * usb_set_device_state - change a device's current state (usbcore, hcds) + * @udev: pointer to device whose state should be changed + * @new_state: new state value to be stored + * + * udev->state is _not_ fully protected by the device lock. Although + * most transitions are made only while holding the lock, the state can + * can change to USB_STATE_NOTATTACHED at almost any time. This + * is so that devices can be marked as disconnected as soon as possible, + * without having to wait for any semaphores to be released. As a result, + * all changes to any device's state must be protected by the + * device_state_lock spinlock. + * + * Once a device has been added to the device tree, all changes to its state + * should be made using this routine. The state should _not_ be set directly. + * + * If udev->state is already USB_STATE_NOTATTACHED then no change is made. + * Otherwise udev->state is set to new_state, and if new_state is + * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set + * to USB_STATE_NOTATTACHED. + */ +void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state) +{ + unsigned long flags; + + spin_lock_irqsave(&device_state_lock, flags); + if (udev->state == USB_STATE_NOTATTACHED) + ; /* do nothing */ + else if (new_state != USB_STATE_NOTATTACHED) + udev->state = new_state; + else + recursively_mark_NOTATTACHED(udev); + spin_unlock_irqrestore(&device_state_lock, flags); +} +EXPORT_SYMBOL(usb_set_device_state); + + +static void choose_address(struct usb_device *udev) +{ + int devnum; + struct usb_bus *bus = udev->bus; + + /* If khubd ever becomes multithreaded, this will need a lock */ + + /* Try to allocate the next devnum beginning at bus->devnum_next. */ + devnum = find_next_zero_bit(bus->devmap.devicemap, 128, + bus->devnum_next); + if (devnum >= 128) + devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1); + + bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); + + if (devnum < 128) { + set_bit(devnum, bus->devmap.devicemap); + udev->devnum = devnum; + } +} + +static void release_address(struct usb_device *udev) +{ + if (udev->devnum > 0) { + clear_bit(udev->devnum, udev->bus->devmap.devicemap); + udev->devnum = -1; + } +} + +/** + * usb_disconnect - disconnect a device (usbcore-internal) + * @pdev: pointer to device being disconnected + * Context: !in_interrupt () + * + * Something got disconnected. Get rid of it and all of its children. + * + * If *pdev is a normal device then the parent hub must already be locked. + * If *pdev is a root hub then this routine will acquire the + * usb_bus_list_lock on behalf of the caller. + * + * Only hub drivers (including virtual root hub drivers for host + * controllers) should ever call this. + * + * This call is synchronous, and may not be used in an interrupt context. + */ +void usb_disconnect(struct usb_device **pdev) +{ + struct usb_device *udev = *pdev; + int i; + + if (!udev) { + pr_debug ("%s nodev\n", __FUNCTION__); + return; + } + + /* mark the device as inactive, so any further urb submissions for + * this device (and any of its children) will fail immediately. + * this quiesces everyting except pending urbs. + */ + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + + /* lock the bus list on behalf of HCDs unregistering their root hubs */ + if (!udev->parent) { + down(&usb_bus_list_lock); + usb_lock_device(udev); + } else + down(&udev->serialize); + + dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); + + /* Free up all the children before we remove this device */ + for (i = 0; i < USB_MAXCHILDREN; i++) { + if (udev->children[i]) + usb_disconnect(&udev->children[i]); + } + + /* deallocate hcd/hardware state ... nuking all pending urbs and + * cleaning up all state associated with the current configuration + * so that the hardware is now fully quiesced. + */ + usb_disable_device(udev, 0); + + /* Free the device number, remove the /proc/bus/usb entry and + * the sysfs attributes, and delete the parent's children[] + * (or root_hub) pointer. + */ + dev_dbg (&udev->dev, "unregistering device\n"); + release_address(udev); + usbfs_remove_device(udev); + usb_remove_sysfs_dev_files(udev); + + /* Avoid races with recursively_mark_NOTATTACHED() */ + spin_lock_irq(&device_state_lock); + *pdev = NULL; + spin_unlock_irq(&device_state_lock); + + if (!udev->parent) { + usb_unlock_device(udev); + up(&usb_bus_list_lock); + } else + up(&udev->serialize); + + device_unregister(&udev->dev); +} + +static int choose_configuration(struct usb_device *udev) +{ + int c, i; + + /* NOTE: this should interact with hub power budgeting */ + + c = udev->config[0].desc.bConfigurationValue; + if (udev->descriptor.bNumConfigurations != 1) { + for (i = 0; i < udev->descriptor.bNumConfigurations; i++) { + struct usb_interface_descriptor *desc; + + /* heuristic: Linux is more likely to have class + * drivers, so avoid vendor-specific interfaces. + */ + desc = &udev->config[i].intf_cache[0] + ->altsetting->desc; + if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) + continue; + /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. + * MSFT needs this to be the first config; never use + * it as the default unless Linux has host-side RNDIS. + * A second config would ideally be CDC-Ethernet, but + * may instead be the "vendor specific" CDC subset + * long used by ARM Linux for sa1100 or pxa255. + */ + if (desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff) { + c = udev->config[1].desc.bConfigurationValue; + continue; + } + c = udev->config[i].desc.bConfigurationValue; + break; + } + dev_info(&udev->dev, + "configuration #%d chosen from %d choices\n", + c, udev->descriptor.bNumConfigurations); + } + return c; +} + +#ifdef DEBUG +static void show_string(struct usb_device *udev, char *id, char *string) +{ + if (!string) + return; + dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string); +} + +#else +static inline void show_string(struct usb_device *udev, char *id, char *string) +{} +#endif + +static void get_string(struct usb_device *udev, char **string, int index) +{ + char *buf; + + if (!index) + return; + buf = kmalloc(256, GFP_KERNEL); + if (!buf) + return; + if (usb_string(udev, index, buf, 256) > 0) + *string = buf; + else + kfree(buf); +} + + +#ifdef CONFIG_USB_OTG +#include "otg_whitelist.h" +#endif + +/** + * usb_new_device - perform initial device setup (usbcore-internal) + * @udev: newly addressed device (in ADDRESS state) + * + * This is called with devices which have been enumerated, but not yet + * configured. The device descriptor is available, but not descriptors + * for any device configuration. The caller must have locked udev and + * either the parent hub (if udev is a normal device) or else the + * usb_bus_list_lock (if udev is a root hub). The parent's pointer to + * udev has already been installed, but udev is not yet visible through + * sysfs or other filesystem code. + * + * Returns 0 for success (device is configured and listed, with its + * interfaces, in sysfs); else a negative errno value. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Only the hub driver should ever call this; root hub registration + * uses it indirectly. + */ +int usb_new_device(struct usb_device *udev) +{ + int err; + int c; + + err = usb_get_configuration(udev); + if (err < 0) { + dev_err(&udev->dev, "can't read configurations, error %d\n", + err); + goto fail; + } + + /* read the standard strings and cache them if present */ + get_string(udev, &udev->product, udev->descriptor.iProduct); + get_string(udev, &udev->manufacturer, udev->descriptor.iManufacturer); + get_string(udev, &udev->serial, udev->descriptor.iSerialNumber); + + /* Tell the world! */ + dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, " + "SerialNumber=%d\n", + udev->descriptor.iManufacturer, + udev->descriptor.iProduct, + udev->descriptor.iSerialNumber); + show_string(udev, "Product", udev->product); + show_string(udev, "Manufacturer", udev->manufacturer); + show_string(udev, "SerialNumber", udev->serial); + +#ifdef CONFIG_USB_OTG + /* + * OTG-aware devices on OTG-capable root hubs may be able to use SRP, + * to wake us after we've powered off VBUS; and HNP, switching roles + * "host" to "peripheral". The OTG descriptor helps figure this out. + */ + if (!udev->bus->is_b_host + && udev->config + && udev->parent == udev->bus->root_hub) { + struct usb_otg_descriptor *desc = 0; + struct usb_bus *bus = udev->bus; + + /* descriptor may appear anywhere in config */ + if (__usb_get_extra_descriptor (udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc) == 0) { + if (desc->bmAttributes & USB_OTG_HNP) { + unsigned port1; + struct usb_device *root = udev->parent; + + for (port1 = 1; port1 <= root->maxchild; + port1++) { + if (root->children[port1-1] == udev) + break; + } + + dev_info(&udev->dev, + "Dual-Role OTG device on %sHNP port\n", + (port1 == bus->otg_port) + ? "" : "non-"); + + /* enable HNP before suspend, it's simpler */ + if (port1 == bus->otg_port) + bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + bus->b_hnp_enable + ? USB_DEVICE_B_HNP_ENABLE + : USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (err < 0) { + /* OTG MESSAGE: report errors here, + * customize to match your product. + */ + dev_info(&udev->dev, + "can't set HNP mode; %d\n", + err); + bus->b_hnp_enable = 0; + } + } + } + } + + if (!is_targeted(udev)) { + + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ + if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { + static int __usb_suspend_device (struct usb_device *, + int port1, pm_message_t state); + err = __usb_suspend_device(udev, + udev->bus->otg_port, + PMSG_SUSPEND); + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + err = -ENODEV; + goto fail; + } +#endif + + /* put device-specific files into sysfs */ + err = device_add (&udev->dev); + if (err) { + dev_err(&udev->dev, "can't device_add, error %d\n", err); + goto fail; + } + usb_create_sysfs_dev_files (udev); + + /* choose and set the configuration. that registers the interfaces + * with the driver core, and lets usb device drivers bind to them. + */ + c = choose_configuration(udev); + if (c < 0) + dev_warn(&udev->dev, + "can't choose an initial configuration\n"); + else { + err = usb_set_configuration(udev, c); + if (err) { + dev_err(&udev->dev, "can't set config #%d, error %d\n", + c, err); + usb_remove_sysfs_dev_files(udev); + device_del(&udev->dev); + goto fail; + } + } + + /* USB device state == configured ... usable */ + + /* add a /proc/bus/usb entry */ + usbfs_add_device(udev); + return 0; + +fail: + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + return err; +} + + +static int hub_port_status(struct usb_hub *hub, int port1, + u16 *status, u16 *change) +{ + int ret; + + ret = get_port_status(hub->hdev, port1, &hub->status->port); + if (ret < 0) + dev_err (hub->intfdev, + "%s failed (err = %d)\n", __FUNCTION__, ret); + else { + *status = le16_to_cpu(hub->status->port.wPortStatus); + *change = le16_to_cpu(hub->status->port.wPortChange); + ret = 0; + } + return ret; +} + +#define PORT_RESET_TRIES 5 +#define SET_ADDRESS_TRIES 2 +#define GET_DESCRIPTOR_TRIES 2 +#define SET_CONFIG_TRIES (2 * (use_both_schemes + 1)) +#define USE_NEW_SCHEME(i) ((i) / 2 == old_scheme_first) + +#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 + +static int hub_port_wait_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay) +{ + int delay_time, ret; + u16 portstatus; + u16 portchange; + + for (delay_time = 0; + delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + /* wait to give the device a chance to reset */ + msleep(delay); + + /* read and decode port status */ + ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (ret < 0) + return ret; + + /* Device went away? */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + + /* bomb out completely if something weird happened */ + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + return -EINVAL; + + /* if we`ve finished resetting, then break out of the loop */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_ENABLE)) { + if (portstatus & USB_PORT_STAT_HIGH_SPEED) + udev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + udev->speed = USB_SPEED_LOW; + else + udev->speed = USB_SPEED_FULL; + return 0; + } + + /* switch to the long delay after two short delay failures */ + if (delay_time >= 2 * HUB_SHORT_RESET_TIME) + delay = HUB_LONG_RESET_TIME; + + dev_dbg (hub->intfdev, + "port %d not reset yet, waiting %dms\n", + port1, delay); + } + + return -EBUSY; +} + +static int hub_port_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay) +{ + int i, status; + + /* Reset the port */ + for (i = 0; i < PORT_RESET_TRIES; i++) { + status = set_port_feature(hub->hdev, + port1, USB_PORT_FEAT_RESET); + if (status) + dev_err(hub->intfdev, + "cannot reset port %d (err = %d)\n", + port1, status); + else { + status = hub_port_wait_reset(hub, port1, udev, delay); + if (status) + dev_dbg(hub->intfdev, + "port_wait_reset: err = %d\n", + status); + } + + /* return on disconnect or reset */ + switch (status) { + case 0: + /* TRSTRCY = 10 ms */ + msleep(10); + /* FALL THROUGH */ + case -ENOTCONN: + case -ENODEV: + clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_C_RESET); + /* FIXME need disconnect() for NOTATTACHED device */ + usb_set_device_state(udev, status + ? USB_STATE_NOTATTACHED + : USB_STATE_DEFAULT); + return status; + } + + dev_dbg (hub->intfdev, + "port %d not enabled, trying reset again...\n", + port1); + delay = HUB_LONG_RESET_TIME; + } + + dev_err (hub->intfdev, + "Cannot enable port %i. Maybe the USB cable is bad?\n", + port1); + + return status; +} + +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) +{ + struct usb_device *hdev = hub->hdev; + int ret; + + if (hdev->children[port1-1] && set_state) { + usb_set_device_state(hdev->children[port1-1], + USB_STATE_NOTATTACHED); + } + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (ret) + dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", + port1, ret); + + return ret; +} + +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) +{ + dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); + hub_port_disable(hub, port1, 1); + + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - usb_suspend_device(dev,PM_SUSPEND_DISK) + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + + set_bit(port1, hub->change_bits); + kick_khubd(hub); +} + + +#ifdef CONFIG_USB_SUSPEND + +/* + * Selective port suspend reduces power; most suspended devices draw + * less than 500 uA. It's also used in OTG, along with remote wakeup. + * All devices below the suspended port are also suspended. + * + * Devices leave suspend state when the host wakes them up. Some devices + * also support "remote wakeup", where the device can activate the USB + * tree above them to deliver data, such as a keypress or packet. In + * some cases, this wakes the USB host. + */ +static int hub_port_suspend(struct usb_hub *hub, int port1, + struct usb_device *udev) +{ + int status; + + // dev_dbg(hub->intfdev, "suspend port %d\n", port1); + + /* enable remote wakeup when appropriate; this lets the device + * wake up the upstream hub (including maybe the root hub). + * + * NOTE: OTG devices may issue remote wakeup (or SRP) even when + * we don't explicitly enable it here. + */ + if (udev->actconfig + // && FIXME (remote wakeup enabled on this bus) + // ... currently assuming it's always appropriate + && (udev->actconfig->desc.bmAttributes + & USB_CONFIG_ATT_WAKEUP) != 0) { + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (status) + dev_dbg(&udev->dev, + "won't remote wakeup, status %d\n", + status); + } + + /* see 7.1.7.6 */ + status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); + if (status) { + dev_dbg(hub->intfdev, + "can't suspend port %d, status %d\n", + port1, status); + /* paranoia: "should not happen" */ + (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + /* device has up to 10 msec to fully suspend */ + dev_dbg(&udev->dev, "usb suspend\n"); + usb_set_device_state(udev, USB_STATE_SUSPENDED); + msleep(10); + } + return status; +} + +/* + * Devices on USB hub ports have only one "suspend" state, corresponding + * to ACPI D2 (PM_SUSPEND_MEM), "may cause the device to lose some context". + * State transitions include: + * + * - suspend, resume ... when the VBUS power link stays live + * - suspend, disconnect ... VBUS lost + * + * Once VBUS drop breaks the circuit, the port it's using has to go through + * normal re-enumeration procedures, starting with enabling VBUS power. + * Other than re-initializing the hub (plug/unplug, except for root hubs), + * Linux (2.6) currently has NO mechanisms to initiate that: no khubd + * timer, no SRP, no requests through sysfs. + */ +static int __usb_suspend_device (struct usb_device *udev, int port1, + pm_message_t state) +{ + int status; + + /* caller owns the udev device lock */ + if (port1 < 0) + return port1; + + if (udev->state == USB_STATE_SUSPENDED + || udev->state == USB_STATE_NOTATTACHED) { + return 0; + } + + /* suspend interface drivers; if this is a hub, it + * suspends the child devices + */ + if (udev->actconfig) { + int i; + + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *intf; + struct usb_driver *driver; + + intf = udev->actconfig->interface[i]; + if (state <= intf->dev.power.power_state) + continue; + if (!intf->dev.driver) + continue; + driver = to_usb_driver(intf->dev.driver); + + if (driver->suspend) { + status = driver->suspend(intf, state); + if (intf->dev.power.power_state != state + || status) + dev_err(&intf->dev, + "suspend %d fail, code %d\n", + state, status); + } + + /* only drivers with suspend() can ever resume(); + * and after power loss, even they won't. + * bus_rescan_devices() can rebind drivers later. + * + * FIXME the PM core self-deadlocks when unbinding + * drivers during suspend/resume ... everything grabs + * dpm_sem (not a spinlock, ugh). we want to unbind, + * since we know every driver's probe/disconnect works + * even for drivers that can't suspend. + */ + if (!driver->suspend || state > PM_SUSPEND_MEM) { +#if 1 + dev_warn(&intf->dev, "resume is unsafe!\n"); +#else + down_write(&usb_bus_type.rwsem); + device_release_driver(&intf->dev); + up_write(&usb_bus_type.rwsem); +#endif + } + } + } + + /* + * FIXME this needs port power off call paths too, to help force + * USB into the "generic" PM model. At least for devices on + * ports that aren't using ganged switching (usually root hubs). + * + * NOTE: SRP-capable links should adopt more aggressive poweroff + * policies (when HNP doesn't apply) once we have mechanisms to + * turn power back on! (Likely not before 2.7...) + */ + if (state > PM_SUSPEND_MEM) { + dev_warn(&udev->dev, "no poweroff yet, suspending instead\n"); + } + + /* "global suspend" of the HC-to-USB interface (root hub), or + * "selective suspend" of just one hub-device link. + */ + if (!udev->parent) { + struct usb_bus *bus = udev->bus; + if (bus && bus->op->hub_suspend) { + status = bus->op->hub_suspend (bus); + if (status == 0) { + dev_dbg(&udev->dev, "usb suspend\n"); + usb_set_device_state(udev, + USB_STATE_SUSPENDED); + } + } else + status = -EOPNOTSUPP; + } else + status = hub_port_suspend(hdev_to_hub(udev->parent), port1, + udev); + + if (status == 0) + udev->dev.power.power_state = state; + return status; +} + +/** + * usb_suspend_device - suspend a usb device + * @udev: device that's no longer in active use + * @state: PMSG_SUSPEND to suspend + * Context: must be able to sleep; device not locked + * + * Suspends a USB device that isn't in active use, conserving power. + * Devices may wake out of a suspend, if anything important happens, + * using the remote wakeup mechanism. They may also be taken out of + * suspend by the host, using usb_resume_device(). It's also routine + * to disconnect devices while they are suspended. + * + * Suspending OTG devices may trigger HNP, if that's been enabled + * between a pair of dual-role devices. That will change roles, such + * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. + * + * Returns 0 on success, else negative errno. + */ +int usb_suspend_device(struct usb_device *udev, pm_message_t state) +{ + int port1, status; + + port1 = locktree(udev); + if (port1 < 0) + return port1; + + status = __usb_suspend_device(udev, port1, state); + usb_unlock_device(udev); + return status; +} + +/* + * hardware resume signaling is finished, either because of selective + * resume (by host) or remote wakeup (by device) ... now see what changed + * in the tree that's rooted at this device. + */ +static int finish_port_resume(struct usb_device *udev) +{ + int status; + u16 devstatus; + + /* caller owns the udev device lock */ + dev_dbg(&udev->dev, "usb resume\n"); + + /* usb ch9 identifies four variants of SUSPENDED, based on what + * state the device resumes to. Linux currently won't see the + * first two on the host side; they'd be inside hub_port_init() + * during many timeouts, but khubd can't suspend until later. + */ + usb_set_device_state(udev, udev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); + udev->dev.power.power_state = PMSG_ON; + + /* 10.5.4.5 says be sure devices in the tree are still there. + * For now let's assume the device didn't go crazy on resume, + * and device drivers will know about any resume quirks. + */ + status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); + if (status < 0) + dev_dbg(&udev->dev, + "gone after usb resume? status %d\n", + status); + else if (udev->actconfig) { + unsigned i; + + le16_to_cpus(&devstatus); + if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (status) { + dev_dbg(&udev->dev, "disable remote " + "wakeup, status %d\n", status); + status = 0; + } + } + + /* resume interface drivers; if this is a hub, it + * resumes the child devices + */ + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *intf; + struct usb_driver *driver; + + intf = udev->actconfig->interface[i]; + if (intf->dev.power.power_state == PM_SUSPEND_ON) + continue; + if (!intf->dev.driver) { + /* FIXME maybe force to alt 0 */ + continue; + } + driver = to_usb_driver(intf->dev.driver); + + /* bus_rescan_devices() may rebind drivers */ + if (!driver->resume) + continue; + + /* can we do better than just logging errors? */ + status = driver->resume(intf); + if (intf->dev.power.power_state != PM_SUSPEND_ON + || status) + dev_dbg(&intf->dev, + "resume fail, state %d code %d\n", + intf->dev.power.power_state, status); + } + status = 0; + + } else if (udev->devnum <= 0) { + dev_dbg(&udev->dev, "bogus resume!\n"); + status = -EINVAL; + } + return status; +} + +static int +hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) +{ + int status; + + // dev_dbg(hub->intfdev, "resume port %d\n", port1); + + /* see 7.1.7.7; affects power usage, but not budgeting */ + status = clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_SUSPEND); + if (status) { + dev_dbg(hub->intfdev, + "can't resume port %d, status %d\n", + port1, status); + } else { + u16 devstatus; + u16 portchange; + + /* drive resume for at least 20 msec */ + if (udev) + dev_dbg(&udev->dev, "RESUME\n"); + msleep(25); + +#define LIVE_FLAGS ( USB_PORT_STAT_POWER \ + | USB_PORT_STAT_ENABLE \ + | USB_PORT_STAT_CONNECTION) + + /* Virtual root hubs can trigger on GET_PORT_STATUS to + * stop resume signaling. Then finish the resume + * sequence. + */ + devstatus = portchange = 0; + status = hub_port_status(hub, port1, + &devstatus, &portchange); + if (status < 0 + || (devstatus & LIVE_FLAGS) != LIVE_FLAGS + || (devstatus & USB_PORT_STAT_SUSPEND) != 0 + ) { + dev_dbg(hub->intfdev, + "port %d status %04x.%04x after resume, %d\n", + port1, portchange, devstatus, status); + } else { + /* TRSMRCY = 10 msec */ + msleep(10); + if (udev) + status = finish_port_resume(udev); + } + } + if (status < 0) + hub_port_logical_disconnect(hub, port1); + + return status; +} + +static int hub_resume (struct usb_interface *intf); + +/** + * usb_resume_device - re-activate a suspended usb device + * @udev: device to re-activate + * Context: must be able to sleep; device not locked + * + * This will re-activate the suspended device, increasing power usage + * while letting drivers communicate again with its endpoints. + * USB resume explicitly guarantees that the power session between + * the host and the device is the same as it was when the device + * suspended. + * + * Returns 0 on success, else negative errno. + */ +int usb_resume_device(struct usb_device *udev) +{ + int port1, status; + + port1 = locktree(udev); + if (port1 < 0) + return port1; + + /* "global resume" of the HC-to-USB interface (root hub), or + * selective resume of one hub-to-device port + */ + if (!udev->parent) { + struct usb_bus *bus = udev->bus; + if (bus && bus->op->hub_resume) { + status = bus->op->hub_resume (bus); + } else + status = -EOPNOTSUPP; + if (status == 0) { + dev_dbg(&udev->dev, "usb resume\n"); + /* TRSMRCY = 10 msec */ + msleep(10); + usb_set_device_state (udev, USB_STATE_CONFIGURED); + udev->dev.power.power_state = PMSG_ON; + status = hub_resume (udev + ->actconfig->interface[0]); + } + } else if (udev->state == USB_STATE_SUSPENDED) { + // NOTE this fails if parent is also suspended... + status = hub_port_resume(hdev_to_hub(udev->parent), + port1, udev); + } else { + status = 0; + } + if (status < 0) { + dev_dbg(&udev->dev, "can't resume, status %d\n", + status); + } + + usb_unlock_device(udev); + + /* rebind drivers that had no suspend() */ + if (status == 0) { + usb_lock_all_devices(); + bus_rescan_devices(&usb_bus_type); + usb_unlock_all_devices(); + } + return status; +} + +static int remote_wakeup(struct usb_device *udev) +{ + int status = 0; + + /* don't repeat RESUME sequence if this device + * was already woken up by some other task + */ + down(&udev->serialize); + if (udev->state == USB_STATE_SUSPENDED) { + dev_dbg(&udev->dev, "RESUME (wakeup)\n"); + /* TRSMRCY = 10 msec */ + msleep(10); + status = finish_port_resume(udev); + } + up(&udev->serialize); + return status; +} + +static int hub_suspend(struct usb_interface *intf, pm_message_t state) +{ + struct usb_hub *hub = usb_get_intfdata (intf); + struct usb_device *hdev = hub->hdev; + unsigned port1; + int status; + + /* stop khubd and related activity */ + hub_quiesce(hub); + + /* then suspend every port */ + for (port1 = 1; port1 <= hdev->maxchild; port1++) { + struct usb_device *udev; + + udev = hdev->children [port1-1]; + if (!udev) + continue; + down(&udev->serialize); + status = __usb_suspend_device(udev, port1, state); + up(&udev->serialize); + if (status < 0) + dev_dbg(&intf->dev, "suspend port %d --> %d\n", + port1, status); + } + + intf->dev.power.power_state = state; + return 0; +} + +static int hub_resume(struct usb_interface *intf) +{ + struct usb_device *hdev = interface_to_usbdev(intf); + struct usb_hub *hub = usb_get_intfdata (intf); + unsigned port1; + int status; + + if (intf->dev.power.power_state == PM_SUSPEND_ON) + return 0; + + for (port1 = 1; port1 <= hdev->maxchild; port1++) { + struct usb_device *udev; + u16 portstat, portchange; + + udev = hdev->children [port1-1]; + status = hub_port_status(hub, port1, &portstat, &portchange); + if (status == 0) { + if (portchange & USB_PORT_STAT_C_SUSPEND) { + clear_port_feature(hdev, port1, + USB_PORT_FEAT_C_SUSPEND); + portchange &= ~USB_PORT_STAT_C_SUSPEND; + } + + /* let khubd handle disconnects etc */ + if (portchange) + continue; + } + + if (!udev || status < 0) + continue; + down (&udev->serialize); + if (portstat & USB_PORT_STAT_SUSPEND) + status = hub_port_resume(hub, port1, udev); + else { + status = finish_port_resume(udev); + if (status < 0) { + dev_dbg(&intf->dev, "resume port %d --> %d\n", + port1, status); + hub_port_logical_disconnect(hub, port1); + } + } + up(&udev->serialize); + } + intf->dev.power.power_state = PMSG_ON; + + hub->resume_root_hub = 0; + hub_activate(hub); + return 0; +} + +void usb_resume_root_hub(struct usb_device *hdev) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + hub->resume_root_hub = 1; + kick_khubd(hub); +} + +#else /* !CONFIG_USB_SUSPEND */ + +int usb_suspend_device(struct usb_device *udev, pm_message_t state) +{ + return 0; +} + +int usb_resume_device(struct usb_device *udev) +{ + return 0; +} + +#define hub_suspend NULL +#define hub_resume NULL +#define remote_wakeup(x) 0 + +#endif /* CONFIG_USB_SUSPEND */ + +EXPORT_SYMBOL(usb_suspend_device); +EXPORT_SYMBOL(usb_resume_device); + + + +/* USB 2.0 spec, 7.1.7.3 / fig 7-29: + * + * Between connect detection and reset signaling there must be a delay + * of 100ms at least for debounce and power-settling. The corresponding + * timer shall restart whenever the downstream port detects a disconnect. + * + * Apparently there are some bluetooth and irda-dongles and a number of + * low-speed devices for which this debounce period may last over a second. + * Not covered by the spec - but easy to deal with. + * + * This implementation uses a 1500ms total debounce timeout; if the + * connection isn't stable by then it returns -ETIMEDOUT. It checks + * every 25ms for transient disconnects. When the port status has been + * unchanged for 100ms it returns the port status. + */ + +#define HUB_DEBOUNCE_TIMEOUT 1500 +#define HUB_DEBOUNCE_STEP 25 +#define HUB_DEBOUNCE_STABLE 100 + +static int hub_port_debounce(struct usb_hub *hub, int port1) +{ + int ret; + int total_time, stable_time = 0; + u16 portchange, portstatus; + unsigned connection = 0xffff; + + for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { + ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (ret < 0) + return ret; + + if (!(portchange & USB_PORT_STAT_C_CONNECTION) && + (portstatus & USB_PORT_STAT_CONNECTION) == connection) { + stable_time += HUB_DEBOUNCE_STEP; + if (stable_time >= HUB_DEBOUNCE_STABLE) + break; + } else { + stable_time = 0; + connection = portstatus & USB_PORT_STAT_CONNECTION; + } + + if (portchange & USB_PORT_STAT_C_CONNECTION) { + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + } + + if (total_time >= HUB_DEBOUNCE_TIMEOUT) + break; + msleep(HUB_DEBOUNCE_STEP); + } + + dev_dbg (hub->intfdev, + "debounce: port %d: total %dms stable %dms status 0x%x\n", + port1, total_time, stable_time, portstatus); + + if (stable_time < HUB_DEBOUNCE_STABLE) + return -ETIMEDOUT; + return portstatus; +} + +static void ep0_reinit(struct usb_device *udev) +{ + usb_disable_endpoint(udev, 0 + USB_DIR_IN); + usb_disable_endpoint(udev, 0 + USB_DIR_OUT); + udev->ep_in[0] = udev->ep_out[0] = &udev->ep0; +} + +#define usb_sndaddr0pipe() (PIPE_CONTROL << 30) +#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN) + +static int hub_set_address(struct usb_device *udev) +{ + int retval; + + if (udev->devnum == 0) + return -EINVAL; + if (udev->state == USB_STATE_ADDRESS) + return 0; + if (udev->state != USB_STATE_DEFAULT) + return -EINVAL; + retval = usb_control_msg(udev, usb_sndaddr0pipe(), + USB_REQ_SET_ADDRESS, 0, udev->devnum, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (retval == 0) { + usb_set_device_state(udev, USB_STATE_ADDRESS); + ep0_reinit(udev); + } + return retval; +} + +/* Reset device, (re)assign address, get device descriptor. + * Device connection must be stable, no more debouncing needed. + * Returns device in USB_STATE_ADDRESS, except on error. + * + * If this is called for an already-existing device (as part of + * usb_reset_device), the caller must own the device lock. For a + * newly detected device that is not accessible through any global + * pointers, it's not necessary to lock the device. + */ +static int +hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, + int retry_counter) +{ + static DECLARE_MUTEX(usb_address0_sem); + + struct usb_device *hdev = hub->hdev; + int i, j, retval; + unsigned delay = HUB_SHORT_RESET_TIME; + enum usb_device_speed oldspeed = udev->speed; + + /* root hub ports have a slightly longer reset period + * (from USB 2.0 spec, section 7.1.7.5) + */ + if (!hdev->parent) { + delay = HUB_ROOT_RESET_TIME; + if (port1 == hdev->bus->otg_port) + hdev->bus->b_hnp_enable = 0; + } + + /* Some low speed devices have problems with the quick delay, so */ + /* be a bit pessimistic with those devices. RHbug #23670 */ + if (oldspeed == USB_SPEED_LOW) + delay = HUB_LONG_RESET_TIME; + + down(&usb_address0_sem); + + /* Reset the device; full speed may morph to high speed */ + retval = hub_port_reset(hub, port1, udev, delay); + if (retval < 0) /* error or disconnect */ + goto fail; + /* success, speed is known */ + retval = -ENODEV; + + if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { + dev_dbg(&udev->dev, "device reset changed speed!\n"); + goto fail; + } + oldspeed = udev->speed; + + /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... + * it's fixed size except for full speed devices. + */ + switch (udev->speed) { + case USB_SPEED_HIGH: /* fixed at 64 */ + udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); + break; + case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ + /* to determine the ep0 maxpacket size, try to read + * the device descriptor to get bMaxPacketSize0 and + * then correct our initial guess. + */ + udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); + break; + case USB_SPEED_LOW: /* fixed at 8 */ + udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8); + break; + default: + goto fail; + } + + dev_info (&udev->dev, + "%s %s speed USB device using %s and address %d\n", + (udev->config) ? "reset" : "new", + ({ char *speed; switch (udev->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + }; speed;}), + udev->bus->controller->driver->name, + udev->devnum); + + /* Set up TT records, if needed */ + if (hdev->tt) { + udev->tt = hdev->tt; + udev->ttport = hdev->ttport; + } else if (udev->speed != USB_SPEED_HIGH + && hdev->speed == USB_SPEED_HIGH) { + udev->tt = &hub->tt; + udev->ttport = port1; + } + + /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? + * Because device hardware and firmware is sometimes buggy in + * this area, and this is how Linux has done it for ages. + * Change it cautiously. + * + * NOTE: If USE_NEW_SCHEME() is true we will start by issuing + * a 64-byte GET_DESCRIPTOR request. This is what Windows does, + * so it may help with some non-standards-compliant devices. + * Otherwise we start with SET_ADDRESS and then try to read the + * first 8 bytes of the device descriptor to get the ep0 maxpacket + * value. + */ + for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { + if (USE_NEW_SCHEME(retry_counter)) { + struct usb_device_descriptor *buf; + int r = 0; + +#define GET_DESCRIPTOR_BUFSIZE 64 + buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); + if (!buf) { + retval = -ENOMEM; + continue; + } + + /* Use a short timeout the first time through, + * so that recalcitrant full-speed devices with + * 8- or 16-byte ep0-maxpackets won't slow things + * down tremendously by NAKing the unexpectedly + * early status stage. Also, retry on all errors; + * some devices are flakey. + */ + for (j = 0; j < 3; ++j) { + buf->bMaxPacketSize0 = 0; + r = usb_control_msg(udev, usb_rcvaddr0pipe(), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + USB_DT_DEVICE << 8, 0, + buf, GET_DESCRIPTOR_BUFSIZE, + (i ? USB_CTRL_GET_TIMEOUT : 1000)); + switch (buf->bMaxPacketSize0) { + case 8: case 16: case 32: case 64: + if (buf->bDescriptorType == + USB_DT_DEVICE) { + r = 0; + break; + } + /* FALL THROUGH */ + default: + if (r == 0) + r = -EPROTO; + break; + } + if (r == 0) + break; + } + udev->descriptor.bMaxPacketSize0 = + buf->bMaxPacketSize0; + kfree(buf); + + retval = hub_port_reset(hub, port1, udev, delay); + if (retval < 0) /* error or disconnect */ + goto fail; + if (oldspeed != udev->speed) { + dev_dbg(&udev->dev, + "device reset changed speed!\n"); + retval = -ENODEV; + goto fail; + } + if (r) { + dev_err(&udev->dev, "device descriptor " + "read/%s, error %d\n", + "64", r); + retval = -EMSGSIZE; + continue; + } +#undef GET_DESCRIPTOR_BUFSIZE + } + + for (j = 0; j < SET_ADDRESS_TRIES; ++j) { + retval = hub_set_address(udev); + if (retval >= 0) + break; + msleep(200); + } + if (retval < 0) { + dev_err(&udev->dev, + "device not accepting address %d, error %d\n", + udev->devnum, retval); + goto fail; + } + + /* cope with hardware quirkiness: + * - let SET_ADDRESS settle, some device hardware wants it + * - read ep0 maxpacket even for high and low speed, + */ + msleep(10); + if (USE_NEW_SCHEME(retry_counter)) + break; + + retval = usb_get_device_descriptor(udev, 8); + if (retval < 8) { + dev_err(&udev->dev, "device descriptor " + "read/%s, error %d\n", + "8", retval); + if (retval >= 0) + retval = -EMSGSIZE; + } else { + retval = 0; + break; + } + } + if (retval) + goto fail; + + i = udev->descriptor.bMaxPacketSize0; + if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { + if (udev->speed != USB_SPEED_FULL || + !(i == 8 || i == 16 || i == 32 || i == 64)) { + dev_err(&udev->dev, "ep0 maxpacket = %d\n", i); + retval = -EMSGSIZE; + goto fail; + } + dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i); + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i); + ep0_reinit(udev); + } + + retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); + if (retval < (signed)sizeof(udev->descriptor)) { + dev_err(&udev->dev, "device descriptor read/%s, error %d\n", + "all", retval); + if (retval >= 0) + retval = -ENOMSG; + goto fail; + } + + retval = 0; + +fail: + if (retval) + hub_port_disable(hub, port1, 0); + up(&usb_address0_sem); + return retval; +} + +static void +check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) +{ + struct usb_qualifier_descriptor *qual; + int status; + + qual = kmalloc (sizeof *qual, SLAB_KERNEL); + if (qual == NULL) + return; + + status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, + qual, sizeof *qual); + if (status == sizeof *qual) { + dev_info(&udev->dev, "not running at top speed; " + "connect to a high speed hub\n"); + /* hub LEDs are probably harder to miss than syslog */ + if (hub->has_indicators) { + hub->indicator[port1-1] = INDICATOR_GREEN_BLINK; + schedule_work (&hub->leds); + } + } + kfree (qual); +} + +static unsigned +hub_power_remaining (struct usb_hub *hub) +{ + struct usb_device *hdev = hub->hdev; + int remaining; + unsigned i; + + remaining = hub->power_budget; + if (!remaining) /* self-powered */ + return 0; + + for (i = 0; i < hdev->maxchild; i++) { + struct usb_device *udev = hdev->children[i]; + int delta, ceiling; + + if (!udev) + continue; + + /* 100mA per-port ceiling, or 8mA for OTG ports */ + if (i != (udev->bus->otg_port - 1) || hdev->parent) + ceiling = 50; + else + ceiling = 4; + + if (udev->actconfig) + delta = udev->actconfig->desc.bMaxPower; + else + delta = ceiling; + // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta); + if (delta > ceiling) + dev_warn(&udev->dev, "%dmA over %dmA budget!\n", + 2 * (delta - ceiling), 2 * ceiling); + remaining -= delta; + } + if (remaining < 0) { + dev_warn(hub->intfdev, + "%dmA over power budget!\n", + -2 * remaining); + remaining = 0; + } + return remaining; +} + +/* Handle physical or logical connection change events. + * This routine is called when: + * a port connection-change occurs; + * a port enable-change occurs (often caused by EMI); + * usb_reset_device() encounters changed descriptors (as from + * a firmware download) + * caller already locked the hub + */ +static void hub_port_connect_change(struct usb_hub *hub, int port1, + u16 portstatus, u16 portchange) +{ + struct usb_device *hdev = hub->hdev; + struct device *hub_dev = hub->intfdev; + int status, i; + + dev_dbg (hub_dev, + "port %d, status %04x, change %04x, %s\n", + port1, portstatus, portchange, portspeed (portstatus)); + + if (hub->has_indicators) { + set_port_led(hub, port1, HUB_LED_AUTO); + hub->indicator[port1-1] = INDICATOR_AUTO; + } + + /* Disconnect any existing devices under this port */ + if (hdev->children[port1-1]) + usb_disconnect(&hdev->children[port1-1]); + clear_bit(port1, hub->change_bits); + +#ifdef CONFIG_USB_OTG + /* during HNP, don't repeat the debounce */ + if (hdev->bus->is_b_host) + portchange &= ~USB_PORT_STAT_C_CONNECTION; +#endif + + if (portchange & USB_PORT_STAT_C_CONNECTION) { + status = hub_port_debounce(hub, port1); + if (status < 0) { + dev_err (hub_dev, + "connect-debounce failed, port %d disabled\n", + port1); + goto done; + } + portstatus = status; + } + + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + + /* maybe switch power back on (e.g. root hub was reset) */ + if ((hub->descriptor->wHubCharacteristics + & HUB_CHAR_LPSM) < 2 + && !(portstatus & (1 << USB_PORT_FEAT_POWER))) + set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); + + if (portstatus & USB_PORT_STAT_ENABLE) + goto done; + return; + } + +#ifdef CONFIG_USB_SUSPEND + /* If something is connected, but the port is suspended, wake it up. */ + if (portstatus & USB_PORT_STAT_SUSPEND) { + status = hub_port_resume(hub, port1, NULL); + if (status < 0) { + dev_dbg(hub_dev, + "can't clear suspend on port %d; %d\n", + port1, status); + goto done; + } + } +#endif + + for (i = 0; i < SET_CONFIG_TRIES; i++) { + struct usb_device *udev; + + /* reallocate for each attempt, since references + * to the previous one can escape in various ways + */ + udev = usb_alloc_dev(hdev, hdev->bus, port1); + if (!udev) { + dev_err (hub_dev, + "couldn't allocate port %d usb_device\n", + port1); + goto done; + } + + usb_set_device_state(udev, USB_STATE_POWERED); + udev->speed = USB_SPEED_UNKNOWN; + + /* set the address */ + choose_address(udev); + if (udev->devnum <= 0) { + status = -ENOTCONN; /* Don't retry */ + goto loop; + } + + /* reset and get descriptor */ + status = hub_port_init(hub, udev, port1, i); + if (status < 0) + goto loop; + + /* consecutive bus-powered hubs aren't reliable; they can + * violate the voltage drop budget. if the new child has + * a "powered" LED, users should notice we didn't enable it + * (without reading syslog), even without per-port LEDs + * on the parent. + */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB + && hub->power_budget) { + u16 devstat; + + status = usb_get_status(udev, USB_RECIP_DEVICE, 0, + &devstat); + if (status < 0) { + dev_dbg(&udev->dev, "get status %d ?\n", status); + goto loop_disable; + } + cpu_to_le16s(&devstat); + if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { + dev_err(&udev->dev, + "can't connect bus-powered hub " + "to this port\n"); + if (hub->has_indicators) { + hub->indicator[port1-1] = + INDICATOR_AMBER_BLINK; + schedule_work (&hub->leds); + } + status = -ENOTCONN; /* Don't retry */ + goto loop_disable; + } + } + + /* check for devices running slower than they could */ + if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 + && udev->speed == USB_SPEED_FULL + && highspeed_hubs != 0) + check_highspeed (hub, udev, port1); + + /* Store the parent's children[] pointer. At this point + * udev becomes globally accessible, although presumably + * no one will look at it until hdev is unlocked. + */ + down (&udev->serialize); + status = 0; + + /* We mustn't add new devices if the parent hub has + * been disconnected; we would race with the + * recursively_mark_NOTATTACHED() routine. + */ + spin_lock_irq(&device_state_lock); + if (hdev->state == USB_STATE_NOTATTACHED) + status = -ENOTCONN; + else + hdev->children[port1-1] = udev; + spin_unlock_irq(&device_state_lock); + + /* Run it through the hoops (find a driver, etc) */ + if (!status) { + status = usb_new_device(udev); + if (status) { + spin_lock_irq(&device_state_lock); + hdev->children[port1-1] = NULL; + spin_unlock_irq(&device_state_lock); + } + } + + up (&udev->serialize); + if (status) + goto loop_disable; + + status = hub_power_remaining(hub); + if (status) + dev_dbg(hub_dev, + "%dmA power budget left\n", + 2 * status); + + return; + +loop_disable: + hub_port_disable(hub, port1, 1); +loop: + ep0_reinit(udev); + release_address(udev); + usb_put_dev(udev); + if (status == -ENOTCONN) + break; + } + +done: + hub_port_disable(hub, port1, 1); +} + +static void hub_events(void) +{ + struct list_head *tmp; + struct usb_device *hdev; + struct usb_interface *intf; + struct usb_hub *hub; + struct device *hub_dev; + u16 hubstatus; + u16 hubchange; + u16 portstatus; + u16 portchange; + int i, ret; + int connect_change; + + /* + * We restart the list every time to avoid a deadlock with + * deleting hubs downstream from this one. This should be + * safe since we delete the hub from the event list. + * Not the most efficient, but avoids deadlocks. + */ + while (1) { + + /* Grab the first entry at the beginning of the list */ + spin_lock_irq(&hub_event_lock); + if (list_empty(&hub_event_list)) { + spin_unlock_irq(&hub_event_lock); + break; + } + + tmp = hub_event_list.next; + list_del_init(tmp); + + hub = list_entry(tmp, struct usb_hub, event_list); + hdev = hub->hdev; + intf = to_usb_interface(hub->intfdev); + hub_dev = &intf->dev; + + dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", + hdev->state, hub->descriptor + ? hub->descriptor->bNbrPorts + : 0, + /* NOTE: expects max 15 ports... */ + (u16) hub->change_bits[0], + (u16) hub->event_bits[0]); + + usb_get_intf(intf); + i = hub->resume_root_hub; + spin_unlock_irq(&hub_event_lock); + + /* Is this is a root hub wanting to be resumed? */ + if (i) + usb_resume_device(hdev); + + /* Lock the device, then check to see if we were + * disconnected while waiting for the lock to succeed. */ + if (locktree(hdev) < 0) { + usb_put_intf(intf); + continue; + } + if (hub != usb_get_intfdata(intf)) + goto loop; + + /* If the hub has died, clean up after it */ + if (hdev->state == USB_STATE_NOTATTACHED) { + hub_pre_reset(hub); + goto loop; + } + + /* If this is an inactive or suspended hub, do nothing */ + if (hub->quiescing) + goto loop; + + if (hub->error) { + dev_dbg (hub_dev, "resetting for error %d\n", + hub->error); + + ret = usb_reset_device(hdev); + if (ret) { + dev_dbg (hub_dev, + "error resetting hub: %d\n", ret); + goto loop; + } + + hub->nerrors = 0; + hub->error = 0; + } + + /* deal with port status changes */ + for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { + if (test_bit(i, hub->busy_bits)) + continue; + connect_change = test_bit(i, hub->change_bits); + if (!test_and_clear_bit(i, hub->event_bits) && + !connect_change && !hub->activating) + continue; + + ret = hub_port_status(hub, i, + &portstatus, &portchange); + if (ret < 0) + continue; + + if (hub->activating && !hdev->children[i-1] && + (portstatus & + USB_PORT_STAT_CONNECTION)) + connect_change = 1; + + if (portchange & USB_PORT_STAT_C_CONNECTION) { + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_CONNECTION); + connect_change = 1; + } + + if (portchange & USB_PORT_STAT_C_ENABLE) { + if (!connect_change) + dev_dbg (hub_dev, + "port %d enable change, " + "status %08x\n", + i, portstatus); + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_ENABLE); + + /* + * EM interference sometimes causes badly + * shielded USB devices to be shutdown by + * the hub, this hack enables them again. + * Works at least with mouse driver. + */ + if (!(portstatus & USB_PORT_STAT_ENABLE) + && !connect_change + && hdev->children[i-1]) { + dev_err (hub_dev, + "port %i " + "disabled by hub (EMI?), " + "re-enabling...\n", + i); + connect_change = 1; + } + } + + if (portchange & USB_PORT_STAT_C_SUSPEND) { + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_SUSPEND); + if (hdev->children[i-1]) { + ret = remote_wakeup(hdev-> + children[i-1]); + if (ret < 0) + connect_change = 1; + } else { + ret = -ENODEV; + hub_port_disable(hub, i, 1); + } + dev_dbg (hub_dev, + "resume on port %d, status %d\n", + i, ret); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + dev_err (hub_dev, + "over-current change on port %d\n", + i); + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_OVER_CURRENT); + hub_power_on(hub); + } + + if (portchange & USB_PORT_STAT_C_RESET) { + dev_dbg (hub_dev, + "reset change on port %d\n", + i); + clear_port_feature(hdev, i, + USB_PORT_FEAT_C_RESET); + } + + if (connect_change) + hub_port_connect_change(hub, i, + portstatus, portchange); + } /* end for i */ + + /* deal with hub status changes */ + if (test_and_clear_bit(0, hub->event_bits) == 0) + ; /* do nothing */ + else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) + dev_err (hub_dev, "get_hub_status failed\n"); + else { + if (hubchange & HUB_CHANGE_LOCAL_POWER) { + dev_dbg (hub_dev, "power change\n"); + clear_hub_feature(hdev, C_HUB_LOCAL_POWER); + } + if (hubchange & HUB_CHANGE_OVERCURRENT) { + dev_dbg (hub_dev, "overcurrent change\n"); + msleep(500); /* Cool down */ + clear_hub_feature(hdev, C_HUB_OVER_CURRENT); + hub_power_on(hub); + } + } + + hub->activating = 0; + +loop: + usb_unlock_device(hdev); + usb_put_intf(intf); + + } /* end while (1) */ +} + +static int hub_thread(void *__unused) +{ + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources + */ + + daemonize("khubd"); + allow_signal(SIGKILL); + + /* Send me a signal to get me die (for debugging) */ + do { + hub_events(); + wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); + try_to_freeze(PF_FREEZE); + } while (!signal_pending(current)); + + pr_debug ("%s: khubd exiting\n", usbcore_name); + complete_and_exit(&khubd_exited, 0); +} + +static struct usb_device_id hub_id_table [] = { + { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, + .bDeviceClass = USB_CLASS_HUB}, + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_HUB}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, hub_id_table); + +static struct usb_driver hub_driver = { + .owner = THIS_MODULE, + .name = "hub", + .probe = hub_probe, + .disconnect = hub_disconnect, + .suspend = hub_suspend, + .resume = hub_resume, + .ioctl = hub_ioctl, + .id_table = hub_id_table, +}; + +int usb_hub_init(void) +{ + pid_t pid; + + if (usb_register(&hub_driver) < 0) { + printk(KERN_ERR "%s: can't register hub driver\n", + usbcore_name); + return -1; + } + + pid = kernel_thread(hub_thread, NULL, CLONE_KERNEL); + if (pid >= 0) { + khubd_pid = pid; + + return 0; + } + + /* Fall through if kernel_thread failed */ + usb_deregister(&hub_driver); + printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); + + return -1; +} + +void usb_hub_cleanup(void) +{ + int ret; + + /* Kill the thread */ + ret = kill_proc(khubd_pid, SIGKILL, 1); + + wait_for_completion(&khubd_exited); + + /* + * Hub resources are freed for us by usb_deregister. It calls + * usb_driver_purge on every device which in turn calls that + * devices disconnect function if it is using this driver. + * The hub_disconnect function takes care of releasing the + * individual hub resources. -greg + */ + usb_deregister(&hub_driver); +} /* usb_hub_cleanup() */ + + +static int config_descriptors_changed(struct usb_device *udev) +{ + unsigned index; + unsigned len = 0; + struct usb_config_descriptor *buf; + + for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { + if (len < le16_to_cpu(udev->config[index].desc.wTotalLength)) + len = le16_to_cpu(udev->config[index].desc.wTotalLength); + } + buf = kmalloc (len, SLAB_KERNEL); + if (buf == NULL) { + dev_err(&udev->dev, "no mem to re-read configs after reset\n"); + /* assume the worst */ + return 1; + } + for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { + int length; + int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); + + length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, + old_length); + if (length < old_length) { + dev_dbg(&udev->dev, "config index %d, error %d\n", + index, length); + break; + } + if (memcmp (buf, udev->rawdescriptors[index], old_length) + != 0) { + dev_dbg(&udev->dev, "config index %d changed (#%d)\n", + index, buf->bConfigurationValue); + break; + } + } + kfree(buf); + return index != udev->descriptor.bNumConfigurations; +} + +/** + * usb_reset_device - perform a USB port reset to reinitialize a device + * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) + * + * WARNING - don't reset any device unless drivers for all of its + * interfaces are expecting that reset! Maybe some driver->reset() + * method should eventually help ensure sufficient cooperation. + * + * Do a port reset, reassign the device's address, and establish its + * former operating configuration. If the reset fails, or the device's + * descriptors change from their values before the reset, or the original + * configuration and altsettings cannot be restored, a flag will be set + * telling khubd to pretend the device has been disconnected and then + * re-connected. All drivers will be unbound, and the device will be + * re-enumerated and probed all over again. + * + * Returns 0 if the reset succeeded, -ENODEV if the device has been + * flagged for logical disconnection, or some other negative error code + * if the reset wasn't even attempted. + * + * The caller must own the device lock. For example, it's safe to use + * this from a driver probe() routine after downloading new firmware. + * For calls that might not occur during probe(), drivers should lock + * the device using usb_lock_device_for_reset(). + */ +int usb_reset_device(struct usb_device *udev) +{ + struct usb_device *parent_hdev = udev->parent; + struct usb_hub *parent_hub; + struct usb_device_descriptor descriptor = udev->descriptor; + struct usb_hub *hub = NULL; + int i, ret = 0, port1 = -1; + + if (udev->state == USB_STATE_NOTATTACHED || + udev->state == USB_STATE_SUSPENDED) { + dev_dbg(&udev->dev, "device reset not allowed in state %d\n", + udev->state); + return -EINVAL; + } + + if (!parent_hdev) { + /* this requires hcd-specific logic; see OHCI hc_restart() */ + dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); + return -EISDIR; + } + + for (i = 0; i < parent_hdev->maxchild; i++) + if (parent_hdev->children[i] == udev) { + port1 = i + 1; + break; + } + + if (port1 < 0) { + /* If this ever happens, it's very bad */ + dev_err(&udev->dev, "Can't locate device's port!\n"); + return -ENOENT; + } + parent_hub = hdev_to_hub(parent_hdev); + + /* If we're resetting an active hub, take some special actions */ + if (udev->actconfig && + udev->actconfig->interface[0]->dev.driver == + &hub_driver.driver && + (hub = hdev_to_hub(udev)) != NULL) { + hub_pre_reset(hub); + } + + set_bit(port1, parent_hub->busy_bits); + for (i = 0; i < SET_CONFIG_TRIES; ++i) { + + /* ep0 maxpacket size may change; let the HCD know about it. + * Other endpoints will be handled by re-enumeration. */ + ep0_reinit(udev); + ret = hub_port_init(parent_hub, udev, port1, i); + if (ret >= 0) + break; + } + clear_bit(port1, parent_hub->busy_bits); + if (ret < 0) + goto re_enumerate; + + /* Device might have changed firmware (DFU or similar) */ + if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor) + || config_descriptors_changed (udev)) { + dev_info(&udev->dev, "device firmware changed\n"); + udev->descriptor = descriptor; /* for disconnect() calls */ + goto re_enumerate; + } + + if (!udev->actconfig) + goto done; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_CONFIGURATION, 0, + udev->actconfig->desc.bConfigurationValue, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_err(&udev->dev, + "can't restore configuration #%d (error=%d)\n", + udev->actconfig->desc.bConfigurationValue, ret); + goto re_enumerate; + } + usb_set_device_state(udev, USB_STATE_CONFIGURED); + + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *intf = udev->actconfig->interface[i]; + struct usb_interface_descriptor *desc; + + /* set_interface resets host side toggle even + * for altsetting zero. the interface may have no driver. + */ + desc = &intf->cur_altsetting->desc; + ret = usb_set_interface(udev, desc->bInterfaceNumber, + desc->bAlternateSetting); + if (ret < 0) { + dev_err(&udev->dev, "failed to restore interface %d " + "altsetting %d (error=%d)\n", + desc->bInterfaceNumber, + desc->bAlternateSetting, + ret); + goto re_enumerate; + } + } + +done: + if (hub) + hub_post_reset(hub); + return 0; + +re_enumerate: + hub_port_logical_disconnect(parent_hub, port1); + return -ENODEV; +} diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h new file mode 100644 index 0000000..d114b84 --- /dev/null +++ b/drivers/usb/core/hub.h @@ -0,0 +1,238 @@ +#ifndef __LINUX_HUB_H +#define __LINUX_HUB_H + +/* + * Hub protocol and driver data structures. + * + * Some of these are known to the "virtual root hub" code + * in host controller drivers. + */ + +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/compiler.h> /* likely()/unlikely() */ + +/* + * Hub request types + */ + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +/* + * Hub class requests + * See USB 2.0 spec Table 11-16 + */ +#define HUB_CLEAR_TT_BUFFER 8 +#define HUB_RESET_TT 9 +#define HUB_GET_TT_STATE 10 +#define HUB_STOP_TT 11 + +/* + * Hub Class feature numbers + * See USB 2.0 spec Table 11-17 + */ +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +/* + * Port feature numbers + * See USB 2.0 spec Table 11-17 + */ +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_HIGHSPEED 10 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 + +/* + * Hub Status and Hub Change results + * See USB 2.0 spec Table 11-19 and Table 11-20 + */ +struct usb_port_status { + __le16 wPortStatus; + __le16 wPortChange; +} __attribute__ ((packed)); + +/* + * wPortStatus bit field + * See USB 2.0 spec Table 11-21 + */ +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +/* bits 5 to 7 are reserved */ +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 +/* bits 13 to 15 are reserved */ + +/* + * wPortChange bit field + * See USB 2.0 spec Table 11-22 + * Bits 0 to 4 shown, bits 5 to 15 are reserved + */ +#define USB_PORT_STAT_C_CONNECTION 0x0001 +#define USB_PORT_STAT_C_ENABLE 0x0002 +#define USB_PORT_STAT_C_SUSPEND 0x0004 +#define USB_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_PORT_STAT_C_RESET 0x0010 + +/* + * wHubCharacteristics (masks) + * See USB 2.0 spec Table 11-13, offset 3 + */ +#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */ +#define HUB_CHAR_COMPOUND 0x0004 /* D2 */ +#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */ +#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */ +#define HUB_CHAR_PORTIND 0x0080 /* D7 */ + +struct usb_hub_status { + __le16 wHubStatus; + __le16 wHubChange; +} __attribute__ ((packed)); + +/* + * Hub Status & Hub Change bit masks + * See USB 2.0 spec Table 11-19 and Table 11-20 + * Bits 0 and 1 for wHubStatus and wHubChange + * Bits 2 to 15 are reserved for both + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + + +/* + * Hub descriptor + * See USB 2.0 spec Table 11-13 + */ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +#define USB_DT_HUB_NONVAR_SIZE 7 + +struct usb_hub_descriptor { + __u8 bDescLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __u16 wHubCharacteristics; + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + /* add 1 bit for hub status change; round to bytes */ + __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8]; + __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8]; +} __attribute__ ((packed)); + + +/* port indicator status selectors, tables 11-7 and 11-25 */ +#define HUB_LED_AUTO 0 +#define HUB_LED_AMBER 1 +#define HUB_LED_GREEN 2 +#define HUB_LED_OFF 3 + +enum hub_led_mode { + INDICATOR_AUTO = 0, + INDICATOR_CYCLE, + /* software blinks for attention: software, hardware, reserved */ + INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF, + INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF, + INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF +} __attribute__ ((packed)); + +struct usb_device; + +/* + * As of USB 2.0, full/low speed devices are segregated into trees. + * One type grows from USB 1.1 host controllers (OHCI, UHCI etc). + * The other type grows from high speed hubs when they connect to + * full/low speed devices using "Transaction Translators" (TTs). + * + * TTs should only be known to the hub driver, and high speed bus + * drivers (only EHCI for now). They affect periodic scheduling and + * sometimes control/bulk error recovery. + */ +struct usb_tt { + struct usb_device *hub; /* upstream highspeed hub */ + int multi; /* true means one TT per port */ + + /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ + spinlock_t lock; + struct list_head clear_list; /* of usb_tt_clear */ + struct work_struct kevent; +}; + +struct usb_tt_clear { + struct list_head clear_list; + unsigned tt; + u16 devinfo; +}; + +extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe); + +struct usb_hub { + struct device *intfdev; /* the "interface" device */ + struct usb_device *hdev; + struct urb *urb; /* for interrupt polling pipe */ + + /* buffer for urb ... with extra space in case of babble */ + char (*buffer)[8]; + dma_addr_t buffer_dma; /* DMA address for buffer */ + union { + struct usb_hub_status hub; + struct usb_port_status port; + } *status; /* buffer for status reports */ + + int error; /* last reported error */ + int nerrors; /* track consecutive errors */ + + struct list_head event_list; /* hubs w/data or errs ready */ + unsigned long event_bits[1]; /* status change bitmask */ + unsigned long change_bits[1]; /* ports with logical connect + status change */ + unsigned long busy_bits[1]; /* ports being reset */ +#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ +#error event_bits[] is too short! +#endif + + struct usb_hub_descriptor *descriptor; /* class descriptor */ + struct usb_tt tt; /* Transaction Translator */ + + u8 power_budget; /* in 2mA units; or zero */ + + unsigned quiescing:1; + unsigned activating:1; + unsigned resume_root_hub:1; + + unsigned has_indicators:1; + enum hub_led_mode indicator[USB_MAXCHILDREN]; + struct work_struct leds; +}; + +/* use this for low-powered root hubs */ +static inline void +hub_set_power_budget (struct usb_device *hubdev, unsigned mA) +{ + struct usb_hub *hub; + + hub = (struct usb_hub *) + usb_get_intfdata (hubdev->actconfig->interface[0]); + hub->power_budget = min(mA,(unsigned)500)/2; +} + +#endif /* __LINUX_HUB_H */ diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c new file mode 100644 index 0000000..d913407 --- /dev/null +++ b/drivers/usb/core/inode.c @@ -0,0 +1,764 @@ +/*****************************************************************************/ + +/* + * inode.c -- Inode/Dentry functions for the USB device file system. + * + * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * 0.1 04.01.2000 Created + * 0.2 10.12.2001 converted to use the vfs layer better + */ + +/*****************************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/usb.h> +#include <linux/namei.h> +#include <linux/usbdevice_fs.h> +#include <linux/smp_lock.h> +#include <linux/parser.h> +#include <asm/byteorder.h> +#include "usb.h" + +static struct super_operations usbfs_ops; +static struct file_operations default_file_operations; +static struct inode_operations usbfs_dir_inode_operations; +static struct vfsmount *usbfs_mount; +static int usbfs_mount_count; /* = 0 */ +static int ignore_mount = 0; + +static struct dentry *devices_usbfs_dentry; +static int num_buses; /* = 0 */ + +static uid_t devuid; /* = 0 */ +static uid_t busuid; /* = 0 */ +static uid_t listuid; /* = 0 */ +static gid_t devgid; /* = 0 */ +static gid_t busgid; /* = 0 */ +static gid_t listgid; /* = 0 */ +static umode_t devmode = S_IWUSR | S_IRUGO; +static umode_t busmode = S_IXUGO | S_IRUGO; +static umode_t listmode = S_IRUGO; + +enum { + Opt_devuid, Opt_devgid, Opt_devmode, + Opt_busuid, Opt_busgid, Opt_busmode, + Opt_listuid, Opt_listgid, Opt_listmode, + Opt_err, +}; + +static match_table_t tokens = { + {Opt_devuid, "devuid=%u"}, + {Opt_devgid, "devgid=%u"}, + {Opt_devmode, "devmode=%o"}, + {Opt_busuid, "busuid=%u"}, + {Opt_busgid, "busgid=%u"}, + {Opt_busmode, "busmode=%o"}, + {Opt_listuid, "listuid=%u"}, + {Opt_listgid, "listgid=%u"}, + {Opt_listmode, "listmode=%o"}, + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *s, char *data) +{ + char *p; + int option; + + /* (re)set to defaults. */ + devuid = 0; + busuid = 0; + listuid = 0; + devgid = 0; + busgid = 0; + listgid = 0; + devmode = S_IWUSR | S_IRUGO; + busmode = S_IXUGO | S_IRUGO; + listmode = S_IRUGO; + + while ((p = strsep(&data, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_devuid: + if (match_int(&args[0], &option)) + return -EINVAL; + devuid = option; + break; + case Opt_devgid: + if (match_int(&args[0], &option)) + return -EINVAL; + devgid = option; + break; + case Opt_devmode: + if (match_octal(&args[0], &option)) + return -EINVAL; + devmode = option & S_IRWXUGO; + break; + case Opt_busuid: + if (match_int(&args[0], &option)) + return -EINVAL; + busuid = option; + break; + case Opt_busgid: + if (match_int(&args[0], &option)) + return -EINVAL; + busgid = option; + break; + case Opt_busmode: + if (match_octal(&args[0], &option)) + return -EINVAL; + busmode = option & S_IRWXUGO; + break; + case Opt_listuid: + if (match_int(&args[0], &option)) + return -EINVAL; + listuid = option; + break; + case Opt_listgid: + if (match_int(&args[0], &option)) + return -EINVAL; + listgid = option; + break; + case Opt_listmode: + if (match_octal(&args[0], &option)) + return -EINVAL; + listmode = option & S_IRWXUGO; + break; + default: + err("usbfs: unrecognised mount option \"%s\" " + "or missing value\n", p); + return -EINVAL; + } + } + + return 0; +} + +static void update_special(struct dentry *special) +{ + special->d_inode->i_uid = listuid; + special->d_inode->i_gid = listgid; + special->d_inode->i_mode = S_IFREG | listmode; +} + +static void update_dev(struct dentry *dev) +{ + dev->d_inode->i_uid = devuid; + dev->d_inode->i_gid = devgid; + dev->d_inode->i_mode = S_IFREG | devmode; +} + +static void update_bus(struct dentry *bus) +{ + struct dentry *dev = NULL; + + bus->d_inode->i_uid = busuid; + bus->d_inode->i_gid = busgid; + bus->d_inode->i_mode = S_IFDIR | busmode; + + down(&bus->d_inode->i_sem); + + list_for_each_entry(dev, &bus->d_subdirs, d_child) + if (dev->d_inode) + update_dev(dev); + + up(&bus->d_inode->i_sem); +} + +static void update_sb(struct super_block *sb) +{ + struct dentry *root = sb->s_root; + struct dentry *bus = NULL; + + if (!root) + return; + + down(&root->d_inode->i_sem); + + list_for_each_entry(bus, &root->d_subdirs, d_child) { + if (bus->d_inode) { + switch (S_IFMT & bus->d_inode->i_mode) { + case S_IFDIR: + update_bus(bus); + break; + case S_IFREG: + update_special(bus); + break; + default: + warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode); + break; + } + } + } + + up(&root->d_inode->i_sem); +} + +static int remount(struct super_block *sb, int *flags, char *data) +{ + /* If this is not a real mount, + * i.e. it's a simple_pin_fs from create_special_files, + * then ignore it. + */ + if (ignore_mount) + return 0; + + if (parse_options(sb, data)) { + warn("usbfs: mount parameter error:"); + return -EINVAL; + } + + if (usbfs_mount && usbfs_mount->mnt_sb) + update_sb(usbfs_mount->mnt_sb); + + return 0; +} + +static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t dev) +{ + struct inode *inode = new_inode(sb); + + if (inode) { + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + switch (mode & S_IFMT) { + default: + init_special_inode(inode, mode, dev); + break; + case S_IFREG: + inode->i_fop = &default_file_operations; + break; + case S_IFDIR: + inode->i_op = &usbfs_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + break; + } + } + return inode; +} + +/* SMP-safe */ +static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev); + int error = -EPERM; + + if (dentry->d_inode) + return -EEXIST; + + if (inode) { + d_instantiate(dentry, inode); + dget(dentry); + error = 0; + } + return error; +} + +static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) +{ + int res; + + mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR; + res = usbfs_mknod (dir, dentry, mode, 0); + if (!res) + dir->i_nlink++; + return res; +} + +static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode) +{ + mode = (mode & S_IALLUGO) | S_IFREG; + return usbfs_mknod (dir, dentry, mode, 0); +} + +static inline int usbfs_positive (struct dentry *dentry) +{ + return dentry->d_inode && !d_unhashed(dentry); +} + +static int usbfs_empty (struct dentry *dentry) +{ + struct list_head *list; + + spin_lock(&dcache_lock); + + list_for_each(list, &dentry->d_subdirs) { + struct dentry *de = list_entry(list, struct dentry, d_child); + if (usbfs_positive(de)) { + spin_unlock(&dcache_lock); + return 0; + } + } + + spin_unlock(&dcache_lock); + return 1; +} + +static int usbfs_unlink (struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + down(&inode->i_sem); + dentry->d_inode->i_nlink--; + dput(dentry); + up(&inode->i_sem); + d_delete(dentry); + return 0; +} + +static int usbfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int error = -ENOTEMPTY; + struct inode * inode = dentry->d_inode; + + down(&inode->i_sem); + dentry_unhash(dentry); + if (usbfs_empty(dentry)) { + dentry->d_inode->i_nlink -= 2; + dput(dentry); + inode->i_flags |= S_DEAD; + dir->i_nlink--; + error = 0; + } + up(&inode->i_sem); + if (!error) + d_delete(dentry); + dput(dentry); + return error; +} + + +/* default file operations */ +static ssize_t default_read_file (struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t default_write_file (struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) +{ + loff_t retval = -EINVAL; + + down(&file->f_dentry->d_inode->i_sem); + switch(orig) { + case 0: + if (offset > 0) { + file->f_pos = offset; + retval = file->f_pos; + } + break; + case 1: + if ((offset + file->f_pos) > 0) { + file->f_pos += offset; + retval = file->f_pos; + } + break; + default: + break; + } + up(&file->f_dentry->d_inode->i_sem); + return retval; +} + +static int default_open (struct inode *inode, struct file *file) +{ + if (inode->u.generic_ip) + file->private_data = inode->u.generic_ip; + + return 0; +} + +static struct file_operations default_file_operations = { + .read = default_read_file, + .write = default_write_file, + .open = default_open, + .llseek = default_file_lseek, +}; + +static struct inode_operations usbfs_dir_inode_operations = { + .lookup = simple_lookup, +}; + +static struct super_operations usbfs_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, + .remount_fs = remount, +}; + +static int usbfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *inode; + struct dentry *root; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = USBDEVICE_SUPER_MAGIC; + sb->s_op = &usbfs_ops; + sb->s_time_gran = 1; + inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0); + + if (!inode) { + dbg("%s: could not get inode!",__FUNCTION__); + return -ENOMEM; + } + + root = d_alloc_root(inode); + if (!root) { + dbg("%s: could not get root dentry!",__FUNCTION__); + iput(inode); + return -ENOMEM; + } + sb->s_root = root; + return 0; +} + +static struct dentry * get_dentry(struct dentry *parent, const char *name) +{ + struct qstr qstr; + + qstr.name = name; + qstr.len = strlen(name); + qstr.hash = full_name_hash(name,qstr.len); + return lookup_hash(&qstr,parent); +} + + +/* + * fs_create_by_name - create a file, given a name + * @name: name of file + * @mode: type of file + * @parent: dentry of directory to create it in + * @dentry: resulting dentry of file + * + * This function handles both regular files and directories. + */ +static int fs_create_by_name (const char *name, mode_t mode, + struct dentry *parent, struct dentry **dentry) +{ + int error = 0; + + /* If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent ) { + if (usbfs_mount && usbfs_mount->mnt_sb) { + parent = usbfs_mount->mnt_sb->s_root; + } + } + + if (!parent) { + dbg("Ah! can not find a parent!"); + return -EFAULT; + } + + *dentry = NULL; + down(&parent->d_inode->i_sem); + *dentry = get_dentry (parent, name); + if (!IS_ERR(dentry)) { + if ((mode & S_IFMT) == S_IFDIR) + error = usbfs_mkdir (parent->d_inode, *dentry, mode); + else + error = usbfs_create (parent->d_inode, *dentry, mode); + } else + error = PTR_ERR(dentry); + up(&parent->d_inode->i_sem); + + return error; +} + +static struct dentry *fs_create_file (const char *name, mode_t mode, + struct dentry *parent, void *data, + struct file_operations *fops, + uid_t uid, gid_t gid) +{ + struct dentry *dentry; + int error; + + dbg("creating file '%s'",name); + + error = fs_create_by_name (name, mode, parent, &dentry); + if (error) { + dentry = NULL; + } else { + if (dentry->d_inode) { + if (data) + dentry->d_inode->u.generic_ip = data; + if (fops) + dentry->d_inode->i_fop = fops; + dentry->d_inode->i_uid = uid; + dentry->d_inode->i_gid = gid; + } + } + + return dentry; +} + +static void fs_remove_file (struct dentry *dentry) +{ + struct dentry *parent = dentry->d_parent; + + if (!parent || !parent->d_inode) + return; + + down(&parent->d_inode->i_sem); + if (usbfs_positive(dentry)) { + if (dentry->d_inode) { + if (S_ISDIR(dentry->d_inode->i_mode)) + usbfs_rmdir(parent->d_inode, dentry); + else + usbfs_unlink(parent->d_inode, dentry); + dput(dentry); + } + } + up(&parent->d_inode->i_sem); +} + +/* --------------------------------------------------------------------- */ + +static struct super_block *usb_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_single(fs_type, flags, data, usbfs_fill_super); +} + +static struct file_system_type usb_fs_type = { + .owner = THIS_MODULE, + .name = "usbfs", + .get_sb = usb_get_sb, + .kill_sb = kill_litter_super, +}; + +/* --------------------------------------------------------------------- */ + +static int create_special_files (void) +{ + struct dentry *parent; + int retval; + + /* the simple_pin_fs calls will call remount with no options + * without this flag that would overwrite the real mount options (if any) + */ + ignore_mount = 1; + + /* create the devices special file */ + retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); + if (retval) { + err ("Unable to get usbfs mount"); + goto exit; + } + + ignore_mount = 0; + + parent = usbfs_mount->mnt_sb->s_root; + devices_usbfs_dentry = fs_create_file ("devices", + listmode | S_IFREG, parent, + NULL, &usbfs_devices_fops, + listuid, listgid); + if (devices_usbfs_dentry == NULL) { + err ("Unable to create devices usbfs file"); + retval = -ENODEV; + goto error_clean_mounts; + } + + goto exit; + +error_clean_mounts: + simple_release_fs(&usbfs_mount, &usbfs_mount_count); +exit: + return retval; +} + +static void remove_special_files (void) +{ + if (devices_usbfs_dentry) + fs_remove_file (devices_usbfs_dentry); + devices_usbfs_dentry = NULL; + simple_release_fs(&usbfs_mount, &usbfs_mount_count); +} + +void usbfs_update_special (void) +{ + struct inode *inode; + + if (devices_usbfs_dentry) { + inode = devices_usbfs_dentry->d_inode; + if (inode) + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } +} + +void usbfs_add_bus(struct usb_bus *bus) +{ + struct dentry *parent; + char name[8]; + int retval; + + /* create the special files if this is the first bus added */ + if (num_buses == 0) { + retval = create_special_files(); + if (retval) + return; + } + ++num_buses; + + sprintf (name, "%03d", bus->busnum); + + parent = usbfs_mount->mnt_sb->s_root; + bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, + bus, NULL, busuid, busgid); + if (bus->usbfs_dentry == NULL) { + err ("error creating usbfs bus entry"); + return; + } + + usbfs_update_special(); + usbfs_conn_disc_event(); +} + +void usbfs_remove_bus(struct usb_bus *bus) +{ + if (bus->usbfs_dentry) { + fs_remove_file (bus->usbfs_dentry); + bus->usbfs_dentry = NULL; + } + + --num_buses; + if (num_buses <= 0) { + remove_special_files(); + num_buses = 0; + } + + usbfs_update_special(); + usbfs_conn_disc_event(); +} + +void usbfs_add_device(struct usb_device *dev) +{ + char name[8]; + int i; + int i_size; + + sprintf (name, "%03d", dev->devnum); + dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, + dev->bus->usbfs_dentry, dev, + &usbfs_device_file_operations, + devuid, devgid); + if (dev->usbfs_dentry == NULL) { + err ("error creating usbfs device entry"); + return; + } + + /* Set the size of the device's file to be + * equal to the size of the device descriptors. */ + i_size = sizeof (struct usb_device_descriptor); + for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) { + struct usb_config_descriptor *config = + (struct usb_config_descriptor *)dev->rawdescriptors[i]; + i_size += le16_to_cpu(config->wTotalLength); + } + if (dev->usbfs_dentry->d_inode) + dev->usbfs_dentry->d_inode->i_size = i_size; + + usbfs_update_special(); + usbfs_conn_disc_event(); +} + +void usbfs_remove_device(struct usb_device *dev) +{ + struct dev_state *ds; + struct siginfo sinfo; + + if (dev->usbfs_dentry) { + fs_remove_file (dev->usbfs_dentry); + dev->usbfs_dentry = NULL; + } + while (!list_empty(&dev->filelist)) { + ds = list_entry(dev->filelist.next, struct dev_state, list); + wake_up_all(&ds->wait); + list_del_init(&ds->list); + if (ds->discsignr) { + sinfo.si_signo = SIGPIPE; + sinfo.si_errno = EPIPE; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = ds->disccontext; + send_sig_info(ds->discsignr, &sinfo, ds->disctask); + } + } + usbfs_update_special(); + usbfs_conn_disc_event(); +} + +/* --------------------------------------------------------------------- */ + +static struct proc_dir_entry *usbdir = NULL; + +int __init usbfs_init(void) +{ + int retval; + + retval = usb_register(&usbfs_driver); + if (retval) + return retval; + + retval = register_filesystem(&usb_fs_type); + if (retval) { + usb_deregister(&usbfs_driver); + return retval; + } + + /* create mount point for usbfs */ + usbdir = proc_mkdir("usb", proc_bus); + + return 0; +} + +void usbfs_cleanup(void) +{ + usb_deregister(&usbfs_driver); + unregister_filesystem(&usb_fs_type); + if (usbdir) + remove_proc_entry("usb", proc_bus); +} + diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c new file mode 100644 index 0000000..40bdb38 --- /dev/null +++ b/drivers/usb/core/message.c @@ -0,0 +1,1480 @@ +/* + * message.c - synchronous message handling + */ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/pci.h> /* for scatterlist macros */ +#include <linux/usb.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/device.h> +#include <asm/byteorder.h> + +#include "hcd.h" /* for usbcore internals */ +#include "usb.h" + +static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs) +{ + complete((struct completion *)urb->context); +} + + +static void timeout_kill(unsigned long data) +{ + struct urb *urb = (struct urb *) data; + + usb_unlink_urb(urb); +} + +// Starts urb and waits for completion or timeout +// note that this call is NOT interruptible, while +// many device driver i/o requests should be interruptible +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) +{ + struct completion done; + struct timer_list timer; + int status; + + init_completion(&done); + urb->context = &done; + urb->transfer_flags |= URB_ASYNC_UNLINK; + urb->actual_length = 0; + status = usb_submit_urb(urb, GFP_NOIO); + + if (status == 0) { + if (timeout > 0) { + init_timer(&timer); + timer.expires = jiffies + msecs_to_jiffies(timeout); + timer.data = (unsigned long)urb; + timer.function = timeout_kill; + /* grr. timeout _should_ include submit delays. */ + add_timer(&timer); + } + wait_for_completion(&done); + status = urb->status; + /* note: HCDs return ETIMEDOUT for other reasons too */ + if (status == -ECONNRESET) { + dev_dbg(&urb->dev->dev, + "%s timed out on ep%d%s len=%d/%d\n", + current->comm, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->actual_length, + urb->transfer_buffer_length + ); + if (urb->actual_length > 0) + status = 0; + else + status = -ETIMEDOUT; + } + if (timeout > 0) + del_timer_sync(&timer); + } + + if (actual_length) + *actual_length = urb->actual_length; + usb_free_urb(urb); + return status; +} + +/*-------------------------------------------------------------------*/ +// returns status (negative) or length (positive) +static int usb_internal_control_msg(struct usb_device *usb_dev, + unsigned int pipe, + struct usb_ctrlrequest *cmd, + void *data, int len, int timeout) +{ + struct urb *urb; + int retv; + int length; + + urb = usb_alloc_urb(0, GFP_NOIO); + if (!urb) + return -ENOMEM; + + usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data, + len, usb_api_blocking_completion, NULL); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; +} + +/** + * usb_control_msg - Builds a control urb, sends it off and waits for completion + * @dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @request: USB message request value + * @requesttype: USB message request type value + * @value: USB message value + * @index: USB message index value + * @data: pointer to the data to send + * @size: length in bytes of the data to send + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * Context: !in_interrupt () + * + * This function sends a simple control message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * If successful, it returns the number of bytes transferred, otherwise a negative error number. + * + * Don't use this function from within an interrupt context, like a + * bottom half handler. If you need an asynchronous message, or need to send + * a message from within interrupt context, use usb_submit_urb() + * If a thread in your driver uses this call, make sure your disconnect() + * method can wait for it to complete. Since you don't have a handle on + * the URB used, you can't cancel the request. + */ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, + __u16 value, __u16 index, void *data, __u16 size, int timeout) +{ + struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); + int ret; + + if (!dr) + return -ENOMEM; + + dr->bRequestType= requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16p(&value); + dr->wIndex = cpu_to_le16p(&index); + dr->wLength = cpu_to_le16p(&size); + + //dbg("usb_control_msg"); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout); + + kfree(dr); + + return ret; +} + + +/** + * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if 0 the wait is forever) + * Context: !in_interrupt () + * + * This function sends a simple bulk message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * If successful, it returns 0, otherwise a negative error number. + * The number of actual bytes transferred will be stored in the + * actual_length paramater. + * + * Don't use this function from within an interrupt context, like a + * bottom half handler. If you need an asynchronous message, or need to + * send a message from within interrupt context, use usb_submit_urb() + * If a thread in your driver uses this call, make sure your disconnect() + * method can wait for it to complete. Since you don't have a handle on + * the URB used, you can't cancel the request. + */ +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + struct urb *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, + usb_api_blocking_completion, NULL); + + return usb_start_wait_urb(urb, timeout, actual_length); +} + +/*-------------------------------------------------------------------*/ + +static void sg_clean (struct usb_sg_request *io) +{ + if (io->urbs) { + while (io->entries--) + usb_free_urb (io->urbs [io->entries]); + kfree (io->urbs); + io->urbs = NULL; + } + if (io->dev->dev.dma_mask != NULL) + usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents); + io->dev = NULL; +} + +static void sg_complete (struct urb *urb, struct pt_regs *regs) +{ + struct usb_sg_request *io = (struct usb_sg_request *) urb->context; + + spin_lock (&io->lock); + + /* In 2.5 we require hcds' endpoint queues not to progress after fault + * reports, until the completion callback (this!) returns. That lets + * device driver code (like this routine) unlink queued urbs first, + * if it needs to, since the HC won't work on them at all. So it's + * not possible for page N+1 to overwrite page N, and so on. + * + * That's only for "hard" faults; "soft" faults (unlinks) sometimes + * complete before the HCD can get requests away from hardware, + * though never during cleanup after a hard fault. + */ + if (io->status + && (io->status != -ECONNRESET + || urb->status != -ECONNRESET) + && urb->actual_length) { + dev_err (io->dev->bus->controller, + "dev %s ep%d%s scatterlist error %d/%d\n", + io->dev->devpath, + usb_pipeendpoint (urb->pipe), + usb_pipein (urb->pipe) ? "in" : "out", + urb->status, io->status); + // BUG (); + } + + if (io->status == 0 && urb->status && urb->status != -ECONNRESET) { + int i, found, status; + + io->status = urb->status; + + /* the previous urbs, and this one, completed already. + * unlink pending urbs so they won't rx/tx bad data. + * careful: unlink can sometimes be synchronous... + */ + spin_unlock (&io->lock); + for (i = 0, found = 0; i < io->entries; i++) { + if (!io->urbs [i] || !io->urbs [i]->dev) + continue; + if (found) { + status = usb_unlink_urb (io->urbs [i]); + if (status != -EINPROGRESS && status != -EBUSY) + dev_err (&io->dev->dev, + "%s, unlink --> %d\n", + __FUNCTION__, status); + } else if (urb == io->urbs [i]) + found = 1; + } + spin_lock (&io->lock); + } + urb->dev = NULL; + + /* on the last completion, signal usb_sg_wait() */ + io->bytes += urb->actual_length; + io->count--; + if (!io->count) + complete (&io->complete); + + spin_unlock (&io->lock); +} + + +/** + * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request + * @io: request block being initialized. until usb_sg_wait() returns, + * treat this as a pointer to an opaque block of memory, + * @dev: the usb device that will send or receive the data + * @pipe: endpoint "pipe" used to transfer the data + * @period: polling rate for interrupt endpoints, in frames or + * (for high speed endpoints) microframes; ignored for bulk + * @sg: scatterlist entries + * @nents: how many entries in the scatterlist + * @length: how many bytes to send from the scatterlist, or zero to + * send every byte identified in the list. + * @mem_flags: SLAB_* flags affecting memory allocations in this call + * + * Returns zero for success, else a negative errno value. This initializes a + * scatter/gather request, allocating resources such as I/O mappings and urb + * memory (except maybe memory used by USB controller drivers). + * + * The request must be issued using usb_sg_wait(), which waits for the I/O to + * complete (or to be canceled) and then cleans up all resources allocated by + * usb_sg_init(). + * + * The request may be canceled with usb_sg_cancel(), either before or after + * usb_sg_wait() is called. + */ +int usb_sg_init ( + struct usb_sg_request *io, + struct usb_device *dev, + unsigned pipe, + unsigned period, + struct scatterlist *sg, + int nents, + size_t length, + int mem_flags +) +{ + int i; + int urb_flags; + int dma; + + if (!io || !dev || !sg + || usb_pipecontrol (pipe) + || usb_pipeisoc (pipe) + || nents <= 0) + return -EINVAL; + + spin_lock_init (&io->lock); + io->dev = dev; + io->pipe = pipe; + io->sg = sg; + io->nents = nents; + + /* not all host controllers use DMA (like the mainstream pci ones); + * they can use PIO (sl811) or be software over another transport. + */ + dma = (dev->dev.dma_mask != NULL); + if (dma) + io->entries = usb_buffer_map_sg (dev, pipe, sg, nents); + else + io->entries = nents; + + /* initialize all the urbs we'll use */ + if (io->entries <= 0) + return io->entries; + + io->count = io->entries; + io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags); + if (!io->urbs) + goto nomem; + + urb_flags = URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP + | URB_NO_INTERRUPT; + if (usb_pipein (pipe)) + urb_flags |= URB_SHORT_NOT_OK; + + for (i = 0; i < io->entries; i++) { + unsigned len; + + io->urbs [i] = usb_alloc_urb (0, mem_flags); + if (!io->urbs [i]) { + io->entries = i; + goto nomem; + } + + io->urbs [i]->dev = NULL; + io->urbs [i]->pipe = pipe; + io->urbs [i]->interval = period; + io->urbs [i]->transfer_flags = urb_flags; + + io->urbs [i]->complete = sg_complete; + io->urbs [i]->context = io; + io->urbs [i]->status = -EINPROGRESS; + io->urbs [i]->actual_length = 0; + + if (dma) { + /* hc may use _only_ transfer_dma */ + io->urbs [i]->transfer_dma = sg_dma_address (sg + i); + len = sg_dma_len (sg + i); + } else { + /* hc may use _only_ transfer_buffer */ + io->urbs [i]->transfer_buffer = + page_address (sg [i].page) + sg [i].offset; + len = sg [i].length; + } + + if (length) { + len = min_t (unsigned, len, length); + length -= len; + if (length == 0) + io->entries = i + 1; + } + io->urbs [i]->transfer_buffer_length = len; + } + io->urbs [--i]->transfer_flags &= ~URB_NO_INTERRUPT; + + /* transaction state */ + io->status = 0; + io->bytes = 0; + init_completion (&io->complete); + return 0; + +nomem: + sg_clean (io); + return -ENOMEM; +} + + +/** + * usb_sg_wait - synchronously execute scatter/gather request + * @io: request block handle, as initialized with usb_sg_init(). + * some fields become accessible when this call returns. + * Context: !in_interrupt () + * + * This function blocks until the specified I/O operation completes. It + * leverages the grouping of the related I/O requests to get good transfer + * rates, by queueing the requests. At higher speeds, such queuing can + * significantly improve USB throughput. + * + * There are three kinds of completion for this function. + * (1) success, where io->status is zero. The number of io->bytes + * transferred is as requested. + * (2) error, where io->status is a negative errno value. The number + * of io->bytes transferred before the error is usually less + * than requested, and can be nonzero. + * (3) cancelation, a type of error with status -ECONNRESET that + * is initiated by usb_sg_cancel(). + * + * When this function returns, all memory allocated through usb_sg_init() or + * this call will have been freed. The request block parameter may still be + * passed to usb_sg_cancel(), or it may be freed. It could also be + * reinitialized and then reused. + * + * Data Transfer Rates: + * + * Bulk transfers are valid for full or high speed endpoints. + * The best full speed data rate is 19 packets of 64 bytes each + * per frame, or 1216 bytes per millisecond. + * The best high speed data rate is 13 packets of 512 bytes each + * per microframe, or 52 KBytes per millisecond. + * + * The reason to use interrupt transfers through this API would most likely + * be to reserve high speed bandwidth, where up to 24 KBytes per millisecond + * could be transferred. That capability is less useful for low or full + * speed interrupt endpoints, which allow at most one packet per millisecond, + * of at most 8 or 64 bytes (respectively). + */ +void usb_sg_wait (struct usb_sg_request *io) +{ + int i, entries = io->entries; + + /* queue the urbs. */ + spin_lock_irq (&io->lock); + for (i = 0; i < entries && !io->status; i++) { + int retval; + + io->urbs [i]->dev = io->dev; + retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC); + + /* after we submit, let completions or cancelations fire; + * we handshake using io->status. + */ + spin_unlock_irq (&io->lock); + switch (retval) { + /* maybe we retrying will recover */ + case -ENXIO: // hc didn't queue this one + case -EAGAIN: + case -ENOMEM: + io->urbs[i]->dev = NULL; + retval = 0; + i--; + yield (); + break; + + /* no error? continue immediately. + * + * NOTE: to work better with UHCI (4K I/O buffer may + * need 3K of TDs) it may be good to limit how many + * URBs are queued at once; N milliseconds? + */ + case 0: + cpu_relax (); + break; + + /* fail any uncompleted urbs */ + default: + io->urbs [i]->dev = NULL; + io->urbs [i]->status = retval; + dev_dbg (&io->dev->dev, "%s, submit --> %d\n", + __FUNCTION__, retval); + usb_sg_cancel (io); + } + spin_lock_irq (&io->lock); + if (retval && (io->status == 0 || io->status == -ECONNRESET)) + io->status = retval; + } + io->count -= entries - i; + if (io->count == 0) + complete (&io->complete); + spin_unlock_irq (&io->lock); + + /* OK, yes, this could be packaged as non-blocking. + * So could the submit loop above ... but it's easier to + * solve neither problem than to solve both! + */ + wait_for_completion (&io->complete); + + sg_clean (io); +} + +/** + * usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait() + * @io: request block, initialized with usb_sg_init() + * + * This stops a request after it has been started by usb_sg_wait(). + * It can also prevents one initialized by usb_sg_init() from starting, + * so that call just frees resources allocated to the request. + */ +void usb_sg_cancel (struct usb_sg_request *io) +{ + unsigned long flags; + + spin_lock_irqsave (&io->lock, flags); + + /* shut everything down, if it didn't already */ + if (!io->status) { + int i; + + io->status = -ECONNRESET; + spin_unlock (&io->lock); + for (i = 0; i < io->entries; i++) { + int retval; + + if (!io->urbs [i]->dev) + continue; + retval = usb_unlink_urb (io->urbs [i]); + if (retval != -EINPROGRESS && retval != -EBUSY) + dev_warn (&io->dev->dev, "%s, unlink --> %d\n", + __FUNCTION__, retval); + } + spin_lock (&io->lock); + } + spin_unlock_irqrestore (&io->lock, flags); +} + +/*-------------------------------------------------------------------*/ + +/** + * usb_get_descriptor - issues a generic GET_DESCRIPTOR request + * @dev: the device whose descriptor is being retrieved + * @type: the descriptor type (USB_DT_*) + * @index: the number of the descriptor + * @buf: where to put the descriptor + * @size: how big is "buf"? + * Context: !in_interrupt () + * + * Gets a USB descriptor. Convenience functions exist to simplify + * getting some types of descriptors. Use + * usb_get_string() or usb_string() for USB_DT_STRING. + * Device (USB_DT_DEVICE) and configuration descriptors (USB_DT_CONFIG) + * are part of the device structure. + * In addition to a number of USB-standard descriptors, some + * devices also use class-specific or vendor-specific descriptors. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns the number of bytes received on success, or else the status code + * returned by the underlying usb_control_msg() call. + */ +int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) +{ + int i; + int result; + + memset(buf,0,size); // Make sure we parse really received data + + for (i = 0; i < 3; ++i) { + /* retry on length 0 or stall; some devices are flakey */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, buf, size, + USB_CTRL_GET_TIMEOUT); + if (result == 0 || result == -EPIPE) + continue; + if (result > 1 && ((u8 *)buf)[1] != type) { + result = -EPROTO; + continue; + } + break; + } + return result; +} + +/** + * usb_get_string - gets a string descriptor + * @dev: the device whose string descriptor is being retrieved + * @langid: code for language chosen (from string descriptor zero) + * @index: the number of the descriptor + * @buf: where to put the string + * @size: how big is "buf"? + * Context: !in_interrupt () + * + * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character, + * in little-endian byte order). + * The usb_string() function will often be a convenient way to turn + * these strings into kernel-printable form. + * + * Strings may be referenced in device, configuration, interface, or other + * descriptors, and could also be used in vendor-specific ways. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns the number of bytes received on success, or else the status code + * returned by the underlying usb_control_msg() call. + */ +int usb_get_string(struct usb_device *dev, unsigned short langid, + unsigned char index, void *buf, int size) +{ + int i; + int result; + + for (i = 0; i < 3; ++i) { + /* retry on length 0 or stall; some devices are flakey */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, + USB_CTRL_GET_TIMEOUT); + if (!(result == 0 || result == -EPIPE)) + break; + } + return result; +} + +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + +static int usb_string_sub(struct usb_device *dev, unsigned int langid, + unsigned int index, unsigned char *buf) +{ + int rc; + + /* Try to read the string descriptor by asking for the maximum + * possible number of bytes */ + rc = usb_get_string(dev, langid, index, buf, 255); + + /* If that failed try to read the descriptor length, then + * ask for just that many bytes */ + if (rc < 2) { + rc = usb_get_string(dev, langid, index, buf, 2); + if (rc == 2) + rc = usb_get_string(dev, langid, index, buf, buf[0]); + } + + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + + /* There might be extra junk at the end of the descriptor */ + if (buf[0] < rc) + rc = buf[0]; + + rc = rc - (rc & 1); /* force a multiple of two */ + } + + if (rc < 2) + rc = (rc < 0 ? rc : -EINVAL); + + return rc; +} + +/** + * usb_string - returns ISO 8859-1 version of a string descriptor + * @dev: the device whose string descriptor is being retrieved + * @index: the number of the descriptor + * @buf: where to put the string + * @size: how big is "buf"? + * Context: !in_interrupt () + * + * This converts the UTF-16LE encoded strings returned by devices, from + * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones + * that are more usable in most kernel contexts. Note that all characters + * in the chosen descriptor that can't be encoded using ISO-8859-1 + * are converted to the question mark ("?") character, and this function + * chooses strings in the first language supported by the device. + * + * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit + * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode, + * and is appropriate for use many uses of English and several other + * Western European languages. (But it doesn't include the "Euro" symbol.) + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns length of the string (>= 0) or usb_control_msg status (< 0). + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + unsigned char *tbuf; + int err; + unsigned int u, idx; + + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (size <= 0 || !buf || !index) + return -EINVAL; + buf[0] = 0; + tbuf = kmalloc(256, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_string_sub(dev, 0, 0, tbuf); + if (err < 0) { + dev_err (&dev->dev, + "string descriptor 0 read error: %d\n", + err); + goto errout; + } else if (err < 4) { + dev_err (&dev->dev, "string descriptor 0 too short\n"); + err = -EINVAL; + goto errout; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3]<< 8); + /* always use the first langid listed */ + dev_dbg (&dev->dev, "default language 0x%04x\n", + dev->string_langid); + } + } + + err = usb_string_sub(dev, dev->string_langid, index, tbuf); + if (err < 0) + goto errout; + + size--; /* leave room for trailing NULL char in output buffer */ + for (idx = 0, u = 2; u < err; u += 2) { + if (idx >= size) + break; + if (tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non ISO-8859-1 character */ + else + buf[idx++] = tbuf[u]; + } + buf[idx] = 0; + err = idx; + + if (tbuf[1] != USB_DT_STRING) + dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf); + + errout: + kfree(tbuf); + return err; +} + +/* + * usb_get_device_descriptor - (re)reads the device descriptor (usbcore) + * @dev: the device whose device descriptor is being updated + * @size: how much of the descriptor to read + * Context: !in_interrupt () + * + * Updates the copy of the device descriptor stored in the device structure, + * which dedicates space for this purpose. Note that several fields are + * converted to the host CPU's byte order: the USB version (bcdUSB), and + * vendors product and version fields (idVendor, idProduct, and bcdDevice). + * That lets device drivers compare against non-byteswapped constants. + * + * Not exported, only for use by the core. If drivers really want to read + * the device descriptor directly, they can call usb_get_descriptor() with + * type = USB_DT_DEVICE and index = 0. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns the number of bytes received on success, or else the status code + * returned by the underlying usb_control_msg() call. + */ +int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) +{ + struct usb_device_descriptor *desc; + int ret; + + if (size > sizeof(*desc)) + return -EINVAL; + desc = kmalloc(sizeof(*desc), GFP_NOIO); + if (!desc) + return -ENOMEM; + + ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size); + if (ret >= 0) + memcpy(&dev->descriptor, desc, size); + kfree(desc); + return ret; +} + +/** + * usb_get_status - issues a GET_STATUS call + * @dev: the device whose status is being checked + * @type: USB_RECIP_*; for device, interface, or endpoint + * @target: zero (for device), else interface or endpoint number + * @data: pointer to two bytes of bitmap data + * Context: !in_interrupt () + * + * Returns device, interface, or endpoint status. Normally only of + * interest to see if the device is self powered, or has enabled the + * remote wakeup facility; or whether a bulk or interrupt endpoint + * is halted ("stalled"). + * + * Bits in these status bitmaps are set using the SET_FEATURE request, + * and cleared using the CLEAR_FEATURE request. The usb_clear_halt() + * function should be used to clear halt ("stall") status. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns the number of bytes received on success, or else the status code + * returned by the underlying usb_control_msg() call. + */ +int usb_get_status(struct usb_device *dev, int type, int target, void *data) +{ + int ret; + u16 *status = kmalloc(sizeof(*status), GFP_KERNEL); + + if (!status) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status, + sizeof(*status), USB_CTRL_GET_TIMEOUT); + + *(u16 *)data = *status; + kfree(status); + return ret; +} + +/** + * usb_clear_halt - tells device to clear endpoint halt/stall condition + * @dev: device whose endpoint is halted + * @pipe: endpoint "pipe" being cleared + * Context: !in_interrupt () + * + * This is used to clear halt conditions for bulk and interrupt endpoints, + * as reported by URB completion status. Endpoints that are halted are + * sometimes referred to as being "stalled". Such endpoints are unable + * to transmit or receive data until the halt status is cleared. Any URBs + * queued for such an endpoint should normally be unlinked by the driver + * before clearing the halt condition, as described in sections 5.7.5 + * and 5.8.5 of the USB 2.0 spec. + * + * Note that control and isochronous endpoints don't halt, although control + * endpoints report "protocol stall" (for unsupported requests) using the + * same status code used to report a true stall. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Returns zero on success, or else the status code returned by the + * underlying usb_control_msg() call. + */ +int usb_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe); + + if (usb_pipein (pipe)) + endp |= USB_DIR_IN; + + /* we don't care if it wasn't halted first. in fact some devices + * (like some ibmcam model 1 units) seem to expect hosts to make + * this request for iso endpoints, which can't halt! + */ + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, endp, NULL, 0, + USB_CTRL_SET_TIMEOUT); + + /* don't un-halt or force to DATA0 except on success */ + if (result < 0) + return result; + + /* NOTE: seems like Microsoft and Apple don't bother verifying + * the clear "took", so some devices could lock up if you check... + * such as the Hagiwara FlashGate DUAL. So we won't bother. + * + * NOTE: make sure the logic here doesn't diverge much from + * the copy in usb-storage, for as long as we need two copies. + */ + + /* toggle was reset by the clear */ + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + + return 0; +} + +/** + * usb_disable_endpoint -- Disable an endpoint by address + * @dev: the device whose endpoint is being disabled + * @epaddr: the endpoint's address. Endpoint number for output, + * endpoint number + USB_DIR_IN for input + * + * Deallocates hcd/hardware state for this endpoint ... and nukes all + * pending urbs. + * + * If the HCD hasn't registered a disable() function, this sets the + * endpoint's maxpacket size to 0 to prevent further submissions. + */ +void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) +{ + unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; + struct usb_host_endpoint *ep; + + if (!dev) + return; + + if (usb_endpoint_out(epaddr)) { + ep = dev->ep_out[epnum]; + dev->ep_out[epnum] = NULL; + } else { + ep = dev->ep_in[epnum]; + dev->ep_in[epnum] = NULL; + } + if (ep && dev->bus && dev->bus->op && dev->bus->op->disable) + dev->bus->op->disable(dev, ep); +} + +/** + * usb_disable_interface -- Disable all endpoints for an interface + * @dev: the device whose interface is being disabled + * @intf: pointer to the interface descriptor + * + * Disables all the endpoints for the interface's current altsetting. + */ +void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) +{ + struct usb_host_interface *alt = intf->cur_altsetting; + int i; + + for (i = 0; i < alt->desc.bNumEndpoints; ++i) { + usb_disable_endpoint(dev, + alt->endpoint[i].desc.bEndpointAddress); + } +} + +/* + * usb_disable_device - Disable all the endpoints for a USB device + * @dev: the device whose endpoints are being disabled + * @skip_ep0: 0 to disable endpoint 0, 1 to skip it. + * + * Disables all the device's endpoints, potentially including endpoint 0. + * Deallocates hcd/hardware state for the endpoints (nuking all or most + * pending urbs) and usbcore state for the interfaces, so that usbcore + * must usb_set_configuration() before any interfaces could be used. + */ +void usb_disable_device(struct usb_device *dev, int skip_ep0) +{ + int i; + + dev_dbg(&dev->dev, "%s nuking %s URBs\n", __FUNCTION__, + skip_ep0 ? "non-ep0" : "all"); + for (i = skip_ep0; i < 16; ++i) { + usb_disable_endpoint(dev, i); + usb_disable_endpoint(dev, i + USB_DIR_IN); + } + dev->toggle[0] = dev->toggle[1] = 0; + + /* getting rid of interfaces will disconnect + * any drivers bound to them (a key side effect) + */ + if (dev->actconfig) { + for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *interface; + + /* remove this interface */ + interface = dev->actconfig->interface[i]; + dev_dbg (&dev->dev, "unregistering interface %s\n", + interface->dev.bus_id); + usb_remove_sysfs_intf_files(interface); + kfree(interface->cur_altsetting->string); + interface->cur_altsetting->string = NULL; + device_del (&interface->dev); + } + + /* Now that the interfaces are unbound, nobody should + * try to access them. + */ + for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { + put_device (&dev->actconfig->interface[i]->dev); + dev->actconfig->interface[i] = NULL; + } + dev->actconfig = NULL; + if (dev->state == USB_STATE_CONFIGURED) + usb_set_device_state(dev, USB_STATE_ADDRESS); + } +} + + +/* + * usb_enable_endpoint - Enable an endpoint for USB communications + * @dev: the device whose interface is being enabled + * @ep: the endpoint + * + * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. + * For control endpoints, both the input and output sides are handled. + */ +static void +usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) +{ + unsigned int epaddr = ep->desc.bEndpointAddress; + unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; + int is_control; + + is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_CONTROL); + if (usb_endpoint_out(epaddr) || is_control) { + usb_settoggle(dev, epnum, 1, 0); + dev->ep_out[epnum] = ep; + } + if (!usb_endpoint_out(epaddr) || is_control) { + usb_settoggle(dev, epnum, 0, 0); + dev->ep_in[epnum] = ep; + } +} + +/* + * usb_enable_interface - Enable all the endpoints for an interface + * @dev: the device whose interface is being enabled + * @intf: pointer to the interface descriptor + * + * Enables all the endpoints for the interface's current altsetting. + */ +static void usb_enable_interface(struct usb_device *dev, + struct usb_interface *intf) +{ + struct usb_host_interface *alt = intf->cur_altsetting; + int i; + + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_enable_endpoint(dev, &alt->endpoint[i]); +} + +/** + * usb_set_interface - Makes a particular alternate setting be current + * @dev: the device whose interface is being updated + * @interface: the interface being updated + * @alternate: the setting being chosen. + * Context: !in_interrupt () + * + * This is used to enable data transfers on interfaces that may not + * be enabled by default. Not all devices support such configurability. + * Only the driver bound to an interface may change its setting. + * + * Within any given configuration, each interface may have several + * alternative settings. These are often used to control levels of + * bandwidth consumption. For example, the default setting for a high + * speed interrupt endpoint may not send more than 64 bytes per microframe, + * while interrupt transfers of up to 3KBytes per microframe are legal. + * Also, isochronous endpoints may never be part of an + * interface's default setting. To access such bandwidth, alternate + * interface settings must be made current. + * + * Note that in the Linux USB subsystem, bandwidth associated with + * an endpoint in a given alternate setting is not reserved until an URB + * is submitted that needs that bandwidth. Some other operating systems + * allocate bandwidth early, when a configuration is chosen. + * + * This call is synchronous, and may not be used in an interrupt context. + * Also, drivers must not change altsettings while urbs are scheduled for + * endpoints in that interface; all such urbs must first be completed + * (perhaps forced by unlinking). + * + * Returns zero on success, or else the status code returned by the + * underlying usb_control_msg() call. + */ +int usb_set_interface(struct usb_device *dev, int interface, int alternate) +{ + struct usb_interface *iface; + struct usb_host_interface *alt; + int ret; + int manual = 0; + + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + iface = usb_ifnum_to_if(dev, interface); + if (!iface) { + dev_dbg(&dev->dev, "selecting invalid interface %d\n", + interface); + return -EINVAL; + } + + alt = usb_altnum_to_altsetting(iface, alternate); + if (!alt) { + warn("selecting invalid altsetting %d", alternate); + return -EINVAL; + } + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, + alternate, interface, NULL, 0, 5000); + + /* 9.4.10 says devices don't need this and are free to STALL the + * request if the interface only has one alternate setting. + */ + if (ret == -EPIPE && iface->num_altsetting == 1) { + dev_dbg(&dev->dev, + "manual set_interface for iface %d, alt %d\n", + interface, alternate); + manual = 1; + } else if (ret < 0) + return ret; + + /* FIXME drivers shouldn't need to replicate/bugfix the logic here + * when they implement async or easily-killable versions of this or + * other "should-be-internal" functions (like clear_halt). + * should hcd+usbcore postprocess control requests? + */ + + /* prevent submissions using previous endpoint settings */ + usb_disable_interface(dev, iface); + + /* 9.1.1.5 says: + * + * Configuring a device or changing an alternate setting + * causes all of the status and configuration values + * associated with endpoints in the affected interfaces to + * be set to their default values. This includes setting + * the data toggle of any endpoint using data toggles to + * the value DATA0. + * + * Some devices take this too literally and don't reset the data + * toggles if the new altsetting is the same as the old one (the + * command isn't "changing" an alternate setting). We will manually + * reset the toggles when the new and old altsettings are the same. + * Most devices won't need this, but fortunately it doesn't happen + * often. + */ + if (iface->cur_altsetting == alt) + manual = 1; + iface->cur_altsetting = alt; + + /* If the interface only has one altsetting and the device didn't + * accept the request (or whenever the old altsetting is the same + * as the new one), we attempt to carry out the equivalent action + * by manually clearing the HALT feature for each endpoint in the + * new altsetting. + */ + if (manual) { + int i; + + for (i = 0; i < alt->desc.bNumEndpoints; i++) { + unsigned int epaddr = + alt->endpoint[i].desc.bEndpointAddress; + unsigned int pipe = + __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr) + | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN); + + usb_clear_halt(dev, pipe); + } + } + + /* 9.1.1.5: reset toggles for all endpoints in the new altsetting + * + * Note: + * Despite EP0 is always present in all interfaces/AS, the list of + * endpoints from the descriptor does not contain EP0. Due to its + * omnipresence one might expect EP0 being considered "affected" by + * any SetInterface request and hence assume toggles need to be reset. + * However, EP0 toggles are re-synced for every individual transfer + * during the SETUP stage - hence EP0 toggles are "don't care" here. + * (Likewise, EP0 never "halts" on well designed devices.) + */ + usb_enable_interface(dev, iface); + + return 0; +} + +/** + * usb_reset_configuration - lightweight device reset + * @dev: the device whose configuration is being reset + * + * This issues a standard SET_CONFIGURATION request to the device using + * the current configuration. The effect is to reset most USB-related + * state in the device, including interface altsettings (reset to zero), + * endpoint halts (cleared), and data toggle (only for bulk and interrupt + * endpoints). Other usbcore state is unchanged, including bindings of + * usb device drivers to interfaces. + * + * Because this affects multiple interfaces, avoid using this with composite + * (multi-interface) devices. Instead, the driver for each interface may + * use usb_set_interface() on the interfaces it claims. Resetting the whole + * configuration would affect other drivers' interfaces. + * + * The caller must own the device lock. + * + * Returns zero on success, else a negative error code. + */ +int usb_reset_configuration(struct usb_device *dev) +{ + int i, retval; + struct usb_host_config *config; + + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + /* caller must have locked the device and must own + * the usb bus readlock (so driver bindings are stable); + * calls during probe() are fine + */ + + for (i = 1; i < 16; ++i) { + usb_disable_endpoint(dev, i); + usb_disable_endpoint(dev, i + USB_DIR_IN); + } + + config = dev->actconfig; + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, + config->desc.bConfigurationValue, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (retval < 0) { + usb_set_device_state(dev, USB_STATE_ADDRESS); + return retval; + } + + dev->toggle[0] = dev->toggle[1] = 0; + + /* re-init hc/hcd interface/endpoint state */ + for (i = 0; i < config->desc.bNumInterfaces; i++) { + struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + intf->cur_altsetting = alt; + usb_enable_interface(dev, intf); + } + return 0; +} + +static void release_interface(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_interface_cache *intfc = + altsetting_to_usb_interface_cache(intf->altsetting); + + kref_put(&intfc->ref, usb_release_interface_cache); + kfree(intf); +} + +/* + * usb_set_configuration - Makes a particular device setting be current + * @dev: the device whose configuration is being updated + * @configuration: the configuration being chosen. + * Context: !in_interrupt(), caller owns the device lock + * + * This is used to enable non-default device modes. Not all devices + * use this kind of configurability; many devices only have one + * configuration. + * + * USB device configurations may affect Linux interoperability, + * power consumption and the functionality available. For example, + * the default configuration is limited to using 100mA of bus power, + * so that when certain device functionality requires more power, + * and the device is bus powered, that functionality should be in some + * non-default device configuration. Other device modes may also be + * reflected as configuration options, such as whether two ISDN + * channels are available independently; and choosing between open + * standard device protocols (like CDC) or proprietary ones. + * + * Note that USB has an additional level of device configurability, + * associated with interfaces. That configurability is accessed using + * usb_set_interface(). + * + * This call is synchronous. The calling context must be able to sleep, + * must own the device lock, and must not hold the driver model's USB + * bus rwsem; usb device driver probe() methods cannot use this routine. + * + * Returns zero on success, or else the status code returned by the + * underlying call that failed. On succesful completion, each interface + * in the original device configuration has been destroyed, and each one + * in the new configuration has been probed by all relevant usb device + * drivers currently known to the kernel. + */ +int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int i, ret; + struct usb_host_config *cp = NULL; + struct usb_interface **new_interfaces = NULL; + int n, nintf; + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (dev->config[i].desc.bConfigurationValue == configuration) { + cp = &dev->config[i]; + break; + } + } + if ((!cp && configuration != 0)) + return -EINVAL; + + /* The USB spec says configuration 0 means unconfigured. + * But if a device includes a configuration numbered 0, + * we will accept it as a correctly configured state. + */ + if (cp && configuration == 0) + dev_warn(&dev->dev, "config 0 descriptor??\n"); + + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + /* Allocate memory for new interfaces before doing anything else, + * so that if we run out then nothing will have changed. */ + n = nintf = 0; + if (cp) { + nintf = cp->desc.bNumInterfaces; + new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), + GFP_KERNEL); + if (!new_interfaces) { + dev_err(&dev->dev, "Out of memory"); + return -ENOMEM; + } + + for (; n < nintf; ++n) { + new_interfaces[n] = kmalloc( + sizeof(struct usb_interface), + GFP_KERNEL); + if (!new_interfaces[n]) { + dev_err(&dev->dev, "Out of memory"); + ret = -ENOMEM; +free_interfaces: + while (--n >= 0) + kfree(new_interfaces[n]); + kfree(new_interfaces); + return ret; + } + } + } + + /* if it's already configured, clear out old state first. + * getting rid of old interfaces means unbinding their drivers. + */ + if (dev->state != USB_STATE_ADDRESS) + usb_disable_device (dev, 1); // Skip ep0 + + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, configuration, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) + goto free_interfaces; + + dev->actconfig = cp; + if (!cp) + usb_set_device_state(dev, USB_STATE_ADDRESS); + else { + usb_set_device_state(dev, USB_STATE_CONFIGURED); + + /* Initialize the new interface structures and the + * hc/hcd/usbcore interface/endpoint state. + */ + for (i = 0; i < nintf; ++i) { + struct usb_interface_cache *intfc; + struct usb_interface *intf; + struct usb_host_interface *alt; + + cp->interface[i] = intf = new_interfaces[i]; + memset(intf, 0, sizeof(*intf)); + intfc = cp->intf_cache[i]; + intf->altsetting = intfc->altsetting; + intf->num_altsetting = intfc->num_altsetting; + kref_get(&intfc->ref); + + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + intf->cur_altsetting = alt; + usb_enable_interface(dev, intf); + intf->dev.parent = &dev->dev; + intf->dev.driver = NULL; + intf->dev.bus = &usb_bus_type; + intf->dev.dma_mask = dev->dev.dma_mask; + intf->dev.release = release_interface; + device_initialize (&intf->dev); + sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", + dev->bus->busnum, dev->devpath, + configuration, + alt->desc.bInterfaceNumber); + } + kfree(new_interfaces); + + if ((cp->desc.iConfiguration) && + (cp->string == NULL)) { + cp->string = kmalloc(256, GFP_KERNEL); + if (cp->string) + usb_string(dev, cp->desc.iConfiguration, cp->string, 256); + } + + /* Now that all the interfaces are set up, register them + * to trigger binding of drivers to interfaces. probe() + * routines may install different altsettings and may + * claim() any interfaces not yet bound. Many class drivers + * need that: CDC, audio, video, etc. + */ + for (i = 0; i < nintf; ++i) { + struct usb_interface *intf = cp->interface[i]; + struct usb_interface_descriptor *desc; + + desc = &intf->altsetting [0].desc; + dev_dbg (&dev->dev, + "adding %s (config #%d, interface %d)\n", + intf->dev.bus_id, configuration, + desc->bInterfaceNumber); + ret = device_add (&intf->dev); + if (ret != 0) { + dev_err(&dev->dev, + "device_add(%s) --> %d\n", + intf->dev.bus_id, + ret); + continue; + } + if ((intf->cur_altsetting->desc.iInterface) && + (intf->cur_altsetting->string == NULL)) { + intf->cur_altsetting->string = kmalloc(256, GFP_KERNEL); + if (intf->cur_altsetting->string) + usb_string(dev, intf->cur_altsetting->desc.iInterface, + intf->cur_altsetting->string, 256); + } + usb_create_sysfs_intf_files (intf); + } + } + + return ret; +} + +// synchronous request completion model +EXPORT_SYMBOL(usb_control_msg); +EXPORT_SYMBOL(usb_bulk_msg); + +EXPORT_SYMBOL(usb_sg_init); +EXPORT_SYMBOL(usb_sg_cancel); +EXPORT_SYMBOL(usb_sg_wait); + +// synchronous control message convenience routines +EXPORT_SYMBOL(usb_get_descriptor); +EXPORT_SYMBOL(usb_get_status); +EXPORT_SYMBOL(usb_get_string); +EXPORT_SYMBOL(usb_string); + +// synchronous calls that also maintain usbcore state +EXPORT_SYMBOL(usb_clear_halt); +EXPORT_SYMBOL(usb_reset_configuration); +EXPORT_SYMBOL(usb_set_interface); + diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h new file mode 100644 index 0000000..627a5a2 --- /dev/null +++ b/drivers/usb/core/otg_whitelist.h @@ -0,0 +1,112 @@ +/* + * drivers/usb/core/otg_whitelist.h + * + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * This OTG Whitelist is the OTG "Targeted Peripheral List". It should + * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. + * + * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING! + */ + +static struct usb_device_id whitelist_table [] = { + +/* hubs are optional in OTG, but very handy ... */ +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), }, +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), }, + +#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */ +/* FIXME actually, printers are NOT supposed to use device classes; + * they're supposed to use interface classes... + */ +{ USB_DEVICE_INFO(7, 1, 1) }, +{ USB_DEVICE_INFO(7, 1, 2) }, +{ USB_DEVICE_INFO(7, 1, 3) }, +#endif + +#ifdef CONFIG_USB_CDCETHER +/* Linux-USB CDC Ethernet gadget */ +{ USB_DEVICE(0x0525, 0xa4a1), }, +/* Linux-USB CDC Ethernet + RNDIS gadget */ +{ USB_DEVICE(0x0525, 0xa4a2), }, +#endif + +#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE) +/* gadget zero, for testing */ +{ USB_DEVICE(0x0525, 0xa4a0), }, +#endif + +{ } /* Terminating entry */ +}; + +static int is_targeted(struct usb_device *dev) +{ + struct usb_device_id *id = whitelist_table; + + /* possible in developer configs only! */ + if (!dev->bus->otg_port) + return 1; + + /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && + le16_to_cpu(dev->descriptor.idProduct) == 0xbadd)) + return 0; + + /* NOTE: can't use usb_match_id() since interface caches + * aren't set up yet. this is cut/paste from that code. + */ + for (id = whitelist_table; id->match_flags; id++) { + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) + continue; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + continue; + + return 1; + } + + /* add other match criteria here ... */ + + + /* OTG MESSAGE: report errors here, customize to match your product */ + dev_err(&dev->dev, "device v%04x p%04x is not supported\n", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); +#ifdef CONFIG_USB_OTG_WHITELIST + return 0; +#else + return 1; +#endif +} + diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c new file mode 100644 index 0000000..ec9b3bd --- /dev/null +++ b/drivers/usb/core/sysfs.c @@ -0,0 +1,318 @@ +/* + * drivers/usb/core/sysfs.c + * + * (C) Copyright 2002 David Brownell + * (C) Copyright 2002,2004 Greg Kroah-Hartman + * (C) Copyright 2002,2004 IBM Corp. + * + * All of the sysfs file attributes for usb devices and interfaces. + * + */ + + +#include <linux/config.h> +#include <linux/kernel.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/usb.h> + +#include "usb.h" + +/* Active configuration fields */ +#define usb_actconfig_show(field, multiplier, format_string) \ +static ssize_t show_##field (struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + struct usb_host_config *actconfig; \ + \ + udev = to_usb_device (dev); \ + actconfig = udev->actconfig; \ + if (actconfig) \ + return sprintf (buf, format_string, \ + actconfig->desc.field * multiplier); \ + else \ + return 0; \ +} \ + +#define usb_actconfig_attr(field, multiplier, format_string) \ +usb_actconfig_show(field, multiplier, format_string) \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") +usb_actconfig_attr (bmAttributes, 1, "%2x\n") +usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") + +static ssize_t show_configuration_string(struct device *dev, char *buf) +{ + struct usb_device *udev; + struct usb_host_config *actconfig; + int len; + + udev = to_usb_device (dev); + actconfig = udev->actconfig; + if ((!actconfig) || (!actconfig->string)) + return 0; + len = sprintf(buf, actconfig->string, PAGE_SIZE); + if (len < 0) + return 0; + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} +static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL); + +/* configuration value is always present, and r/w */ +usb_actconfig_show(bConfigurationValue, 1, "%u\n"); + +static ssize_t +set_bConfigurationValue (struct device *dev, const char *buf, size_t count) +{ + struct usb_device *udev = udev = to_usb_device (dev); + int config, value; + + if (sscanf (buf, "%u", &config) != 1 || config > 255) + return -EINVAL; + usb_lock_device(udev); + value = usb_set_configuration (udev, config); + usb_unlock_device(udev); + return (value < 0) ? value : count; +} + +static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR, + show_bConfigurationValue, set_bConfigurationValue); + +/* String fields */ +#define usb_string_attr(name) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + int len; \ + \ + udev = to_usb_device (dev); \ + len = snprintf(buf, 256, "%s", udev->name); \ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_string_attr(product); +usb_string_attr(manufacturer); +usb_string_attr(serial); + +static ssize_t +show_speed (struct device *dev, char *buf) +{ + struct usb_device *udev; + char *speed; + + udev = to_usb_device (dev); + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + return sprintf (buf, "%s\n", speed); +} +static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); + +static ssize_t +show_devnum (struct device *dev, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device (dev); + return sprintf (buf, "%d\n", udev->devnum); +} +static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL); + +static ssize_t +show_version (struct device *dev, char *buf) +{ + struct usb_device *udev; + u16 bcdUSB; + + udev = to_usb_device(dev); + bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB); + return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff); +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t +show_maxchild (struct device *dev, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device (dev); + return sprintf (buf, "%d\n", udev->maxchild); +} +static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); + +/* Descriptor fields */ +#define usb_descriptor_attr_le16(field, format_string) \ +static ssize_t \ +show_##field (struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + \ + udev = to_usb_device (dev); \ + return sprintf (buf, format_string, \ + le16_to_cpu(udev->descriptor.field)); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +usb_descriptor_attr_le16(idVendor, "%04x\n") +usb_descriptor_attr_le16(idProduct, "%04x\n") +usb_descriptor_attr_le16(bcdDevice, "%04x\n") + +#define usb_descriptor_attr(field, format_string) \ +static ssize_t \ +show_##field (struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + \ + udev = to_usb_device (dev); \ + return sprintf (buf, format_string, udev->descriptor.field); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +usb_descriptor_attr (bDeviceClass, "%02x\n") +usb_descriptor_attr (bDeviceSubClass, "%02x\n") +usb_descriptor_attr (bDeviceProtocol, "%02x\n") +usb_descriptor_attr (bNumConfigurations, "%d\n") + +static struct attribute *dev_attrs[] = { + /* current configuration's attributes */ + &dev_attr_bNumInterfaces.attr, + &dev_attr_bConfigurationValue.attr, + &dev_attr_bmAttributes.attr, + &dev_attr_bMaxPower.attr, + /* device attributes */ + &dev_attr_idVendor.attr, + &dev_attr_idProduct.attr, + &dev_attr_bcdDevice.attr, + &dev_attr_bDeviceClass.attr, + &dev_attr_bDeviceSubClass.attr, + &dev_attr_bDeviceProtocol.attr, + &dev_attr_bNumConfigurations.attr, + &dev_attr_speed.attr, + &dev_attr_devnum.attr, + &dev_attr_version.attr, + &dev_attr_maxchild.attr, + NULL, +}; +static struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +void usb_create_sysfs_dev_files (struct usb_device *udev) +{ + struct device *dev = &udev->dev; + + sysfs_create_group(&dev->kobj, &dev_attr_grp); + + if (udev->manufacturer) + device_create_file (dev, &dev_attr_manufacturer); + if (udev->product) + device_create_file (dev, &dev_attr_product); + if (udev->serial) + device_create_file (dev, &dev_attr_serial); + device_create_file (dev, &dev_attr_configuration); +} + +void usb_remove_sysfs_dev_files (struct usb_device *udev) +{ + struct device *dev = &udev->dev; + + sysfs_remove_group(&dev->kobj, &dev_attr_grp); + + if (udev->descriptor.iManufacturer) + device_remove_file(dev, &dev_attr_manufacturer); + if (udev->descriptor.iProduct) + device_remove_file(dev, &dev_attr_product); + if (udev->descriptor.iSerialNumber) + device_remove_file(dev, &dev_attr_serial); + device_remove_file (dev, &dev_attr_configuration); +} + +/* Interface fields */ +#define usb_intf_attr(field, format_string) \ +static ssize_t \ +show_##field (struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + \ + return sprintf (buf, format_string, intf->cur_altsetting->desc.field); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +usb_intf_attr (bInterfaceNumber, "%02x\n") +usb_intf_attr (bAlternateSetting, "%2d\n") +usb_intf_attr (bNumEndpoints, "%02x\n") +usb_intf_attr (bInterfaceClass, "%02x\n") +usb_intf_attr (bInterfaceSubClass, "%02x\n") +usb_intf_attr (bInterfaceProtocol, "%02x\n") + +static ssize_t show_interface_string(struct device *dev, char *buf) +{ + struct usb_interface *intf; + struct usb_device *udev; + int len; + + intf = to_usb_interface (dev); + udev = interface_to_usbdev (intf); + len = snprintf(buf, 256, "%s", intf->cur_altsetting->string); + if (len < 0) + return 0; + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} +static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); + +static struct attribute *intf_attrs[] = { + &dev_attr_bInterfaceNumber.attr, + &dev_attr_bAlternateSetting.attr, + &dev_attr_bNumEndpoints.attr, + &dev_attr_bInterfaceClass.attr, + &dev_attr_bInterfaceSubClass.attr, + &dev_attr_bInterfaceProtocol.attr, + NULL, +}; +static struct attribute_group intf_attr_grp = { + .attrs = intf_attrs, +}; + +void usb_create_sysfs_intf_files (struct usb_interface *intf) +{ + sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->string) + device_create_file(&intf->dev, &dev_attr_interface); + +} + +void usb_remove_sysfs_intf_files (struct usb_interface *intf) +{ + sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->string) + device_remove_file(&intf->dev, &dev_attr_interface); + +} diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c new file mode 100644 index 0000000..dc838f8 --- /dev/null +++ b/drivers/usb/core/urb.c @@ -0,0 +1,511 @@ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/init.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/usb.h> +#include "hcd.h" + +#define to_urb(d) container_of(d, struct urb, kref) + +static void urb_destroy(struct kref *kref) +{ + struct urb *urb = to_urb(kref); + kfree(urb); +} + +/** + * usb_init_urb - initializes a urb so that it can be used by a USB driver + * @urb: pointer to the urb to initialize + * + * Initializes a urb so that the USB subsystem can use it properly. + * + * If a urb is created with a call to usb_alloc_urb() it is not + * necessary to call this function. Only use this if you allocate the + * space for a struct urb on your own. If you call this function, be + * careful when freeing the memory for your urb that it is no longer in + * use by the USB core. + * + * Only use this function if you _really_ understand what you are doing. + */ +void usb_init_urb(struct urb *urb) +{ + if (urb) { + memset(urb, 0, sizeof(*urb)); + kref_init(&urb->kref); + spin_lock_init(&urb->lock); + } +} + +/** + * usb_alloc_urb - creates a new urb for a USB driver to use + * @iso_packets: number of iso packets for this urb + * @mem_flags: the type of memory to allocate, see kmalloc() for a list of + * valid options for this. + * + * Creates an urb for the USB driver to use, initializes a few internal + * structures, incrementes the usage counter, and returns a pointer to it. + * + * If no memory is available, NULL is returned. + * + * If the driver want to use this urb for interrupt, control, or bulk + * endpoints, pass '0' as the number of iso packets. + * + * The driver must call usb_free_urb() when it is finished with the urb. + */ +struct urb *usb_alloc_urb(int iso_packets, int mem_flags) +{ + struct urb *urb; + + urb = (struct urb *)kmalloc(sizeof(struct urb) + + iso_packets * sizeof(struct usb_iso_packet_descriptor), + mem_flags); + if (!urb) { + err("alloc_urb: kmalloc failed"); + return NULL; + } + usb_init_urb(urb); + return urb; +} + +/** + * usb_free_urb - frees the memory used by a urb when all users of it are finished + * @urb: pointer to the urb to free, may be NULL + * + * Must be called when a user of a urb is finished with it. When the last user + * of the urb calls this function, the memory of the urb is freed. + * + * Note: The transfer buffer associated with the urb is not freed, that must be + * done elsewhere. + */ +void usb_free_urb(struct urb *urb) +{ + if (urb) + kref_put(&urb->kref, urb_destroy); +} + +/** + * usb_get_urb - increments the reference count of the urb + * @urb: pointer to the urb to modify, may be NULL + * + * This must be called whenever a urb is transferred from a device driver to a + * host controller driver. This allows proper reference counting to happen + * for urbs. + * + * A pointer to the urb with the incremented reference counter is returned. + */ +struct urb * usb_get_urb(struct urb *urb) +{ + if (urb) + kref_get(&urb->kref); + return urb; +} + + +/*-------------------------------------------------------------------*/ + +/** + * usb_submit_urb - issue an asynchronous transfer request for an endpoint + * @urb: pointer to the urb describing the request + * @mem_flags: the type of memory to allocate, see kmalloc() for a list + * of valid options for this. + * + * This submits a transfer request, and transfers control of the URB + * describing that request to the USB subsystem. Request completion will + * be indicated later, asynchronously, by calling the completion handler. + * The three types of completion are success, error, and unlink + * (a software-induced fault, also called "request cancelation"). + * + * URBs may be submitted in interrupt context. + * + * The caller must have correctly initialized the URB before submitting + * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are + * available to ensure that most fields are correctly initialized, for + * the particular kind of transfer, although they will not initialize + * any transfer flags. + * + * Successful submissions return 0; otherwise this routine returns a + * negative error number. If the submission is successful, the complete() + * callback from the URB will be called exactly once, when the USB core and + * Host Controller Driver (HCD) are finished with the URB. When the completion + * function is called, control of the URB is returned to the device + * driver which issued the request. The completion handler may then + * immediately free or reuse that URB. + * + * With few exceptions, USB device drivers should never access URB fields + * provided by usbcore or the HCD until its complete() is called. + * The exceptions relate to periodic transfer scheduling. For both + * interrupt and isochronous urbs, as part of successful URB submission + * urb->interval is modified to reflect the actual transfer period used + * (normally some power of two units). And for isochronous urbs, + * urb->start_frame is modified to reflect when the URB's transfers were + * scheduled to start. Not all isochronous transfer scheduling policies + * will work, but most host controller drivers should easily handle ISO + * queues going from now until 10-200 msec into the future. + * + * For control endpoints, the synchronous usb_control_msg() call is + * often used (in non-interrupt context) instead of this call. + * That is often used through convenience wrappers, for the requests + * that are standardized in the USB 2.0 specification. For bulk + * endpoints, a synchronous usb_bulk_msg() call is available. + * + * Request Queuing: + * + * URBs may be submitted to endpoints before previous ones complete, to + * minimize the impact of interrupt latencies and system overhead on data + * throughput. With that queuing policy, an endpoint's queue would never + * be empty. This is required for continuous isochronous data streams, + * and may also be required for some kinds of interrupt transfers. Such + * queuing also maximizes bandwidth utilization by letting USB controllers + * start work on later requests before driver software has finished the + * completion processing for earlier (successful) requests. + * + * As of Linux 2.6, all USB endpoint transfer queues support depths greater + * than one. This was previously a HCD-specific behavior, except for ISO + * transfers. Non-isochronous endpoint queues are inactive during cleanup + * after faults (transfer errors or cancelation). + * + * Reserved Bandwidth Transfers: + * + * Periodic transfers (interrupt or isochronous) are performed repeatedly, + * using the interval specified in the urb. Submitting the first urb to + * the endpoint reserves the bandwidth necessary to make those transfers. + * If the USB subsystem can't allocate sufficient bandwidth to perform + * the periodic request, submitting such a periodic request should fail. + * + * Device drivers must explicitly request that repetition, by ensuring that + * some URB is always on the endpoint's queue (except possibly for short + * periods during completion callacks). When there is no longer an urb + * queued, the endpoint's bandwidth reservation is canceled. This means + * drivers can use their completion handlers to ensure they keep bandwidth + * they need, by reinitializing and resubmitting the just-completed urb + * until the driver longer needs that periodic bandwidth. + * + * Memory Flags: + * + * The general rules for how to decide which mem_flags to use + * are the same as for kmalloc. There are four + * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and + * GFP_ATOMIC. + * + * GFP_NOFS is not ever used, as it has not been implemented yet. + * + * GFP_ATOMIC is used when + * (a) you are inside a completion handler, an interrupt, bottom half, + * tasklet or timer, or + * (b) you are holding a spinlock or rwlock (does not apply to + * semaphores), or + * (c) current->state != TASK_RUNNING, this is the case only after + * you've changed it. + * + * GFP_NOIO is used in the block io path and error handling of storage + * devices. + * + * All other situations use GFP_KERNEL. + * + * Some more specific rules for mem_flags can be inferred, such as + * (1) start_xmit, timeout, and receive methods of network drivers must + * use GFP_ATOMIC (they are called with a spinlock held); + * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also + * called with a spinlock held); + * (3) If you use a kernel thread with a network driver you must use + * GFP_NOIO, unless (b) or (c) apply; + * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c) + * apply or your are in a storage driver's block io path; + * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and + * (6) changing firmware on a running storage or net device uses + * GFP_NOIO, unless b) or c) apply + * + */ +int usb_submit_urb(struct urb *urb, int mem_flags) +{ + int pipe, temp, max; + struct usb_device *dev; + struct usb_operations *op; + int is_out; + + if (!urb || urb->hcpriv || !urb->complete) + return -EINVAL; + if (!(dev = urb->dev) || + (dev->state < USB_STATE_DEFAULT) || + (!dev->bus) || (dev->devnum <= 0)) + return -ENODEV; + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (!(op = dev->bus->op) || !op->submit_urb) + return -ENODEV; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->bandwidth = 0; + + /* Lots of sanity checks, so HCDs can rely on clean data + * and don't need to duplicate tests + */ + pipe = urb->pipe; + temp = usb_pipetype (pipe); + is_out = usb_pipeout (pipe); + + if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED) + return -ENODEV; + + /* FIXME there should be a sharable lock protecting us against + * config/altsetting changes and disconnects, kicking in here. + * (here == before maxpacket, and eventually endpoint type, + * checks get made.) + */ + + max = usb_maxpacket (dev, pipe, is_out); + if (max <= 0) { + dev_dbg(&dev->dev, + "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", + usb_pipeendpoint (pipe), is_out ? "out" : "in", + __FUNCTION__, max); + return -EMSGSIZE; + } + + /* periodic transfers limit size per frame/uframe, + * but drivers only control those sizes for ISO. + * while we're checking, initialize return status. + */ + if (temp == PIPE_ISOCHRONOUS) { + int n, len; + + /* "high bandwidth" mode, 1-3 packets/uframe? */ + if (dev->speed == USB_SPEED_HIGH) { + int mult = 1 + ((max >> 11) & 0x03); + max &= 0x07ff; + max *= mult; + } + + if (urb->number_of_packets <= 0) + return -EINVAL; + for (n = 0; n < urb->number_of_packets; n++) { + len = urb->iso_frame_desc [n].length; + if (len < 0 || len > max) + return -EMSGSIZE; + urb->iso_frame_desc [n].status = -EXDEV; + urb->iso_frame_desc [n].actual_length = 0; + } + } + + /* the I/O buffer must be mapped/unmapped, except when length=0 */ + if (urb->transfer_buffer_length < 0) + return -EMSGSIZE; + +#ifdef DEBUG + /* stuff that drivers shouldn't do, but which shouldn't + * cause problems in HCDs if they get it wrong. + */ + { + unsigned int orig_flags = urb->transfer_flags; + unsigned int allowed; + + /* enforce simple/standard policy */ + allowed = URB_ASYNC_UNLINK; // affects later unlinks + allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); + allowed |= URB_NO_INTERRUPT; + switch (temp) { + case PIPE_BULK: + if (is_out) + allowed |= URB_ZERO_PACKET; + /* FALLTHROUGH */ + case PIPE_CONTROL: + allowed |= URB_NO_FSBR; /* only affects UHCI */ + /* FALLTHROUGH */ + default: /* all non-iso endpoints */ + if (!is_out) + allowed |= URB_SHORT_NOT_OK; + break; + case PIPE_ISOCHRONOUS: + allowed |= URB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; + + /* fail if submitter gave bogus flags */ + if (urb->transfer_flags != orig_flags) { + err ("BOGUS urb flags, %x --> %x", + orig_flags, urb->transfer_flags); + return -EINVAL; + } + } +#endif + /* + * Force periodic transfer intervals to be legal values that are + * a power of two (so HCDs don't need to). + * + * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC + * supports different values... this uses EHCI/UHCI defaults (and + * EHCI can use smaller non-default values). + */ + switch (temp) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + /* too small? */ + if (urb->interval <= 0) + return -EINVAL; + /* too big? */ + switch (dev->speed) { + case USB_SPEED_HIGH: /* units are microframes */ + // NOTE usb handles 2^15 + if (urb->interval > (1024 * 8)) + urb->interval = 1024 * 8; + temp = 1024 * 8; + break; + case USB_SPEED_FULL: /* units are frames/msec */ + case USB_SPEED_LOW: + if (temp == PIPE_INTERRUPT) { + if (urb->interval > 255) + return -EINVAL; + // NOTE ohci only handles up to 32 + temp = 128; + } else { + if (urb->interval > 1024) + urb->interval = 1024; + // NOTE usb and ohci handle up to 2^15 + temp = 1024; + } + break; + default: + return -EINVAL; + } + /* power of two? */ + while (temp > urb->interval) + temp >>= 1; + urb->interval = temp; + } + + return op->submit_urb (urb, mem_flags); +} + +/*-------------------------------------------------------------------*/ + +/** + * usb_unlink_urb - abort/cancel a transfer request for an endpoint + * @urb: pointer to urb describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. URBs complete only + * once per submission, and may be canceled only once per submission. + * Successful cancelation means the requests's completion handler will + * be called with a status code indicating that the request has been + * canceled (rather than any other code) and will quickly be removed + * from host controller data structures. + * + * In the past, clearing the URB_ASYNC_UNLINK transfer flag for the + * URB indicated that the request was synchronous. This usage is now + * deprecated; if the flag is clear the call will be forwarded to + * usb_kill_urb() and the return value will be 0. In the future, drivers + * should call usb_kill_urb() directly for synchronous unlinking. + * + * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this + * request is asynchronous. Success is indicated by returning -EINPROGRESS, + * at which time the URB will normally have been unlinked but not yet + * given back to the device driver. When it is called, the completion + * function will see urb->status == -ECONNRESET. Failure is indicated + * by any other return value. Unlinking will fail when the URB is not + * currently "linked" (i.e., it was never submitted, or it was unlinked + * before, or the hardware is already finished with it), even if the + * completion handler has not yet run. + * + * Unlinking and Endpoint Queues: + * + * Host Controller Drivers (HCDs) place all the URBs for a particular + * endpoint in a queue. Normally the queue advances as the controller + * hardware processes each request. But when an URB terminates with any + * fault (such as an error, or being unlinked) its queue stops, at least + * until that URB's completion routine returns. It is guaranteed that + * the queue will not restart until all its unlinked URBs have been fully + * retired, with their completion routines run, even if that's not until + * some time after the original completion handler returns. + * + * This means that USB device drivers can safely build deep queues for + * large or complex transfers, and clean them up reliably after any sort + * of aborted transfer by unlinking all pending URBs at the first fault. + * + * Note that an URB terminating early because a short packet was received + * will count as an error if and only if the URB_SHORT_NOT_OK flag is set. + * Also, that all unlinks performed in any URB completion handler must + * be asynchronous. + * + * Queues for isochronous endpoints are treated differently, because they + * advance at fixed rates. Such queues do not stop when an URB is unlinked. + * An unlinked URB may leave a gap in the stream of packets. It is undefined + * whether such gaps can be filled in. + * + * When a control URB terminates with an error, it is likely that the + * status stage of the transfer will not take place, even if it is merely + * a soft error resulting from a short-packet with URB_SHORT_NOT_OK set. + */ +int usb_unlink_urb(struct urb *urb) +{ + if (!urb) + return -EINVAL; + if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { +#ifdef CONFIG_DEBUG_KERNEL + if (printk_ratelimit()) { + printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " + "synchronous unlinks. Use usb_kill_urb() instead.\n"); + WARN_ON(1); + } +#endif + usb_kill_urb(urb); + return 0; + } + if (!(urb->dev && urb->dev->bus && urb->dev->bus->op)) + return -ENODEV; + return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET); +} + +/** + * usb_kill_urb - cancel a transfer request and wait for it to finish + * @urb: pointer to URB describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. It is guaranteed that + * upon return all completion handlers will have finished and the URB + * will be totally idle and available for reuse. These features make + * this an ideal way to stop I/O in a disconnect() callback or close() + * function. If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * While the routine is running, attempts to resubmit the URB will fail + * with error -EPERM. Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * This routine may not be used in an interrupt context (such as a bottom + * half or a completion handler), or when holding a spinlock, or in other + * situations where the caller can't schedule(). + */ +void usb_kill_urb(struct urb *urb) +{ + if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op)) + return; + spin_lock_irq(&urb->lock); + ++urb->reject; + spin_unlock_irq(&urb->lock); + + urb->dev->bus->op->unlink_urb(urb, -ENOENT); + wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); + + spin_lock_irq(&urb->lock); + --urb->reject; + spin_unlock_irq(&urb->lock); +} + +EXPORT_SYMBOL(usb_init_urb); +EXPORT_SYMBOL(usb_alloc_urb); +EXPORT_SYMBOL(usb_free_urb); +EXPORT_SYMBOL(usb_get_urb); +EXPORT_SYMBOL(usb_submit_urb); +EXPORT_SYMBOL(usb_unlink_urb); +EXPORT_SYMBOL(usb_kill_urb); + diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c new file mode 100644 index 0000000..f0534ee --- /dev/null +++ b/drivers/usb/core/usb.c @@ -0,0 +1,1573 @@ +/* + * drivers/usb/usb.c + * + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2004 + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * (C) Copyright Greg Kroah-Hartman 2002-2003 + * + * NOTE! This is not actually a driver at all, rather this is + * just a collection of helper routines that implement the + * generic USB things that the real drivers can use.. + * + * Think of this as a "USB library" rather than anything else. + * It should be considered a slave, with no callbacks. Callbacks + * are evil. + */ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/module.h> +#include <linux/string.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/interrupt.h> /* for in_interrupt() */ +#include <linux/kmod.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/smp_lock.h> +#include <linux/rwsem.h> +#include <linux/usb.h> + +#include <asm/io.h> +#include <asm/scatterlist.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> + +#include "hcd.h" +#include "usb.h" + +extern int usb_hub_init(void); +extern void usb_hub_cleanup(void); +extern int usb_major_init(void); +extern void usb_major_cleanup(void); +extern int usb_host_init(void); +extern void usb_host_cleanup(void); + + +const char *usbcore_name = "usbcore"; + +static int nousb; /* Disable USB when built into kernel image */ + /* Not honored on modular build */ + +static DECLARE_RWSEM(usb_all_devices_rwsem); + + +static int generic_probe (struct device *dev) +{ + return 0; +} +static int generic_remove (struct device *dev) +{ + return 0; +} + +static struct device_driver usb_generic_driver = { + .owner = THIS_MODULE, + .name = "usb", + .bus = &usb_bus_type, + .probe = generic_probe, + .remove = generic_remove, +}; + +static int usb_generic_driver_data; + +/* called from driver core with usb_bus_type.subsys writelock */ +static int usb_probe_interface(struct device *dev) +{ + struct usb_interface * intf = to_usb_interface(dev); + struct usb_driver * driver = to_usb_driver(dev->driver); + const struct usb_device_id *id; + int error = -ENODEV; + + dev_dbg(dev, "%s\n", __FUNCTION__); + + if (!driver->probe) + return error; + /* FIXME we'd much prefer to just resume it ... */ + if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + id = usb_match_id (intf, driver->id_table); + if (id) { + dev_dbg (dev, "%s - got id\n", __FUNCTION__); + intf->condition = USB_INTERFACE_BINDING; + error = driver->probe (intf, id); + intf->condition = error ? USB_INTERFACE_UNBOUND : + USB_INTERFACE_BOUND; + } + + return error; +} + +/* called from driver core with usb_bus_type.subsys writelock */ +static int usb_unbind_interface(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + intf->condition = USB_INTERFACE_UNBINDING; + + /* release all urbs for this interface */ + usb_disable_interface(interface_to_usbdev(intf), intf); + + if (driver && driver->disconnect) + driver->disconnect(intf); + + /* reset other interface state */ + usb_set_interface(interface_to_usbdev(intf), + intf->altsetting[0].desc.bInterfaceNumber, + 0); + usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; + + return 0; +} + +/** + * usb_register - register a USB driver + * @new_driver: USB operations for the driver + * + * Registers a USB driver with the USB core. The list of unattached + * interfaces will be rescanned whenever a new driver is added, allowing + * the new driver to attach to any recognized devices. + * Returns a negative error code on failure and 0 on success. + * + * NOTE: if you want your driver to use the USB major number, you must call + * usb_register_dev() to enable that functionality. This function no longer + * takes care of that. + */ +int usb_register(struct usb_driver *new_driver) +{ + int retval = 0; + + if (nousb) + return -ENODEV; + + new_driver->driver.name = (char *)new_driver->name; + new_driver->driver.bus = &usb_bus_type; + new_driver->driver.probe = usb_probe_interface; + new_driver->driver.remove = usb_unbind_interface; + new_driver->driver.owner = new_driver->owner; + + usb_lock_all_devices(); + retval = driver_register(&new_driver->driver); + usb_unlock_all_devices(); + + if (!retval) { + pr_info("%s: registered new driver %s\n", + usbcore_name, new_driver->name); + usbfs_update_special(); + } else { + printk(KERN_ERR "%s: error %d registering driver %s\n", + usbcore_name, retval, new_driver->name); + } + + return retval; +} + +/** + * usb_deregister - unregister a USB driver + * @driver: USB operations of the driver to unregister + * Context: must be able to sleep + * + * Unlinks the specified driver from the internal USB driver list. + * + * NOTE: If you called usb_register_dev(), you still need to call + * usb_deregister_dev() to clean up your driver's allocated minor numbers, + * this * call will no longer do it for you. + */ +void usb_deregister(struct usb_driver *driver) +{ + pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + + usb_lock_all_devices(); + driver_unregister (&driver->driver); + usb_unlock_all_devices(); + + usbfs_update_special(); +} + +/** + * usb_ifnum_to_if - get the interface object with a given interface number + * @dev: the device whose current configuration is considered + * @ifnum: the desired interface + * + * This walks the device descriptor for the currently active configuration + * and returns a pointer to the interface with that particular interface + * number, or null. + * + * Note that configuration descriptors are not required to assign interface + * numbers sequentially, so that it would be incorrect to assume that + * the first interface in that descriptor corresponds to interface zero. + * This routine helps device drivers avoid such mistakes. + * However, you should make sure that you do the right thing with any + * alternate settings available for this interfaces. + * + * Don't call this function unless you are bound to one of the interfaces + * on this device or you have locked the device! + */ +struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) +{ + struct usb_host_config *config = dev->actconfig; + int i; + + if (!config) + return NULL; + for (i = 0; i < config->desc.bNumInterfaces; i++) + if (config->interface[i]->altsetting[0] + .desc.bInterfaceNumber == ifnum) + return config->interface[i]; + + return NULL; +} + +/** + * usb_altnum_to_altsetting - get the altsetting structure with a given + * alternate setting number. + * @intf: the interface containing the altsetting in question + * @altnum: the desired alternate setting number + * + * This searches the altsetting array of the specified interface for + * an entry with the correct bAlternateSetting value and returns a pointer + * to that entry, or null. + * + * Note that altsettings need not be stored sequentially by number, so + * it would be incorrect to assume that the first altsetting entry in + * the array corresponds to altsetting zero. This routine helps device + * drivers avoid such mistakes. + * + * Don't call this function unless you are bound to the intf interface + * or you have locked the device! + */ +struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, + unsigned int altnum) +{ + int i; + + for (i = 0; i < intf->num_altsetting; i++) { + if (intf->altsetting[i].desc.bAlternateSetting == altnum) + return &intf->altsetting[i]; + } + return NULL; +} + +/** + * usb_driver_claim_interface - bind a driver to an interface + * @driver: the driver to be bound + * @iface: the interface to which it will be bound; must be in the + * usb device's active configuration + * @priv: driver data associated with that interface + * + * This is used by usb device drivers that need to claim more than one + * interface on a device when probing (audio and acm are current examples). + * No device driver should directly modify internal usb_interface or + * usb_device structure members. + * + * Few drivers should need to use this routine, since the most natural + * way to bind to an interface is to return the private data from + * the driver's probe() method. + * + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver probe() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. + */ +int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void* priv) +{ + struct device *dev = &iface->dev; + + if (dev->driver) + return -EBUSY; + + dev->driver = &driver->driver; + usb_set_intfdata(iface, priv); + iface->condition = USB_INTERFACE_BOUND; + + /* if interface was already added, bind now; else let + * the future device_add() bind it, bypassing probe() + */ + if (!list_empty (&dev->bus_list)) + device_bind_driver(dev); + + return 0; +} + +/** + * usb_driver_release_interface - unbind a driver from an interface + * @driver: the driver to be unbound + * @iface: the interface from which it will be unbound + * + * This can be used by drivers to release an interface without waiting + * for their disconnect() methods to be called. In typical cases this + * also causes the driver disconnect() method to be called. + * + * This call is synchronous, and may not be used in an interrupt context. + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver disconnect() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. + */ +void usb_driver_release_interface(struct usb_driver *driver, + struct usb_interface *iface) +{ + struct device *dev = &iface->dev; + + /* this should never happen, don't release something that's not ours */ + if (!dev->driver || dev->driver != &driver->driver) + return; + + /* don't disconnect from disconnect(), or before dev_add() */ + if (!list_empty (&dev->driver_list) && !list_empty (&dev->bus_list)) + device_release_driver(dev); + + dev->driver = NULL; + usb_set_intfdata(iface, NULL); + iface->condition = USB_INTERFACE_UNBOUND; +} + +/** + * usb_match_id - find first usb_device_id matching device or interface + * @interface: the interface of interest + * @id: array of usb_device_id structures, terminated by zero entry + * + * usb_match_id searches an array of usb_device_id's and returns + * the first one matching the device or interface, or null. + * This is used when binding (or rebinding) a driver to an interface. + * Most USB device drivers will use this indirectly, through the usb core, + * but some layered driver frameworks use it directly. + * These device tables are exported with MODULE_DEVICE_TABLE, through + * modutils and "modules.usbmap", to support the driver loading + * functionality of USB hotplugging. + * + * What Matches: + * + * The "match_flags" element in a usb_device_id controls which + * members are used. If the corresponding bit is set, the + * value in the device_id must match its corresponding member + * in the device or interface descriptor, or else the device_id + * does not match. + * + * "driver_info" is normally used only by device drivers, + * but you can create a wildcard "matches anything" usb_device_id + * as a driver's "modules.usbmap" entry if you provide an id with + * only a nonzero "driver_info" field. If you do this, the USB device + * driver's probe() routine should use additional intelligence to + * decide whether to bind to the specified interface. + * + * What Makes Good usb_device_id Tables: + * + * The match algorithm is very simple, so that intelligence in + * driver selection must come from smart driver id records. + * Unless you have good reasons to use another selection policy, + * provide match elements only in related groups, and order match + * specifiers from specific to general. Use the macros provided + * for that purpose if you can. + * + * The most specific match specifiers use device descriptor + * data. These are commonly used with product-specific matches; + * the USB_DEVICE macro lets you provide vendor and product IDs, + * and you can also match against ranges of product revisions. + * These are widely used for devices with application or vendor + * specific bDeviceClass values. + * + * Matches based on device class/subclass/protocol specifications + * are slightly more general; use the USB_DEVICE_INFO macro, or + * its siblings. These are used with single-function devices + * where bDeviceClass doesn't specify that each interface has + * its own class. + * + * Matches based on interface class/subclass/protocol are the + * most general; they let drivers bind to any interface on a + * multiple-function device. Use the USB_INTERFACE_INFO + * macro, or its siblings, to match class-per-interface style + * devices (as recorded in bDeviceClass). + * + * Within those groups, remember that not all combinations are + * meaningful. For example, don't give a product version range + * without vendor and product IDs; or specify a protocol without + * its associated class and subclass. + */ +const struct usb_device_id * +usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_host_interface *intf; + struct usb_device *dev; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return NULL; + + intf = interface->cur_altsetting; + dev = interface_to_usbdev(interface); + + /* It is important to check that id->driver_info is nonzero, + since an entry that is all zeroes except for a nonzero + id->driver_info is the way to create an entry that + indicates that the driver want to examine every + device and interface. */ + for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || + id->driver_info; id++) { + + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) + continue; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (id->bInterfaceClass != intf->desc.bInterfaceClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && + (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && + (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) + continue; + + return id; + } + + return NULL; +} + +/** + * usb_find_interface - find usb_interface pointer for driver and device + * @drv: the driver whose current configuration is considered + * @minor: the minor number of the desired device + * + * This walks the driver device list and returns a pointer to the interface + * with the matching minor. Note, this only works for devices that share the + * USB major number. + */ +struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) +{ + struct list_head *entry; + struct device *dev; + struct usb_interface *intf; + + list_for_each(entry, &drv->driver.devices) { + dev = container_of(entry, struct device, driver_list); + + /* can't look at usb devices, only interfaces */ + if (dev->driver == &usb_generic_driver) + continue; + + intf = to_usb_interface(dev); + if (intf->minor == -1) + continue; + if (intf->minor == minor) + return intf; + } + + /* no device found that matches */ + return NULL; +} + +static int usb_device_match (struct device *dev, struct device_driver *drv) +{ + struct usb_interface *intf; + struct usb_driver *usb_drv; + const struct usb_device_id *id; + + /* check for generic driver, which we don't match any device with */ + if (drv == &usb_generic_driver) + return 0; + + intf = to_usb_interface(dev); + usb_drv = to_usb_driver(drv); + + id = usb_match_id (intf, usb_drv->id_table); + if (id) + return 1; + + return 0; +} + + +#ifdef CONFIG_HOTPLUG + +/* + * USB hotplugging invokes what /proc/sys/kernel/hotplug says + * (normally /sbin/hotplug) when USB devices get added or removed. + * + * This invokes a user mode policy agent, typically helping to load driver + * or other modules, configure the device, and more. Drivers can provide + * a MODULE_DEVICE_TABLE to help with module loading subtasks. + * + * We're called either from khubd (the typical case) or from root hub + * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle + * delays in event delivery. Use sysfs (and DEVPATH) to make sure the + * device (and this configuration!) are still present. + */ +static int usb_hotplug (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct usb_interface *intf; + struct usb_device *usb_dev; + int i = 0; + int length = 0; + + if (!dev) + return -ENODEV; + + /* driver is often null here; dev_dbg() would oops */ + pr_debug ("usb %s: hotplug\n", dev->bus_id); + + /* Must check driver_data here, as on remove driver is always NULL */ + if ((dev->driver == &usb_generic_driver) || + (dev->driver_data == &usb_generic_driver_data)) + return 0; + + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev (intf); + + if (usb_dev->devnum < 0) { + pr_debug ("usb %s: already deleted?\n", dev->bus_id); + return -ENODEV; + } + if (!usb_dev->bus) { + pr_debug ("usb %s: bus removed?\n", dev->bus_id); + return -ENODEV; + } + +#ifdef CONFIG_USB_DEVICEFS + /* If this is available, userspace programs can directly read + * all the device descriptors we don't tell them about. Or + * even act as usermode drivers. + * + * FIXME reduce hardwired intelligence here + */ + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVICE=/proc/bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum)) + return -ENOMEM; +#endif + + /* per-device configurations are common */ + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PRODUCT=%x/%x/%x", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice))) + return -ENOMEM; + + /* class-based driver binding models */ + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "TYPE=%d/%d/%d", + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol)) + return -ENOMEM; + + if (usb_dev->descriptor.bDeviceClass == 0) { + struct usb_host_interface *alt = intf->cur_altsetting; + + /* 2.4 only exposed interface zero. in 2.5, hotplug + * agents are called for all interfaces, and can use + * $DEVPATH/bInterfaceNumber if necessary. + */ + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "INTERFACE=%d/%d/%d", + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=usb:v%04Xp%04Xdl%04Xdh%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol, + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + } else { + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=usb:v%04Xp%04Xdl%04Xdh%04Xdc%02Xdsc%02Xdp%02Xic*isc*ip*", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol)) + return -ENOMEM; + } + + envp[i] = NULL; + + return 0; +} + +#else + +static int usb_hotplug (struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + +/** + * usb_release_dev - free a usb device structure when all users of it are finished. + * @dev: device that's been disconnected + * + * Will be called only by the device core when all users of this usb device are + * done. + */ +static void usb_release_dev(struct device *dev) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + + usb_destroy_configuration(udev); + usb_bus_put(udev->bus); + kfree(udev->product); + kfree(udev->manufacturer); + kfree(udev->serial); + kfree(udev); +} + +/** + * usb_alloc_dev - usb device constructor (usbcore-internal) + * @parent: hub to which device is connected; null to allocate a root hub + * @bus: bus used to access the device + * @port1: one-based index of port; ignored for root hubs + * Context: !in_interrupt () + * + * Only hub drivers (including virtual root hub drivers for host + * controllers) should ever call this. + * + * This call may not be used in a non-sleeping context. + */ +struct usb_device * +usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) +{ + struct usb_device *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + memset(dev, 0, sizeof(*dev)); + + bus = usb_bus_get(bus); + if (!bus) { + kfree(dev); + return NULL; + } + + device_initialize(&dev->dev); + dev->dev.bus = &usb_bus_type; + dev->dev.dma_mask = bus->controller->dma_mask; + dev->dev.driver_data = &usb_generic_driver_data; + dev->dev.driver = &usb_generic_driver; + dev->dev.release = usb_release_dev; + dev->state = USB_STATE_ATTACHED; + + INIT_LIST_HEAD(&dev->ep0.urb_list); + dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; + dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; + /* ep0 maxpacket comes later, from device descriptor */ + dev->ep_in[0] = dev->ep_out[0] = &dev->ep0; + + /* Save readable and stable topology id, distinguishing devices + * by location for diagnostics, tools, driver model, etc. The + * string is a path along hub ports, from the root. Each device's + * dev->devpath will be stable until USB is re-cabled, and hubs + * are often labeled with these port numbers. The bus_id isn't + * as stable: bus->busnum changes easily from modprobe order, + * cardbus or pci hotplugging, and so on. + */ + if (unlikely (!parent)) { + dev->devpath [0] = '0'; + + dev->dev.parent = bus->controller; + sprintf (&dev->dev.bus_id[0], "usb%d", bus->busnum); + } else { + /* match any labeling on the hubs; it's one-based */ + if (parent->devpath [0] == '0') + snprintf (dev->devpath, sizeof dev->devpath, + "%d", port1); + else + snprintf (dev->devpath, sizeof dev->devpath, + "%s.%d", parent->devpath, port1); + + dev->dev.parent = &parent->dev; + sprintf (&dev->dev.bus_id[0], "%d-%s", + bus->busnum, dev->devpath); + + /* hub driver sets up TT records */ + } + + dev->bus = bus; + dev->parent = parent; + INIT_LIST_HEAD(&dev->filelist); + + init_MUTEX(&dev->serialize); + + return dev; +} + +/** + * usb_get_dev - increments the reference count of the usb device structure + * @dev: the device being referenced + * + * Each live reference to a device should be refcounted. + * + * Drivers for USB interfaces should normally record such references in + * their probe() methods, when they bind to an interface, and release + * them by calling usb_put_dev(), in their disconnect() methods. + * + * A pointer to the device with the incremented reference counter is returned. + */ +struct usb_device *usb_get_dev(struct usb_device *dev) +{ + if (dev) + get_device(&dev->dev); + return dev; +} + +/** + * usb_put_dev - release a use of the usb device structure + * @dev: device that's been disconnected + * + * Must be called when a user of a device is finished with it. When the last + * user of the device calls this function, the memory of the device is freed. + */ +void usb_put_dev(struct usb_device *dev) +{ + if (dev) + put_device(&dev->dev); +} + +/** + * usb_get_intf - increments the reference count of the usb interface structure + * @intf: the interface being referenced + * + * Each live reference to a interface must be refcounted. + * + * Drivers for USB interfaces should normally record such references in + * their probe() methods, when they bind to an interface, and release + * them by calling usb_put_intf(), in their disconnect() methods. + * + * A pointer to the interface with the incremented reference counter is + * returned. + */ +struct usb_interface *usb_get_intf(struct usb_interface *intf) +{ + if (intf) + get_device(&intf->dev); + return intf; +} + +/** + * usb_put_intf - release a use of the usb interface structure + * @intf: interface that's been decremented + * + * Must be called when a user of an interface is finished with it. When the + * last user of the interface calls this function, the memory of the interface + * is freed. + */ +void usb_put_intf(struct usb_interface *intf) +{ + if (intf) + put_device(&intf->dev); +} + + +/* USB device locking + * + * Although locking USB devices should be straightforward, it is + * complicated by the way the driver-model core works. When a new USB + * driver is registered or unregistered, the core will automatically + * probe or disconnect all matching interfaces on all USB devices while + * holding the USB subsystem writelock. There's no good way for us to + * tell which devices will be used or to lock them beforehand; our only + * option is to effectively lock all the USB devices. + * + * We do that by using a private rw-semaphore, usb_all_devices_rwsem. + * When locking an individual device you must first acquire the rwsem's + * readlock. When a driver is registered or unregistered the writelock + * must be held. These actions are encapsulated in the subroutines + * below, so all a driver needs to do is call usb_lock_device() and + * usb_unlock_device(). + * + * Complications arise when several devices are to be locked at the same + * time. Only hub-aware drivers that are part of usbcore ever have to + * do this; nobody else needs to worry about it. The problem is that + * usb_lock_device() must not be called to lock a second device since it + * would acquire the rwsem's readlock reentrantly, leading to deadlock if + * another thread was waiting for the writelock. The solution is simple: + * + * When locking more than one device, call usb_lock_device() + * to lock the first one. Lock the others by calling + * down(&udev->serialize) directly. + * + * When unlocking multiple devices, use up(&udev->serialize) + * to unlock all but the last one. Unlock the last one by + * calling usb_unlock_device(). + * + * When locking both a device and its parent, always lock the + * the parent first. + */ + +/** + * usb_lock_device - acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Use this routine when you don't hold any other device locks; + * to acquire nested inner locks call down(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_lock_device(struct usb_device *udev) +{ + down_read(&usb_all_devices_rwsem); + down(&udev->serialize); +} + +/** + * usb_trylock_device - attempt to acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Don't use this routine if you already hold a device lock; + * use down_trylock(&udev->serialize) instead. + * This is necessary for proper interaction with usb_lock_all_devices(). + * + * Returns 1 if successful, 0 if contention. + */ +int usb_trylock_device(struct usb_device *udev) +{ + if (!down_read_trylock(&usb_all_devices_rwsem)) + return 0; + if (down_trylock(&udev->serialize)) { + up_read(&usb_all_devices_rwsem); + return 0; + } + return 1; +} + +/** + * usb_lock_device_for_reset - cautiously acquire the lock for a + * usb device structure + * @udev: device that's being locked + * @iface: interface bound to the driver making the request (optional) + * + * Attempts to acquire the device lock, but fails if the device is + * NOTATTACHED or SUSPENDED, or if iface is specified and the interface + * is neither BINDING nor BOUND. Rather than sleeping to wait for the + * lock, the routine polls repeatedly. This is to prevent deadlock with + * disconnect; in some drivers (such as usb-storage) the disconnect() + * callback will block waiting for a device reset to complete. + * + * Returns a negative error code for failure, otherwise 1 or 0 to indicate + * that the device will or will not have to be unlocked. (0 can be + * returned when an interface is given and is BINDING, because in that + * case the driver already owns the device lock.) + */ +int usb_lock_device_for_reset(struct usb_device *udev, + struct usb_interface *iface) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface) { + switch (iface->condition) { + case USB_INTERFACE_BINDING: + return 0; + case USB_INTERFACE_BOUND: + break; + default: + return -EINTR; + } + } + + while (!usb_trylock_device(udev)) { + msleep(15); + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface && iface->condition != USB_INTERFACE_BOUND) + return -EINTR; + } + return 1; +} + +/** + * usb_unlock_device - release the lock for a usb device structure + * @udev: device that's being unlocked + * + * Use this routine when releasing the only device lock you hold; + * to release inner nested locks call up(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_unlock_device(struct usb_device *udev) +{ + up(&udev->serialize); + up_read(&usb_all_devices_rwsem); +} + +/** + * usb_lock_all_devices - acquire the lock for all usb device structures + * + * This is necessary when registering a new driver or probing a bus, + * since the driver-model core may try to use any usb_device. + */ +void usb_lock_all_devices(void) +{ + down_write(&usb_all_devices_rwsem); +} + +/** + * usb_unlock_all_devices - release the lock for all usb device structures + */ +void usb_unlock_all_devices(void) +{ + up_write(&usb_all_devices_rwsem); +} + + +static struct usb_device *match_device(struct usb_device *dev, + u16 vendor_id, u16 product_id) +{ + struct usb_device *ret_dev = NULL; + int child; + + dev_dbg(&dev->dev, "check for vendor %04x, product %04x ...\n", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* see if this device matches */ + if ((vendor_id == le16_to_cpu(dev->descriptor.idVendor)) && + (product_id == le16_to_cpu(dev->descriptor.idProduct))) { + dev_dbg (&dev->dev, "matched this device!\n"); + ret_dev = usb_get_dev(dev); + goto exit; + } + + /* look through all of the children of this device */ + for (child = 0; child < dev->maxchild; ++child) { + if (dev->children[child]) { + down(&dev->children[child]->serialize); + ret_dev = match_device(dev->children[child], + vendor_id, product_id); + up(&dev->children[child]->serialize); + if (ret_dev) + goto exit; + } + } +exit: + return ret_dev; +} + +/** + * usb_find_device - find a specific usb device in the system + * @vendor_id: the vendor id of the device to find + * @product_id: the product id of the device to find + * + * Returns a pointer to a struct usb_device if such a specified usb + * device is present in the system currently. The usage count of the + * device will be incremented if a device is found. Make sure to call + * usb_put_dev() when the caller is finished with the device. + * + * If a device with the specified vendor and product id is not found, + * NULL is returned. + */ +struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) +{ + struct list_head *buslist; + struct usb_bus *bus; + struct usb_device *dev = NULL; + + down(&usb_bus_list_lock); + for (buslist = usb_bus_list.next; + buslist != &usb_bus_list; + buslist = buslist->next) { + bus = container_of(buslist, struct usb_bus, bus_list); + if (!bus->root_hub) + continue; + usb_lock_device(bus->root_hub); + dev = match_device(bus->root_hub, vendor_id, product_id); + usb_unlock_device(bus->root_hub); + if (dev) + goto exit; + } +exit: + up(&usb_bus_list_lock); + return dev; +} + +/** + * usb_get_current_frame_number - return current bus frame number + * @dev: the device whose bus is being queried + * + * Returns the current frame number for the USB host controller + * used with the given USB device. This can be used when scheduling + * isochronous requests. + * + * Note that different kinds of host controller have different + * "scheduling horizons". While one type might support scheduling only + * 32 frames into the future, others could support scheduling up to + * 1024 frames into the future. + */ +int usb_get_current_frame_number(struct usb_device *dev) +{ + return dev->bus->op->get_frame_number (dev); +} + +/*-------------------------------------------------------------------*/ +/* + * __usb_get_extra_descriptor() finds a descriptor of specific type in the + * extra field of the interface and endpoint descriptor structs. + */ + +int __usb_get_extra_descriptor(char *buffer, unsigned size, + unsigned char type, void **ptr) +{ + struct usb_descriptor_header *header; + + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if (header->bLength < 2) { + printk(KERN_ERR + "%s: bogus descriptor, type %d length %d\n", + usbcore_name, + header->bDescriptorType, + header->bLength); + return -1; + } + + if (header->bDescriptorType == type) { + *ptr = header; + return 0; + } + + buffer += header->bLength; + size -= header->bLength; + } + return -1; +} + +/** + * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * + * Return value is either null (indicating no buffer could be allocated), or + * the cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device. Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + * + * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags + * to avoid behaviors like using "DMA bounce buffers", or tying down I/O + * mapping hardware for long idle periods. The implementation varies between + * platforms, depending on details of how DMA will work to this device. + * Using these buffers also helps prevent cacheline sharing problems on + * architectures where CPU caches are not DMA-coherent. + * + * When the buffer is no longer used, free it with usb_buffer_free(). + */ +void *usb_buffer_alloc ( + struct usb_device *dev, + size_t size, + int mem_flags, + dma_addr_t *dma +) +{ + if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc) + return NULL; + return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma); +} + +/** + * usb_buffer_free - free memory allocated with usb_buffer_alloc() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dma: DMA address of buffer + * + * This reclaims an I/O buffer, letting it be reused. The memory must have + * been allocated using usb_buffer_alloc(), and the parameters must match + * those provided in that allocation request. + */ +void usb_buffer_free ( + struct usb_device *dev, + size_t size, + void *addr, + dma_addr_t dma +) +{ + if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) + return; + dev->bus->op->buffer_free (dev->bus, size, addr, dma); +} + +/** + * usb_buffer_map - create DMA mapping(s) for an urb + * @urb: urb whose transfer_buffer/setup_packet will be mapped + * + * Return value is either null (indicating no buffer could be mapped), or + * the parameter. URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP are + * added to urb->transfer_flags if the operation succeeds. If the device + * is connected to this system through a non-DMA controller, this operation + * always succeeds. + * + * This call would normally be used for an urb which is reused, perhaps + * as the target of a large periodic transfer, with usb_buffer_dmasync() + * calls to synchronize memory and dma state. + * + * Reverse the effect of this call with usb_buffer_unmap(). + */ +#if 0 +struct urb *usb_buffer_map (struct urb *urb) +{ + struct usb_bus *bus; + struct device *controller; + + if (!urb + || !urb->dev + || !(bus = urb->dev->bus) + || !(controller = bus->controller)) + return NULL; + + if (controller->dma_mask) { + urb->transfer_dma = dma_map_single (controller, + urb->transfer_buffer, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (usb_pipecontrol (urb->pipe)) + urb->setup_dma = dma_map_single (controller, + urb->setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + // FIXME generic api broken like pci, can't report errors + // if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; + } else + urb->transfer_dma = ~0; + urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP + | URB_NO_SETUP_DMA_MAP); + return urb; +} +#endif /* 0 */ + +/* XXX DISABLED, no users currently. If you wish to re-enable this + * XXX please determine whether the sync is to transfer ownership of + * XXX the buffer from device to cpu or vice verse, and thusly use the + * XXX appropriate _for_{cpu,device}() method. -DaveM + */ +#if 0 + +/** + * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) + * @urb: urb whose transfer_buffer/setup_packet will be synchronized + */ +void usb_buffer_dmasync (struct urb *urb) +{ + struct usb_bus *bus; + struct device *controller; + + if (!urb + || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + || !urb->dev + || !(bus = urb->dev->bus) + || !(controller = bus->controller)) + return; + + if (controller->dma_mask) { + dma_sync_single (controller, + urb->transfer_dma, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (usb_pipecontrol (urb->pipe)) + dma_sync_single (controller, + urb->setup_dma, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + } +} +#endif + +/** + * usb_buffer_unmap - free DMA mapping(s) for an urb + * @urb: urb whose transfer_buffer will be unmapped + * + * Reverses the effect of usb_buffer_map(). + */ +#if 0 +void usb_buffer_unmap (struct urb *urb) +{ + struct usb_bus *bus; + struct device *controller; + + if (!urb + || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + || !urb->dev + || !(bus = urb->dev->bus) + || !(controller = bus->controller)) + return; + + if (controller->dma_mask) { + dma_unmap_single (controller, + urb->transfer_dma, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (usb_pipecontrol (urb->pipe)) + dma_unmap_single (controller, + urb->setup_dma, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + } + urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP + | URB_NO_SETUP_DMA_MAP); +} +#endif /* 0 */ + +/** + * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint + * @dev: device to which the scatterlist will be mapped + * @pipe: endpoint defining the mapping direction + * @sg: the scatterlist to map + * @nents: the number of entries in the scatterlist + * + * Return value is either < 0 (indicating no buffers could be mapped), or + * the number of DMA mapping array entries in the scatterlist. + * + * The caller is responsible for placing the resulting DMA addresses from + * the scatterlist into URB transfer buffer pointers, and for setting the + * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. + * + * Top I/O rates come from queuing URBs, instead of waiting for each one + * to complete before starting the next I/O. This is particularly easy + * to do with scatterlists. Just allocate and submit one URB for each DMA + * mapping entry returned, stopping on the first error or when all succeed. + * Better yet, use the usb_sg_*() calls, which do that (and more) for you. + * + * This call would normally be used when translating scatterlist requests, + * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it + * may be able to coalesce mappings for improved I/O efficiency. + * + * Reverse the effect of this call with usb_buffer_unmap_sg(). + */ +int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int nents) +{ + struct usb_bus *bus; + struct device *controller; + + if (!dev + || usb_pipecontrol (pipe) + || !(bus = dev->bus) + || !(controller = bus->controller) + || !controller->dma_mask) + return -1; + + // FIXME generic api broken like pci, can't report errors + return dma_map_sg (controller, sg, nents, + usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +/* XXX DISABLED, no users currently. If you wish to re-enable this + * XXX please determine whether the sync is to transfer ownership of + * XXX the buffer from device to cpu or vice verse, and thusly use the + * XXX appropriate _for_{cpu,device}() method. -DaveM + */ +#if 0 + +/** + * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s) + * @dev: device to which the scatterlist will be mapped + * @pipe: endpoint defining the mapping direction + * @sg: the scatterlist to synchronize + * @n_hw_ents: the positive return value from usb_buffer_map_sg + * + * Use this when you are re-using a scatterlist's data buffers for + * another USB request. + */ +void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents) +{ + struct usb_bus *bus; + struct device *controller; + + if (!dev + || !(bus = dev->bus) + || !(controller = bus->controller) + || !controller->dma_mask) + return; + + dma_sync_sg (controller, sg, n_hw_ents, + usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} +#endif + +/** + * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist + * @dev: device to which the scatterlist will be mapped + * @pipe: endpoint defining the mapping direction + * @sg: the scatterlist to unmap + * @n_hw_ents: the positive return value from usb_buffer_map_sg + * + * Reverses the effect of usb_buffer_map_sg(). + */ +void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, + struct scatterlist *sg, int n_hw_ents) +{ + struct usb_bus *bus; + struct device *controller; + + if (!dev + || !(bus = dev->bus) + || !(controller = bus->controller) + || !controller->dma_mask) + return; + + dma_unmap_sg (controller, sg, n_hw_ents, + usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static int usb_generic_suspend(struct device *dev, u32 state) +{ + struct usb_interface *intf; + struct usb_driver *driver; + + if (dev->driver == &usb_generic_driver) + return usb_suspend_device (to_usb_device(dev), state); + + if ((dev->driver == NULL) || + (dev->driver_data == &usb_generic_driver_data)) + return 0; + + intf = to_usb_interface(dev); + driver = to_usb_driver(dev->driver); + + /* there's only one USB suspend state */ + if (intf->dev.power.power_state) + return 0; + + if (driver->suspend) + return driver->suspend(intf, state); + return 0; +} + +static int usb_generic_resume(struct device *dev) +{ + struct usb_interface *intf; + struct usb_driver *driver; + + /* devices resume through their hub */ + if (dev->driver == &usb_generic_driver) + return usb_resume_device (to_usb_device(dev)); + + if ((dev->driver == NULL) || + (dev->driver_data == &usb_generic_driver_data)) + return 0; + + intf = to_usb_interface(dev); + driver = to_usb_driver(dev->driver); + + if (driver->resume) + return driver->resume(intf); + return 0; +} + +struct bus_type usb_bus_type = { + .name = "usb", + .match = usb_device_match, + .hotplug = usb_hotplug, + .suspend = usb_generic_suspend, + .resume = usb_generic_resume, +}; + +#ifndef MODULE + +static int __init usb_setup_disable(char *str) +{ + nousb = 1; + return 1; +} + +/* format to disable USB on kernel command line is: nousb */ +__setup("nousb", usb_setup_disable); + +#endif + +/* + * for external read access to <nousb> + */ +int usb_disabled(void) +{ + return nousb; +} + +/* + * Init + */ +static int __init usb_init(void) +{ + int retval; + if (nousb) { + pr_info ("%s: USB support disabled\n", usbcore_name); + return 0; + } + + retval = bus_register(&usb_bus_type); + if (retval) + goto out; + retval = usb_host_init(); + if (retval) + goto host_init_failed; + retval = usb_major_init(); + if (retval) + goto major_init_failed; + retval = usbfs_init(); + if (retval) + goto fs_init_failed; + retval = usb_hub_init(); + if (retval) + goto hub_init_failed; + + retval = driver_register(&usb_generic_driver); + if (!retval) + goto out; + + usb_hub_cleanup(); +hub_init_failed: + usbfs_cleanup(); +fs_init_failed: + usb_major_cleanup(); +major_init_failed: + usb_host_cleanup(); +host_init_failed: + bus_unregister(&usb_bus_type); +out: + return retval; +} + +/* + * Cleanup + */ +static void __exit usb_exit(void) +{ + /* This will matter if shutdown/reboot does exitcalls. */ + if (nousb) + return; + + driver_unregister(&usb_generic_driver); + usb_major_cleanup(); + usbfs_cleanup(); + usb_hub_cleanup(); + usb_host_cleanup(); + bus_unregister(&usb_bus_type); +} + +subsys_initcall(usb_init); +module_exit(usb_exit); + +/* + * USB may be built into the kernel or be built as modules. + * These symbols are exported for device (or host controller) + * driver modules to use. + */ + +EXPORT_SYMBOL(usb_register); +EXPORT_SYMBOL(usb_deregister); +EXPORT_SYMBOL(usb_disabled); + +EXPORT_SYMBOL(usb_alloc_dev); +EXPORT_SYMBOL(usb_put_dev); +EXPORT_SYMBOL(usb_get_dev); +EXPORT_SYMBOL(usb_hub_tt_clear_buffer); + +EXPORT_SYMBOL(usb_lock_device); +EXPORT_SYMBOL(usb_trylock_device); +EXPORT_SYMBOL(usb_lock_device_for_reset); +EXPORT_SYMBOL(usb_unlock_device); + +EXPORT_SYMBOL(usb_driver_claim_interface); +EXPORT_SYMBOL(usb_driver_release_interface); +EXPORT_SYMBOL(usb_match_id); +EXPORT_SYMBOL(usb_find_interface); +EXPORT_SYMBOL(usb_ifnum_to_if); +EXPORT_SYMBOL(usb_altnum_to_altsetting); + +EXPORT_SYMBOL(usb_reset_device); +EXPORT_SYMBOL(usb_disconnect); + +EXPORT_SYMBOL(__usb_get_extra_descriptor); + +EXPORT_SYMBOL(usb_find_device); +EXPORT_SYMBOL(usb_get_current_frame_number); + +EXPORT_SYMBOL (usb_buffer_alloc); +EXPORT_SYMBOL (usb_buffer_free); + +#if 0 +EXPORT_SYMBOL (usb_buffer_map); +EXPORT_SYMBOL (usb_buffer_dmasync); +EXPORT_SYMBOL (usb_buffer_unmap); +#endif + +EXPORT_SYMBOL (usb_buffer_map_sg); +#if 0 +EXPORT_SYMBOL (usb_buffer_dmasync_sg); +#endif +EXPORT_SYMBOL (usb_buffer_unmap_sg); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h new file mode 100644 index 0000000..4c33eee --- /dev/null +++ b/drivers/usb/core/usb.h @@ -0,0 +1,46 @@ +/* Functions local to drivers/usb/core/ */ + +extern void usb_create_sysfs_dev_files (struct usb_device *dev); +extern void usb_remove_sysfs_dev_files (struct usb_device *dev); +extern void usb_create_sysfs_intf_files (struct usb_interface *intf); +extern void usb_remove_sysfs_intf_files (struct usb_interface *intf); + +extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr); +extern void usb_disable_interface (struct usb_device *dev, + struct usb_interface *intf); +extern void usb_release_interface_cache(struct kref *ref); +extern void usb_disable_device (struct usb_device *dev, int skip_ep0); + +extern int usb_get_device_descriptor(struct usb_device *dev, + unsigned int size); +extern int usb_set_configuration(struct usb_device *dev, int configuration); + +extern void usb_lock_all_devices(void); +extern void usb_unlock_all_devices(void); + +extern void usb_kick_khubd(struct usb_device *dev); +extern void usb_resume_root_hub(struct usb_device *dev); + +/* for labeling diagnostics */ +extern const char *usbcore_name; + +/* usbfs stuff */ +extern struct usb_driver usbfs_driver; +extern struct file_operations usbfs_devices_fops; +extern struct file_operations usbfs_device_file_operations; +extern void usbfs_conn_disc_event(void); + +struct dev_state { + struct list_head list; /* state list */ + struct usb_device *dev; + struct file *file; + spinlock_t lock; /* protects the async urb lists */ + struct list_head async_pending; + struct list_head async_completed; + wait_queue_head_t wait; /* wake up if a request completed */ + unsigned int discsignr; + struct task_struct *disctask; + void __user *disccontext; + unsigned long ifclaimed; +}; + |