From: Ragner Magalhaes <[EMAIL PROTECTED]>

Add <linux/usb/composite.h> interfaces for composite gadget drivers:

    -   struct usb_function ... grouping one or more interfaces into
                a function that will often be managed as one unit;

    -   struct usb_composite_driver ... grouping one or more such
                functions into a gadget driver.

    -   struct usb_composite_dev ... what's managed by the composite
        driver.

There's an implementation library behind the "composite_driver" which
exposes the "composite_dev".

Separate patches will be updating some gadget drivers to use this
new interface.


Signed-off-by: David Brownell <[EMAIL PROTECTED]>
---

 drivers/usb/gadget/composite.c |  679 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/usbstring.c |    2 
 include/linux/usb/composite.h  |  137 ++++++++
 include/linux/usb_gadget.h     |    2 
 4 files changed, 818 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
new file mode 100644
index 0000000..495c660
--- /dev/null
+++ b/drivers/usb/gadget/composite.c
@@ -0,0 +1,679 @@
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/composite.h>
+
+
+#define xprintk(d,level,fmt,args...) \
+       dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+       xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+       do { } while(0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG   DBG
+#else
+#define VDBG(dev,fmt,args...) \
+       do { } while(0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+       xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+       xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+       xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ     512
+
+
+static struct usb_composite_driver     *composite;
+
+static inline int is_dualspeed(struct usb_gadget *g)
+{
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+       return g->is_dualspeed;
+#else
+       return 0;
+#endif
+}
+
+static inline int is_otg(struct usb_gadget *g)
+{
+#ifdef CONFIG_USB_OTG
+       return g->is_otg;
+#else
+       return 0;
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* 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).
+ */
+
+#define        CONFIG_NUMBER   1
+
+static int
+config_buf(struct usb_composite_dev *cdev, void *buf, u8 type)
+{
+       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;
+
+       if (is_dualspeed(cdev->gadget)) {
+               hs = (cdev->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->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.
+        */
+
+       /* add each function's descriptors */
+       list_for_each_entry (f, &composite->functions, function) {
+               int                     status;
+
+               status = usb_descriptor_fillbuf(next, len,
+                               hs ? f->hs_descriptors : f->descriptors);
+               if (status < 0)
+                       return status;
+               len -= status;
+               next += status;
+       }
+
+       len = next - buf;
+       c->wTotalLength = cpu_to_le16(len);
+       return len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_reset_config(
+       struct usb_composite_dev        *cdev,
+       struct usb_ctrlrequest          *req
+) {
+       struct usb_function             *f;
+       int                             result;
+
+       DBG(cdev, "reset config\n");
+
+       req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+       req->bRequest = USB_REQ_SET_CONFIGURATION;
+
+       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);
+       }
+       cdev->config.bConfigurationValue = 0;
+}
+
+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;
+
+       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);
+
+       switch (number) {
+       default:
+               result = -EINVAL;
+               /* FALLTHROUGH */
+       case 0:
+               usb_gadget_vbus_draw(gadget, is_otg(gadget) ? 8 : 100);
+               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;
+                       }
+               }
+
+               cdev->config.bConfigurationValue = number;
+               usb_gadget_vbus_draw(gadget, 2 * cdev->config.bMaxPower);
+               break;
+       }
+
+       INFO(cdev, "%s speed config #%d\n",
+               ({ char *speed;
+               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);
+
+       return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_collect_langs(const struct usb_gadget_strings **sp, __le16 *buf)
+{
+       const struct usb_gadget_strings *s;
+       u16                             language;
+       __le16                          *tmp;
+
+       while (*sp) {
+               s = *sp;
+               language = cpu_to_le16(s->language);
+               for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+                       if (*tmp == language)
+                               goto repeat;
+               }
+               *tmp++ = language;
+repeat:
+               sp++;
+       }
+}
+
+static int composite_check_string(
+       const struct usb_gadget_strings **sp,
+       void                            *buf,
+       u16                             language,
+       int                             id
+)
+{
+       const struct usb_gadget_strings *s;
+       int                             value;
+
+       while (*sp) {
+               s = *sp++;
+               if (s->language != language)
+                       continue;
+               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)
+{
+       struct usb_function             *f;
+       int                             len;
+
+       /* 0 == report all available language codes */
+       if (id == 0) {
+               struct usb_string_descriptor    *s = buf;
+               const struct usb_gadget_strings **sp;
+
+               memset(s, 0, 256);
+               s->bDescriptorType = USB_DT_STRING;
+
+               sp = composite->strings;
+               if (sp)
+                       composite_collect_langs(sp, s->wData);
+
+               list_for_each_entry (f, &composite->functions, function) {
+                       sp = f->strings;
+                       if (sp)
+                               composite_collect_langs(sp, s->wData);
+               }
+
+               for (len = 0; s->wData[len] && len <= 126; len++)
+                       continue;
+               if (!len)
+                       return -EINVAL;
+
+               s->bLength = 2 * (len + 1);
+               return s->bLength;
+       }
+
+       /* 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) {
+               if (!f->strings)
+                       continue;
+               len = composite_check_string(f->strings, buf, language, id);
+               if (len > 0)
+                       return len;
+       }
+       return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request 
*req)
+{
+       if (req->status || req->actual != req->length)
+               DBG((struct usb_composite_dev *) ep->driver_data,
+                               "setup complete --> %d, %d/%d\n",
+                               req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+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;
+       switch (ctrl->bRequest) {
+
+       case USB_REQ_GET_DESCRIPTOR:
+               if (ctrl->bRequestType != USB_DIR_IN)
+                       goto unknown;
+               switch (w_value >> 8) {
+
+               case USB_DT_DEVICE:
+                       value = min(w_length, (u16) sizeof cdev->dev);
+                       memcpy(req->buf, &cdev->dev, value);
+                       break;
+               case USB_DT_DEVICE_QUALIFIER:
+                       if (!is_dualspeed(gadget))
+                               break;
+                       value = min(w_length, (u16) sizeof cdev->qual);
+                       memcpy(req->buf, &cdev->qual, value);
+                       break;
+
+               case USB_DT_OTHER_SPEED_CONFIG:
+                       if (!is_dualspeed(gadget))
+                               break;
+                       // FALLTHROUGH
+
+               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);
+                       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);
+                       break;
+               }
+               break;
+
+       /* currently one config, two speeds */
+       case USB_REQ_SET_CONFIGURATION:
+               if (ctrl->bRequestType != 0)
+                       goto unknown;
+               if (gadget->a_hnp_support)
+                       DBG(cdev, "HNP available\n");
+               else if (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);
+               break;
+       case USB_REQ_GET_CONFIGURATION:
+               if (ctrl->bRequestType != USB_DIR_IN)
+                       goto unknown;
+               *(u8 *)req->buf = cdev->config.bConfigurationValue;
+               value = min(w_length, (u16) 1);
+               break;
+
+       /* function drivers must handle get/set altsetting */
+       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);
+               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);
+               break;
+       default:
+unknown:
+               VDBG(dev,
+                       "unknown control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer before status phase? */
+       if (value >= 0) {
+               req->length = value;
+               req->zero = value < w_length;
+               value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0) {
+                       DBG(cdev, "ep_queue --> %d\n", value);
+                       req->status = 0;
+                       composite_setup_complete(gadget->ep0, req);
+               }
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static void
+composite_disconnect(struct usb_gadget *gadget)
+{
+       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
+       unsigned long                   flags;
+       struct usb_ctrlrequest  req;
+
+       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);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void /* __init_or_exit */
+composite_unbind(struct usb_gadget *gadget)
+{
+       struct usb_composite_dev        *cdev = get_gadget_data(gadget);
+       struct usb_function             *f;
+
+       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);
+       if (cdev->req) {
+               kfree(cdev->req->buf);
+               usb_ep_free_request(gadget->ep0, cdev->req);
+       }
+       kfree(cdev);
+       set_gadget_data(gadget, NULL);
+}
+
+static int __init
+composite_bind(struct usb_gadget *gadget)
+{
+       struct usb_composite_dev        *cdev;
+       struct usb_function             *f;
+       int                             status = -ENOMEM;
+
+       cdev = kzalloc(sizeof *cdev, SLAB_KERNEL);
+       if (!cdev)
+               return status;
+
+       spin_lock_init(&cdev->lock);
+       cdev->gadget = gadget;
+       set_gadget_data(gadget, cdev);
+
+       /* preallocate control response and buffer */
+       cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+       if (!cdev->req)
+               goto fail;
+       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;
+                       goto fail;
+               }
+       }
+       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;
+
+               cdev->qual.bcdUSB = cdev->dev.bcdUSB;
+               cdev->qual.bDeviceClass = cdev->dev.bDeviceClass;
+               cdev->qual.bDeviceProtocol = cdev->dev.bDeviceProtocol;
+
+               /* assume ep0 uses the same value for both speeds ... */
+               cdev->qual.bMaxPacketSize0 = cdev->dev.bMaxPacketSize0;
+
+               cdev->qual.bNumConfigurations = cdev->dev.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_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);
+       }
+}
+
+static void
+composite_resume(struct usb_gadget *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);
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver composite_driver = {
+       .speed          = USB_SPEED_HIGH,
+
+       .bind           = composite_bind,
+       .unbind         = __exit_p(composite_unbind),
+
+       .setup          = composite_setup,
+       .disconnect     = composite_disconnect,
+
+       .suspend        = composite_suspend,
+       .resume         = composite_resume,
+
+       .driver = {
+               .owner          = THIS_MODULE,
+       },
+};
+
+int usb_composite_register(struct usb_composite_driver *d)
+{
+       struct usb_function     *f;
+
+       if (!d || !d->dev || composite)
+               return -EINVAL;
+
+       list_for_each_entry (f, &d->functions, function) {
+               if (!f->bind || !f->setup)
+                       return -EINVAL;
+
+               /* REVISIT erm, hs_descriptors may be unknown here? */
+               if (!f->hs_descriptors) {
+                       composite_driver.speed = USB_SPEED_FULL;
+                       break;
+               }
+       }
+
+       if (!d->name)
+               d->name = "composite";
+       composite_driver.function =  (char *) d->name;
+       composite_driver.driver.name = d->name;
+       composite = d;
+
+       return usb_gadget_register_driver(&composite_driver);
+}
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 3459ea6..9de6f2e 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -102,7 +102,7 @@ fail:
  * characters (which are also widely used in C strings).
  */
 int
-usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 *buf)
 {
        struct usb_string       *s;
        int                     len;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
new file mode 100644
index 0000000..82fdb13
--- /dev/null
+++ b/include/linux/usb/composite.h
@@ -0,0 +1,137 @@
+/* <linux/usb/composite.h> */
+
+#ifndef        __LINUX_USB_COMPOSITE_H
+#define        __LINUX_USB_COMPOSITE_H
+
+#ifdef __KERNEL__
+
+/* Support for composite gadgets, built from distinct function drivers.
+ * Example:  one offering both network and mass storage functionality.
+ */
+
+struct usb_composite_dev;
+struct usb_composite_driver;
+
+/**
+ * struct usb_function - describes one function of a composite device
+ * @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
+ * @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()
+ * @bind: before the gadget can register, all of its functions bind() to the
+ *     available resources including identifiers for strings, interfaces,
+ *     and endpoints
+ * @unbind: before the gadget can unregister, all of its functions unbind()
+ *     the resources they have allocated.
+ * @setup: used for SET_INTERFACE, SET_CONFIGURATION, and interface-specific
+ *     control requests
+ * @disconnect: used to notify functions when the host has disconnected
+ * @suspend: notifies functions when the host stops sending USB traffic
+ * @disconnect: notifies functions when the host reactivates the USB link
+ *
+ * A single USB function uses one or more interfaces, and supports dual speed
+ * operation on appropriate hardware.
+ */
+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 list_head                        function;
+
+       /* REVISIT want multi-config at *same* speed too ...
+        * e.g. one has RNDIS, another has CDC-Ethernet
+        * not just high speed versions of each.
+        */
+
+       int                     (*bind)(struct usb_composite_dev *);
+       void                    (*unbind)(struct usb_composite_dev *);
+       int                     (*setup)(struct usb_composite_dev *,
+                                       const struct usb_ctrlrequest *);
+       void                    (*disconnect)(struct usb_composite_dev *);
+       void                    (*suspend)(struct usb_composite_dev *);
+       void                    (*resume)(struct usb_composite_dev *);
+};
+
+/**
+ * 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;
+
+       int                     (*bind)(struct usb_composite_dev *);
+       int                     (*unbind)(struct usb_composite_dev *);
+       int                     (*setup)(struct usb_composite_dev *,
+                                       const struct usb_ctrlrequest *);
+
+       /* REVISIT disconnect(), suspend(), resume() too ?? */
+};
+
+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
+ * @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
+ */
+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;
+
+       /* REVISIT need per-function state hook ... maybe a
+        * (void*) returned from bind() and passed to callbacks?
+        * arguably, interface descriptors are part of that...
+        */
+
+       /* INTERNALS -- not for function drivers */
+       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);
+
+#endif  /* __KERNEL__ */
+
+#endif /* __LINUX_USB_COMPOSITE_H */
diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
index e17186d..f1c9b73 100644
--- a/include/linux/usb_gadget.h
+++ b/include/linux/usb_gadget.h
@@ -853,7 +853,7 @@ struct usb_gadget_strings {
 };
 
 /* put descriptor for string with that id into buf (buflen >= 256) */
-int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf);
+int usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 
*buf);
 
 /*-------------------------------------------------------------------------*/
 

-------------------------------------------------------------------------
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