Merge branch 'for-rmk/samsung6' of git://git.fluff.org/bjdooks/linux into devel-stable
[safe/jmp/linux-2.6] / drivers / usb / gadget / composite.c
index 5d11c29..09289bb 100644 (file)
@@ -149,16 +149,17 @@ done:
 int usb_function_deactivate(struct usb_function *function)
 {
        struct usb_composite_dev        *cdev = function->config->cdev;
+       unsigned long                   flags;
        int                             status = 0;
 
-       spin_lock(&cdev->lock);
+       spin_lock_irqsave(&cdev->lock, flags);
 
        if (cdev->deactivations == 0)
                status = usb_gadget_disconnect(cdev->gadget);
        if (status == 0)
                cdev->deactivations++;
 
-       spin_unlock(&cdev->lock);
+       spin_unlock_irqrestore(&cdev->lock, flags);
        return status;
 }
 
@@ -372,6 +373,8 @@ static void reset_config(struct usb_composite_dev *cdev)
        list_for_each_entry(f, &cdev->config->functions, list) {
                if (f->disable)
                        f->disable(f);
+
+               bitmap_zero(f->endpoints, 32);
        }
        cdev->config = NULL;
 }
@@ -417,10 +420,35 @@ static int set_config(struct usb_composite_dev *cdev,
        /* Initialize all interfaces by setting them to altsetting zero. */
        for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
                struct usb_function     *f = c->interface[tmp];
+               struct usb_descriptor_header **descriptors;
 
                if (!f)
                        break;
 
+               /*
+                * Record which endpoints are used by the function. This is used
+                * to dispatch control requests targeted at that endpoint to the
+                * function's setup callback instead of the current
+                * configuration's setup callback.
+                */
+               if (gadget->speed == USB_SPEED_HIGH)
+                       descriptors = f->hs_descriptors;
+               else
+                       descriptors = f->descriptors;
+
+               for (; *descriptors; ++descriptors) {
+                       struct usb_endpoint_descriptor *ep;
+                       int addr;
+
+                       if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+                               continue;
+
+                       ep = (struct usb_endpoint_descriptor *)*descriptors;
+                       addr = ((ep->bEndpointAddress & 0x80) >> 3)
+                            |  (ep->bEndpointAddress & 0x0f);
+                       set_bit(addr, f->endpoints);
+               }
+
                result = f->set_alt(f, tmp, 0);
                if (result < 0) {
                        DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
@@ -601,7 +629,7 @@ static int get_string(struct usb_composite_dev *cdev,
                        }
                }
 
-               for (len = 0; s->wData[len] && len <= 126; len++)
+               for (len = 0; len <= 126 && s->wData[len]; len++)
                        continue;
                if (!len)
                        return -EINVAL;
@@ -687,6 +715,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        u16                             w_value = le16_to_cpu(ctrl->wValue);
        u16                             w_length = le16_to_cpu(ctrl->wLength);
        struct usb_function             *f = NULL;
+       u8                              endp;
 
        /* partial re-init of the response message; the function or the
         * gadget might need to intercept e.g. a control-OUT completion
@@ -799,23 +828,33 @@ unknown:
                        ctrl->bRequestType, ctrl->bRequest,
                        w_value, w_index, w_length);
 
-               /* functions always handle their interfaces ... punt other
-                * recipients (endpoint, other, WUSB, ...) to the current
+               /* functions always handle their interfaces and endpoints...
+                * punt other recipients (other, WUSB, ...) to the current
                 * configuration code.
                 *
                 * REVISIT it could make sense to let the composite device
                 * take such requests too, if that's ever needed:  to work
                 * in config 0, etc.
                 */
-               if ((ctrl->bRequestType & USB_RECIP_MASK)
-                               == USB_RECIP_INTERFACE) {
+               switch (ctrl->bRequestType & USB_RECIP_MASK) {
+               case USB_RECIP_INTERFACE:
                        f = cdev->config->interface[intf];
-                       if (f && f->setup)
-                               value = f->setup(f, ctrl);
-                       else
+                       break;
+
+               case USB_RECIP_ENDPOINT:
+                       endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+                       list_for_each_entry(f, &cdev->config->functions, list) {
+                               if (test_bit(endp, f->endpoints))
+                                       break;
+                       }
+                       if (&f->list == &cdev->config->functions)
                                f = NULL;
+                       break;
                }
-               if (value < 0 && !f) {
+
+               if (f && f->setup)
+                       value = f->setup(f, ctrl);
+               else {
                        struct usb_configuration        *c;
 
                        c = cdev->config;
@@ -1013,7 +1052,7 @@ composite_suspend(struct usb_gadget *gadget)
        struct usb_composite_dev        *cdev = get_gadget_data(gadget);
        struct usb_function             *f;
 
-       /* REVISIT:  should we have config and device level
+       /* REVISIT:  should we have config level
         * suspend/resume callbacks?
         */
        DBG(cdev, "suspend\n");
@@ -1023,6 +1062,8 @@ composite_suspend(struct usb_gadget *gadget)
                                f->suspend(f);
                }
        }
+       if (composite->suspend)
+               composite->suspend(cdev);
 }
 
 static void
@@ -1031,10 +1072,12 @@ composite_resume(struct usb_gadget *gadget)
        struct usb_composite_dev        *cdev = get_gadget_data(gadget);
        struct usb_function             *f;
 
-       /* REVISIT:  should we have config and device level
+       /* REVISIT:  should we have config level
         * suspend/resume callbacks?
         */
        DBG(cdev, "resume\n");
+       if (composite->resume)
+               composite->resume(cdev);
        if (cdev->config) {
                list_for_each_entry(f, &cdev->config->functions, list) {
                        if (f->resume)
@@ -1049,7 +1092,8 @@ static struct usb_gadget_driver composite_driver = {
        .speed          = USB_SPEED_HIGH,
 
        .bind           = composite_bind,
-       .unbind         = __exit_p(composite_unbind),
+       /* .unbind              = __exit_p(composite_unbind), */
+       .unbind         = composite_unbind,
 
        .setup          = composite_setup,
        .disconnect     = composite_disconnect,
@@ -1098,7 +1142,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
  * This function is used to unregister drivers using the composite
  * driver framework.
  */
-void __exit usb_composite_unregister(struct usb_composite_driver *driver)
+void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
 {
        if (composite != driver)
                return;