diff options
Diffstat (limited to 'drivers/usb/gadget/serial.c')
-rw-r--r-- | drivers/usb/gadget/serial.c | 263 |
1 files changed, 147 insertions, 116 deletions
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 98a501b891..b3314a356a 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -1,6 +1,19 @@ +/* + * serial.c -- USB gadget serial driver + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * 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 that License or (at your option) any later version. + */ + #include <common.h> #include <errno.h> #include <init.h> +#include <linux/err.h> #include <usb/ch9.h> #include <usb/gadget.h> #include <usb/composite.h> @@ -8,6 +21,8 @@ #include <asm/byteorder.h> #include "u_serial.h" +#include "gadget_chips.h" + /* Defines */ @@ -17,6 +32,9 @@ #define GS_LONG_NAME "Gadget Serial" #define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR +/*-------------------------------------------------------------------------*/ +static struct usb_composite_overwrite coverwrite; + /* Thanks to NetChip Technologies for donating this product ID. * * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -29,15 +47,12 @@ /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 - -static char manufacturer[50]; +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, { } /* end of list */ }; @@ -52,30 +67,6 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static int use_acm = 1; -#ifdef HAVE_OBEX -static int use_obex = 0; -#endif -static unsigned n_ports = 1; - -static int serial_bind_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) { - if (use_acm) - status = acm_bind_config(c, i); -#ifdef HAVE_OBEX - else if (use_obex) - status = obex_bind_config(c, i); -#endif - else - status = gser_bind_config(c, i); - } - return status; -} - static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, @@ -86,97 +77,164 @@ static struct usb_device_descriptor device_desc = { /* .bMaxPacketSize0 = f(hardware) */ .idVendor = cpu_to_le16(GS_VENDOR_ID), /* .idProduct = f(use_acm) */ - /* .bcdDevice = f(hardware) */ + .bcdDevice = cpu_to_le16(GS_VERSION_NUM), /* .iManufacturer = DYNAMIC */ /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, }; +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +/* Module */ +MODULE_DESCRIPTION(GS_VERSION_NAME); +MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); + +static bool use_acm = true; + +static bool use_obex = false; + +static unsigned n_ports = 1; + +/*-------------------------------------------------------------------------*/ + static struct usb_configuration serial_config_driver = { /* .label = f(use_acm) */ - .bind = serial_bind_config, /* .bConfigurationValue = f(use_acm) */ /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; -static int gs_bind(struct usb_composite_dev *cdev) +static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS]; +static struct usb_function *f_serial[MAX_U_SERIAL_PORTS]; + +static int serial_register_ports(struct usb_composite_dev *cdev, + struct usb_configuration *c, const char *f_name) { - int gcnum; - struct usb_gadget *gadget = cdev->gadget; - int status; + int i; + int ret; + + ret = usb_add_config_only(cdev, c); + if (ret) + goto out; + + for (i = 0; i < n_ports; i++) { + + fi_serial[i] = usb_get_function_instance(f_name); + if (IS_ERR(fi_serial[i])) { + ret = PTR_ERR(fi_serial[i]); + goto fail; + } + + f_serial[i] = usb_get_function(fi_serial[i]); + if (IS_ERR(f_serial[i])) { + ret = PTR_ERR(f_serial[i]); + goto err_get_func; + } + + ret = usb_add_function(c, f_serial[i]); + if (ret) + goto err_add_func; + } - status = gserial_setup(cdev->gadget, n_ports); - if (status < 0) - return status; + return 0; - /* Allocate string descriptor numbers ... note that string - * contents can be overridden by the composite_dev glue. - */ +err_add_func: + usb_put_function(f_serial[i]); +err_get_func: + usb_put_function_instance(fi_serial[i]); - /* device description: manufacturer, product */ - sprintf(manufacturer, "barebox with %s", - gadget->name); - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - - device_desc.iManufacturer = status; +fail: + i--; + while (i >= 0) { + usb_remove_function(c, f_serial[i]); + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + i--; + } +out: + return ret; +} - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_PRODUCT_IDX].id = status; +static int __init gs_bind(struct usb_composite_dev *cdev) +{ + int status; - device_desc.iProduct = status; + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ - /* config description */ - status = usb_string_id(cdev); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - strings_dev[STRING_DESCRIPTION_IDX].id = status; - + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + status = strings_dev[STRING_DESCRIPTION_IDX].id; serial_config_driver.iConfiguration = status; - /* set up other descriptors */ -// gcnum = usb_gadget_controller_number(gadget); - gcnum = 0x19; - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); - else { - /* this is so simple (for now, no altsettings) that it - * SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrcognized controllers -- don't panic. - * - * things like configuration and altsetting numbering - * can need hardware-specific attention though. - */ - pr_warning("gs_bind: controller '%s' not recognized\n", - gadget->name); - device_desc.bcdDevice = - cpu_to_le16(GS_VERSION_NUM | 0x0099); + if (gadget_is_otg(cdev->gadget)) { + serial_config_driver.descriptors = otg_desc; + serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } /* register our configuration */ - status = usb_add_config(cdev, &serial_config_driver); + if (use_acm) { + status = serial_register_ports(cdev, &serial_config_driver, + "acm"); + usb_ep_autoconfig_reset(cdev->gadget); + } else if (use_obex) + status = serial_register_ports(cdev, &serial_config_driver, + "obex"); + else { + status = serial_register_ports(cdev, &serial_config_driver, + "gser"); + } if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s\n", GS_VERSION_NAME); return 0; fail: -// gserial_cleanup(); return status; } +static int gs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + for (i = 0; i < n_ports; i++) { + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + } + return 0; +} + static struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, .bind = gs_bind, + .unbind = gs_unbind, }; int usb_serial_register(struct usb_serial_pdata *pdata) @@ -185,32 +243,15 @@ int usb_serial_register(struct usb_serial_pdata *pdata) * but neither of these product IDs was defined that way. */ + use_acm = pdata->acm; + /* * PXA CPU suffer a silicon bug which prevents them from being a * compound device, forbiding the ACM configurations. */ -#ifdef CONFIG_ARCH_PXA2XX - use_acm = 0; -#endif - switch (pdata->mode) { - case 1: -#ifdef HAVE_OBEX - use_obex = 1; -#endif - use_acm = 0; - break; - case 2: -#ifdef HAVE_OBEX - use_obex = 1; -#endif + + if (IS_ENABLED(CONFIG_ARCH_PXA2XX)) use_acm = 0; - break; - default: -#ifdef HAVE_OBEX - use_obex = 0; -#endif - use_acm = 1; - } if (use_acm) { serial_config_driver.label = "CDC ACM config"; @@ -218,32 +259,22 @@ int usb_serial_register(struct usb_serial_pdata *pdata) device_desc.bDeviceClass = USB_CLASS_COMM; device_desc.idProduct = cpu_to_le16(GS_CDC_PRODUCT_ID); - } -#ifdef HAVE_OBEX - else if (use_obex) { - serial_config_driver.label = "CDC OBEX config"; - serial_config_driver.bConfigurationValue = 3; - device_desc.bDeviceClass = USB_CLASS_COMM; - device_desc.idProduct = - cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID); - } -#endif - else { + } else { serial_config_driver.label = "Generic Serial config"; serial_config_driver.bConfigurationValue = 1; device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; device_desc.idProduct = cpu_to_le16(GS_PRODUCT_ID); } - strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + if (pdata->idVendor) device_desc.idVendor = pdata->idVendor; if (pdata->idProduct) device_desc.idProduct = pdata->idProduct; - strings_dev[STRING_MANUFACTURER_IDX].s = pdata->manufacturer; - strings_dev[STRING_PRODUCT_IDX].s = pdata->productname; + strings_dev[USB_GADGET_MANUFACTURER_IDX].s = pdata->manufacturer; + strings_dev[USB_GADGET_PRODUCT_IDX].s = pdata->productname; - return usb_composite_register(&gserial_driver); + return usb_composite_probe(&gserial_driver); } void usb_serial_unregister(void) |