Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
[sfrench/cifs-2.6.git] / drivers / usb / core / message.c
index b7434787db5fb312cb705545fbc4fa64ab9dfc0e..d8f7b089a8f0ad243f3218cbb50c78deed02301b 100644 (file)
 #include "hcd.h"       /* for usbcore internals */
 #include "usb.h"
 
+struct api_context {
+       struct completion       done;
+       int                     status;
+};
+
 static void usb_api_blocking_completion(struct urb *urb)
 {
-       complete((struct completion *)urb->context);
+       struct api_context *ctx = urb->context;
+
+       ctx->status = urb->status;
+       complete(&ctx->done);
 }
 
 
@@ -32,19 +40,21 @@ static void usb_api_blocking_completion(struct urb *urb)
  */
 static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
 { 
-       struct completion done;
+       struct api_context ctx;
        unsigned long expire;
-       int status;
+       int retval;
 
-       init_completion(&done);         
-       urb->context = &done;
+       init_completion(&ctx.done);
+       urb->context = &ctx;
        urb->actual_length = 0;
-       status = usb_submit_urb(urb, GFP_NOIO);
-       if (unlikely(status))
+       retval = usb_submit_urb(urb, GFP_NOIO);
+       if (unlikely(retval))
                goto out;
 
        expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
-       if (!wait_for_completion_timeout(&done, expire)) {
+       if (!wait_for_completion_timeout(&ctx.done, expire)) {
+               usb_kill_urb(urb);
+               retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
 
                dev_dbg(&urb->dev->dev,
                        "%s timed out on ep%d%s len=%d/%d\n",
@@ -53,17 +63,14 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
                        usb_pipein(urb->pipe) ? "in" : "out",
                        urb->actual_length,
                        urb->transfer_buffer_length);
-
-               usb_kill_urb(urb);
-               status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
        } else
-               status = urb->status;
+               retval = ctx.status;
 out:
        if (actual_length)
                *actual_length = urb->actual_length;
 
        usb_free_urb(urb);
-       return status;
+       return retval;
 }
 
 /*-------------------------------------------------------------------*/
@@ -221,15 +228,10 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
 
        if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
                        USB_ENDPOINT_XFER_INT) {
-               int interval;
-
-               if (usb_dev->speed == USB_SPEED_HIGH)
-                       interval = 1 << min(15, ep->desc.bInterval - 1);
-               else
-                       interval = ep->desc.bInterval;
                pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
                usb_fill_int_urb(urb, usb_dev, pipe, data, len,
-                               usb_api_blocking_completion, NULL, interval);
+                               usb_api_blocking_completion, NULL,
+                               ep->desc.bInterval);
        } else
                usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL);
@@ -255,6 +257,7 @@ static void sg_clean (struct usb_sg_request *io)
 static void sg_complete (struct urb *urb)
 {
        struct usb_sg_request   *io = urb->context;
+       int status = urb->status;
 
        spin_lock (&io->lock);
 
@@ -270,21 +273,21 @@ static void sg_complete (struct urb *urb)
         */
        if (io->status
                        && (io->status != -ECONNRESET
-                               || urb->status != -ECONNRESET)
+                               || 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);
+                       status, io->status);
                // BUG ();
        }
 
-       if (io->status == 0 && urb->status && urb->status != -ECONNRESET) {
-               int             i, found, status;
+       if (io->status == 0 && status && status != -ECONNRESET) {
+               int i, found, retval;
 
-               io->status = urb->status;
+               io->status = status;
 
                /* the previous urbs, and this one, completed already.
                 * unlink pending urbs so they won't rx/tx bad data.
@@ -295,13 +298,13 @@ static void sg_complete (struct urb *urb)
                        if (!io->urbs [i] || !io->urbs [i]->dev)
                                continue;
                        if (found) {
-                               status = usb_unlink_urb (io->urbs [i]);
-                               if (status != -EINPROGRESS
-                                               && status != -ENODEV
-                                               && status != -EBUSY)
+                               retval = usb_unlink_urb (io->urbs [i]);
+                               if (retval != -EINPROGRESS &&
+                                   retval != -ENODEV &&
+                                   retval != -EBUSY)
                                        dev_err (&io->dev->dev,
                                                "%s, unlink --> %d\n",
-                                               __FUNCTION__, status);
+                                               __FUNCTION__, retval);
                        } else if (urb == io->urbs [i])
                                found = 1;
                }
@@ -409,22 +412,27 @@ 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
+                * work on systems without:
+                *
+                *  - HIGHMEM, since DMA buffers located in high memory are
+                *    not directly addressable by the CPU for PIO;
+                *
+                *  - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
+                *    make virtually discontiguous buffers be "dma-contiguous"
+                *    so that PIO and DMA need diferent numbers of URBs.
+                *
+                * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
                 * to prevent stale pointers and to help spot bugs.
                 */
                if (dma) {
                        io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
                        len = sg_dma_len (sg + i);
-#ifdef CONFIG_HIGHMEM
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_IOMMU)
                        io->urbs[i]->transfer_buffer = NULL;
 #else
                        io->urbs[i]->transfer_buffer =
@@ -504,7 +512,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;
@@ -521,7 +530,6 @@ void usb_sg_wait (struct usb_sg_request *io)
                case -ENOMEM:
                        io->urbs[i]->dev = NULL;
                        retval = 0;
-                       i--;
                        yield ();
                        break;
 
@@ -532,6 +540,7 @@ void usb_sg_wait (struct usb_sg_request *io)
                         * URBs are queued at once; N milliseconds?
                         */
                case 0:
+                       ++i;
                        cpu_relax ();
                        break;
 
@@ -628,12 +637,12 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char
        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 */
+               /* retry on length 0 or error; 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)
+               if (result <= 0 && result != -ETIMEDOUT)
                        continue;
                if (result > 1 && ((u8 *)buf)[1] != type) {
                        result = -EPROTO;
@@ -1349,6 +1358,30 @@ static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
        usb_dev = interface_to_usbdev(intf);
        alt = intf->cur_altsetting;
 
+#ifdef CONFIG_USB_DEVICEFS
+       if (add_uevent_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
+
+       if (add_uevent_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;
+
+       if (add_uevent_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 (add_uevent_var(envp, num_envp, &i,
                   buffer, buffer_size, &length,
                   "INTERFACE=%d/%d/%d",
@@ -1390,6 +1423,36 @@ struct device_type usb_if_device_type = {
        .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
@@ -1536,6 +1599,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);