From: Ragner Magalhaes <[EMAIL PROTECTED]>

Modifications on David Brownell's patch.
Some improvements on David Brownell's patch. It adds some
functions to Composite Framework as well.

Signed-off-by: Felipe Balbi <[EMAIL PROTECTED]>
Signed-off-by: Ragner Magalhaes <[EMAIL PROTECTED]>
---

 drivers/usb/gadget/composite.c | 1046 +++++++++++++++++++++++++++++-----------
 include/linux/usb/composite.h  |  169 ++++--
 2 files changed, 861 insertions(+), 354 deletions(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 495c660..03d26c7 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1,5 +1,19 @@
+/*
+ * composite.c -- USB Layer for Composite Gadget Support
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of the License of (at you option) any later version.
+ *
+ * Framework that let the gadget driver to work with multiples
+ * functions together.
+ */
+
+#if 0
 #define DEBUG 1
-// #define VERBOSE
+#define VERBOSE 1
+#define DUMP_MSGS 1
+#endif
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -21,10 +35,23 @@
 #include <asm/system.h>
 #include <asm/unaligned.h>
 
-#include <linux/usb_ch9.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb/composite.h>
 
+#include "gadget_chips.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION         "April 24th, 2007"
+#define DRIVER_DESCRIPTOR      "Composite Gadget"
+
+static const char shortname[]  = "composite";
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_composite_dev       *cdev;
 
 #define xprintk(d,level,fmt,args...) \
        dev_printk(level , &(d)->gadget->dev , fmt , ## args)
@@ -35,6 +62,8 @@
 #else
 #define DBG(dev,fmt,args...) \
        do { } while(0)
+#undef VERBOSE
+#undef DUMP_MSGS
 #endif /* DEBUG */
 
 #ifdef VERBOSE
@@ -54,10 +83,123 @@
 /*-------------------------------------------------------------------------*/
 
 /* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ     512
+#define USB_BUFSIZ             256
+
+/* An impossibly large value as file_storage */
+#define DELAYED_STATUS         (USB_BUFSIZ + 999)
+
+/* take from zero.c USB OTG Compliance test device */
+#define DRIVER_VENDOR_NUM      0x1a0a
+#define DRIVER_PRODUCT_NUM     0xbadd
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
 
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
 
-static struct usb_composite_driver     *composite;
+/*-------------------------------------------------------------------------*/
+
+/* USB String IDs */
+#define STRING_MANUFACTURER    1
+#define STRING_PRODUCT         2
+#define STRING_SERIAL          3
+#define STRING_CONFIG01        4
+#define STRING_CONFIG02        5
+
+#define CONFIG_NUMBER01        1
+#define CONFIG_NUMBER02        2
+
+static struct usb_device_descriptor
+device_desc = {
+       .bLength                = sizeof device_desc,
+       .bDescriptorType        = USB_DT_DEVICE,
+
+       .bcdUSB                 = __constant_cpu_to_le16(0x0200),
+       .bDeviceClass           = USB_CLASS_PER_INTERFACE,
+
+       .idVendor =             __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+       .idProduct =            __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+       .iManufacturer          = STRING_MANUFACTURER,
+       .iProduct               = STRING_PRODUCT,
+       .iSerialNumber          = STRING_SERIAL,
+};
+
+static struct usb_config_descriptor
+config_desc = {
+       .bLength                = sizeof config_desc,
+       .bDescriptorType        = USB_DT_CONFIG,
+
+       /* compute wTotalLength on the fly */
+       .bConfigurationValue    = CONFIG_NUMBER01,
+       .iConfiguration         = STRING_CONFIG01,
+       .bmAttributes           = USB_CONFIG_ATT_ONE
+                               | USB_CONFIG_ATT_SELFPOWER,
+       .bMaxPower              = 1,    /* self-powered */
+};
+
+static struct usb_otg_descriptor
+otg_descriptor = {
+       .bLength                = sizeof otg_descriptor,
+       .bDescriptorType        = USB_DT_OTG,
+
+       .bmAttributes           = USB_OTG_SRP,
+};
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+       .bLength                = sizeof dev_qualifier,
+       .bDescriptorType        = USB_DT_DEVICE_QUALIFIER,
+
+       .bcdUSB                 = __constant_cpu_to_le16(0x0200),
+       .bDeviceClass           = USB_CLASS_VENDOR_SPEC,
+};
+
+static const struct usb_descriptor_header *shared_function[] = {
+       (struct usb_descriptor_header *)&otg_descriptor,
+       NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = DRIVER_DESCRIPTOR;
+static char serial_number[40];
+
+static char config_cfunc[126];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings[] = {
+       {STRING_MANUFACTURER, manufacturer,},
+       {STRING_PRODUCT, product_desc,},
+       {STRING_SERIAL, serial_number,},
+       {STRING_CONFIG01, config_cfunc,},
+       {STRING_CONFIG02, config_cfunc,},
+       {}                      /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+       .language = 0x0409,     /* en-us */
+       .strings = strings,
+};
+
+/*-------------------------------------------------------------------------*/
 
 static inline int is_dualspeed(struct usb_gadget *g)
 {
@@ -79,185 +221,382 @@ static inline int is_otg(struct usb_gadget *g)
 
 /*-------------------------------------------------------------------------*/
 
-/* To simplify, we expect to have only ONE real configuration, working the
- * same no matter what speed it connects with.  A given function may expose
- * multiple interfaces.  Each interface includes its descriptor, plus optional
- * class and endpoint descriptors (as usual).
- */
+static void
+free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+       if (req->buf)
+               kfree (req->buf);
+       usb_ep_free_request(ep, req);
+}
+
+static int
+set_interface_number(struct usb_composite_dev *cdev,
+       struct usb_interface_descriptor *intf)
+{
+       if (intf->iInterface)
+               intf->iInterface = STRID_FUNC2COMPOSITE (cdev->func,
+                                               intf->iInterface);
+       intf->bInterfaceNumber = INTRF_FUNC2COMPOSITE (cdev->func,
+                                               intf->bInterfaceNumber);
+       cdev->interface[intf->bInterfaceNumber] = cdev->func;
+       if (!intf->bAlternateSetting)
+               return 1;
+       return 0;
+}
 
-#define        CONFIG_NUMBER   1
+static void
+set_cs_interface_number(struct usb_composite_dev *cdev,
+       struct usb_cdc_header_desc *cdc_desc)
+{
+       u8                              *i;
+       switch (cdc_desc->bDescriptorSubType) {
+       case USB_CDC_UNION_TYPE:
+               i = &((struct usb_cdc_union_desc *)
+                                       cdc_desc)->bMasterInterface0;
+               *i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+
+               i = &((struct usb_cdc_union_desc *)
+                                       cdc_desc)->bSlaveInterface0;
+               *i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+               break;
+       case USB_CDC_CALL_MANAGEMENT_TYPE:
+               i = &((struct usb_cdc_call_mgmt_descriptor *)
+                                               cdc_desc)->bDataInterface;
+               *i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+               break;
+       case USB_CDC_ETHERNET_TYPE:
+               i = &((struct usb_cdc_ether_desc *) cdc_desc)->iMACAddress;
+               if (*i > 0)
+                       *i = STRID_FUNC2COMPOSITE (cdev->func, *i);
+               break;
+       }
+}
+
+int usb_composite_interface_id(struct usb_composite_dev *cdev,
+void *buf, int size)
+{
+       int                             status = 0;
+       int                             i = 0;
+       struct usb_descriptor_header * src = buf;
+
+       if (cdev->next_interface_id >= MAX_COMPOSITE_INTERFACES)
+               return -EBUSY;
+
+       if (!src)
+               return -EINVAL;
+
+       while (i < size) {
+               switch (src->bDescriptorType) {
+               case USB_DT_INTERFACE:
+                       status += set_interface_number(cdev,
+                               (struct usb_interface_descriptor *) src);
+                       break;
+               case USB_DT_CS_INTERFACE:
+                       set_cs_interface_number(cdev,
+                               (struct usb_cdc_header_desc *) src);
+                       break;
+               }
+               i += src->bLength;
+               buf += src->bLength;
+               src = buf;
+       }
+
+       return status;
+}
+
+static inline struct usb_string *
+get_func_strings(struct usb_function *f)
+{
+       return f->strings->strings;
+}
+
+int usb_composite_string_id(struct usb_composite_dev *cdev)
+{
+       struct usb_string               *src = get_func_strings(cdev->func);
+       int                             status = 0;
+
+       if (cdev->next_string_id >= 255)
+               return -EBUSY;
+
+       for (; src && src->s ; src++) {
+               status ++;
+       }
+       return status;
+}
+
+/*-------------------------------------------------------------------------*/
 
 static int
-config_buf(struct usb_composite_dev *cdev, void *buf, u8 type)
+config_buf(struct usb_composite_dev *cdev, void *buf, u8 type, unsigned index)
 {
        struct usb_config_descriptor    *c = buf;
        void                            *next = buf + USB_DT_CONFIG_SIZE;
        int                             len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
        int                             hs;
        struct usb_function             *f;
+       struct usb_gadget               *gadget = cdev->gadget;
+       struct usb_ctrlrequest          req;
+       int                             status;
+       int                             i;
+       const struct usb_descriptor_header **function;
+
+       if (index >= device_desc.bNumConfigurations)
+               return -EINVAL;
 
-       if (is_dualspeed(cdev->gadget)) {
-               hs = (cdev->gadget->speed == USB_SPEED_HIGH);
+       if (is_dualspeed(gadget)) {
+               hs = (gadget->speed == USB_SPEED_HIGH);
                if (type == USB_DT_OTHER_SPEED_CONFIG)
                        hs = !hs;
        } else
                hs = 0;
 
+
        /* write a config descriptor */
-       *c = cdev->config;
+       *c = config_desc;
        c->bLength = USB_DT_CONFIG_SIZE;
        c->bDescriptorType = type;
-       c->bConfigurationValue = CONFIG_NUMBER;
 
-       /* REVISIT some configurations might need other descriptors,
-        * independent of the interfaces they implement ... notably
-        * OTG descriptors.
-        */
+       if (index == 0) {
+               c->iConfiguration = STRING_CONFIG01;
+               c->bConfigurationValue = CONFIG_NUMBER01;
+       } else {
+               c->iConfiguration = STRING_CONFIG02;
+               c->bConfigurationValue = CONFIG_NUMBER02;
+       }
 
-       /* add each function's descriptors */
-       list_for_each_entry (f, &composite->functions, function) {
-               int                     status;
+       function = shared_function;
+       if (!is_otg(gadget))
+               function++;
 
-               status = usb_descriptor_fillbuf(next, len,
-                               hs ? f->hs_descriptors : f->descriptors);
+       /* shared function descriptors */
+       if (function) {
+               status = usb_descriptor_fillbuf(next, len, function);
                if (status < 0)
                        return status;
                len -= status;
                next += status;
        }
 
-       len = next - buf;
-       c->wTotalLength = cpu_to_le16(len);
-       return len;
-}
+       /* add each function's descriptors */
+       c->bNumInterfaces =0;
+       cdev->next_interface_id = 0;
+       cdev->next_string_id = 0;
 
-/*-------------------------------------------------------------------------*/
+       if (!cdev->func)
+               goto result;
 
-static void composite_reset_config(
-       struct usb_composite_dev        *cdev,
-       struct usb_ctrlrequest          *req
-) {
-       struct usb_function             *f;
-       int                             result;
+       memset(&req, 0, sizeof req);
+       req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+       req.bRequest = USB_REQ_GET_DESCRIPTOR;
+       req.wLength = USB_BUFSIZ;
+
+       index = (index < cdev->func->num_conf) ? index : 0;
+
+       list_for_each_entry (f, &cdev->functions, function) {
+
+               req.wValue = type << 8;
+               req.wValue |= index;
 
-       DBG(cdev, "reset config\n");
+               f->interface_shift = cdev->next_interface_id;
+               f->stringid_shift = cdev->next_string_id;
 
-       req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
-       req->bRequest = USB_REQ_SET_CONFIGURATION;
+               cdev->req->length = next - buf;
+               cdev->func = f;
+               status = f->setup(gadget, &req);
 
-       list_for_each_entry (f, &composite->functions, function) {
-               result = f->setup(cdev, req);
-               if (result < 0)
-                       DBG(cdev, "reset function %s --> %d\n",
-                                       f->name, result);
+               if (status < 0) {
+                       DBG(cdev, " %s - invalid descriptor header.\n", 
f->name);
+                       return status;
+               }
+
+               i = usb_composite_interface_id(cdev, next, status);
+               if (i < 0)
+                       return i;
+               cdev->next_interface_id += i;
+
+               i = usb_composite_string_id(cdev);
+               if (i < 0)
+                       return i;
+               cdev->next_string_id += i;
+
+               /* exchange the configuration of the fist function only
+                * in the buf of the two composite's configurations.
+                */
+               index = 0;
+
+               len -= status;
+               next += status;
        }
-       cdev->config.bConfigurationValue = 0;
+result:
+
+       len = next - buf;
+       c->wTotalLength = cpu_to_le16(len);
+       c->bNumInterfaces = cdev->next_interface_id;
+
+       return len;
 }
 
 static int
 composite_set_config(struct usb_composite_dev *cdev, unsigned number)
 {
-       int                     result = 0, tmp;
-       struct usb_gadget       *gadget = cdev->gadget;
-       struct usb_ctrlrequest  req;
+       int                             result = 0;
+       struct usb_gadget               *gadget = cdev->gadget;
+       struct usb_ctrlrequest          req;
+       struct usb_function             *f;
+       int                             i;
 
        memset(&req, 0, sizeof req);
 
-       /* for now function drivers should assume SET_CONFIGURATION means
-        * reset/deconfigure, with SET_INTERFACE to each interface used
-        * to activate some altsetting in "the single" configuration.
-        */
-       composite_reset_config(cdev, &req);
+       req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+       req.bRequest = USB_REQ_SET_CONFIGURATION;
+       req.wValue = number;
+
+       /* reset config */
+       cdev->config = 0;
 
        switch (number) {
        default:
                result = -EINVAL;
                /* FALLTHROUGH */
        case 0:
-               usb_gadget_vbus_draw(gadget, is_otg(gadget) ? 8 : 100);
+               list_for_each_entry (f, &cdev->functions, function) {
+                       cdev->func = f;
+                       result = f->setup(gadget, &req);
+                       if (result < 0)
+                               break;
+               }
                break;
-       case CONFIG_NUMBER:
-               req.bRequestType = USB_DIR_OUT
-                               | USB_TYPE_STANDARD
-                               | USB_RECIP_INTERFACE;
-               req.bRequest = USB_REQ_SET_INTERFACE;
-
-               for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
-                       struct usb_function     *f = cdev->interface[tmp];
 
-                       if (!f)
-                               continue;
-                       req.wIndex = cpu_to_le16(tmp);
-                       result = f->setup(cdev, &req);
-                       if (result < 0) {
-                               DBG(cdev, "interface %d/%s alt 0--> %d\n",
-                                               tmp, f->name, result);
-                               (void) composite_set_config(cdev, 0);
-                               return result;
-                       }
+       case CONFIG_NUMBER01:
+               i = 0;
+               list_for_each_entry (f, &cdev->functions, function) {
+                       req.wValue = f->config[i]->bConfigurationValue;
+                       cdev->func = f;
+                       result = f->setup(gadget, &req);
+                       if (result < 0)
+                               break;
                }
+               break;
 
-               cdev->config.bConfigurationValue = number;
-               usb_gadget_vbus_draw(gadget, 2 * cdev->config.bMaxPower);
+       case CONFIG_NUMBER02:
+               if (!cdev->func)
+                       break;
+               i = (1 < cdev->func->num_conf) ? 1 : 0;
+               list_for_each_entry (f, &cdev->functions, function) {
+                       req.wValue = f->config[i]->bConfigurationValue;
+                       cdev->func = f;
+                       result = f->setup(gadget, &req);
+                       if (result < 0)
+                               break;
+                       i = 0;
+               }
                break;
        }
 
-       INFO(cdev, "%s speed config #%d\n",
-               ({ char *speed;
+       if (result < 0)
+               usb_gadget_vbus_draw(gadget, is_otg(gadget) ? 8 : 100);
+       else {
+               char *speed;
+               unsigned power;
+
+               power = 2 * config_desc.bMaxPower;
+               usb_gadget_vbus_draw(gadget, power);
+
                switch (gadget->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; }), number);
+               case USB_SPEED_LOW:
+                       speed = "low"; break;
+               case USB_SPEED_FULL:
+                       speed = "full"; break;
+               case USB_SPEED_HIGH:
+                       speed = "high"; break;
+               default:
+                       speed = "?"; break;
+               }
 
+               cdev->config = number;
+               sprintf(config_cfunc,"Composite gadget");
+               INFO (cdev, "%s speed config #%d: %d mA, %s\n",
+                               speed, number, power, config_cfunc);
+               result = 0;
+       }
        return result;
 }
 
 /*-------------------------------------------------------------------------*/
 
+static struct usb_function *
+get_function_head(struct usb_composite_dev *c)
+{
+       if (list_empty(&c->functions))
+               return NULL;
+       return  list_entry(c->functions.next, struct usb_function, function);
+}
+
+void usb_func_ep_reset (struct usb_gadget *g, struct usb_function *f)
+{
+       struct usb_ep   *ep;
+
+       list_for_each_entry (ep, &g->ep_list, ep_list) {
+               if (ep->driver_data == f->driver_data) {
+                       usb_ep_disable (ep);
+                       ep->driver_data = NULL;
+               }
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const char *get_string (const struct usb_gadget_strings *table, int id)
+{
+       struct usb_string       *s;
+
+       for (s = table->strings; s && s->s; s++)
+               if (s->id == id)
+                       break;
+       return s->s;
+}
+
 static void
-composite_collect_langs(const struct usb_gadget_strings **sp, __le16 *buf)
+composite_collect_langs(struct usb_gadget_strings *sp, __le16 * buf)
 {
-       const struct usb_gadget_strings *s;
+       struct usb_gadget_strings       *s;
        u16                             language;
        __le16                          *tmp;
 
-       while (*sp) {
-               s = *sp;
-               language = cpu_to_le16(s->language);
+       if (sp) {
+               s = sp;
+               language = cpu_to_le16 (s->language);
                for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
                        if (*tmp == language)
-                               goto repeat;
+                               return;
                }
                *tmp++ = language;
-repeat:
-               sp++;
        }
 }
 
 static int composite_check_string(
-       const struct usb_gadget_strings **sp,
+       struct usb_gadget_strings       *sp,
        void                            *buf,
        u16                             language,
        int                             id
 )
 {
-       const struct usb_gadget_strings *s;
+       struct usb_gadget_strings       *s;
        int                             value;
 
-       while (*sp) {
-               s = *sp++;
+       if (sp) {
+               s = sp;
                if (s->language != language)
-                       continue;
-               value = usb_gadget_get_string(s, id, buf);
+                       return -EINVAL;
+               value = usb_gadget_get_string (s, id, buf);
                if (value > 0)
                        return value;
        }
        return -EINVAL;
 }
 
-static int composite_lookup_string(void *buf, u16 language, int id)
+static int composite_lookup_string(struct usb_composite_dev *cdev,
+               void *buf, u16 language, int id)
 {
        struct usb_function             *f;
        int                             len;
@@ -265,16 +604,16 @@ static int composite_lookup_string(void *buf, u16 
language, int id)
        /* 0 == report all available language codes */
        if (id == 0) {
                struct usb_string_descriptor    *s = buf;
-               const struct usb_gadget_strings **sp;
+               struct usb_gadget_strings       *sp;
 
-               memset(s, 0, 256);
+               memset(s, 0, USB_BUFSIZ);
                s->bDescriptorType = USB_DT_STRING;
 
-               sp = composite->strings;
+               sp = &stringtab;
                if (sp)
                        composite_collect_langs(sp, s->wData);
 
-               list_for_each_entry (f, &composite->functions, function) {
+               list_for_each_entry (f, &cdev->functions, function) {
                        sp = f->strings;
                        if (sp)
                                composite_collect_langs(sp, s->wData);
@@ -287,19 +626,48 @@ static int composite_lookup_string(void *buf, u16 
language, int id)
 
                s->bLength = 2 * (len + 1);
                return s->bLength;
+       } else if (cdev->func && ((id == STRING_CONFIG01)
+                               || (id == STRING_CONFIG02))) {
+               const char      *s;
+               int             l = 0;
+               int             i;
+               int             bl = 126;
+
+               if (id == STRING_CONFIG01 || cdev->func->num_conf < 2)
+                       i = 0;
+               else
+                       i = 1;
+
+               list_for_each_entry (f, &cdev->functions, function) {
+                       s = get_string(f->strings,
+                                       f->config[i]->iConfiguration);
+                       if (!s)
+                               continue;
+
+                       bl -= strlen(s);
+                       if (bl <= 0)
+                               break;
+                       if (l > 0) {
+                               config_cfunc[l] = ',';
+                               config_cfunc[l + 1] = ' ';
+                               l += 2;
+                       }
+                       strcpy(config_cfunc + l, s);
+                       l = strlen(config_cfunc);
+                       i = 0;
+               }
        }
 
        /* otherwise, look up and return a specified string */
-       if (composite->strings) {
-               len = composite_check_string(composite->strings,
-                               buf, language, id);
-               if (len > 0)
-                       return len;
-       }
-       list_for_each_entry (f, &composite->functions, function) {
+       len = composite_check_string(&stringtab, buf, language, id);
+       if (len > 0)
+               return len;
+
+       list_for_each_entry (f, &cdev->functions, function) {
                if (!f->strings)
                        continue;
-               len = composite_check_string(f->strings, buf, language, id);
+               len = composite_check_string(f->strings, buf, language,
+                                               STRID_COMPOSITE2FUNC (f,id));
                if (len > 0)
                        return len;
        }
@@ -308,14 +676,84 @@ static int composite_lookup_string(void *buf, u16 
language, int id)
 
 /*-------------------------------------------------------------------------*/
 
-static void composite_setup_complete(struct usb_ep *ep, struct usb_request 
*req)
+#ifdef DUMP_MSGS
+
+static void dump_msg(struct usb_composite_dev *cdev, const char *label,
+               const u8 *buf, unsigned int length)
 {
+       unsigned int    start, num, i;
+       char            line[52], *p;
+
+       if (length >= 512)
+               return;
+       DBG(cdev, "%s, length %u:\n", label, length);
+
+       start = 0;
+       while (length > 0) {
+               num = min(length, 16u);
+               p = line;
+               for (i = 0; i < num; ++i) {
+                       if (i == 8)
+                               *p++ = ' ';
+                       sprintf(p, " %02x", buf[i]);
+                       p += 3;
+               }
+               *p = 0;
+               printk(KERN_DEBUG "%6x: %s\n", start, line);
+               buf += num;
+               start += num;
+               length -= num;
+       }
+}
+
+#else
+
+static void inline dump_msg(struct usb_composite_dev *cdev, const char *label,
+               const u8 *buf, unsigned int length)
+{}
+
+#endif /* DUMP_MSGS */
+
+static void
+composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct usb_composite_dev *cdev = ep->driver_data;
+       if (req->actual > 0)
+               dump_msg(cdev, cdev->req_name, req->buf, req->actual);
        if (req->status || req->actual != req->length)
-               DBG((struct usb_composite_dev *) ep->driver_data,
-                               "setup complete --> %d, %d/%d\n",
+               DBG(cdev, "setup complete --> %d, %u/%u\n",
                                req->status, req->actual, req->length);
+       if (req->status == -ECONNRESET)         // Request was cancelled
+               usb_ep_fifo_flush(ep);
+}
+
+static int
+func_setup(struct usb_composite_dev *cdev,
+                               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_ctrlrequest          fctrl;
+       int                             value = -EOPNOTSUPP;
+       u16                             w_index = le16_to_cpu(ctrl->wIndex);
+
+       if (w_index >= MAX_COMPOSITE_INTERFACES
+                       || !cdev->interface[w_index])
+               goto out;
+
+       memcpy (&fctrl, ctrl, sizeof fctrl);
+
+       cdev->func = cdev->interface[w_index];
+       fctrl.wIndex = cpu_to_le16 (INTRF_COMPOSITE2FUNC(cdev->func, w_index));
+       fctrl.wValue = ctrl->wValue;
+       fctrl.wLength = ctrl->wLength;
+
+       spin_lock(&cdev->lock);
+       value = cdev->func->setup(cdev->gadget, &fctrl);
+       spin_unlock(&cdev->lock);
+out:
+       return value;
 }
 
+
 /*
  * The setup() callback implements all the ep0 functionality that's
  * not handled lower down, in hardware or the hardware driver(like
@@ -324,16 +762,14 @@ static void composite_setup_complete(struct usb_ep *ep, 
struct usb_request *req)
  * the work is in config-specific setup.
  */
 static int
-composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+setup_standard(struct usb_composite_dev        *cdev, const struct 
usb_ctrlrequest *ctrl)
 {
-       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
        struct usb_request              *req = cdev->req;
        int                             value = -EOPNOTSUPP;
        u16                             w_index = le16_to_cpu(ctrl->wIndex);
        u16                             w_value = le16_to_cpu(ctrl->wValue);
        u16                             w_length = le16_to_cpu(ctrl->wLength);
 
-       req->zero = 0;
        switch (ctrl->bRequest) {
 
        case USB_REQ_GET_DESCRIPTOR:
@@ -342,57 +778,61 @@ composite_setup(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
                switch (w_value >> 8) {
 
                case USB_DT_DEVICE:
-                       value = min(w_length, (u16) sizeof cdev->dev);
-                       memcpy(req->buf, &cdev->dev, value);
+                       if (cdev->func)
+                               device_desc.bNumConfigurations =
+                                               cdev->func->num_conf;
+                       else
+                               device_desc.bNumConfigurations = 1;
+
+                       value = sizeof device_desc;
+                       memcpy(req->buf, &device_desc, value);
                        break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
                case USB_DT_DEVICE_QUALIFIER:
-                       if (!is_dualspeed(gadget))
+                       if (!is_dualspeed (cdev->gadget))
                                break;
-                       value = min(w_length, (u16) sizeof cdev->qual);
-                       memcpy(req->buf, &cdev->qual, value);
+                       value = sizeof dev_qualifier;
+                       memcpy(req->buf, &dev_qualifier, value);
                        break;
 
                case USB_DT_OTHER_SPEED_CONFIG:
-                       if (!is_dualspeed(gadget))
+                       if (!is_dualspeed (cdev->gadget))
                                break;
                        // FALLTHROUGH
-
+#endif                         /* CONFIG_USB_GADGET_DUALSPEED */
                case USB_DT_CONFIG:
-                       /* one config ... so it must always be index 0 */
-                       if (w_value & 0xff)
-                               break;
-
-                       value = config_buf(cdev, req->buf, w_value >> 8);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
+                       value = config_buf(cdev, req->buf, w_value >> 8,
+                                                       w_value & 0xff);
+                       req->complete = composite_setup_complete;
+                       cdev->gadget->ep0->driver_data = cdev;
                        break;
+
                case USB_DT_STRING:
-                       value = composite_lookup_string(req->buf, w_index,
-                                               w_value & 0xff);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
+                       value = composite_lookup_string(cdev, req->buf,
+                                               w_index, w_value & 0xff);
                        break;
                }
                break;
 
-       /* currently one config, two speeds */
        case USB_REQ_SET_CONFIGURATION:
                if (ctrl->bRequestType != 0)
                        goto unknown;
-               if (gadget->a_hnp_support)
+               if (cdev->gadget->a_hnp_support)
                        DBG(cdev, "HNP available\n");
-               else if (gadget->a_alt_hnp_support)
+               else if (cdev->gadget->a_alt_hnp_support)
                        DBG(cdev, "HNP needs a different root port\n");
                else
                        VDBG(cdev, "HNP inactive\n");
                spin_lock(&cdev->lock);
                value = composite_set_config(cdev, w_value);
                spin_unlock(&cdev->lock);
+               req->complete = composite_setup_complete;
+               cdev->gadget->ep0->driver_data = cdev;
                break;
        case USB_REQ_GET_CONFIGURATION:
                if (ctrl->bRequestType != USB_DIR_IN)
                        goto unknown;
-               *(u8 *)req->buf = cdev->config.bConfigurationValue;
+               *(u8 *)req->buf = cdev->config;
                value = min(w_length, (u16) 1);
                break;
 
@@ -400,45 +840,74 @@ composite_setup(struct usb_gadget *gadget, const struct 
usb_ctrlrequest *ctrl)
        case USB_REQ_SET_INTERFACE:
                if (ctrl->bRequestType != USB_RECIP_INTERFACE)
                        goto unknown;
-               if (!cdev->config.bConfigurationValue
-                               || w_index >= MAX_COMPOSITE_INTERFACES
-                               || !cdev->interface[w_index])
-                       break;
-               spin_lock(&cdev->lock);
-               value = cdev->interface[w_index]->setup(cdev, ctrl);
-               spin_unlock(&cdev->lock);
+               value = func_setup(cdev, ctrl);
                break;
+
        case USB_REQ_GET_INTERFACE:
                if (ctrl->bRequestType !=(USB_DIR_IN|USB_RECIP_INTERFACE))
                        goto unknown;
-               if (!cdev->config.bConfigurationValue
-                               || w_index >= MAX_COMPOSITE_INTERFACES
-                               || !cdev->interface[w_index])
-                       break;
-               spin_lock(&cdev->lock);
-               /* function must set cdev->req->buf[0] */
-               value = cdev->interface[w_index]->setup(cdev, ctrl);
-               spin_unlock(&cdev->lock);
-               value = min(w_length, (u16) 1);
+               value = func_setup(cdev, ctrl);
                break;
+
        default:
+               value = func_setup(cdev, ctrl);
+               if (value != -EOPNOTSUPP)
+                       break;
 unknown:
-               VDBG(dev,
-                       "unknown control req%02x.%02x v%04x i%04x l%d\n",
+               VDBG(cdev,
+                       "unknown standard control "
+                       "req%02x.%02x v%04x i%04x l%d\n",
                        ctrl->bRequestType, ctrl->bRequest,
                        w_value, w_index, w_length);
        }
+       return value;
+}
+
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev        *cdev = get_gadget_data (gadget);
+       struct usb_request              *req = cdev->req;
+       int                             value = -EOPNOTSUPP;
+       u16                             w_index = le16_to_cpu(ctrl->wIndex);
+       u16                             w_value = le16_to_cpu(ctrl->wValue);
+       u16                             w_length = le16_to_cpu(ctrl->wLength);
+
+       req->zero = 0;
+       req->length = 0;
+       req->complete = composite_setup_complete;
+       gadget->ep0->driver_data = cdev;
+       switch (ctrl->bRequestType & USB_TYPE_MASK) {
+
+       case USB_TYPE_STANDARD:
+               cdev->func = get_function_head(cdev);
+               value = setup_standard(cdev, ctrl);
+               break;
+
+       default:
+               value = func_setup(cdev, ctrl);
+       }
 
        /* respond with data transfer before status phase? */
-       if (value >= 0) {
-               req->length = value;
-               req->zero = value < w_length;
+       if (value >= 0 && value != DELAYED_STATUS) {
+               value = min((u16)value, w_length);
+               cdev->req->length = value;
+               cdev->req->zero = value < w_length;
+               cdev->req_name = (ctrl->bRequestType & USB_DIR_IN ?
+                                        "ep0-in" : "ep0-out");
+
                value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0) {
-                       DBG(cdev, "ep_queue --> %d\n", value);
+               if (value != 0 && value != -ESHUTDOWN) {
+                       WARN(cdev, "error in submission: %s --> %d\n",
+                                       cdev->req_name, value);
                        req->status = 0;
-                       composite_setup_complete(gadget->ep0, req);
                }
+       } else {
+               DBG(cdev, "setup req%02x.%02x v%04x i%04x l%d --> %d\n",
+                               ctrl->bRequestType, ctrl->bRequest,
+                               w_value, w_index, w_length, value);
+               /* to eliminate compile warnings */
+               w_index = w_value = 0;
        }
 
        /* device either stalls (value < 0) or reports success */
@@ -448,17 +917,16 @@ unknown:
 static void
 composite_disconnect(struct usb_gadget *gadget)
 {
-       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
-       unsigned long                   flags;
-       struct usb_ctrlrequest  req;
+       struct usb_composite_dev        *cdev = get_gadget_data (gadget);
+       struct usb_function             *f;
 
        DBG(cdev, "disconnect\n");
 
-       memset(&req, 0, sizeof req);
-
-       spin_lock_irqsave(&cdev->lock, flags);
-       composite_reset_config(cdev, &req);
-       spin_unlock_irqrestore(&cdev->lock, flags);
+       list_for_each_entry (f, &cdev->functions, function) {
+               /*current func */
+               cdev->func = f;
+               f->disconnect (gadget);
+       }
 }
 
 /*-------------------------------------------------------------------------*/
@@ -466,37 +934,41 @@ composite_disconnect(struct usb_gadget *gadget)
 static void /* __init_or_exit */
 composite_unbind(struct usb_gadget *gadget)
 {
-       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
-       struct usb_function             *f;
+       struct usb_composite_dev        *cdev = get_gadget_data (gadget);
 
        DBG(cdev, "unbind\n");
 
-       list_for_each_entry (f, &cdev->driver->functions, function) {
-               if (f->unbind)
-                       f->unbind(cdev);
-               if (f == cdev->current_bind)
-                       break;
-       }
-       if (composite->unbind)
-               composite->unbind(cdev);
+       /* we've already been disconnected ... no i/o is active */
        if (cdev->req) {
-               kfree(cdev->req->buf);
-               usb_ep_free_request(gadget->ep0, cdev->req);
+               cdev->req->length = USB_BUFSIZ;
+               free_ep_req(gadget->ep0, cdev->req);
+               cdev->req = NULL;
        }
        kfree(cdev);
+
        set_gadget_data(gadget, NULL);
 }
 
-static int __init
+static int __devinit
 composite_bind(struct usb_gadget *gadget)
 {
-       struct usb_composite_dev        *cdev;
-       struct usb_function             *f;
        int                             status = -ENOMEM;
+       int                             gcnum;
+       int                             i;
+
+       gcnum = usb_gadget_controller_number(gadget);
+       if (gcnum >= 0)
+               device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+       else {
+               printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+                      shortname, gadget->name);
+               device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+       }
 
-       cdev = kzalloc(sizeof *cdev, SLAB_KERNEL);
+       cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
        if (!cdev)
                return status;
+       INIT_LIST_HEAD(&cdev->functions);
 
        spin_lock_init(&cdev->lock);
        cdev->gadget = gadget;
@@ -506,136 +978,108 @@ composite_bind(struct usb_gadget *gadget)
        cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
        if (!cdev->req)
                goto fail;
-       cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+       cdev->req->buf = kmalloc (USB_BUFSIZ, GFP_KERNEL);
        if (!cdev->req->buf)
                goto fail;
 
-       cdev->req->complete = composite_setup_complete;
        gadget->ep0->driver_data = cdev;
 
-       cdev->driver = composite;
-       cdev->dev = *composite->dev;
-       cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
-       usb_gadget_set_selfpowered(gadget);
-
-       /* mostly to assign strings for whole device, like serial number,
-        * but potentially also to update power state and consumption etc
-        */
-       if (composite->bind) {
-               status = composite->bind(cdev);
-               if (status < 0)
-                       goto fail;
-       }
-
-       /* function binding involves updating interface, class, and
-        * endpoint descriptors:
-        *  - allocating any string IDs needed (interface, class)
-        *  - allocating interface IDs (ditto)
-        *  - allocating endpoints (endpoint, class)
-        *  - allocating non-USB resources (buffers etc)
-        *
-        * NOTE:  we assume that if there are multiple controllers, they
-        * are all the same type (e.g. net2280 in pci slots) so functions
-        * never need different descriptors.
-        */
        usb_ep_autoconfig_reset(gadget);
-       list_for_each_entry (f, &cdev->driver->functions, function) {
-               cdev->current_bind = f;
-               status = f->bind(cdev);
-               if (status < 0)
-                       goto fail;
-
-               if ((is_dualspeed(gadget) && !f->hs_descriptors)
-                               || !f->descriptors) {
-                       status = -ENXIO;
+       /* support optional vendor/distro customization */
+       if (idVendor) {
+               if (!idProduct) {
+                       dev_err (&gadget->dev, "idVendor needs idProduct!\n");
+                       status = -EINVAL;
                        goto fail;
                }
+               device_desc.idVendor = cpu_to_le16(idVendor);
+               device_desc.idProduct = cpu_to_le16(idProduct);
+               if (bcdDevice)
+                       device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+       }
+       if (iManufacturer)
+               strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
+       else
+               snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+                               init_utsname()->sysname,
+                               init_utsname()->release,
+                               gadget->name);
+       if (iProduct)
+               strlcpy (product_desc, iProduct, sizeof product_desc);
+       if (iSerialNumber)
+               strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+       else for (i = 0; i < strlen(DRIVER_VERSION)
+                       && i < sizeof(serial_number); i ++) {
+               unsigned char           c = DRIVER_VERSION[i];
+               if (!c)
+                       break;
+               sprintf(&serial_number[i], "%x", c);
        }
-       cdev->current_bind = NULL;
-       cdev->config.bNumInterfaces = cdev->next_interface_id;
 
-       /* REVISIT eventually we want e.g. RNDIS and non-RNDIS configs,
-        * at each available device speed ...
-        */
-       cdev->dev.bNumConfigurations = 1;
 
-       if (is_dualspeed(gadget)) {
-               cdev->qual.bLength = sizeof cdev->qual;
-               cdev->qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+       device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 
-               cdev->qual.bcdUSB = cdev->dev.bcdUSB;
-               cdev->qual.bDeviceClass = cdev->dev.bDeviceClass;
-               cdev->qual.bDeviceProtocol = cdev->dev.bDeviceProtocol;
+       if (is_otg(gadget)) {
+               otg_descriptor.bmAttributes |= USB_OTG_HNP,
+               config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+               config_desc.bMaxPower = 4;
+       }
 
+       if (is_dualspeed(gadget)) {
                /* assume ep0 uses the same value for both speeds ... */
-               cdev->qual.bMaxPacketSize0 = cdev->dev.bMaxPacketSize0;
-
-               cdev->qual.bNumConfigurations = cdev->dev.bNumConfigurations;
+               dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+               dev_qualifier.bcdUSB = device_desc.bcdUSB;
+               dev_qualifier.bNumConfigurations =
+                       device_desc.bNumConfigurations;
        }
 
-       INFO(cdev, "%s ready\n", composite->name);
        return 0;
-
 fail:
        composite_unbind(gadget);
        return status;
 }
 
-int usb_composite_string_id(struct usb_composite_dev *cdev)
-{
-       if (cdev->current_bind && cdev->next_string_id < 255)
-               return cdev->next_string_id++;
-       return -ENODEV;
-}
-
-int usb_composite_interface_id(struct usb_composite_dev *cdev)
-{
-       if (cdev->next_interface_id < MAX_COMPOSITE_INTERFACES
-                       && cdev->current_bind) {
-               cdev->interface[cdev->next_interface_id] = cdev->current_bind;
-               return cdev->next_interface_id++;
-       }
-       return -ENODEV;
-}
-
 /*-------------------------------------------------------------------------*/
 
 static void
 composite_suspend(struct usb_gadget *gadget)
 {
-       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
+       struct usb_composite_dev        *cdev = get_gadget_data (gadget);
        struct usb_function             *f;
 
        DBG(cdev, "suspend\n");
-       /* revisit -- iterate cdev->interface? */
-       list_for_each_entry (f, &cdev->driver->functions, function) {
-               if (!f->suspend)
-                       continue;
-               f->suspend(cdev);
+
+       list_for_each_entry (f, &cdev->functions, function) {
+               /*current func */
+               cdev->func = f;
+               f->suspend (gadget);
        }
 }
 
 static void
 composite_resume(struct usb_gadget *gadget)
 {
-       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
+       struct usb_composite_dev        *cdev = get_gadget_data (gadget);
        struct usb_function             *f;
 
        DBG(cdev, "resume\n");
-       /* revisit -- iterate cdev->interface? */
-       list_for_each_entry (f, &cdev->driver->functions, function) {
-               if (!f->resume)
-                       continue;
-               f->resume(cdev);
+
+       list_for_each_entry (f, &cdev->functions, function) {
+               /*current func */
+               cdev->func = f;
+               f->resume (gadget);
        }
 }
 
-
 /*-------------------------------------------------------------------------*/
 
-static struct usb_gadget_driver composite_driver = {
+static struct usb_gadget_driver composite_drv = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        .speed          = USB_SPEED_HIGH,
+#else
+       .speed          = USB_SPEED_FULL,
+#endif
+       .function       = (char *) product_desc,
 
        .bind           = composite_bind,
        .unbind         = __exit_p(composite_unbind),
@@ -647,33 +1091,61 @@ static struct usb_gadget_driver composite_driver = {
        .resume         = composite_resume,
 
        .driver = {
-               .owner          = THIS_MODULE,
+               .name   = (char *) shortname,
+               .owner  = THIS_MODULE,
        },
 };
 
-int usb_composite_register(struct usb_composite_driver *d)
+
+int usb_func_register(struct usb_function *f)
 {
-       struct usb_function     *f;
+       int                             status = -EINVAL;
 
-       if (!d || !d->dev || composite)
+       if (!f || !f->bind)
                return -EINVAL;
 
-       list_for_each_entry (f, &d->functions, function) {
-               if (!f->bind || !f->setup)
-                       return -EINVAL;
+       /* current func */
+       cdev->func = f;
+       status = f->bind (cdev->gadget);
+       if (status < 0)
+               goto fail;
 
-               /* REVISIT erm, hs_descriptors may be unknown here? */
-               if (!f->hs_descriptors) {
-                       composite_driver.speed = USB_SPEED_FULL;
-                       break;
-               }
-       }
+       list_add_tail(&f->function, &cdev->functions);
+
+       return 0;
+fail:
+       return status;
+}
+EXPORT_SYMBOL(usb_func_register);
+
+void usb_func_unregister(struct usb_function *f)
+{
+       if (!f)
+               return;
 
-       if (!d->name)
-               d->name = "composite";
-       composite_driver.function =  (char *) d->name;
-       composite_driver.driver.name = d->name;
-       composite = d;
+       /* current func */
+       cdev->func = f;
+       if (f->unbind)
+               f->unbind(cdev->gadget);
 
-       return usb_gadget_register_driver(&composite_driver);
+       cdev->gadget->ep0->driver_data = cdev;
+       usb_func_ep_reset (cdev->gadget, f);
+       list_del(&f->function);
 }
+EXPORT_SYMBOL(usb_func_unregister);
+
+MODULE_AUTHOR("David Brownell, Felipe Balbi, Ragner Magalhaes");
+MODULE_LICENSE("GPL");
+
+static int __init init (void)
+{
+       return usb_gadget_register_driver (&composite_drv);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+       usb_gadget_unregister_driver (&composite_drv);
+}
+module_exit (cleanup);
+
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 82fdb13..3c6a92f 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -5,6 +5,32 @@
 
 #ifdef __KERNEL__
 
+#if    defined(CONFIG_USB_COMPOSITE) || defined(CONFIG_USB_COMPOSITE_MODULE)
+#define USB_COMPOSITE
+#define is_composite() 1
+#else
+#define is_composite() 0
+#endif
+
+#define func_printk(level,f,fmt,args...)       \
+printk(level "%s: " fmt , (f)->name , ## args)
+
+#define INTRF_FUNC2COMPOSITE(f,i)              \
+i + (f)->interface_shift
+
+#define INTRF_COMPOSITE2FUNC(f,i)              \
+i - (f)->interface_shift
+
+#define STRID_FUNC2COMPOSITE(f,i)              \
+i + (f)->stringid_shift
+
+#define STRID_COMPOSITE2FUNC(f,i)              \
+i - (f)->stringid_shift
+
+
+#define MAX_CONFIGS            2
+
+
 /* Support for composite gadgets, built from distinct function drivers.
  * Example:  one offering both network and mass storage functionality.
  */
@@ -17,13 +43,14 @@ struct usb_composite_driver;
  * @name: for diagnostics, identifies the function
  * @strings: tables of strings, keyed by identifiers assigned during bind()
  *     and language IDs provided in control requests
- * @descriptors: table of low/full speed descriptors, using interface and
- *     string identifiers assigned during bind()
- * @hs_descriptors: table of high speed descriptors, using interface and
- *     string identifiers assigned during bind(); or null
+ * @configs: function's configs supported, max two configs.
  * @function: each function is on the usb_composite.function list when the
  *     composite gadget is initialized, and then places itself in the right
  *     position(s) in usb_composite.functions[] during bind()
+ * @driver_data: function's driver data.
+ * @num_conf: number of configurations in the function.
+ * @interface_shift: function's interface shift in the composite driver.
+ * @stringid_shift: function's string id shift in the composite driver.
  * @bind: before the gadget can register, all of its functions bind() to the
  *     available resources including identifiers for strings, interfaces,
  *     and endpoints
@@ -40,97 +67,105 @@ struct usb_composite_driver;
  */
 struct usb_function {
        const char                              *name;
-       const struct usb_gadget_strings         **strings;
-       const struct usb_descriptor_header      **descriptors;
-       const struct usb_descriptor_header      **hs_descriptors;
+       struct usb_gadget_strings               *strings;
+       const struct usb_config_descriptor      *config[MAX_CONFIGS];
 
        struct list_head                        function;
+       /* function's driver */
+       void                    *driver_data;
 
-       /* REVISIT want multi-config at *same* speed too ...
-        * e.g. one has RNDIS, another has CDC-Ethernet
-        * not just high speed versions of each.
-        */
+       u8                      num_conf;
+       u8                      interface_shift;
+       u8                      stringid_shift;
 
-       int                     (*bind)(struct usb_composite_dev *);
-       void                    (*unbind)(struct usb_composite_dev *);
-       int                     (*setup)(struct usb_composite_dev *,
+       int                     (*bind)(struct usb_gadget *);
+       void                    (*unbind)(struct usb_gadget *);
+       int                     (*setup)(struct usb_gadget *,
                                        const struct usb_ctrlrequest *);
-       void                    (*disconnect)(struct usb_composite_dev *);
-       void                    (*suspend)(struct usb_composite_dev *);
-       void                    (*resume)(struct usb_composite_dev *);
+       void                    (*disconnect)(struct usb_gadget *);
+       void                    (*suspend)(struct usb_gadget *);
+       void                    (*resume)(struct usb_gadget *);
 };
 
-/**
- * struct usb_composite_driver -- groups functions into one gadget driver
- * @name: for diagnostics, identifies the gadget driver
- * @dev: descriptor for the device
- * @strings: tables of strings, keyed by identifiers assigned during bind()
- *     and language IDs provided in control requests
- * @functions: each function on this list when usb_composite_init() is called
- *     will be bound and initialized
- * @bind: optionally used for whole-device resources such as string IDs,
- *     updating the device and configuration descriptors before functions
- *     bind(); and updating whole-device policies.
- * @unbind: optionally reverses bind()
- * @setup: optionally used to delegate class and vendor control requests
- */
-struct usb_composite_driver {
-       const char                              *name;
-       const struct usb_device_descriptor      *dev;
-       const struct usb_gadget_strings         **strings;
-
-       /* REVISIT want a general "add more descriptors for config N"
-        * hook; OTG would fall out naturally
-        */
-
-       struct list_head        functions;
+extern int usb_func_register(struct usb_function *);
+extern void usb_func_unregister(struct usb_function *);
 
-       int                     (*bind)(struct usb_composite_dev *);
-       int                     (*unbind)(struct usb_composite_dev *);
-       int                     (*setup)(struct usb_composite_dev *,
-                                       const struct usb_ctrlrequest *);
+/* struct usb_gadget_dev - represents one usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @config: selected config
+ * @func: function of the gadget device
+ */
+struct usb_gadget_dev {
+       spinlock_t                      lock;
+       struct usb_gadget               *gadget;
+       struct usb_request              *req;
+       u8                              config;
 
-       /* REVISIT disconnect(), suspend(), resume() too ?? */
+       struct usb_function     *func;
 };
 
-extern int usb_composite_register(struct usb_composite_driver *);
-
-
 #define        MAX_COMPOSITE_INTERFACES                8       /* max 16 */
 
 /**
- * struct usb_composite_device - represents one composite usb gadget
+ * struct usb_composite_dev - represents one composite usb gadget
  * @gadget: read-only, abstracts the gadget's usb peripheral controller
  * @req: used for control responses; buffer is pre-allocated
- * @dev: device descriptor
- * @config: configuration descriptor; if the device is not configured,
- *     its bConfigurationValue is zero and other fields are ignored
+ * @config: selected config
+ * @func: current function
  */
 struct usb_composite_dev {
        spinlock_t                      lock;
        struct usb_gadget               *gadget;
        struct usb_request              *req;
-       struct usb_device_descriptor    dev;
-       struct usb_config_descriptor    config;
+       u8                              config;
 
-       /* REVISIT need per-function state hook ... maybe a
-        * (void*) returned from bind() and passed to callbacks?
-        * arguably, interface descriptors are part of that...
-        */
+       struct usb_function     *func;
+       struct list_head        functions;
 
-       /* INTERNALS -- not for function drivers */
+       const char              *req_name;
        u8                      next_string_id;
        u8                      next_interface_id;
-       struct usb_function     *current_bind;
        struct usb_function     *interface[MAX_COMPOSITE_INTERFACES];
-
-       struct usb_composite_driver     *driver;
-       struct usb_qualifier_descriptor qual;
 };
 
-/* IDs may be assigned ONLY during function driver bind() */
-extern int usb_composite_string_id(struct usb_composite_dev *c);
-extern int usb_composite_interface_id(struct usb_composite_dev *c);
+/*---------------------------------------------------------------------------*/
+
+static inline struct usb_gadget_dev *
+get_usb_gadget_dev (struct usb_gadget *g)
+{
+       return get_gadget_data (g);
+}
+
+static inline struct usb_request *
+get_usb_gadget_request (struct usb_gadget *g)
+{
+       return get_usb_gadget_dev(g)->req;
+}
+
+static inline void
+set_usb_function (struct usb_gadget *g, struct usb_function *f)
+{
+       get_usb_gadget_dev (g)->func = f;
+}
+
+static inline struct usb_function *
+get_usb_function (struct usb_gadget *g)
+{
+       return get_usb_gadget_dev (g)->func;
+}
+
+static inline void
+set_usb_function_data (struct usb_function *f, void *d)
+{
+       f->driver_data = d;
+}
+
+static inline void *
+get_usb_function_data (struct usb_gadget *g)
+{
+       return get_usb_function (g)->driver_data;
+}
 
 #endif  /* __KERNEL__ */
 

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to