/* <linux/usb/composite.h> */
#ifndef __LINUX_USB_COMPOSITE_H
#define __LINUX_USB_COMPOSITE_H
#ifdef __KERNEL__
#include <linux/usb/ch9.h>
#include <linux/usb_gadget.h>
/* Support for composite configs, 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 configuration
* @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.
*
* Only one instance of a function may be associated with a given composite
* configuration. Accordingly, data which a function driver needs in order
* to implement any callback can reasonably be static to that driver's module,
* or part of a structure within which the function struct has been embedded.
*/
struct usb_function {
const char *name;
struct usb_gadget_strings **strings;
const struct usb_descriptor_header **descriptors;
const struct usb_descriptor_header **hs_descriptors;
struct list_head function;
void *private_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.
*/
int (*bind)(struct usb_composite_dev *,
struct usb_function *);
void (*unbind)(struct usb_composite_dev *,
struct usb_function *);
int (*setup)(struct usb_composite_dev *,
const struct usb_ctrlrequest *,struct usb_function *);
void (*disconnect)(struct usb_composite_dev *,struct usb_function *);
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
};
/**
* struct usb_composite_driver -- groups functions into one gadget config
* @name: for diagnostics, identifies the driver
* @dev: descriptor for the device
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and language IDs provided in control requests
* @bind: Used to build the list of functions, and to allocate for resources
* shared across the whole device such as string IDs. This can update
* device and configuration descriptors before functions bind().
* @unbind: optionally reverses bind()
* @setup: optionally used to delegate class and vendor control requests
*
* As with any gadget driver, there may be only one per system. Data that a
* composite gadget driver needs to implement any callback can reasonably be
* static to that driver's module, or part of a structure within which the
* driver struct has been embedded.
*
* FIXME device descriptor shouldn't be part of a per-config structure...
*/
struct usb_composite_driver {
const char *name;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
/* REVISIT want a general "add more descriptors for config N"
* hook; OTG should fall out naturally
*/
int (*bind)(struct usb_composite_dev *);
int (*unbind)(struct usb_composite_dev *);
int (*setup)(struct usb_composite_dev *,
const struct usb_ctrlrequest *);
void (*suspend)(struct usb_composite_dev *);
void (*suspend)(struct usb_composite_dev *);
/* REVISIT disconnect(), suspend(), resume() too ?? */
};
extern int usb_composite_register(struct usb_composite_driver *);
extern void usb_composite_unregister(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
* @bufsiz: size of buffer pre-allocated in @req
* @dev: device descriptor
* @functions: each function on this list after driver->bind() returns
* will be bound and initialized
* @config: configuration descriptor; if the device is not configured,
* its bConfigurationValue is zero and other fields are ignored
*
* REVISIT: this represents a composite configuration, and should
* be renamed accordingly...
*/
struct usb_composite_dev {
/* lock protects writes to all fields of this structure */
spinlock_t lock;
struct usb_gadget *gadget;
struct usb_request *req;
unsigned bufsiz;
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...
*/
struct list_head functions;
struct usb_composite_driver *driver;
/* fields in the config descriptor */
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
/* INTERNALS -- not for function drivers */
u8 next_string_id;
u8 next_interface_id;
void *current_bind;
struct usb_function *interface[MAX_COMPOSITE_INTERFACES];
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 void
usb_composite_add_function(struct usb_composite_dev *c, struct usb_function *f)
{
list_add_tail(&f->function, &c->functions);
}
#endif /* __KERNEL__ */
#endif /* __LINUX_USB_COMPOSITE_H */
/*
* composite.c - infrastructure for Composite USB Gadgets
*
* Copyright (C) 2006-2007 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/usb/g_composite.h>
/*
* The code in this file is utility code, used to build a gadget driver
* from one or more "function" drivers and a "usb_composite_driver" to
* glue them together along with the relevant device-wide data.
*/
#define DBG(comp, fmt, args...) \
dev_dbg(&(comp)->gadget->dev , fmt , ## args)
#define VDBG(comp, fmt, args...) \
dev_vdbg(&(comp)->gadget->dev , fmt , ## args)
#define ERROR(comp, fmt, args...) \
dev_err(&(comp)->gadget->dev , fmt , ## args)
#define WARN(comp, fmt, args...) \
dev_warn(&(comp)->gadget->dev , fmt , ## args)
#define INFO(comp, fmt, args...) \
dev_info(&(comp)->gadget->dev , fmt , ## args)
/* big enough to hold our biggest descriptor */
#define USB_BUFSIZ 512
static struct usb_composite_driver *composite;
static struct usb_composite_dev *cdev;
/* Some systems will need runtime overrides for the product identifers
* published in the device descriptor, either numbers or strings or both.
* String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
#if 0
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 string");
#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).
*
* Note that the configuration numbers are *NOT* related to how many configs
* a device has ... some hardware places restrictions on config numbers.
* So having a single configuration, number 3, would be fine.
*
* REVISIT we do need to lift this restriction, at least for RNDIS.
* For PXA compat, stick to config numbers 1, 2, and 3; for SH, just 1.
*/
#define CONFIG_NUMBER 1
static int
config_buf(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;
INFO(cdev, "config buf\n");
if (gadget_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;
c->iConfiguration = cdev->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | cdev->bmAttributes;
c->bMaxPower = cdev->bMaxPower;
INFO(cdev, "device config\n");
/* REVISIT some configurations might need other descriptors,
* independent of the interfaces they implement ... notably
* OTG descriptors.
*/
/* add each function's descriptors */
INFO(cdev, "speed: %d\n",hs);
list_for_each_entry(f, &cdev->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;
INFO(cdev, "interface config\n");
}
len = next - buf;
c->wTotalLength = cpu_to_le16(len);
return len;
}
/*-------------------------------------------------------------------------*/
static void composite_reset_config(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, &cdev->functions, function) {
result = f->setup(cdev, req,f);
if (result < 0)
DBG(cdev, "reset function %s --> %d\n",
f->name, result);
}
cdev->config.bConfigurationValue = 0;
}
static int
composite_set_config(const struct usb_ctrlrequest *ctrl, unsigned number)
{
int result = 0;
int 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(&req);
switch (number) {
default:
result = -EINVAL;
req.wValue = cpu_to_le16(0);
/* FALLTHROUGH */
case 0:
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
f->setup(cdev, ctrl, f);
}
usb_gadget_vbus_draw(gadget, 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, f);
if (result < 0) {
DBG(cdev, "interface %d/%s alt 0--> %d\n",
tmp, f->name, result);
(void) composite_set_config(ctrl, 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(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(
struct usb_gadget_strings **sp,
void *buf,
u16 language,
int id
)
{
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;
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, &cdev->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, &cdev->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);
int tmp,result;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion.
*/
req->zero = 0;
req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
INFO(cdev,
"control req %02x.%02x v %04x i %04x l %d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
if((ctrl->bRequestType&USB_TYPE_MASK) != USB_TYPE_STANDARD)
{
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++)
{
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
result = f->setup(cdev, ctrl, f);
if(result > 0)
value = result;
value = min(w_length, (u16) value);
}
((char *)(req->buf))[value]=0;
INFO(cdev, "Result Len: %d %s\n",value,&req->buf[2]);
}
else
{
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 (!gadget_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 (!gadget_is_dualspeed(gadget))
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
INFO(cdev, "Get Config Descriptor\n");
/* one config ... so it must always be index 0 */
if (w_value & 0xff)
break;
value = config_buf(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(ctrl, 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, cdev->interface[w_index]);
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, cdev->interface[w_index]);
spin_unlock(&cdev->lock);
value = min(w_length, (u16) 1);
break;
default:
unknown:
VDBG(cdev,
"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 % gadget->ep0->maxpacket) == 0);
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (value < 0) {
INFO(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(&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->functions, function) {
if (f->unbind)
f->unbind(cdev, f);
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);
composite = NULL;
}
static void
string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
{
struct usb_string *str = tab->strings;
for (str = tab->strings; str->s; str++) {
if (str->id == id) {
str->s = s;
return;
}
}
}
static void
string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
{
while (*tab) {
string_override_one(*tab, id, s);
tab++;
}
}
static int __init
composite_bind(struct usb_gadget *gadget)
{
struct usb_function *f;
int status = -ENOMEM;
if (cdev)
return -EBUSY;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->functions);
/* 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->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero */
usb_ep_autoconfig_reset(gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
cdev->current_bind = gadget;
status = composite->bind(cdev);
if (status < 0)
goto fail;
cdev->current_bind = NULL;
cdev->dev = *composite->dev;
cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
/*if (idVendor)
cdev->dev.idVendor = idVendor;
if (idProduct)
cdev->dev.idProduct = idProduct;
if (bcdDevice)
cdev->dev.bcdDevice = bcdDevice;
if (cdev->dev.iManufacturer && iManufacturer)
string_override(composite->strings,
cdev->dev.iManufacturer, iManufacturer);
if (cdev->dev.iProduct && iProduct)
string_override(composite->strings,
cdev->dev.iProduct, iProduct);
if (cdev->dev.iSerialNumber && iSerialNumber)
string_override(composite->strings,
cdev->dev.iSerialNumber, iSerialNumber);*/
/* 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.
*/
list_for_each_entry(f, &cdev->functions, function) {
if (!f->bind || !f->setup) {
status = -EINVAL;
goto fail;
}
cdev->current_bind = f;
status = f->bind(cdev, f);
if (status < 0)
goto fail;
if ((gadget_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 (gadget_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;
}
/*
* usb_composite_string_id() is called from bind() callbacks to allocate
* a new string ID. The function (or composite gadget) driver will then
* store that ID in the appropriate descriptors and string table.
*/
int usb_composite_string_id(struct usb_composite_dev *cdev)
{
if (cdev->current_bind && cdev->next_string_id < 254) {
/* string id 0 is reserved */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
/*
* usb_composite_interface_id() is called from bind() callbacks to
* allocate a new interface ID. The function (or composite gadget)
* driver will then store that ID in interface, association, cdc union,
* and other appropriate descriptors.
*/
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_function *f;
DBG(cdev, "suspend\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->suspend)
continue;
f->suspend(cdev);
}
}
static void
composite_resume(struct usb_gadget *gadget)
{
struct usb_function *f;
DBG(cdev, "resume\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->resume)
continue;
f->resume(cdev);
}
}
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_FULL,
.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)
{
if (!d || !d->dev || !d->bind || composite)
return -EINVAL;
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);
}
void usb_composite_unregister(struct usb_composite_driver *d)
{
if (composite != d)
return;
usb_gadget_unregister_driver(&composite_driver);
}
/*
* composite.c - infrastructure for Composite USB Gadgets
*
* Copyright (C) 2006-2007 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/usb/g_composite.h>
/*
* The code in this file is utility code, used to build a gadget driver
* from one or more "function" drivers and a "usb_composite_driver" to
* glue them together along with the relevant device-wide data.
*/
#define DBG(comp, fmt, args...) \
dev_dbg(&(comp)->gadget->dev , fmt , ## args)
#define VDBG(comp, fmt, args...) \
dev_vdbg(&(comp)->gadget->dev , fmt , ## args)
#define ERROR(comp, fmt, args...) \
dev_err(&(comp)->gadget->dev , fmt , ## args)
#define WARN(comp, fmt, args...) \
dev_warn(&(comp)->gadget->dev , fmt , ## args)
#define INFO(comp, fmt, args...) \
dev_info(&(comp)->gadget->dev , fmt , ## args)
/* big enough to hold our biggest descriptor */
#define USB_BUFSIZ 512
static struct usb_composite_driver *composite;
static struct usb_composite_dev *cdev;
/* Some systems will need runtime overrides for the product identifers
* published in the device descriptor, either numbers or strings or both.
* String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
#if 0
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 string");
#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).
*
* Note that the configuration numbers are *NOT* related to how many configs
* a device has ... some hardware places restrictions on config numbers.
* So having a single configuration, number 3, would be fine.
*
* REVISIT we do need to lift this restriction, at least for RNDIS.
* For PXA compat, stick to config numbers 1, 2, and 3; for SH, just 1.
*/
#define CONFIG_NUMBER 1
static int
config_buf(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;
INFO(cdev, "config buf\n");
if (gadget_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;
c->iConfiguration = cdev->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | cdev->bmAttributes;
c->bMaxPower = cdev->bMaxPower;
INFO(cdev, "device config\n");
/* REVISIT some configurations might need other descriptors,
* independent of the interfaces they implement ... notably
* OTG descriptors.
*/
/* add each function's descriptors */
INFO(cdev, "speed: %d\n",hs);
list_for_each_entry(f, &cdev->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;
INFO(cdev, "interface config\n");
}
len = next - buf;
c->wTotalLength = cpu_to_le16(len);
return len;
}
/*-------------------------------------------------------------------------*/
static void composite_reset_config(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, &cdev->functions, function) {
result = f->setup(cdev, req,f);
if (result < 0)
DBG(cdev, "reset function %s --> %d\n",
f->name, result);
}
cdev->config.bConfigurationValue = 0;
}
static int
composite_set_config(const struct usb_ctrlrequest *ctrl, unsigned number)
{
int result = 0;
int 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(&req);
switch (number) {
default:
result = -EINVAL;
req.wValue = cpu_to_le16(0);
/* FALLTHROUGH */
case 0:
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
f->setup(cdev, ctrl, f);
}
usb_gadget_vbus_draw(gadget, 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, f);
if (result < 0) {
DBG(cdev, "interface %d/%s alt 0--> %d\n",
tmp, f->name, result);
(void) composite_set_config(ctrl, 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(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(
struct usb_gadget_strings **sp,
void *buf,
u16 language,
int id
)
{
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;
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, &cdev->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, &cdev->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);
int tmp,result;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion.
*/
req->zero = 0;
req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
INFO(cdev,
"control req %02x.%02x v %04x i %04x l %d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
if((ctrl->bRequestType&USB_TYPE_MASK) != USB_TYPE_STANDARD)
{
for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++)
{
struct usb_function *f = cdev->interface[tmp];
if (!f)
continue;
result = f->setup(cdev, ctrl, f);
if(result > 0)
value = result;
value = min(w_length, (u16) value);
}
((char *)(req->buf))[value]=0;
INFO(cdev, "Result Len: %d %s\n",value,&req->buf[2]);
}
else
{
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 (!gadget_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 (!gadget_is_dualspeed(gadget))
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
INFO(cdev, "Get Config Descriptor\n");
/* one config ... so it must always be index 0 */
if (w_value & 0xff)
break;
value = config_buf(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(ctrl, 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, cdev->interface[w_index]);
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, cdev->interface[w_index]);
spin_unlock(&cdev->lock);
value = min(w_length, (u16) 1);
break;
default:
unknown:
VDBG(cdev,
"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 % gadget->ep0->maxpacket) == 0);
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (value < 0) {
INFO(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(&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->functions, function) {
if (f->unbind)
f->unbind(cdev, f);
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);
composite = NULL;
}
static void
string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
{
struct usb_string *str = tab->strings;
for (str = tab->strings; str->s; str++) {
if (str->id == id) {
str->s = s;
return;
}
}
}
static void
string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
{
while (*tab) {
string_override_one(*tab, id, s);
tab++;
}
}
static int __init
composite_bind(struct usb_gadget *gadget)
{
struct usb_function *f;
int status = -ENOMEM;
if (cdev)
return -EBUSY;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->functions);
/* 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->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero */
usb_ep_autoconfig_reset(gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
cdev->current_bind = gadget;
status = composite->bind(cdev);
if (status < 0)
goto fail;
cdev->current_bind = NULL;
cdev->dev = *composite->dev;
cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
/*if (idVendor)
cdev->dev.idVendor = idVendor;
if (idProduct)
cdev->dev.idProduct = idProduct;
if (bcdDevice)
cdev->dev.bcdDevice = bcdDevice;
if (cdev->dev.iManufacturer && iManufacturer)
string_override(composite->strings,
cdev->dev.iManufacturer, iManufacturer);
if (cdev->dev.iProduct && iProduct)
string_override(composite->strings,
cdev->dev.iProduct, iProduct);
if (cdev->dev.iSerialNumber && iSerialNumber)
string_override(composite->strings,
cdev->dev.iSerialNumber, iSerialNumber);*/
/* 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.
*/
list_for_each_entry(f, &cdev->functions, function) {
if (!f->bind || !f->setup) {
status = -EINVAL;
goto fail;
}
cdev->current_bind = f;
status = f->bind(cdev, f);
if (status < 0)
goto fail;
if ((gadget_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 (gadget_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;
}
/*
* usb_composite_string_id() is called from bind() callbacks to allocate
* a new string ID. The function (or composite gadget) driver will then
* store that ID in the appropriate descriptors and string table.
*/
int usb_composite_string_id(struct usb_composite_dev *cdev)
{
if (cdev->current_bind && cdev->next_string_id < 254) {
/* string id 0 is reserved */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
/*
* usb_composite_interface_id() is called from bind() callbacks to
* allocate a new interface ID. The function (or composite gadget)
* driver will then store that ID in interface, association, cdc union,
* and other appropriate descriptors.
*/
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_function *f;
DBG(cdev, "suspend\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->suspend)
continue;
f->suspend(cdev);
}
}
static void
composite_resume(struct usb_gadget *gadget)
{
struct usb_function *f;
DBG(cdev, "resume\n");
/* revisit -- iterate cdev->interface? */
list_for_each_entry(f, &cdev->functions, function) {
if (!f->resume)
continue;
f->resume(cdev);
}
}
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_FULL,
.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)
{
if (!d || !d->dev || !d->bind || composite)
return -EINVAL;
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);
}
void usb_composite_unregister(struct usb_composite_driver *d)
{
if (composite != d)
return;
usb_gadget_unregister_driver(&composite_driver);
}