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