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