]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/message.c
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6-omap-h63xx.git] / drivers / usb / core / message.c
index 2f17468b5c1efe19fdfa8c7a52e3f9fc3a670865..530e854961ce99785291b399d92253151c626d9e 100644 (file)
@@ -404,13 +404,25 @@ int usb_sg_init (
 
                io->urbs [i]->complete = sg_complete;
                io->urbs [i]->context = io;
-               io->urbs [i]->status = -EINPROGRESS;
-               io->urbs [i]->actual_length = 0;
 
+               /*
+                * Some systems need to revert to PIO when DMA is temporarily
+                * unavailable.  For their sakes, both transfer_buffer and
+                * transfer_dma are set when possible.  However this can only
+                * work on systems without HIGHMEM, since DMA buffers located
+                * in high memory are not directly addressable by the CPU for
+                * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
+                * to prevent stale pointers and to help spot bugs.
+                */
                if (dma) {
-                       /* hc may use _only_ transfer_dma */
                        io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
                        len = sg_dma_len (sg + i);
+#ifdef CONFIG_HIGHMEM
+                       io->urbs[i]->transfer_buffer = NULL;
+#else
+                       io->urbs[i]->transfer_buffer =
+                               page_address(sg[i].page) + sg[i].offset;
+#endif
                } else {
                        /* hc may use _only_ transfer_buffer */
                        io->urbs [i]->transfer_buffer =
@@ -485,7 +497,8 @@ void usb_sg_wait (struct usb_sg_request *io)
 
        /* queue the urbs.  */
        spin_lock_irq (&io->lock);
-       for (i = 0; i < entries && !io->status; i++) {
+       i = 0;
+       while (i < entries && !io->status) {
                int     retval;
 
                io->urbs [i]->dev = io->dev;
@@ -502,7 +515,6 @@ void usb_sg_wait (struct usb_sg_request *io)
                case -ENOMEM:
                        io->urbs[i]->dev = NULL;
                        retval = 0;
-                       i--;
                        yield ();
                        break;
 
@@ -513,6 +525,7 @@ void usb_sg_wait (struct usb_sg_request *io)
                         * URBs are queued at once; N milliseconds?
                         */
                case 0:
+                       ++i;
                        cpu_relax ();
                        break;
 
@@ -1300,7 +1313,7 @@ int usb_reset_configuration(struct usb_device *dev)
        return 0;
 }
 
-static void release_interface(struct device *dev)
+void usb_release_interface(struct device *dev)
 {
        struct usb_interface *intf = to_usb_interface(dev);
        struct usb_interface_cache *intfc =
@@ -1310,6 +1323,97 @@ static void release_interface(struct device *dev)
        kfree(intf);
 }
 
+#ifdef CONFIG_HOTPLUG
+static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
+                char *buffer, int buffer_size)
+{
+       struct usb_device *usb_dev;
+       struct usb_interface *intf;
+       struct usb_host_interface *alt;
+       int i = 0;
+       int length = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       /* driver is often null here; dev_dbg() would oops */
+       pr_debug ("usb %s: uevent\n", dev->bus_id);
+
+       intf = to_usb_interface(dev);
+       usb_dev = interface_to_usbdev(intf);
+       alt = intf->cur_altsetting;
+
+       if (add_uevent_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_uevent_var(envp, num_envp, &i,
+                  buffer, buffer_size, &length,
+                  "MODALIAS=usb:v%04Xp%04Xd%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),
+                  usb_dev->descriptor.bDeviceClass,
+                  usb_dev->descriptor.bDeviceSubClass,
+                  usb_dev->descriptor.bDeviceProtocol,
+                  alt->desc.bInterfaceClass,
+                  alt->desc.bInterfaceSubClass,
+                  alt->desc.bInterfaceProtocol))
+               return -ENOMEM;
+
+       envp[i] = NULL;
+       return 0;
+}
+
+#else
+
+static int usb_if_uevent(struct device *dev, char **envp,
+                        int num_envp, char *buffer, int buffer_size)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_HOTPLUG */
+
+struct device_type usb_if_device_type = {
+       .name =         "usb_interface",
+       .release =      usb_release_interface,
+       .uevent =       usb_if_uevent,
+};
+
+static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
+                                                      struct usb_host_config *config,
+                                                      u8 inum)
+{
+       struct usb_interface_assoc_descriptor *retval = NULL;
+       struct usb_interface_assoc_descriptor *intf_assoc;
+       int first_intf;
+       int last_intf;
+       int i;
+
+       for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) {
+               intf_assoc = config->intf_assoc[i];
+               if (intf_assoc->bInterfaceCount == 0)
+                       continue;
+
+               first_intf = intf_assoc->bFirstInterface;
+               last_intf = first_intf + (intf_assoc->bInterfaceCount - 1);
+               if (inum >= first_intf && inum <= last_intf) {
+                       if (!retval)
+                               retval = intf_assoc;
+                       else
+                               dev_err(&dev->dev, "Interface #%d referenced"
+                                       " by multiple IADs\n", inum);
+               }
+       }
+
+       return retval;
+}
+
+
 /*
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
@@ -1344,7 +1448,7 @@ static void release_interface(struct device *dev)
  *
  * 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.
+ * bus mutex; 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 successful completion, each interface
@@ -1456,6 +1560,7 @@ free_interfaces:
                intfc = cp->intf_cache[i];
                intf->altsetting = intfc->altsetting;
                intf->num_altsetting = intfc->num_altsetting;
+               intf->intf_assoc = find_iad(dev, cp, i);
                kref_get(&intfc->ref);
 
                alt = usb_altnum_to_altsetting(intf, 0);
@@ -1473,8 +1578,8 @@ free_interfaces:
                intf->dev.parent = &dev->dev;
                intf->dev.driver = NULL;
                intf->dev.bus = &usb_bus_type;
+               intf->dev.type = &usb_if_device_type;
                intf->dev.dma_mask = dev->dev.dma_mask;
-               intf->dev.release = release_interface;
                device_initialize (&intf->dev);
                mark_quiesced(intf);
                sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",