USB gadget: Handle endpoint requests at the function level
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Tue, 20 Oct 2009 22:03:38 +0000 (00:03 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 19:55:15 +0000 (11:55 -0800)
Control requests targeted at an endpoint (that is sent to EP0 but
specifying the target endpoint address in wIndex) are dispatched to the
current configuration's setup callback, requiring all gadget drivers to
dispatch the requests to the correct function driver.

To avoid this, record which endpoints are used by each function in the
composite driver SET CONFIGURATION handler and dispatch requests
targeted at endpoints to the correct function.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/composite.c
include/linux/usb/composite.h

index d05397ec8a183ebb4467b10f55fe9b67e1cce189..8498f1a114d50083294437f504c93c5b0eb6b05c 100644 (file)
@@ -373,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;
 }
@@ -418,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",
@@ -688,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
@@ -800,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;
index 4f6bb3d2160e9f8d84516aae44363a662418431a..738ea1a691cb8a5dab64d631792871273fd45053 100644 (file)
@@ -127,6 +127,7 @@ struct usb_function {
        /* private: */
        /* internals */
        struct list_head                list;
+       DECLARE_BITMAP(endpoints, 32);
 };
 
 int usb_add_function(struct usb_configuration *, struct usb_function *);