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