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/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel