diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 6e784d2..117d0c7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -118,10 +118,10 @@ config USB_AMD5536UDC config USB_GADGET_ATMEL_USBA boolean "Atmel USBA" select USB_GADGET_DUALSPEED - depends on AVR32 || ARCH_AT91CAP9 + depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL help USBA is the integrated high-speed USB Device controller on - the AT32AP700x and AT91CAP9 processors from Atmel. + the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. config USB_ATMEL_USBA tristate @@ -172,7 +172,7 @@ config USB_NET2280 default USB_GADGET select USB_GADGET_SELECTED -config USB_GADGET_PXA2XX +config USB_GADGET_PXA25X boolean "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX help @@ -184,19 +184,19 @@ config USB_GADGET_PXA2XX zero (for control transfers). Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa2xx_udc" and force all + dynamically linked module called "pxa25x_udc" and force all gadget drivers to also be dynamically linked. -config USB_PXA2XX +config USB_PXA25X tristate - depends on USB_GADGET_PXA2XX + depends on USB_GADGET_PXA25X default USB_GADGET select USB_GADGET_SELECTED # if there's only one gadget driver, using only two bulk endpoints, # don't waste memory for the other endpoints -config USB_PXA2XX_SMALL - depends on USB_GADGET_PXA2XX +config USB_PXA25X_SMALL + depends on USB_GADGET_PXA25X bool default n if USB_ETH_RNDIS default y if USB_ZERO @@ -284,6 +284,16 @@ config USB_LH7A40X default USB_GADGET select USB_GADGET_SELECTED +# built in ../musb along with host support +config USB_GADGET_MUSB_HDRC + boolean "Inventra HDRC USB Peripheral (TI, ...)" + depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) + select USB_GADGET_DUALSPEED + select USB_GADGET_SELECTED + help + This OTG-capable silicon IP is used in dual designs including + the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. + config USB_GADGET_OMAP boolean "OMAP USB Device Controller" depends on ARCH_OMAP @@ -355,6 +365,20 @@ config USB_AT91 depends on USB_GADGET_AT91 default USB_GADGET +config USB_GADGET_SA1100 + boolean "SA1100 USB Device Port" + depends on ARCH_SA1100 + help + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "sa1100_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_SA1100 + tristate + depends on USB_GADGET_SA1100 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) @@ -504,6 +528,20 @@ config USB_ETH_RNDIS XP, you'll need to download drivers from Microsoft's website; a URL is given in comments found in that info file. +config USB_ETH_NO_MDLM + bool "Disable MDLM support for CDC_SUBSET" + depends on USB_ETH + default n + help + A variation on CDC_SUBSET support is used in newer kernel + implementations for use with a proprietary Microsoft Windows driver + used for example with the Zaurus linux distribution. + However, the MDLM extensions break the older host side drivers making + g_ether incompatible with older kernels. + + If you say "y" here, the Ethernet gadget driver will use the older + pre-MDLM extensions. + config USB_GADGETFS tristate "Gadget Filesystem (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -586,6 +624,20 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET + help + This driver provides two functions in one configuration: + a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. + + This driver requires four bulk and two interrupt endpoints, + plus the ability to handle altsettings. Not all peripheral + controllers are that capable. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1235725..79a69ae 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -8,7 +8,7 @@ endif obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o -obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o +obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o @@ -18,22 +18,27 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_SA1100) += sa1100_udc.o # # USB gadget drivers # -g_zero-objs := zero.o usbstring.o config.o epautoconf.o -g_ether-objs := ether.o usbstring.o config.o epautoconf.o -g_serial-objs := serial.o usbstring.o config.o epautoconf.o +C_UTILS = composite.o usbstring.o config.o epautoconf.o + +g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) +g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS) +g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o g_printer-objs := printer.o usbstring.o config.o \ epautoconf.o +g_cdc-objs := cdc2.o u_ether.o f_ecm.o \ + u_serial.o f_acm.o $(C_UTILS) ifeq ($(CONFIG_USB_ETH_RNDIS),y) - g_ether-objs += rndis.o + g_ether-objs += f_rndis.o rndis.o endif obj-$(CONFIG_USB_ZERO) += g_zero.o @@ -43,4 +48,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index f261d2a..abf8192 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -3342,7 +3341,7 @@ static int udc_probe(struct udc *dev) spin_lock_init(&dev->lock); dev->gadget.ops = &udc_ops; - strcpy(dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.name = name; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 274c60a..a8a1de4 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -40,16 +40,15 @@ #include #include -#include +#include #include #include #include -#include #include -#include -#include -#include +#include +#include +#include #include "at91_udc.h" @@ -888,7 +887,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, 0); if (cpu_is_at91rm9200()) gpio_set_value(udc->board.pullup_pin, active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc |= AT91_UDP_TXVC_PUON; @@ -906,7 +905,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); if (cpu_is_at91rm9200()) gpio_set_value(udc->board.pullup_pin, !active); - else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc &= ~AT91_UDP_TXVC_PUON; @@ -1687,6 +1686,19 @@ static int __init at91udc_probe(struct platform_device *pdev) udc->board.pullup_active_low); } + /* newer chips have more FIFO memory than rm9200 */ + if (cpu_is_at91sam9260()) { + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + udc->ep[4].maxpacket = 512; + udc->ep[5].maxpacket = 512; + } else if (cpu_is_at91sam9261()) { + udc->ep[3].maxpacket = 64; + } else if (cpu_is_at91sam9263()) { + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + } + udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); if (!udc->udp_baseaddr) { retval = -ENOMEM; diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index a973f2a..c65d622 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -171,7 +171,7 @@ struct at91_request { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 07e5a0b..ae30ab1 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include "atmel_usba_udc.h" @@ -334,7 +334,7 @@ static void toggle_bias(int is_on) #elif defined(CONFIG_ARCH_AT91) -#include +#include static void toggle_bias(int is_on) { diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c new file mode 100644 index 0000000..a39a4b9 --- /dev/null +++ b/drivers/usb/gadget/cdc2.c @@ -0,0 +1,246 @@ +/* + * cdc2.c -- CDC Composite driver, with ECM and ACM support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "u_ether.h" +#include "u_serial.h" + + +#define DRIVER_DESC "CDC Composite Gadget" +#define DRIVER_VERSION "King Kamehameha Day 2008" + +/*-------------------------------------------------------------------------*/ + +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only this composite CDC configuration. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .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, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static u8 hostaddr[ETH_ALEN]; + +/*-------------------------------------------------------------------------*/ + +/* + * We _always_ have both CDC ECM and CDC ACM functions. + */ +static int __init cdc_do_config(struct usb_configuration *c) +{ + int status; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + status = ecm_bind_config(c, hostaddr); + if (status < 0) + return status; + + status = acm_bind_config(c, 0); + if (status < 0) + return status; + + return 0; +} + +static struct usb_configuration cdc_config_driver = { + .label = "CDC Composite (ECM + ACM)", + .bind = cdc_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; + +/*-------------------------------------------------------------------------*/ + +static int __init cdc_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; + + if (!can_support_ecm(cdev->gadget)) { + ERROR(cdev, "controller '%s' not usable\n", gadget->name); + return -EINVAL; + } + + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + return status; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + goto fail0; + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + else { + /* We assume that can_support_ecm() tells the truth; + * but if the controller isn't recognized at all then + * that assumption is a bit more likely to be wrong. + */ + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + cdc_config_driver.label); + device_desc.bcdDevice = + __constant_cpu_to_le16(0x0300 | 0x0099); + } + + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + /* register our configuration */ + status = usb_add_config(cdev, &cdc_config_driver); + if (status < 0) + goto fail1; + + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); + + return 0; + +fail1: + gserial_cleanup(); +fail0: + gether_cleanup(); + return status; +} + +static int __exit cdc_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + gether_cleanup(); + return 0; +} + +static struct usb_composite_driver cdc_driver = { + .name = "g_cdc", + .dev = &device_desc, + .strings = dev_strings, + .bind = cdc_bind, + .unbind = __exit_p(cdc_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); + +static int __init init(void) +{ + return usb_composite_register(&cdc_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&cdc_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 0000000..85c876c --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1041 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include + +#include + + +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 512 + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int __init usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int __init usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower; + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); + qual->bRESERVED = 0; +} + +/*-------------------------------------------------------------------------*/ + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + + if (!f) + break; + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = 2 * c->bMaxPower; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int __init usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; s->wData[len] && len <= 126; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ +int __init usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + if (w_value && !f->get_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces ... punt other + * recipients (endpoint, other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + if ((ctrl->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + f = cdev->config->interface[w_index]; + if (f && f->setup) + value = f->setup(f, ctrl); + else + f = NULL; + } + if (value < 0 && !f) { + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} + +static void __init +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void __init +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int __init composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = __exit_p(composite_unbind), + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int __init usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void __exit usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index a4e54b2..1ca1c32 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -96,7 +96,7 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + *cp = *config; /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, @@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header **__init +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = kmalloc(bytes, GFP_KERNEL); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +/** + * usb_find_endpoint - find a copy of an endpoint descriptor + * @src: original vector of descriptors + * @copy: copy of @src + * @ep: endpoint descriptor found in @src + * + * This returns the copy of the @match descriptor made for @copy. Its + * intended use is to help remembering the endpoint descriptor to use + * when enabling a given endpoint. + */ +struct usb_endpoint_descriptor *__init +usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match +) +{ + while (*src) { + if (*src == (void *) match) + return (void *)*copy; + src++; + copy++; + } + return NULL; +} diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 4203619..7600a0c 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -542,13 +542,14 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, req->req.context = dum; req->req.complete = fifo_complete; + list_add_tail(&req->queue, &ep->queue); spin_unlock (&dum->lock); _req->actual = _req->length; _req->status = 0; _req->complete (_ep, _req); spin_lock (&dum->lock); - } - list_add_tail (&req->queue, &ep->queue); + } else + list_add_tail(&req->queue, &ep->queue); spin_unlock_irqrestore (&dum->lock, flags); /* real hardware would likely enable transfers here, in case @@ -862,7 +863,7 @@ static int dummy_udc_probe (struct platform_device *pdev) /* maybe claim OTG support, though we won't complete HNP */ dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); - strcpy (dum->gadget.dev.bus_id, "gadget"); + dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; rc = device_register (&dum->gadget.dev); @@ -1865,7 +1866,7 @@ static int dummy_hcd_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); - hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) return -ENOMEM; the_controller = hcd_to_dummy (hcd); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 8bdad22..9462e30 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -159,6 +159,7 @@ ep_matches ( /* MATCH!! */ /* report address */ + desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { u8 num = simple_strtol (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 8d61ea6..bcac2e6 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -1,8 +1,9 @@ /* * ether.c -- Ethernet gadget driver, with CDC and non-CDC options * - * Copyright (C) 2003-2005 David Brownell + * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,18 +24,9 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include "u_ether.h" -#include "gadget_chips.h" - -/*-------------------------------------------------------------------------*/ /* * Ethernet gadget driver -- with CDC and non-CDC options @@ -46,7 +38,11 @@ * this USB-IF standard as its open-systems interoperability solution; * most host side USB stacks (except from Microsoft) support it. * - * There's some hardware that can't talk CDC. We make that hardware + * This is sometimes called "CDC ECM" (Ethernet Control Model) to support + * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new + * "CDC EEM" (Ethernet Emulation Model) is starting to spread. + * + * There's some hardware that can't talk CDC ECM. We make that hardware * implement a "minimalist" vendor-agnostic CDC core: same framing, but * link-level setup only requires activating the configuration. Only the * endpoint descriptors, and product/vendor IDs, are relevant; no control @@ -64,70 +60,40 @@ * A third option is also in use. Rather than CDC Ethernet, or something * simpler, Microsoft pushes their own approach: RNDIS. The published * RNDIS specs are ambiguous and appear to be incomplete, and are also - * needlessly complex. + * needlessly complex. They borrow more from CDC ACM than CDC ECM. */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "May Day 2005" - -static const char shortname [] = "ether"; -static const char driver_desc [] = DRIVER_DESC; - -#define RX_EXTRA 20 /* guard against rx overflows */ - -#include "rndis.h" +#define DRIVER_VERSION "Memorial Day 2008" -#ifndef CONFIG_USB_ETH_RNDIS -#define rndis_uninit(x) do{}while(0) -#define rndis_deregister(c) do{}while(0) -#define rndis_exit() do{}while(0) +#ifdef CONFIG_USB_ETH_RNDIS +#define PREFIX "RNDIS/" +#else +#define PREFIX "" #endif -/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ -#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ - |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ - |USB_CDC_PACKET_TYPE_PROMISCUOUS \ - |USB_CDC_PACKET_TYPE_DIRECTED) - - -/*-------------------------------------------------------------------------*/ - -struct eth_dev { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - struct usb_request *stat_req; /* for cdc & rndis status */ - - u8 config; - struct usb_ep *in_ep, *out_ep, *status_ep; - const struct usb_endpoint_descriptor - *in, *out, *status; - - spinlock_t req_lock; - struct list_head tx_reqs, rx_reqs; - - struct net_device *net; - struct net_device_stats stats; - atomic_t tx_qlen; - - struct work_struct work; - unsigned zlp:1; - unsigned cdc:1; - unsigned rndis:1; - unsigned suspended:1; - u16 cdc_filter; - unsigned long todo; -#define WORK_RX_MEMORY 0 - int rndis_config; - u8 host_mac [ETH_ALEN]; -}; - -/* This version autoconfigures as much as possible at run-time. +/* + * This driver aims for interoperability by using CDC ECM unless + * + * can_support_ecm() + * + * returns false, in which case it supports the CDC Subset. By default, + * that returns true; most hardware has no problems with CDC ECM, that's + * a good default. Previous versions of this driver had no default; this + * version changes that, removing overhead for new controller support. * - * It also ASSUMES a self-powered device, without remote wakeup, - * although remote wakeup support would make sense. + * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE! */ +static inline bool has_rndis(void) +{ +#ifdef CONFIG_USB_ETH_RNDIS + return true; +#else + return false; +#endif +} + /*-------------------------------------------------------------------------*/ /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -137,8 +103,8 @@ struct eth_dev { /* Thanks to NetChip Technologies for donating this product ID. * It's for devices with only CDC Ethernet configurations. */ -#define CDC_VENDOR_NUM 0x0525 /* NetChip */ -#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ /* For hardware that can't talk CDC, we use the same vendor ID that * ARM Linux has used for ethernet-over-usb, both with sa1100 and @@ -162,274 +128,9 @@ struct eth_dev { #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ - -/* Some systems will want different product identifers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -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"); - -/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ -static char *dev_addr; -module_param(dev_addr, charp, S_IRUGO); -MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); - -/* this address is invisible to ifconfig */ -static char *host_addr; -module_param(host_addr, charp, S_IRUGO); -MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); - - -/*-------------------------------------------------------------------------*/ - -/* Include CDC support if we could run on CDC-capable hardware. */ - -#ifdef CONFIG_USB_GADGET_NET2280 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_DUMMY_HCD -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_GOKU -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_LH7A40X -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MQ11XX -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_OMAP -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_N9604 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_S3C2410 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_AT91 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MUSBHSFC -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MUSB_HDRC -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_ATMEL_USBA -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_FSL_USB2 -#define DEV_CONFIG_CDC -#endif - -/* For CDC-incapable hardware, choose the simple cdc subset. - * Anything that talks bulk (without notable bugs) can do this. - */ -#ifdef CONFIG_USB_GADGET_PXA2XX -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_PXA27X -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_SUPERH -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_SA1100 -/* use non-CDC for backwards compatibility */ -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_M66592 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_AMD5536UDC -#define DEV_CONFIG_CDC -#endif - - -/*-------------------------------------------------------------------------*/ - -/* "main" config is either CDC, or its simple subset */ -static inline int is_cdc(struct eth_dev *dev) -{ -#if !defined(DEV_CONFIG_SUBSET) - return 1; /* only cdc possible */ -#elif !defined (DEV_CONFIG_CDC) - return 0; /* only subset possible */ -#else - return dev->cdc; /* depends on what hardware we found */ -#endif -} - -/* "secondary" RNDIS config may sometimes be activated */ -static inline int rndis_active(struct eth_dev *dev) -{ -#ifdef CONFIG_USB_ETH_RNDIS - return dev->rndis; -#else - return 0; -#endif -} - -#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) -#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev)) - - - -#define DEFAULT_QLEN 2 /* double buffering by default */ - -/* peak bulk transfer bits-per-second */ -#define HS_BPS (13 * 512 * 8 * 1000 * 8) -#define FS_BPS (19 * 64 * 1 * 1000 * 8) - -#ifdef CONFIG_USB_GADGET_DUALSPEED -#define DEVSPEED USB_SPEED_HIGH - -static unsigned qmult = 5; -module_param (qmult, uint, S_IRUGO|S_IWUSR); - - -/* for dual-speed hardware, use deeper queues at highspeed */ -#define qlen(gadget) \ - (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) - -static inline int BITRATE(struct usb_gadget *g) -{ - return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS; -} - -#else /* full speed (low speed doesn't do bulk) */ - -#define qmult 1 - -#define DEVSPEED USB_SPEED_FULL - -#define qlen(gadget) DEFAULT_QLEN - -static inline int BITRATE(struct usb_gadget *g) -{ - return FS_BPS; -} -#endif - - -/*-------------------------------------------------------------------------*/ - -#define xprintk(d,level,fmt,args...) \ - printk(level "%s: " fmt , (d)->net->name , ## args) - -#ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ - xprintk(dev , KERN_DEBUG , fmt , ## args) -#else -#define DEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE_DEBUG -#define VDEBUG DEBUG -#else -#define VDEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#define ERROR(dev,fmt,args...) \ - xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) -#define INFO(dev,fmt,args...) \ - xprintk(dev , KERN_INFO , fmt , ## args) - /*-------------------------------------------------------------------------*/ -/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly - * ep0 implementation: descriptors, config management, setup(). - * also optional class-specific notification interrupt transfer. - */ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. For now we do either full CDC, or - * our simple subset, with RNDIS as an optional second configuration. - * - * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But - * the class descriptors match a modem (they're ignored; it's really just - * Ethernet functionality), they don't need the NOP altsetting, and the - * status transfer endpoint isn't optional. - */ - -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_ETHADDR 3 -#define STRING_DATA 4 -#define STRING_CONTROL 5 -#define STRING_RNDIS_CONTROL 6 -#define STRING_CDC 7 -#define STRING_SUBSET 8 -#define STRING_RNDIS 9 -#define STRING_SERIALNUMBER 10 - -/* holds our biggest descriptor (or RNDIS response) */ -#define USB_BUFSIZ 256 - -/* - * This device advertises one configuration, eth_config, unless RNDIS - * is enabled (rndis_config) on hardware supporting at least two configs. - * - * NOTE: Controllers like superh_udc should probably be able to use - * an RNDIS-only configuration. - * - * FIXME define some higher-powered configurations to make it easier - * to recharge batteries ... - */ - -#define DEV_CONFIG_VALUE 1 /* cdc or subset */ -#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ - -static struct usb_device_descriptor -device_desc = { +static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -438,2220 +139,234 @@ device_desc = { .bDeviceClass = USB_CLASS_COMM, .bDeviceSubClass = 0, .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + /* Vendor and product id defaults change according to what configs + * we support. (As does bNumConfigurations.) These values can + * also be overridden by module parameters. + */ .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM), .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ .bNumConfigurations = 1, }; -static struct usb_otg_descriptor -otg_descriptor = { +static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -static struct usb_config_descriptor -eth_config = { - .bLength = sizeof eth_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CDC, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, -}; - -#ifdef CONFIG_USB_ETH_RNDIS -static struct usb_config_descriptor -rndis_config = { - .bLength = sizeof rndis_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, - .iConfiguration = STRING_RNDIS, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, -}; -#endif - -/* - * Compared to the simple CDC subset, the full CDC Ethernet model adds - * three class descriptors, two interface descriptors, optional status - * endpoint. Both have a "data" interface and two bulk endpoints. - * There are also differences in how control requests are handled. - * - * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the - * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it - * may hang or oops. Since bugfixes (or accurate specs, letting Linux - * work around those bugs) are unlikely to ever come from MSFT, you may - * wish to avoid using RNDIS. - * - * MCCI offers an alternative to RNDIS if you need to connect to Windows - * but have hardware that can't support CDC Ethernet. We add descriptors - * to present the CDC Subset as a (nonconformant) CDC MDLM variant called - * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can - * get those drivers from MCCI, or bundled with various products. - */ - -#ifdef DEV_CONFIG_CDC -static struct usb_interface_descriptor -control_intf = { - .bLength = sizeof control_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - /* status endpoint is optional; this may be patched later */ - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bInterfaceProtocol = USB_CDC_PROTO_NONE, - .iInterface = STRING_CONTROL, -}; -#endif - -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_interface_descriptor -rndis_control_intf = { - .bLength = sizeof rndis_control_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, - .iInterface = STRING_RNDIS_CONTROL, -}; -#endif - -static const struct usb_cdc_header_desc header_desc = { - .bLength = sizeof header_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_HEADER_TYPE, - - .bcdCDC = __constant_cpu_to_le16 (0x0110), -}; - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - -static const struct usb_cdc_union_desc union_desc = { - .bLength = sizeof union_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_UNION_TYPE, - - .bMasterInterface0 = 0, /* index of control interface */ - .bSlaveInterface0 = 1, /* index of DATA interface */ -}; - -#endif /* CDC || RNDIS */ - -#ifdef CONFIG_USB_ETH_RNDIS - -static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { - .bLength = sizeof call_mgmt_descriptor, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - - .bmCapabilities = 0x00, - .bDataInterface = 0x01, -}; - -static const struct usb_cdc_acm_descriptor acm_descriptor = { - .bLength = sizeof acm_descriptor, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ACM_TYPE, - - .bmCapabilities = 0x00, -}; - -#endif - -#ifndef DEV_CONFIG_CDC - -/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various - * ways: data endpoints live in the control interface, there's no data - * interface, and it's not used to talk to a cell phone radio. - */ - -static const struct usb_cdc_mdlm_desc mdlm_desc = { - .bLength = sizeof mdlm_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_MDLM_TYPE, - - .bcdVersion = __constant_cpu_to_le16(0x0100), - .bGUID = { - 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, - 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, - }, -}; - -/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we - * can't really use its struct. All we do here is say that we're using - * the submode of "SAFE" which directly matches the CDC Subset. - */ -static const u8 mdlm_detail_desc[] = { - 6, - USB_DT_CS_INTERFACE, - USB_CDC_MDLM_DETAIL_TYPE, - - 0, /* "SAFE" */ - 0, /* network control capabilities (none) */ - 0, /* network data capabilities ("raw" encapsulation) */ -}; - -#endif - -static const struct usb_cdc_ether_desc ether_desc = { - .bLength = sizeof ether_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, - - /* this descriptor actually adds value, surprise! */ - .iMACAddress = STRING_ETHADDR, - .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */ - .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN), - .wNumberMCFilters = __constant_cpu_to_le16 (0), - .bNumberPowerFilters = 0, -}; - - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - -/* include the status endpoint if we can, even where it's optional. - * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one - * packet, to simplify cancellation; and a big transfer interval, to - * waste less bandwidth. - * - * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even - * if they ignore the connect/disconnect notifications that real aether - * can provide. more advanced cdc configurations might want to support - * encapsulated commands (vendor-specific, using control-OUT). - * - * RNDIS requires the status endpoint, since it uses that encapsulation - * mechanism for its funky RPC scheme. - */ - -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ - -static struct usb_endpoint_descriptor -fs_status_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, -}; -#endif - -#ifdef DEV_CONFIG_CDC - -/* the default data interface has no endpoints ... */ - -static const struct usb_interface_descriptor -data_nop_intf = { - .bLength = sizeof data_nop_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, -}; - -/* ... but the "real" data interface has two bulk endpoints */ - -static const struct usb_interface_descriptor -data_intf = { - .bLength = sizeof data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif - -#ifdef CONFIG_USB_ETH_RNDIS - -/* RNDIS doesn't activate by changing to the "real" altsetting */ - -static const struct usb_interface_descriptor -rndis_data_intf = { - .bLength = sizeof rndis_data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif - -#ifdef DEV_CONFIG_SUBSET - -/* - * "Simple" CDC-subset option is a simple vendor-neutral model that most - * full speed controllers can handle: one interface, two bulk endpoints. - * - * To assist host side drivers, we fancy it up a bit, and add descriptors - * so some host side drivers will understand it as a "SAFE" variant. - */ - -static const struct usb_interface_descriptor -subset_data_intf = { - .bLength = sizeof subset_data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif /* SUBSET */ - - -static struct usb_endpoint_descriptor -fs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor -fs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* 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 *fs_eth_function [11] = { +static const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, -#ifdef DEV_CONFIG_CDC - /* "cdc" mode descriptors */ - (struct usb_descriptor_header *) &control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) ðer_desc, - /* NOTE: status endpoint may need to be removed */ - (struct usb_descriptor_header *) &fs_status_desc, - /* data interface, with altsetting */ - (struct usb_descriptor_header *) &data_nop_intf, - (struct usb_descriptor_header *) &data_intf, - (struct usb_descriptor_header *) &fs_source_desc, - (struct usb_descriptor_header *) &fs_sink_desc, NULL, -#endif /* DEV_CONFIG_CDC */ }; -static inline void __init fs_subset_descriptors(void) -{ -#ifdef DEV_CONFIG_SUBSET - /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ - fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; - fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; - fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; - fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; - fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc; - fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc; - fs_eth_function[8] = NULL; -#else - fs_eth_function[1] = NULL; -#endif -} -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_descriptor_header *fs_rndis_function [] = { - (struct usb_descriptor_header *) &otg_descriptor, - /* control interface matches ACM, not Ethernet */ - (struct usb_descriptor_header *) &rndis_control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) &fs_status_desc, - /* data interface has no altsetting */ - (struct usb_descriptor_header *) &rndis_data_intf, - (struct usb_descriptor_header *) &fs_source_desc, - (struct usb_descriptor_header *) &fs_sink_desc, - NULL, -}; -#endif - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - */ - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static struct usb_endpoint_descriptor -hs_status_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, -}; -#endif /* DEV_CONFIG_CDC */ - -static struct usb_endpoint_descriptor -hs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (512), -}; - -static struct usb_endpoint_descriptor -hs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (512), -}; +/* string IDs are assigned dynamically */ -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 - .bcdUSB = __constant_cpu_to_le16 (0x0200), - .bDeviceClass = USB_CLASS_COMM, +static char manufacturer[50]; - .bNumConfigurations = 1, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + { } /* end of list */ }; -static const struct usb_descriptor_header *hs_eth_function [11] = { - (struct usb_descriptor_header *) &otg_descriptor, -#ifdef DEV_CONFIG_CDC - /* "cdc" mode descriptors */ - (struct usb_descriptor_header *) &control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) ðer_desc, - /* NOTE: status endpoint may need to be removed */ - (struct usb_descriptor_header *) &hs_status_desc, - /* data interface, with altsetting */ - (struct usb_descriptor_header *) &data_nop_intf, - (struct usb_descriptor_header *) &data_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, - NULL, -#endif /* DEV_CONFIG_CDC */ +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, }; -static inline void __init hs_subset_descriptors(void) -{ -#ifdef DEV_CONFIG_SUBSET - /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ - hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; - hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; - hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; - hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; - hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc; - hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc; - hs_eth_function[8] = NULL; -#else - hs_eth_function[1] = NULL; -#endif -} - -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_descriptor_header *hs_rndis_function [] = { - (struct usb_descriptor_header *) &otg_descriptor, - /* control interface matches ACM, not Ethernet */ - (struct usb_descriptor_header *) &rndis_control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) &hs_status_desc, - /* data interface has no altsetting */ - (struct usb_descriptor_header *) &rndis_data_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, NULL, }; -#endif - - -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} +static u8 hostaddr[ETH_ALEN]; /*-------------------------------------------------------------------------*/ -/* descriptors that are built on-demand */ - -static char manufacturer [50]; -static char product_desc [40] = DRIVER_DESC; -static char serial_number [20]; - -/* address that the host will use ... usually assigned at random */ -static char ethaddr [2 * ETH_ALEN + 1]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIALNUMBER, serial_number, }, - { STRING_DATA, "Ethernet Data", }, - { STRING_ETHADDR, ethaddr, }, -#ifdef DEV_CONFIG_CDC - { STRING_CDC, "CDC Ethernet", }, - { STRING_CONTROL, "CDC Communications Control", }, -#endif -#ifdef DEV_CONFIG_SUBSET - { STRING_SUBSET, "CDC Ethernet Subset", }, -#endif -#ifdef CONFIG_USB_ETH_RNDIS - { STRING_RNDIS, "RNDIS", }, - { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, -#endif - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab = { - .language = 0x0409, /* en-us */ - .strings = strings, -}; - /* - * one config, two interfaces: control, data. - * complications: class descriptors, and an altsetting. - */ -static int -config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) -{ - int len; - const struct usb_config_descriptor *config; - const struct usb_descriptor_header **function; - int hs = 0; - - if (gadget_is_dualspeed(g)) { - hs = (g->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - } -#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) - - if (index >= device_desc.bNumConfigurations) - return -EINVAL; - -#ifdef CONFIG_USB_ETH_RNDIS - /* list the RNDIS config first, to make Microsoft's drivers - * happy. DOCSIS 1.0 needs this too. - */ - if (device_desc.bNumConfigurations == 2 && index == 0) { - config = &rndis_config; - function = which_fn (rndis); - } else -#endif - { - config = ð_config; - function = which_fn (eth); - } - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function); - if (len < 0) - return len; - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - -/*-------------------------------------------------------------------------*/ - -static void eth_start (struct eth_dev *dev, gfp_t gfp_flags); -static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags); - -static int -set_ether_config (struct eth_dev *dev, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - /* status endpoint used for RNDIS and (optionally) CDC */ - if (!subset_active(dev) && dev->status_ep) { - dev->status = ep_desc (gadget, &hs_status_desc, - &fs_status_desc); - dev->status_ep->driver_data = dev; - - result = usb_ep_enable (dev->status_ep, dev->status); - if (result != 0) { - DEBUG (dev, "enable %s --> %d\n", - dev->status_ep->name, result); - goto done; - } - } -#endif - - dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - dev->in_ep->driver_data = dev; - - dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - dev->out_ep->driver_data = dev; - - /* With CDC, the host isn't allowed to use these two data - * endpoints in the default altsetting for the interface. - * so we don't activate them yet. Reset from SET_INTERFACE. - * - * Strictly speaking RNDIS should work the same: activation is - * a side effect of setting a packet filter. Deactivation is - * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. - */ - if (!cdc_active(dev)) { - result = usb_ep_enable (dev->in_ep, dev->in); - if (result != 0) { - DEBUG(dev, "enable %s --> %d\n", - dev->in_ep->name, result); - goto done; - } - - result = usb_ep_enable (dev->out_ep, dev->out); - if (result != 0) { - DEBUG (dev, "enable %s --> %d\n", - dev->out_ep->name, result); - goto done; - } - } - -done: - if (result == 0) - result = alloc_requests (dev, qlen (gadget), gfp_flags); - - /* on error, disable any endpoints */ - if (result < 0) { - if (!subset_active(dev) && dev->status_ep) - (void) usb_ep_disable (dev->status_ep); - dev->status = NULL; - (void) usb_ep_disable (dev->in_ep); - (void) usb_ep_disable (dev->out_ep); - dev->in = NULL; - dev->out = NULL; - } - - /* activate non-CDC configs right away - * this isn't strictly according to the RNDIS spec - */ - else if (!cdc_active (dev)) { - netif_carrier_on (dev->net); - if (netif_running (dev->net)) { - spin_unlock (&dev->lock); - eth_start (dev, GFP_ATOMIC); - spin_lock (&dev->lock); - } - } - - if (result == 0) - DEBUG (dev, "qlen %d\n", qlen (gadget)); - - /* caller is responsible for cleanup on error */ - return result; -} - -static void eth_reset_config (struct eth_dev *dev) -{ - struct usb_request *req; - - if (dev->config == 0) - return; - - DEBUG (dev, "%s\n", __func__); - - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - rndis_uninit(dev->rndis_config); - - /* disable endpoints, forcing (synchronous) completion of - * pending i/o. then free the requests. - */ - if (dev->in) { - usb_ep_disable (dev->in_ep); - spin_lock(&dev->req_lock); - while (likely (!list_empty (&dev->tx_reqs))) { - req = container_of (dev->tx_reqs.next, - struct usb_request, list); - list_del (&req->list); - - spin_unlock(&dev->req_lock); - usb_ep_free_request (dev->in_ep, req); - spin_lock(&dev->req_lock); - } - spin_unlock(&dev->req_lock); - } - if (dev->out) { - usb_ep_disable (dev->out_ep); - spin_lock(&dev->req_lock); - while (likely (!list_empty (&dev->rx_reqs))) { - req = container_of (dev->rx_reqs.next, - struct usb_request, list); - list_del (&req->list); - - spin_unlock(&dev->req_lock); - usb_ep_free_request (dev->out_ep, req); - spin_lock(&dev->req_lock); - } - spin_unlock(&dev->req_lock); - } - - if (dev->status) { - usb_ep_disable (dev->status_ep); - } - dev->rndis = 0; - dev->cdc_filter = 0; - dev->config = 0; -} - -/* change our operational config. must agree with the code - * that returns config descriptors, and altsetting code. + * We may not have an RNDIS configuration, but if we do it needs to be + * the first one present. That's to make Microsoft's drivers happy, + * and to follow DOCSIS 1.0 (cable modem standard). */ -static int -eth_set_config (struct eth_dev *dev, unsigned number, gfp_t gfp_flags) +static int __init rndis_do_config(struct usb_configuration *c) { - int result = 0; - struct usb_gadget *gadget = dev->gadget; - - if (gadget_is_sa1100 (gadget) - && dev->config - && atomic_read (&dev->tx_qlen) != 0) { - /* tx fifo is full, but we can't clear it...*/ - INFO (dev, "can't change configurations\n"); - return -ESPIPE; - } - eth_reset_config (dev); - - switch (number) { - case DEV_CONFIG_VALUE: - result = set_ether_config (dev, gfp_flags); - break; -#ifdef CONFIG_USB_ETH_RNDIS - case DEV_RNDIS_CONFIG_VALUE: - dev->rndis = 1; - result = set_ether_config (dev, gfp_flags); - break; -#endif - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - break; - } - - if (result) { - if (number) - eth_reset_config (dev); - usb_gadget_vbus_draw(dev->gadget, - gadget_is_otg(dev->gadget) ? 8 : 100); - } else { - char *speed; - unsigned power; - - power = 2 * eth_config.bMaxPower; - usb_gadget_vbus_draw(dev->gadget, power); + /* FIXME alloc iConfiguration string, set it in c->strings */ - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - - dev->config = number; - INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", - speed, number, power, driver_desc, - rndis_active(dev) - ? "RNDIS" - : (cdc_active(dev) - ? "CDC Ethernet" - : "CDC Ethernet Subset")); + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return result; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEV_CONFIG_CDC -/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM) - * only to notify the host about link status changes (which we support) or - * report completion of some encapsulated command (as used in RNDIS). Since - * we want this CDC Ethernet code to be vendor-neutral, we don't use that - * command mechanism; and only one status request is ever queued. - */ - -static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct usb_cdc_notification *event = req->buf; - int value = req->status; - struct eth_dev *dev = ep->driver_data; - - /* issue the second notification if host reads the first */ - if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION - && value == 0) { - __le32 *data = req->buf + sizeof *event; - - event->bmRequestType = 0xA1; - event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; - event->wValue = __constant_cpu_to_le16 (0); - event->wIndex = __constant_cpu_to_le16 (1); - event->wLength = __constant_cpu_to_le16 (8); - - /* SPEED_CHANGE data is up/down speeds in bits/sec */ - data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget)); - - req->length = STATUS_BYTECOUNT; - value = usb_ep_queue (ep, req, GFP_ATOMIC); - DEBUG (dev, "send SPEED_CHANGE --> %d\n", value); - if (value == 0) - return; - } else if (value != -ECONNRESET) - DEBUG (dev, "event %02x --> %d\n", - event->bNotificationType, value); - req->context = NULL; + return rndis_bind_config(c, hostaddr); } -static void issue_start_status (struct eth_dev *dev) -{ - struct usb_request *req = dev->stat_req; - struct usb_cdc_notification *event; - int value; - - DEBUG (dev, "%s, flush old status first\n", __func__); - - /* flush old status - * - * FIXME ugly idiom, maybe we'd be better with just - * a "cancel the whole queue" primitive since any - * unlink-one primitive has way too many error modes. - * here, we "know" toggle is already clear... - * - * FIXME iff req->context != null just dequeue it - */ - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - - /* 3.8.1 says to issue first NETWORK_CONNECTION, then - * a SPEED_CHANGE. could be useful in some configs. - */ - event = req->buf; - event->bmRequestType = 0xA1; - event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; - event->wValue = __constant_cpu_to_le16 (1); /* connected */ - event->wIndex = __constant_cpu_to_le16 (1); - event->wLength = 0; - - req->length = sizeof *event; - req->complete = eth_status_complete; - req->context = dev; - - value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC); - if (value < 0) - DEBUG (dev, "status buf queue --> %d\n", value); -} - -#endif +static struct usb_configuration rndis_config_driver = { + .label = "RNDIS", + .bind = rndis_do_config, + .bConfigurationValue = 2, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; /*-------------------------------------------------------------------------*/ -static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); -} - -#ifdef CONFIG_USB_ETH_RNDIS - -static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "rndis response complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - - /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ -} - -static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct eth_dev *dev = ep->driver_data; - int status; - - /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ - spin_lock(&dev->lock); - status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); - if (status < 0) - ERROR(dev, "%s: rndis parse error %d\n", __func__, status); - spin_unlock(&dev->lock); -} - -#endif /* RNDIS */ - /* - * The setup() callback implements all the ep0 functionality that's not - * handled lower down. CDC has a number of less-common features: - * - * - two interfaces: control, and ethernet data - * - Ethernet data interface has two altsettings: default, and active - * - class-specific descriptors for the control interface - * - class-specific control requests + * We _always_ have an ECM or CDC Subset configuration. */ -static int -eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +static int __init eth_do_config(struct usb_configuration *c) { - struct eth_dev *dev = get_gadget_data (gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - /* descriptors just go into the pre-allocated ep0 buffer, - * while config change events may enable network traffic. - */ - req->complete = eth_setup_complete; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - break; - switch (wValue >> 8) { - - case USB_DT_DEVICE: - value = min (wLength, (u16) sizeof device_desc); - memcpy (req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - value = min (wLength, (u16) sizeof dev_qualifier); - memcpy (req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - // FALLTHROUGH - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - wValue >> 8, - wValue & 0xff, - gadget_is_otg(gadget)); - if (value >= 0) - value = min (wLength, (u16) value); - break; - - case USB_DT_STRING: - value = usb_gadget_get_string (&stringtab, - wValue & 0xff, req->buf); - if (value >= 0) - value = min (wLength, (u16) value); - break; - } - break; - - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - break; - if (gadget->a_hnp_support) - DEBUG (dev, "HNP available\n"); - else if (gadget->a_alt_hnp_support) - DEBUG (dev, "HNP needs a different root port\n"); - spin_lock (&dev->lock); - value = eth_set_config (dev, wValue, GFP_ATOMIC); - spin_unlock (&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - break; - *(u8 *)req->buf = dev->config; - value = min (wLength, (u16) 1); - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->config - || wIndex > 1) - break; - if (!cdc_active(dev) && wIndex != 0) - break; - spin_lock (&dev->lock); - - /* PXA hardware partially handles SET_INTERFACE; - * we need to kluge around that interference. - */ - if (gadget_is_pxa (gadget)) { - value = eth_set_config (dev, DEV_CONFIG_VALUE, - GFP_ATOMIC); - goto done_set_intf; - } - -#ifdef DEV_CONFIG_CDC - switch (wIndex) { - case 0: /* control/master intf */ - if (wValue != 0) - break; - if (dev->status) { - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - } - value = 0; - break; - case 1: /* data intf */ - if (wValue > 1) - break; - usb_ep_disable (dev->in_ep); - usb_ep_disable (dev->out_ep); - - /* CDC requires the data transfers not be done from - * the default interface setting ... also, setting - * the non-default interface resets filters etc. - */ - if (wValue == 1) { - if (!cdc_active (dev)) - break; - usb_ep_enable (dev->in_ep, dev->in); - usb_ep_enable (dev->out_ep, dev->out); - dev->cdc_filter = DEFAULT_FILTER; - netif_carrier_on (dev->net); - if (dev->status) - issue_start_status (dev); - if (netif_running (dev->net)) { - spin_unlock (&dev->lock); - eth_start (dev, GFP_ATOMIC); - spin_lock (&dev->lock); - } - } else { - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - } - value = 0; - break; - } -#else - /* FIXME this is wrong, as is the assumption that - * all non-PXA hardware talks real CDC ... - */ - dev_warn (&gadget->dev, "set_interface ignored!\n"); -#endif /* DEV_CONFIG_CDC */ - -done_set_intf: - spin_unlock (&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) - || !dev->config - || wIndex > 1) - break; - if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) - break; - - /* for CDC, iff carrier is on, data interface is active. */ - if (rndis_active(dev) || wIndex != 1) - *(u8 *)req->buf = 0; - else - *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; - value = min (wLength, (u16) 1); - break; - -#ifdef DEV_CONFIG_CDC - case USB_CDC_SET_ETHERNET_PACKET_FILTER: - /* see 6.2.30: no data, wIndex = interface, - * wValue = packet filter bitmap - */ - if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !cdc_active(dev) - || wLength != 0 - || wIndex > 1) - break; - DEBUG (dev, "packet filter %02x\n", wValue); - dev->cdc_filter = wValue; - value = 0; - break; - - /* and potentially: - * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: - * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: - * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: - * case USB_CDC_GET_ETHERNET_STATISTIC: - */ - -#endif /* DEV_CONFIG_CDC */ + /* FIXME alloc iConfiguration string, set it in c->strings */ -#ifdef CONFIG_USB_ETH_RNDIS - /* RNDIS uses the CDC command encapsulation mechanism to implement - * an RPC scheme, with much getting/setting of attributes by OID. - */ - case USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !rndis_active(dev) - || wLength > USB_BUFSIZ - || wValue - || rndis_control_intf.bInterfaceNumber - != wIndex) - break; - /* read the request, then process it */ - value = wLength; - req->complete = rndis_command_complete; - /* later, rndis_control_ack () sends a notification */ - break; - - case USB_CDC_GET_ENCAPSULATED_RESPONSE: - if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) - == ctrl->bRequestType - && rndis_active(dev) - // && wLength >= 0x0400 - && !wValue - && rndis_control_intf.bInterfaceNumber - == wIndex) { - u8 *buf; - u32 n; - - /* return the result */ - buf = rndis_get_next_response(dev->rndis_config, &n); - if (buf) { - memcpy(req->buf, buf, n); - req->complete = rndis_response_complete; - rndis_free_response(dev->rndis_config, buf); - value = n; - } - /* else stalls ... spec says to avoid that */ - } - break; -#endif /* RNDIS */ - - default: - VDEBUG (dev, - "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < wLength - && (value % gadget->ep0->maxpacket) == 0; - value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DEBUG (dev, "ep_queue --> %d\n", value); - req->status = 0; - eth_setup_complete (gadget->ep0, req); - } - } - - /* host either stalls (value < 0) or reports success */ - return value; -} - -static void -eth_disconnect (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - unsigned long flags; - - spin_lock_irqsave (&dev->lock, flags); - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - eth_reset_config (dev); - spin_unlock_irqrestore (&dev->lock, flags); - - /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -/*-------------------------------------------------------------------------*/ - -/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ - -static int eth_change_mtu (struct net_device *net, int new_mtu) -{ - struct eth_dev *dev = netdev_priv(net); - - if (dev->rndis) - return -EBUSY; - - if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) - return -ERANGE; - /* no zero-length packet read wanted after mtu-sized packets */ - if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) - return -EDOM; - net->mtu = new_mtu; - return 0; -} - -static struct net_device_stats *eth_get_stats (struct net_device *net) -{ - return &((struct eth_dev *)netdev_priv(net))->stats; -} - -static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) -{ - struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, shortname, sizeof p->driver); - strlcpy(p->version, DRIVER_VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info); -} - -static u32 eth_get_link(struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - return dev->gadget->speed != USB_SPEED_UNKNOWN; -} - -static struct ethtool_ops ops = { - .get_drvinfo = eth_get_drvinfo, - .get_link = eth_get_link -}; - -static void defer_kevent (struct eth_dev *dev, int flag) -{ - if (test_and_set_bit (flag, &dev->todo)) - return; - if (!schedule_work (&dev->work)) - ERROR (dev, "kevent %d may have been dropped\n", flag); + if (can_support_ecm(c->cdev->gadget)) + return ecm_bind_config(c, hostaddr); else - DEBUG (dev, "kevent %d scheduled\n", flag); -} - -static void rx_complete (struct usb_ep *ep, struct usb_request *req); - -static int -rx_submit (struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) -{ - struct sk_buff *skb; - int retval = -ENOMEM; - size_t size; - - /* Padding up to RX_EXTRA handles minor disagreements with host. - * Normally we use the USB "terminate on short read" convention; - * so allow up to (N*maxpacket), since that memory is normally - * already allocated. Some hardware doesn't deal well with short - * reads (e.g. DMA must be N*maxpacket), so for now don't trim a - * byte off the end (to force hardware errors on overflow). - * - * RNDIS uses internal framing, and explicitly allows senders to - * pad to end-of-packet. That's potentially nice for speed, - * but means receivers can't recover synch on their own. - */ - size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); - size += dev->out_ep->maxpacket - 1; - if (rndis_active(dev)) - size += sizeof (struct rndis_packet_msg_type); - size -= size % dev->out_ep->maxpacket; - - skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); - if (skb == NULL) { - DEBUG (dev, "no rx skb\n"); - goto enomem; - } - - /* Some platforms perform better when IP packets are aligned, - * but on at least one, checksumming fails otherwise. Note: - * RNDIS headers involve variable numbers of LE32 values. - */ - skb_reserve(skb, NET_IP_ALIGN); - - req->buf = skb->data; - req->length = size; - req->complete = rx_complete; - req->context = skb; - - retval = usb_ep_queue (dev->out_ep, req, gfp_flags); - if (retval == -ENOMEM) -enomem: - defer_kevent (dev, WORK_RX_MEMORY); - if (retval) { - DEBUG (dev, "rx submit --> %d\n", retval); - if (skb) - dev_kfree_skb_any(skb); - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->rx_reqs); - spin_unlock(&dev->req_lock); - } - return retval; -} - -static void rx_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct sk_buff *skb = req->context; - struct eth_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - /* normal completion */ - case 0: - skb_put (skb, req->actual); - /* we know MaxPacketsPerTransfer == 1 here */ - if (rndis_active(dev)) - status = rndis_rm_hdr (skb); - if (status < 0 - || ETH_HLEN > skb->len - || skb->len > ETH_FRAME_LEN) { - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - DEBUG (dev, "rx length %d\n", skb->len); - break; - } - - skb->protocol = eth_type_trans (skb, dev->net); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx (skb); - skb = NULL; - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: // unlink - case -ESHUTDOWN: // disconnect etc - VDEBUG (dev, "rx shutdown, code %d\n", status); - goto quiesce; - - /* for hardware automagic (such as pxa) */ - case -ECONNABORTED: // endpoint reset - DEBUG (dev, "rx %s reset\n", ep->name); - defer_kevent (dev, WORK_RX_MEMORY); -quiesce: - dev_kfree_skb_any (skb); - goto clean; - - /* data overrun */ - case -EOVERFLOW: - dev->stats.rx_over_errors++; - // FALLTHROUGH - - default: - dev->stats.rx_errors++; - DEBUG (dev, "rx status %d\n", status); - break; - } - - if (skb) - dev_kfree_skb_any (skb); - if (!netif_running (dev->net)) { -clean: - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->rx_reqs); - spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit (dev, req, GFP_ATOMIC); -} - -static int prealloc (struct list_head *list, struct usb_ep *ep, - unsigned n, gfp_t gfp_flags) -{ - unsigned i; - struct usb_request *req; - - if (!n) - return -ENOMEM; - - /* queue/recycle up to N requests */ - i = n; - list_for_each_entry (req, list, list) { - if (i-- == 0) - goto extra; - } - while (i--) { - req = usb_ep_alloc_request (ep, gfp_flags); - if (!req) - return list_empty (list) ? -ENOMEM : 0; - list_add (&req->list, list); - } - return 0; - -extra: - /* free extras */ - for (;;) { - struct list_head *next; - - next = req->list.next; - list_del (&req->list); - usb_ep_free_request (ep, req); - - if (next == list) - break; - - req = container_of (next, struct usb_request, list); - } - return 0; -} - -static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags) -{ - int status; - - spin_lock(&dev->req_lock); - status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); - if (status < 0) - goto fail; - status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); - if (status < 0) - goto fail; - goto done; -fail: - DEBUG (dev, "can't alloc requests\n"); -done: - spin_unlock(&dev->req_lock); - return status; -} - -static void rx_fill (struct eth_dev *dev, gfp_t gfp_flags) -{ - struct usb_request *req; - unsigned long flags; - - /* fill unused rxq slots with some skb */ - spin_lock_irqsave(&dev->req_lock, flags); - while (!list_empty (&dev->rx_reqs)) { - req = container_of (dev->rx_reqs.next, - struct usb_request, list); - list_del_init (&req->list); - spin_unlock_irqrestore(&dev->req_lock, flags); - - if (rx_submit (dev, req, gfp_flags) < 0) { - defer_kevent (dev, WORK_RX_MEMORY); - return; - } - - spin_lock_irqsave(&dev->req_lock, flags); - } - spin_unlock_irqrestore(&dev->req_lock, flags); -} - -static void eth_work (struct work_struct *work) -{ - struct eth_dev *dev = container_of(work, struct eth_dev, work); - - if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) { - if (netif_running (dev->net)) - rx_fill (dev, GFP_KERNEL); - } - - if (dev->todo) - DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo); -} - -static void tx_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct sk_buff *skb = req->context; - struct eth_dev *dev = ep->driver_data; - - switch (req->status) { - default: - dev->stats.tx_errors++; - VDEBUG (dev, "tx err %d\n", req->status); - /* FALLTHROUGH */ - case -ECONNRESET: // unlink - case -ESHUTDOWN: // disconnect etc - break; - case 0: - dev->stats.tx_bytes += skb->len; - } - dev->stats.tx_packets++; - - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->tx_reqs); - spin_unlock(&dev->req_lock); - dev_kfree_skb_any (skb); - - atomic_dec (&dev->tx_qlen); - if (netif_carrier_ok (dev->net)) - netif_wake_queue (dev->net); -} - -static inline int eth_is_promisc (struct eth_dev *dev) -{ - /* no filters for the CDC subset; always promisc */ - if (subset_active (dev)) - return 1; - return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; -} - -static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - int length = skb->len; - int retval; - struct usb_request *req = NULL; - unsigned long flags; - - /* apply outgoing CDC or RNDIS filters */ - if (!eth_is_promisc (dev)) { - u8 *dest = skb->data; - - if (is_multicast_ether_addr(dest)) { - u16 type; - - /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host - * SET_ETHERNET_MULTICAST_FILTERS requests - */ - if (is_broadcast_ether_addr(dest)) - type = USB_CDC_PACKET_TYPE_BROADCAST; - else - type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; - if (!(dev->cdc_filter & type)) { - dev_kfree_skb_any (skb); - return 0; - } - } - /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ - } - - spin_lock_irqsave(&dev->req_lock, flags); - /* - * this freelist can be empty if an interrupt triggered disconnect() - * and reconfigured the gadget (shutting down this queue) after the - * network stack decided to xmit but before we got the spinlock. - */ - if (list_empty(&dev->tx_reqs)) { - spin_unlock_irqrestore(&dev->req_lock, flags); - return 1; - } - - req = container_of (dev->tx_reqs.next, struct usb_request, list); - list_del (&req->list); - - /* temporarily stop TX queue when the freelist empties */ - if (list_empty (&dev->tx_reqs)) - netif_stop_queue (net); - spin_unlock_irqrestore(&dev->req_lock, flags); - - /* no buffer copies needed, unless the network stack did it - * or the hardware can't use skb buffers. - * or there's not enough space for any RNDIS headers we need - */ - if (rndis_active(dev)) { - struct sk_buff *skb_rndis; - - skb_rndis = skb_realloc_headroom (skb, - sizeof (struct rndis_packet_msg_type)); - if (!skb_rndis) - goto drop; - - dev_kfree_skb_any (skb); - skb = skb_rndis; - rndis_add_hdr (skb); - length = skb->len; - } - req->buf = skb->data; - req->context = skb; - req->complete = tx_complete; - - /* use zlp framing on tx for strict CDC-Ether conformance, - * though any robust network rx path ignores extra padding. - * and some hardware doesn't like to write zlps. - */ - req->zero = 1; - if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) - length++; - - req->length = length; - - /* throttle highspeed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) - ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) - : 0; - - retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); - switch (retval) { - default: - DEBUG (dev, "tx queue err %d\n", retval); - break; - case 0: - net->trans_start = jiffies; - atomic_inc (&dev->tx_qlen); - } - - if (retval) { -drop: - dev->stats.tx_dropped++; - dev_kfree_skb_any (skb); - spin_lock_irqsave(&dev->req_lock, flags); - if (list_empty (&dev->tx_reqs)) - netif_start_queue (net); - list_add (&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->req_lock, flags); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_ETH_RNDIS - -/* The interrupt endpoint is used in RNDIS to notify the host when messages - * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT - * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even - * REMOTE_NDIS_KEEPALIVE_MSG. - * - * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and - * normally just one notification will be queued. - */ - -static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, gfp_t); -static void eth_req_free (struct usb_ep *ep, struct usb_request *req); - -static void -rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct eth_dev *dev = ep->driver_data; - - if (req->status || req->actual != req->length) - DEBUG (dev, - "rndis control ack complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - req->context = NULL; - - if (req != dev->stat_req) - eth_req_free(ep, req); -} - -static int rndis_control_ack (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - int length; - struct usb_request *resp = dev->stat_req; - - /* in case RNDIS calls this after disconnect */ - if (!dev->status) { - DEBUG (dev, "status ENODEV\n"); - return -ENODEV; - } - - /* in case queue length > 1 */ - if (resp->context) { - resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC); - if (!resp) - return -ENOMEM; - } - - /* Send RNDIS RESPONSE_AVAILABLE notification; - * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too - */ - resp->length = 8; - resp->complete = rndis_control_ack_complete; - resp->context = dev; - - *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); - *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); - - length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); - if (length < 0) { - resp->status = 0; - rndis_control_ack_complete (dev->status_ep, resp); - } - - return 0; -} - -#else - -#define rndis_control_ack NULL - -#endif /* RNDIS */ - -static void eth_start (struct eth_dev *dev, gfp_t gfp_flags) -{ - DEBUG (dev, "%s\n", __func__); - - /* fill the rx queue */ - rx_fill (dev, gfp_flags); - - /* and open the tx floodgates */ - atomic_set (&dev->tx_qlen, 0); - netif_wake_queue (dev->net); - if (rndis_active(dev)) { - rndis_set_param_medium (dev->rndis_config, - NDIS_MEDIUM_802_3, - BITRATE(dev->gadget)/100); - (void) rndis_signal_connect (dev->rndis_config); - } -} - -static int eth_open (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - - DEBUG (dev, "%s\n", __func__); - if (netif_carrier_ok (dev->net)) - eth_start (dev, GFP_KERNEL); - return 0; + return geth_bind_config(c, hostaddr); } -static int eth_stop (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - - VDEBUG (dev, "%s\n", __func__); - netif_stop_queue (net); - - DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", - dev->stats.rx_packets, dev->stats.tx_packets, - dev->stats.rx_errors, dev->stats.tx_errors - ); - - /* ensure there are no more active requests */ - if (dev->config) { - usb_ep_disable (dev->in_ep); - usb_ep_disable (dev->out_ep); - if (netif_carrier_ok (dev->net)) { - DEBUG (dev, "host still using in/out endpoints\n"); - // FIXME idiom may leave toggle wrong here - usb_ep_enable (dev->in_ep, dev->in); - usb_ep_enable (dev->out_ep, dev->out); - } - if (dev->status_ep) { - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - } - } - - if (rndis_active(dev)) { - rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); - (void) rndis_signal_disconnect (dev->rndis_config); - } - - return 0; -} +static struct usb_configuration eth_config_driver = { + /* .label = f(hardware) */ + .bind = eth_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; /*-------------------------------------------------------------------------*/ -static struct usb_request * -eth_req_alloc (struct usb_ep *ep, unsigned size, gfp_t gfp_flags) +static int __init eth_bind(struct usb_composite_dev *cdev) { - struct usb_request *req; - - req = usb_ep_alloc_request (ep, gfp_flags); - if (!req) - return NULL; - - req->buf = kmalloc (size, gfp_flags); - if (!req->buf) { - usb_ep_free_request (ep, req); - req = NULL; - } - return req; -} - -static void -eth_req_free (struct usb_ep *ep, struct usb_request *req) -{ - kfree (req->buf); - usb_ep_free_request (ep, req); -} - - -static void /* __init_or_exit */ -eth_unbind (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "unbind\n"); - rndis_deregister (dev->rndis_config); - rndis_exit (); - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - eth_req_free (gadget->ep0, dev->req); - dev->req = NULL; - } - if (dev->stat_req) { - eth_req_free (dev->status_ep, dev->stat_req); - dev->stat_req = NULL; - } - - unregister_netdev (dev->net); - free_netdev(dev->net); - - /* assuming we used keventd, it must quiesce too */ - flush_scheduled_work (); - set_gadget_data (gadget, NULL); -} - -static u8 __init nibble (unsigned char c) -{ - if (likely (isdigit (c))) - return c - '0'; - c = toupper (c); - if (likely (isxdigit (c))) - return 10 + c - 'A'; - return 0; -} + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; -static int __init get_ether_addr(const char *str, u8 *dev_addr) -{ - if (str) { - unsigned i; + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + return status; - for (i = 0; i < 6; i++) { - unsigned char num; + /* set up main config label and device descriptor */ + if (can_support_ecm(cdev->gadget)) { + /* ECM */ + eth_config_driver.label = "CDC Ethernet (ECM)"; + } else { + /* CDC Subset */ + eth_config_driver.label = "CDC Subset/SAFE"; - if((*str == '.') || (*str == ':')) - str++; - num = nibble(*str++) << 4; - num |= (nibble(*str++)); - dev_addr [i] = num; - } - if (is_valid_ether_addr (dev_addr)) - return 0; + device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM), + device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } - random_ether_addr(dev_addr); - return 1; -} - -static int __init -eth_bind (struct usb_gadget *gadget) -{ - struct eth_dev *dev; - struct net_device *net; - u8 cdc = 1, zlp = 1, rndis = 1; - struct usb_ep *in_ep, *out_ep, *status_ep = NULL; - int status = -ENOMEM; - int gcnum; - - /* these flags are only ever cleared; compiler take note */ -#ifndef DEV_CONFIG_CDC - cdc = 0; -#endif -#ifndef CONFIG_USB_ETH_RNDIS - rndis = 0; -#endif - /* Because most host side USB stacks handle CDC Ethernet, that - * standard protocol is _strongly_ preferred for interop purposes. - * (By everyone except Microsoft.) - */ - if (gadget_is_pxa (gadget)) { - /* pxa doesn't support altsettings */ - cdc = 0; - } else if (gadget_is_musbhdrc(gadget)) { - /* reduce tx dma overhead by avoiding special cases */ - zlp = 0; - } else if (gadget_is_sh(gadget)) { - /* sh doesn't support multiple interfaces or configs */ - cdc = 0; - rndis = 0; - } else if (gadget_is_sa1100 (gadget)) { - /* hardware can't write zlps */ - zlp = 0; - /* sa1100 CAN do CDC, without status endpoint ... we use - * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". - */ - cdc = 0; + if (has_rndis()) { + /* RNDIS plus ECM-or-Subset */ + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), + device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), + device_desc.bNumConfigurations = 2; } - gcnum = usb_gadget_controller_number (gadget); + gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum); + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); else { - /* can't assume CDC works. don't want to default to - * anything less functional on CDC-capable hardware, - * so we fail in this case. + /* We assume that can_support_ecm() tells the truth; + * but if the controller isn't recognized at all then + * that assumption is a bit more likely to be wrong. */ - dev_err (&gadget->dev, - "controller '%s' not recognized\n", - gadget->name); - return -ENODEV; - } - snprintf (manufacturer, sizeof manufacturer, "%s %s/%s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - /* If there's an RNDIS configuration, that's what Windows wants to - * be using ... so use these product IDs here and in the "linux.inf" - * needed to install MSFT drivers. Current Linux kernels will use - * the second configuration if it's CDC Ethernet, and need some help - * to choose the right configuration otherwise. - */ - if (rndis) { - device_desc.idVendor = - __constant_cpu_to_le16(RNDIS_VENDOR_NUM); - device_desc.idProduct = - __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); - snprintf (product_desc, sizeof product_desc, - "RNDIS/%s", driver_desc); - - /* CDC subset ... recognized by Linux since 2.4.10, but Windows - * drivers aren't widely available. (That may be improved by - * supporting one submode of the "SAFE" variant of MDLM.) - */ - } else if (!cdc) { - device_desc.idVendor = - __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); - device_desc.idProduct = - __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); - } - - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - dev_err (&gadget->dev, "idVendor needs idProduct!\n"); - return -ENODEV; - } - 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); - if (iProduct) - strlcpy (product_desc, iProduct, sizeof product_desc); - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIALNUMBER, - strlcpy(serial_number, iSerialNumber, sizeof serial_number); - } - - /* all we really need is bulk IN/OUT */ - usb_ep_autoconfig_reset (gadget); - in_ep = usb_ep_autoconfig (gadget, &fs_source_desc); - if (!in_ep) { -autoconf_fail: - dev_err (&gadget->dev, - "can't autoconfigure on %s\n", - gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - /* CDC Ethernet control interface doesn't require a status endpoint. - * Since some hosts expect one, try to allocate one anyway. - */ - if (cdc || rndis) { - status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); - if (status_ep) { - status_ep->driver_data = status_ep; /* claim */ - } else if (rndis) { - dev_err (&gadget->dev, - "can't run RNDIS on %s\n", - gadget->name); - return -ENODEV; -#ifdef DEV_CONFIG_CDC - /* pxa25x only does CDC subset; often used with RNDIS */ - } else if (cdc) { - control_intf.bNumEndpoints = 0; - /* FIXME remove endpoint from descriptor list */ -#endif - } - } -#endif - - /* one config: cdc, else minimal subset */ - if (!cdc) { - eth_config.bNumInterfaces = 1; - eth_config.iConfiguration = STRING_SUBSET; - - /* use functions to set these up, in case we're built to work - * with multiple controllers and must override CDC Ethernet. - */ - fs_subset_descriptors(); - hs_subset_descriptors(); - } - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - usb_gadget_set_selfpowered (gadget); - - /* For now RNDIS is always a second config */ - if (rndis) - device_desc.bNumConfigurations = 2; - - if (gadget_is_dualspeed(gadget)) { - if (rndis) - dev_qualifier.bNumConfigurations = 2; - else if (!cdc) - dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; - - /* assumes ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (status_ep) - hs_status_desc.bEndpointAddress = - fs_status_desc.bEndpointAddress; -#endif + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + eth_config_driver.label); + device_desc.bcdDevice = + __constant_cpu_to_le16(0x0300 | 0x0099); } - if (gadget_is_otg(gadget)) { - otg_descriptor.bmAttributes |= USB_OTG_HNP, - eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - eth_config.bMaxPower = 4; -#ifdef CONFIG_USB_ETH_RNDIS - rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - rndis_config.bMaxPower = 4; -#endif - } - - net = alloc_etherdev (sizeof *dev); - if (!net) - return status; - dev = netdev_priv(net); - spin_lock_init (&dev->lock); - spin_lock_init (&dev->req_lock); - INIT_WORK (&dev->work, eth_work); - INIT_LIST_HEAD (&dev->tx_reqs); - INIT_LIST_HEAD (&dev->rx_reqs); - - /* network device setup */ - dev->net = net; - strcpy (net->name, "usb%d"); - dev->cdc = cdc; - dev->zlp = zlp; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - dev->status_ep = status_ep; - - /* Module params for these addresses should come from ID proms. - * The host side address is used with CDC and RNDIS, and commonly - * ends up in a persistent config database. It's not clear if - * host side code for the SAFE thing cares -- its original BLAN - * thing didn't, Sharp never assigned those addresses on Zaurii. + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. */ - if (get_ether_addr(dev_addr, net->dev_addr)) - dev_warn(&gadget->dev, - "using random %s ethernet address\n", "self"); - if (get_ether_addr(host_addr, dev->host_mac)) - dev_warn(&gadget->dev, - "using random %s ethernet address\n", "host"); - snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", - dev->host_mac [0], dev->host_mac [1], - dev->host_mac [2], dev->host_mac [3], - dev->host_mac [4], dev->host_mac [5]); - - if (rndis) { - status = rndis_init(); - if (status < 0) { - dev_err (&gadget->dev, "can't init RNDIS, %d\n", - status); - goto fail; - } - } - net->change_mtu = eth_change_mtu; - net->get_stats = eth_get_stats; - net->hard_start_xmit = eth_start_xmit; - net->open = eth_open; - net->stop = eth_stop; - // watchdog_timeo, tx_timeout ... - // set_multicast_list - SET_ETHTOOL_OPS(net, &ops); + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; - /* preallocate control message data and buffer */ - dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL); - if (!dev->req) + status = usb_string_id(cdev); + if (status < 0) goto fail; - dev->req->complete = eth_setup_complete; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; - /* ... and maybe likewise for status transfer */ -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (dev->status_ep) { - dev->stat_req = eth_req_alloc (dev->status_ep, - STATUS_BYTECOUNT, GFP_KERNEL); - if (!dev->stat_req) { - eth_req_free (gadget->ep0, dev->req); + /* register our configuration(s); RNDIS first, if it's used */ + if (has_rndis()) { + status = usb_add_config(cdev, &rndis_config_driver); + if (status < 0) goto fail; - } - dev->stat_req->context = NULL; } -#endif - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; - set_gadget_data (gadget, dev); - gadget->ep0->driver_data = dev; - /* two kinds of host-initiated state changes: - * - iff DATA transfer is active, carrier is "on" - * - tx queueing enabled if open *and* carrier is "on" - */ - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - - SET_NETDEV_DEV (dev->net, &gadget->dev); - status = register_netdev (dev->net); + status = usb_add_config(cdev, ð_config_driver); if (status < 0) - goto fail1; - - INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, - out_ep->name, in_ep->name, - status_ep ? " STATUS " : "", - status_ep ? status_ep->name : "" - ); - INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - net->dev_addr [0], net->dev_addr [1], - net->dev_addr [2], net->dev_addr [3], - net->dev_addr [4], net->dev_addr [5]); - - if (cdc || rndis) - INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->host_mac [0], dev->host_mac [1], - dev->host_mac [2], dev->host_mac [3], - dev->host_mac [4], dev->host_mac [5]); - - if (rndis) { - u32 vendorID = 0; - - /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ - - dev->rndis_config = rndis_register (rndis_control_ack); - if (dev->rndis_config < 0) { -fail0: - unregister_netdev (dev->net); - status = -ENODEV; - goto fail; - } + goto fail; - /* these set up a lot of the OIDs that RNDIS needs */ - rndis_set_host_mac (dev->rndis_config, dev->host_mac); - if (rndis_set_param_dev (dev->rndis_config, dev->net, - &dev->stats, &dev->cdc_filter)) - goto fail0; - if (rndis_set_param_vendor(dev->rndis_config, vendorID, - manufacturer)) - goto fail0; - if (rndis_set_param_medium(dev->rndis_config, - NDIS_MEDIUM_802_3, 0)) - goto fail0; - INFO (dev, "RNDIS ready\n"); - } + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); - return status; + return 0; -fail1: - dev_dbg(&gadget->dev, "register_netdev failed, %d\n", status); fail: - eth_unbind (gadget); + gether_cleanup(); return status; } -/*-------------------------------------------------------------------------*/ - -static void -eth_suspend (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "suspend\n"); - dev->suspended = 1; -} - -static void -eth_resume (struct usb_gadget *gadget) +static int __exit eth_unbind(struct usb_composite_dev *cdev) { - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "resume\n"); - dev->suspended = 0; + gether_cleanup(); + return 0; } -/*-------------------------------------------------------------------------*/ - -static struct usb_gadget_driver eth_driver = { - .speed = DEVSPEED, - - .function = (char *) driver_desc, +static struct usb_composite_driver eth_driver = { + .name = "g_ether", + .dev = &device_desc, + .strings = dev_strings, .bind = eth_bind, - .unbind = eth_unbind, - - .setup = eth_setup, - .disconnect = eth_disconnect, - - .suspend = eth_suspend, - .resume = eth_resume, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - }, + .unbind = __exit_p(eth_unbind), }; -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); -MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION(PREFIX DRIVER_DESC); +MODULE_AUTHOR("David Brownell, Benedikt Spanger"); +MODULE_LICENSE("GPL"); - -static int __init init (void) +static int __init init(void) { - return usb_gadget_register_driver (ð_driver); + return usb_composite_register(ð_driver); } -module_init (init); +module_init(init); -static void __exit cleanup (void) +static void __exit cleanup(void) { - usb_gadget_unregister_driver (ð_driver); + usb_composite_unregister(ð_driver); } -module_exit (cleanup); - +module_exit(cleanup); diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c new file mode 100644 index 0000000..5ee1590 --- /dev/null +++ b/drivers/usb/gadget/f_acm.c @@ -0,0 +1,759 @@ +/* + * f_acm.c -- USB CDC serial (ACM) function 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. + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This CDC ACM function support just wraps control functions and + * notifications around the generic serial-over-usb code. + * + * Because CDC ACM is standardized by the USB-IF, many host operating + * systems have drivers for it. Accordingly, ACM is the preferred + * interop solution for serial-port type connections. The control + * models are often not necessary, and in any case don't do much in + * this bare-bones implementation. + * + * Note that even MS-Windows has some support for ACM. However, that + * support is somewhat broken because when you use ACM in a composite + * device, having multiple interfaces confuses the poor OS. It doesn't + * seem to understand CDC Union descriptors. The new "association" + * descriptors (roughly equivalent to CDC Unions) may sometimes help. + */ + +struct acm_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +struct f_acm { + struct gserial port; + u8 ctrl_id, data_id; + u8 port_num; + + u8 pending; + + /* lock is mostly for pending and notify_req ... they get accessed + * by callbacks both from tty (open/close/break) under its spinlock, + * and notify_req.complete() which can't use that lock. + */ + spinlock_t lock; + + struct acm_ep_descs fs; + struct acm_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + + struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ + + /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */ + u16 port_handshake_bits; +#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ +#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ + + /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */ + u16 serial_state; +#define ACM_CTRL_OVERRUN (1 << 6) +#define ACM_CTRL_PARITY (1 << 5) +#define ACM_CTRL_FRAMING (1 << 4) +#define ACM_CTRL_RI (1 << 3) +#define ACM_CTRL_BRK (1 << 2) +#define ACM_CTRL_DSR (1 << 1) +#define ACM_CTRL_DCD (1 << 0) +}; + +static inline struct f_acm *func_to_acm(struct usb_function *f) +{ + return container_of(f, struct f_acm, port.func); +} + +static inline struct f_acm *port_to_acm(struct gserial *p) +{ + return container_of(p, struct f_acm, port); +} + +/*-------------------------------------------------------------------------*/ + +/* notification endpoint uses smallish and infrequent fixed-size messages */ + +#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ + +/* interface and class descriptors: */ + +static struct usb_interface_descriptor acm_control_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_interface_descriptor acm_data_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc acm_header_desc __initdata = { + .bLength = sizeof(acm_header_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_call_mgmt_descriptor +acm_call_mgmt_descriptor __initdata = { + .bLength = sizeof(acm_call_mgmt_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + .bmCapabilities = 0, + /* .bDataInterface = DYNAMIC */ +}; + +static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { + .bLength = sizeof(acm_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + .bmCapabilities = USB_CDC_CAP_LINE, +}; + +static struct usb_cdc_union_desc acm_union_desc __initdata = { + .bLength = sizeof(acm_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), + .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *acm_fs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_fs_notify_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_fs_in_desc, + (struct usb_descriptor_header *) &acm_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), + .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, +}; + +static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *acm_hs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_hs_notify_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_hs_in_desc, + (struct usb_descriptor_header *) &acm_hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +#define ACM_CTRL_IDX 0 +#define ACM_DATA_IDX 1 + +/* static strings, in UTF-8 */ +static struct usb_string acm_string_defs[] = { + [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", + [ACM_DATA_IDX].s = "CDC ACM Data", + { /* ZEROES END LIST */ }, +}; + +static struct usb_gadget_strings acm_string_table = { + .language = 0x0409, /* en-us */ + .strings = acm_string_defs, +}; + +static struct usb_gadget_strings *acm_strings[] = { + &acm_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +/* ACM control ... data handling is delegated to tty library code. + * The main task of this function is to activate and deactivate + * that code based on device state; track parameters like line + * speed, handshake state, and so on; and issue notifications. + */ + +static void acm_complete_set_line_coding(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_acm *acm = ep->driver_data; + struct usb_composite_dev *cdev = acm->port.func.config->cdev; + + if (req->status != 0) { + DBG(cdev, "acm ttyGS%d completion, err %d\n", + acm->port_num, req->status); + return; + } + + /* normal completion */ + if (req->actual != sizeof(acm->port_line_coding)) { + DBG(cdev, "acm ttyGS%d short resp, len %d\n", + acm->port_num, req->actual); + usb_ep_set_halt(ep); + } else { + struct usb_cdc_line_coding *value = req->buf; + + /* REVISIT: we currently just remember this data. + * If we change that, (a) validate it first, then + * (b) update whatever hardware needs updating, + * (c) worry about locking. This is information on + * the order of 9600-8-N-1 ... most of which means + * nothing unless we control a real RS232 line. + */ + acm->port_line_coding = *value; + } +} + +static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + 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); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + * + * Note CDC spec table 4 lists the ACM request profile. It requires + * encapsulated command support ... we don't handle any, and respond + * to them by stalling. Options include get/set/clear comm features + * (not that useful) and SEND_BREAK. + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + /* SET_LINE_CODING ... just read and save what the host sends */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_LINE_CODING: + if (w_length != sizeof(struct usb_cdc_line_coding) + || w_index != acm->ctrl_id) + goto invalid; + + value = w_length; + cdev->gadget->ep0->driver_data = acm; + req->complete = acm_complete_set_line_coding; + break; + + /* GET_LINE_CODING ... return what host sent, or initial value */ + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_GET_LINE_CODING: + if (w_index != acm->ctrl_id) + goto invalid; + + value = min_t(unsigned, w_length, + sizeof(struct usb_cdc_line_coding)); + memcpy(req->buf, &acm->port_line_coding, value); + break; + + /* SET_CONTROL_LINE_STATE ... save what the host sent */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_CONTROL_LINE_STATE: + if (w_index != acm->ctrl_id) + goto invalid; + + value = 0; + + /* FIXME we should not allow data to flow until the + * host sets the ACM_CTRL_DTR bit; and when it clears + * that bit, we should return to that no-flow state. + */ + acm->port_handshake_bits = w_value; + break; + + default: +invalid: + VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", + acm->port_num, ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "acm response on ttyGS%d, err %d\n", + acm->port_num, value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0, so this is an activation or a reset */ + + if (intf == acm->ctrl_id) { + if (acm->notify->driver_data) { + VDBG(cdev, "reset acm control interface %d\n", intf); + usb_ep_disable(acm->notify); + } else { + VDBG(cdev, "init acm ctrl interface %d\n", intf); + acm->notify_desc = ep_choose(cdev->gadget, + acm->hs.notify, + acm->fs.notify); + } + usb_ep_enable(acm->notify, acm->notify_desc); + acm->notify->driver_data = acm; + + } else if (intf == acm->data_id) { + if (acm->port.in->driver_data) { + DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); + gserial_disconnect(&acm->port); + } else { + DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); + acm->port.in_desc = ep_choose(cdev->gadget, + acm->hs.in, acm->fs.in); + acm->port.out_desc = ep_choose(cdev->gadget, + acm->hs.out, acm->fs.out); + } + gserial_connect(&acm->port, acm->port_num); + + } else + return -EINVAL; + + return 0; +} + +static void acm_disable(struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); + gserial_disconnect(&acm->port); + usb_ep_disable(acm->notify); + acm->notify->driver_data = NULL; +} + +/*-------------------------------------------------------------------------*/ + +/** + * acm_cdc_notify - issue CDC notification to host + * @acm: wraps host to be notified + * @type: notification type + * @value: Refer to cdc specs, wValue field. + * @data: data to be sent + * @length: size of data + * Context: irqs blocked, acm->lock held, acm_notify_req non-null + * + * Returns zero on sucess or a negative errno. + * + * See section 6.3.5 of the CDC 1.1 specification for information + * about the only notification we issue: SerialState change. + */ +static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, + void *data, unsigned length) +{ + struct usb_ep *ep = acm->notify; + struct usb_request *req; + struct usb_cdc_notification *notify; + const unsigned len = sizeof(*notify) + length; + void *buf; + int status; + + req = acm->notify_req; + acm->notify_req = NULL; + acm->pending = false; + + req->length = len; + notify = req->buf; + buf = notify + 1; + + notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS + | USB_RECIP_INTERFACE; + notify->bNotificationType = type; + notify->wValue = cpu_to_le16(value); + notify->wIndex = cpu_to_le16(acm->ctrl_id); + notify->wLength = cpu_to_le16(length); + memcpy(buf, data, length); + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status < 0) { + ERROR(acm->port.func.config->cdev, + "acm ttyGS%d can't notify serial state, %d\n", + acm->port_num, status); + acm->notify_req = req; + } + + return status; +} + +static int acm_notify_serial_state(struct f_acm *acm) +{ + struct usb_composite_dev *cdev = acm->port.func.config->cdev; + int status; + + spin_lock(&acm->lock); + if (acm->notify_req) { + DBG(cdev, "acm ttyGS%d serial state %04x\n", + acm->port_num, acm->serial_state); + status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, + 0, &acm->serial_state, sizeof(acm->serial_state)); + } else { + acm->pending = true; + status = 0; + } + spin_unlock(&acm->lock); + return status; +} + +static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_acm *acm = req->context; + u8 doit = false; + + /* on this call path we do NOT hold the port spinlock, + * which is why ACM needs its own spinlock + */ + spin_lock(&acm->lock); + if (req->status != -ESHUTDOWN) + doit = acm->pending; + acm->notify_req = req; + spin_unlock(&acm->lock); + + if (doit) + acm_notify_serial_state(acm); +} + +/* connect == the TTY link is open */ + +static void acm_connect(struct gserial *port) +{ + struct f_acm *acm = port_to_acm(port); + + acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD; + acm_notify_serial_state(acm); +} + +static void acm_disconnect(struct gserial *port) +{ + struct f_acm *acm = port_to_acm(port); + + acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD); + acm_notify_serial_state(acm); +} + +static int acm_send_break(struct gserial *port, int duration) +{ + struct f_acm *acm = port_to_acm(port); + u16 state; + + state = acm->serial_state; + state &= ~ACM_CTRL_BRK; + if (duration) + state |= ACM_CTRL_BRK; + + acm->serial_state = state; + return acm_notify_serial_state(acm); +} + +/*-------------------------------------------------------------------------*/ + +/* ACM function driver setup/binding */ +static int __init +acm_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_acm *acm = func_to_acm(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + acm->ctrl_id = status; + + acm_control_interface_desc.bInterfaceNumber = status; + acm_union_desc .bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + acm->data_id = status; + + acm_data_interface_desc.bInterfaceNumber = status; + acm_union_desc.bSlaveInterface0 = status; + acm_call_mgmt_descriptor.bDataInterface = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); + if (!ep) + goto fail; + acm->port.in = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); + if (!ep) + goto fail; + acm->port.out = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); + if (!ep) + goto fail; + acm->notify = ep; + ep->driver_data = cdev; /* claim */ + + /* allocate notification */ + acm->notify_req = gs_alloc_req(ep, + sizeof(struct usb_cdc_notification) + 2, + GFP_KERNEL); + if (!acm->notify_req) + goto fail; + + acm->notify_req->complete = acm_cdc_notify_complete; + acm->notify_req->context = acm; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(acm_fs_function); + if (!f->descriptors) + goto fail; + + acm->fs.in = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_in_desc); + acm->fs.out = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_out_desc); + acm->fs.notify = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acm_hs_in_desc.bEndpointAddress = + acm_fs_in_desc.bEndpointAddress; + acm_hs_out_desc.bEndpointAddress = + acm_fs_out_desc.bEndpointAddress; + acm_hs_notify_desc.bEndpointAddress = + acm_fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(acm_hs_function); + + acm->hs.in = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_in_desc); + acm->hs.out = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_out_desc); + acm->hs.notify = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_notify_desc); + } + + DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", + acm->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + acm->port.in->name, acm->port.out->name, + acm->notify->name); + return 0; + +fail: + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); + + /* we might as well release our claims on endpoints */ + if (acm->notify) + acm->notify->driver_data = NULL; + if (acm->port.out) + acm->port.out->driver_data = NULL; + if (acm->port.in) + acm->port.in->driver_data = NULL; + + ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); + + return status; +} + +static void +acm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + gs_free_req(acm->notify, acm->notify_req); + kfree(acm); +} + +/* Some controllers can't support CDC ACM ... */ +static inline bool can_support_cdc(struct usb_configuration *c) +{ + /* SH3 doesn't support multiple interfaces */ + if (gadget_is_sh(c->cdev->gadget)) + return false; + + /* sa1100 doesn't have a third interrupt endpoint */ + if (gadget_is_sa1100(c->cdev->gadget)) + return false; + + /* everything else is *probably* fine ... */ + return true; +} + +/** + * acm_bind_config - add a CDC ACM function to a configuration + * @c: the configuration to support the CDC ACM instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds. Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init acm_bind_config(struct usb_configuration *c, u8 port_num) +{ + struct f_acm *acm; + int status; + + if (!can_support_cdc(c)) + return -EINVAL; + + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (acm_string_defs[ACM_CTRL_IDX].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + acm_string_defs[ACM_CTRL_IDX].id = status; + + acm_control_interface_desc.iInterface = status; + + status = usb_string_id(c->cdev); + if (status < 0) + return status; + acm_string_defs[ACM_DATA_IDX].id = status; + + acm_data_interface_desc.iInterface = status; + } + + /* allocate and initialize one new instance */ + acm = kzalloc(sizeof *acm, GFP_KERNEL); + if (!acm) + return -ENOMEM; + + spin_lock_init(&acm->lock); + + acm->port_num = port_num; + + acm->port.connect = acm_connect; + acm->port.disconnect = acm_disconnect; + acm->port.send_break = acm_send_break; + + acm->port.func.name = "acm"; + acm->port.func.strings = acm_strings; + /* descriptors are per-instance copies */ + acm->port.func.bind = acm_bind; + acm->port.func.unbind = acm_unbind; + acm->port.func.set_alt = acm_set_alt; + acm->port.func.setup = acm_setup; + acm->port.func.disable = acm_disable; + + status = usb_add_function(c, &acm->port.func); + if (status) + kfree(acm); + return status; +} diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c new file mode 100644 index 0000000..a2b5c09 --- /dev/null +++ b/drivers/usb/gadget/f_ecm.c @@ -0,0 +1,831 @@ +/* + * f_ecm.c -- USB CDC Ethernet (ECM) link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include "u_ether.h" + + +/* + * This function is a "CDC Ethernet Networking Control Model" (CDC ECM) + * Ethernet link. The data transfer model is simple (packets sent and + * received over bulk endpoints using normal short packet termination), + * and the control model exposes various data and optional notifications. + * + * ECM is well standardized and (except for Microsoft) supported by most + * operating systems with USB host support. It's the preferred interop + * solution for Ethernet over USB, at least for firmware based solutions. + * (Hardware solutions tend to be more minimalist.) A newer and simpler + * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on. + * + * Note that ECM requires the use of "alternate settings" for its data + * interface. This means that the set_alt() method has real work to do, + * and also means that a get_alt() method is required. + */ + +struct ecm_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +enum ecm_notify_state { + ECM_NOTIFY_NONE, /* don't notify */ + ECM_NOTIFY_CONNECT, /* issue CONNECT next */ + ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ +}; + +struct f_ecm { + struct gether port; + u8 ctrl_id, data_id; + + char ethaddr[14]; + + struct ecm_ep_descs fs; + struct ecm_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + u8 notify_state; + bool is_open; + + /* FIXME is_open needs some irq-ish locking + * ... possibly the same as port.ioport + */ +}; + +static inline struct f_ecm *func_to_ecm(struct usb_function *f) +{ + return container_of(f, struct f_ecm, port.func); +} + +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static inline unsigned bitrate(struct usb_gadget *g) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + +/*-------------------------------------------------------------------------*/ + +/* + * Include the status endpoint if we can, even though it's optional. + * + * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one + * packet, to simplify cancellation; and a big transfer interval, to + * waste less bandwidth. + * + * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even + * if they ignore the connect/disconnect notifications that real aether + * can provide. More advanced cdc configurations might want to support + * encapsulated commands (vendor-specific, using control-OUT). + */ + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ + + +/* interface descriptor: */ + +static struct usb_interface_descriptor ecm_control_intf __initdata = { + .bLength = sizeof ecm_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + /* status endpoint is optional; this could be patched later */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_union_desc ecm_union_desc __initdata = { + .bLength = sizeof(ecm_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +static struct usb_cdc_ether_desc ether_desc __initdata = { + .bLength = sizeof ether_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + /* .iMACAddress = DYNAMIC */ + .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = __constant_cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +/* the default data interface has no endpoints ... */ + +static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { + .bLength = sizeof ecm_data_nop_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* ... but the "real" data interface has two bulk endpoints */ + +static struct usb_interface_descriptor ecm_data_intf __initdata = { + .bLength = sizeof ecm_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *eth_fs_function[] __initdata = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *eth_hs_function[] __initdata = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string ecm_string_defs[] = { + [0].s = "CDC Ethernet Control Model (ECM)", + [1].s = NULL /* DYNAMIC */, + [2].s = "CDC Ethernet Data", + { } /* end of list */ +}; + +static struct usb_gadget_strings ecm_string_table = { + .language = 0x0409, /* en-us */ + .strings = ecm_string_defs, +}; + +static struct usb_gadget_strings *ecm_strings[] = { + &ecm_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void ecm_do_notify(struct f_ecm *ecm) +{ + struct usb_request *req = ecm->notify_req; + struct usb_cdc_notification *event; + struct usb_composite_dev *cdev = ecm->port.func.config->cdev; + __le32 *data; + int status; + + /* notification already in flight? */ + if (!req) + return; + + event = req->buf; + switch (ecm->notify_state) { + case ECM_NOTIFY_NONE: + return; + + case ECM_NOTIFY_CONNECT: + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + if (ecm->is_open) + event->wValue = cpu_to_le16(1); + else + event->wValue = cpu_to_le16(0); + event->wLength = 0; + req->length = sizeof *event; + + DBG(cdev, "notify connect %s\n", + ecm->is_open ? "true" : "false"); + ecm->notify_state = ECM_NOTIFY_SPEED; + break; + + case ECM_NOTIFY_SPEED: + event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; + event->wValue = cpu_to_le16(0); + event->wLength = cpu_to_le16(8); + req->length = STATUS_BYTECOUNT; + + /* SPEED_CHANGE data is up/down speeds in bits/sec */ + data = req->buf + sizeof *event; + data[0] = cpu_to_le32(bitrate(cdev->gadget)); + data[1] = data[0]; + + DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget)); + ecm->notify_state = ECM_NOTIFY_NONE; + break; + } + event->bmRequestType = 0xA1; + event->wIndex = cpu_to_le16(ecm->ctrl_id); + + ecm->notify_req = NULL; + status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); + if (status < 0) { + ecm->notify_req = req; + DBG(cdev, "notify --> %d\n", status); + } +} + +static void ecm_notify(struct f_ecm *ecm) +{ + /* NOTE on most versions of Linux, host side cdc-ethernet + * won't listen for notifications until its netdevice opens. + * The first notification then sits in the FIFO for a long + * time, and the second one is queued. + */ + ecm->notify_state = ECM_NOTIFY_CONNECT; + ecm_do_notify(ecm); +} + +static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ecm *ecm = req->context; + struct usb_composite_dev *cdev = ecm->port.func.config->cdev; + struct usb_cdc_notification *event = req->buf; + + switch (req->status) { + case 0: + /* no fault */ + break; + case -ECONNRESET: + case -ESHUTDOWN: + ecm->notify_state = ECM_NOTIFY_NONE; + break; + default: + DBG(cdev, "event %02x --> %d\n", + event->bNotificationType, req->status); + break; + } + ecm->notify_req = req; + ecm_do_notify(ecm); +} + +static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + 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); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_ETHERNET_PACKET_FILTER: + /* see 6.2.30: no data, wIndex = interface, + * wValue = packet filter bitmap + */ + if (w_length != 0 || w_index != ecm->ctrl_id) + goto invalid; + DBG(cdev, "packet filter %02x\n", w_value); + /* REVISIT locking of cdc_filter. This assumes the UDC + * driver won't have a concurrent packet TX irq running on + * another CPU; or that if it does, this write is atomic... + */ + ecm->port.cdc_filter = w_value; + value = 0; + break; + + /* and optionally: + * case USB_CDC_SEND_ENCAPSULATED_COMMAND: + * case USB_CDC_GET_ENCAPSULATED_RESPONSE: + * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_STATISTIC: + */ + + default: +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "ecm req %02x.%02x response err %d\n", + ctrl->bRequestType, ctrl->bRequest, + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* Control interface has only altsetting 0 */ + if (intf == ecm->ctrl_id) { + if (alt != 0) + goto fail; + + if (ecm->notify->driver_data) { + VDBG(cdev, "reset ecm control %d\n", intf); + usb_ep_disable(ecm->notify); + } else { + VDBG(cdev, "init ecm ctrl %d\n", intf); + ecm->notify_desc = ep_choose(cdev->gadget, + ecm->hs.notify, + ecm->fs.notify); + } + usb_ep_enable(ecm->notify, ecm->notify_desc); + ecm->notify->driver_data = ecm; + + /* Data interface has two altsettings, 0 and 1 */ + } else if (intf == ecm->data_id) { + if (alt > 1) + goto fail; + + if (ecm->port.in_ep->driver_data) { + DBG(cdev, "reset ecm\n"); + gether_disconnect(&ecm->port); + } + + if (!ecm->port.in) { + DBG(cdev, "init ecm\n"); + ecm->port.in = ep_choose(cdev->gadget, + ecm->hs.in, ecm->fs.in); + ecm->port.out = ep_choose(cdev->gadget, + ecm->hs.out, ecm->fs.out); + } + + /* CDC Ethernet only sends data in non-default altsettings. + * Changing altsettings resets filters, statistics, etc. + */ + if (alt == 1) { + struct net_device *net; + + /* Enable zlps by default for ECM conformance; + * override for musb_hdrc (avoids txdma ovhead) + * and sa1100 (can't). + */ + ecm->port.is_zlp_ok = !( + gadget_is_sa1100(cdev->gadget) + || gadget_is_musbhdrc(cdev->gadget) + ); + ecm->port.cdc_filter = DEFAULT_FILTER; + DBG(cdev, "activate ecm\n"); + net = gether_connect(&ecm->port); + if (IS_ERR(net)) + return PTR_ERR(net); + } + + /* NOTE this can be a minor disagreement with the ECM spec, + * which says speed notifications will "always" follow + * connection notifications. But we allow one connect to + * follow another (if the first is in flight), and instead + * just guarantee that a speed notification is always sent. + */ + ecm_notify(ecm); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +/* Because the data interface supports multiple altsettings, + * this ECM function *MUST* implement a get_alt() method. + */ +static int ecm_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_ecm *ecm = func_to_ecm(f); + + if (intf == ecm->ctrl_id) + return 0; + return ecm->port.in_ep->driver_data ? 1 : 0; +} + +static void ecm_disable(struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "ecm deactivated\n"); + + if (ecm->port.in_ep->driver_data) + gether_disconnect(&ecm->port); + + if (ecm->notify->driver_data) { + usb_ep_disable(ecm->notify); + ecm->notify->driver_data = NULL; + ecm->notify_desc = NULL; + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * Callbacks let us notify the host about connect/disconnect when the + * net device is opened or closed. + * + * For testing, note that link states on this side include both opened + * and closed variants of: + * + * - disconnected/unconfigured + * - configured but inactive (data alt 0) + * - configured and active (data alt 1) + * + * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and + * SET_INTERFACE (altsetting). Remember also that "configured" doesn't + * imply the host is actually polling the notification endpoint, and + * likewise that "active" doesn't imply it's actually using the data + * endpoints for traffic. + */ + +static void ecm_open(struct gether *geth) +{ + struct f_ecm *ecm = func_to_ecm(&geth->func); + + DBG(ecm->port.func.config->cdev, "%s\n", __func__); + + ecm->is_open = true; + ecm_notify(ecm); +} + +static void ecm_close(struct gether *geth) +{ + struct f_ecm *ecm = func_to_ecm(&geth->func); + + DBG(ecm->port.func.config->cdev, "%s\n", __func__); + + ecm->is_open = false; + ecm_notify(ecm); +} + +/*-------------------------------------------------------------------------*/ + +/* ethernet function driver setup/binding */ + +static int __init +ecm_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_ecm *ecm = func_to_ecm(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ecm->ctrl_id = status; + + ecm_control_intf.bInterfaceNumber = status; + ecm_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ecm->data_id = status; + + ecm_data_nop_intf.bInterfaceNumber = status; + ecm_data_intf.bInterfaceNumber = status; + ecm_union_desc.bSlaveInterface0 = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + ecm->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + ecm->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* NOTE: a status/notification endpoint is *OPTIONAL* but we + * don't treat it that way. It's simpler, and some newer CDC + * profiles (wireless handsets) no longer treat it as optional. + */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); + if (!ep) + goto fail; + ecm->notify = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* allocate notification request and buffer */ + ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!ecm->notify_req) + goto fail; + ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!ecm->notify_req->buf) + goto fail; + ecm->notify_req->context = ecm; + ecm->notify_req->complete = ecm_notify_complete; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(eth_fs_function); + if (!f->descriptors) + goto fail; + + ecm->fs.in = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_in_desc); + ecm->fs.out = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_out_desc); + ecm->fs.notify = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + hs_notify_desc.bEndpointAddress = + fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(eth_hs_function); + if (!f->hs_descriptors) + goto fail; + + ecm->hs.in = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_in_desc); + ecm->hs.out = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_out_desc); + ecm->hs.notify = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_notify_desc); + } + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + ecm->port.open = ecm_open; + ecm->port.close = ecm_close; + + DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + ecm->port.in_ep->name, ecm->port.out_ep->name, + ecm->notify->name); + return 0; + +fail: + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + if (ecm->notify_req) { + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (ecm->notify) + ecm->notify->driver_data = NULL; + if (ecm->port.out) + ecm->port.out_ep->driver_data = NULL; + if (ecm->port.in) + ecm->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +ecm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + + DBG(c->cdev, "ecm unbind\n"); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); + + ecm_string_defs[1].s = NULL; + kfree(ecm); +} + +/** + * ecm_bind_config - add CDC Ethernet network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_ecm *ecm; + int status; + + if (!can_support_ecm(c->cdev->gadget) || !ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (ecm_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[0].id = status; + ecm_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[2].id = status; + ecm_data_intf.iInterface = status; + + /* MAC address */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[1].id = status; + ether_desc.iMACAddress = status; + } + + /* allocate and initialize one new instance */ + ecm = kzalloc(sizeof *ecm, GFP_KERNEL); + if (!ecm) + return -ENOMEM; + + /* export host's Ethernet address in CDC format */ + snprintf(ecm->ethaddr, sizeof ecm->ethaddr, + "%02X%02X%02X%02X%02X%02X", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + ecm_string_defs[1].s = ecm->ethaddr; + + ecm->port.cdc_filter = DEFAULT_FILTER; + + ecm->port.func.name = "cdc_ethernet"; + ecm->port.func.strings = ecm_strings; + /* descriptors are per-instance copies */ + ecm->port.func.bind = ecm_bind; + ecm->port.func.unbind = ecm_unbind; + ecm->port.func.set_alt = ecm_set_alt; + ecm->port.func.get_alt = ecm_get_alt; + ecm->port.func.setup = ecm_setup; + ecm->port.func.disable = ecm_disable; + + status = usb_add_function(c, &ecm->port.func); + if (status) { + ecm_string_defs[1].s = NULL; + kfree(ecm); + } + return status; +} diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c new file mode 100644 index 0000000..eda4cde --- /dev/null +++ b/drivers/usb/gadget/f_loopback.c @@ -0,0 +1,381 @@ +/* + * f_loopback.c - USB peripheral loopback configuration driver + * + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include "g_zero.h" +#include "gadget_chips.h" + + +/* + * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, + * + * This takes messages of various sizes written OUT to a device, and loops + * them back so they can be read IN from it. It has been used by certain + * test applications. It supports limited testing of data queueing logic. + * + * + * This is currently packaged as a configuration driver, which can't be + * combined with other functions to make composite devices. However, it + * can be combined with other independent configurations. + */ +struct f_loopback { + struct usb_function function; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; +}; + +static inline struct f_loopback *func_to_loop(struct usb_function *f) +{ + return container_of(f, struct f_loopback, function); +} + +static unsigned qlen = 32; +module_param(qlen, uint, 0); +MODULE_PARM_DESC(qlenn, "depth of loopback queue"); + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor loopback_intf = { + .bLength = sizeof loopback_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &fs_sink_desc, + (struct usb_descriptor_header *) &fs_source_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +}; + +/* function-specific strings: */ + +static struct usb_string strings_loopback[] = { + [0].s = "loop input to output", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_loop = { + .language = 0x0409, /* en-us */ + .strings = strings_loopback, +}; + +static struct usb_gadget_strings *loopback_strings[] = { + &stringtab_loop, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init +loopback_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_loopback *loop = func_to_loop(f); + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + loopback_intf.bInterfaceNumber = id; + + /* allocate endpoints */ + + loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); + if (!loop->in_ep) { +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; + } + loop->in_ep->driver_data = cdev; /* claim */ + + loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); + if (!loop->out_ep) + goto autoconf_fail; + loop->out_ep->driver_data = cdev; /* claim */ + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + f->hs_descriptors = hs_loopback_descs; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, loop->in_ep->name, loop->out_ep->name); + return 0; +} + +static void +loopback_unbind(struct usb_configuration *c, struct usb_function *f) +{ + kfree(func_to_loop(f)); +} + +static void loopback_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_loopback *loop = ep->driver_data; + struct usb_composite_dev *cdev = loop->function.config->cdev; + int status = req->status; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == loop->out_ep) { + /* loop this OUT packet back IN to the host */ + req->zero = (req->actual < req->length); + req->length = req->actual; + status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); + if (status == 0) + return; + + /* "should never get here" */ + ERROR(cdev, "can't loop %s to %s: %d\n", + ep->name, loop->in_ep->name, + status); + } + + /* queue the buffer for some later OUT packet */ + req->length = buflen; + status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); + if (status == 0) + return; + + /* "should never get here" */ + /* FALLTHROUGH */ + + default: + ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + /* FALLTHROUGH */ + + /* NOTE: since this driver doesn't maintain an explicit record + * of requests it submitted (just maintains qlen count), we + * rely on the hardware driver to clean up on disconnect or + * endpoint disable. + */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + free_ep_req(ep, req); + return; + } +} + +static void disable_loopback(struct f_loopback *loop) +{ + struct usb_composite_dev *cdev; + + cdev = loop->function.config->cdev; + disable_endpoints(cdev, loop->in_ep, loop->out_ep); + VDBG(cdev, "%s disabled\n", loop->function.name); +} + +static int +enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) +{ + int result = 0; + const struct usb_endpoint_descriptor *src, *sink; + struct usb_ep *ep; + struct usb_request *req; + unsigned i; + + src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); + sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); + + /* one endpoint writes data back IN to the host */ + ep = loop->in_ep; + result = usb_ep_enable(ep, src); + if (result < 0) + return result; + ep->driver_data = loop; + + /* one endpoint just reads OUT packets */ + ep = loop->out_ep; + result = usb_ep_enable(ep, sink); + if (result < 0) { +fail0: + ep = loop->in_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + return result; + } + ep->driver_data = loop; + + /* allocate a bunch of read buffers and queue them all at once. + * we buffer at most 'qlen' transfers; fewer if any need more + * than 'buflen' bytes each. + */ + for (i = 0; i < qlen && result == 0; i++) { + req = alloc_ep_req(ep); + if (req) { + req->complete = loopback_complete; + result = usb_ep_queue(ep, req, GFP_ATOMIC); + if (result) + ERROR(cdev, "%s queue req --> %d\n", + ep->name, result); + } else { + usb_ep_disable(ep); + ep->driver_data = NULL; + result = -ENOMEM; + goto fail0; + } + } + + DBG(cdev, "%s enabled\n", loop->function.name); + return result; +} + +static int loopback_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct f_loopback *loop = func_to_loop(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt is zero */ + if (loop->in_ep->driver_data) + disable_loopback(loop); + return enable_loopback(cdev, loop); +} + +static void loopback_disable(struct usb_function *f) +{ + struct f_loopback *loop = func_to_loop(f); + + disable_loopback(loop); +} + +/*-------------------------------------------------------------------------*/ + +static int __init loopback_bind_config(struct usb_configuration *c) +{ + struct f_loopback *loop; + int status; + + loop = kzalloc(sizeof *loop, GFP_KERNEL); + if (!loop) + return -ENOMEM; + + loop->function.name = "loopback"; + loop->function.descriptors = fs_loopback_descs; + loop->function.bind = loopback_bind; + loop->function.unbind = loopback_unbind; + loop->function.set_alt = loopback_set_alt; + loop->function.disable = loopback_disable; + + status = usb_add_function(c, &loop->function); + if (status) + kfree(loop); + return status; +} + +static struct usb_configuration loopback_driver = { + .label = "loopback", + .strings = loopback_strings, + .bind = loopback_bind_config, + .bConfigurationValue = 2, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ + /* .iConfiguration = DYNAMIC */ +}; + +/** + * loopback_add - add a loopback testing configuration to a device + * @cdev: the device to support the loopback configuration + */ +int __init loopback_add(struct usb_composite_dev *cdev) +{ + int id; + + /* allocate string ID(s) */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_loopback[0].id = id; + + loopback_intf.iInterface = id; + loopback_driver.iConfiguration = id; + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + loopback_driver.descriptors = otg_desc; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + return usb_add_config(cdev, &loopback_driver); +} diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c new file mode 100644 index 0000000..659b3d9 --- /dev/null +++ b/drivers/usb/gadget/f_rndis.c @@ -0,0 +1,825 @@ +/* + * f_rndis.c -- RNDIS link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include + +#include "u_ether.h" +#include "rndis.h" + + +/* + * This function is an RNDIS Ethernet port -- a Microsoft protocol that's + * been promoted instead of the standard CDC Ethernet. The published RNDIS + * spec is ambiguous, incomplete, and needlessly complex. Variants such as + * ActiveSync have even worse status in terms of specification. + * + * In short: it's a protocol controlled by (and for) Microsoft, not for an + * Open ecosystem or markets. Linux supports it *only* because Microsoft + * doesn't support the CDC Ethernet standard. + * + * The RNDIS data transfer model is complex, with multiple Ethernet packets + * per USB message, and out of band data. The control model is built around + * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM + * (modem, not Ethernet) veneer, with those ACM descriptors being entirely + * useless (they're ignored). RNDIS expects to be the only function in its + * configuration, so it's no real help if you need composite devices; and + * it expects to be the first configuration too. + * + * There is a single technical advantage of RNDIS over CDC Ethernet, if you + * discount the fluff that its RPC can be made to deliver: it doesn't need + * a NOP altsetting for the data interface. That lets it work on some of the + * "so smart it's stupid" hardware which takes over configuration changes + * from the software, and adds restrictions like "no altsettings". + * + * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and + * have all sorts of contrary-to-specification oddities that can prevent + * them from working sanely. Since bugfixes (or accurate specs, letting + * Linux work around those bugs) are unlikely to ever come from MSFT, you + * may want to avoid using RNDIS on purely operational grounds. + * + * Omissions from the RNDIS 1.0 specification include: + * + * - Power management ... references data that's scattered around lots + * of other documentation, which is incorrect/incomplete there too. + * + * - There are various undocumented protocol requirements, like the need + * to send garbage in some control-OUT messages. + * + * - MS-Windows drivers sometimes emit undocumented requests. + */ + +struct rndis_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +struct f_rndis { + struct gether port; + u8 ctrl_id, data_id; + u8 ethaddr[ETH_ALEN]; + int config; + + struct rndis_ep_descs fs; + struct rndis_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + atomic_t notify_count; +}; + +static inline struct f_rndis *func_to_rndis(struct usb_function *f) +{ + return container_of(f, struct f_rndis, port.func); +} + +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static unsigned int bitrate(struct usb_gadget *g) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + +/*-------------------------------------------------------------------------*/ + +/* + */ + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 8 /* 8 bytes data */ + + +/* interface descriptor: */ + +static struct usb_interface_descriptor rndis_control_intf __initdata = { + .bLength = sizeof rndis_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + /* status endpoint is optional; this could be patched later */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + + .bmCapabilities = 0x00, +}; + +static struct usb_cdc_union_desc rndis_union_desc __initdata = { + .bLength = sizeof(rndis_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +/* the data interface has two bulk endpoints */ + +static struct usb_interface_descriptor rndis_data_intf __initdata = { + .bLength = sizeof rndis_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *eth_fs_function[] __initdata = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *eth_hs_function[] __initdata = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string rndis_string_defs[] = { + [0].s = "RNDIS Communications Control", + [1].s = "RNDIS Ethernet Data", + { } /* end of list */ +}; + +static struct usb_gadget_strings rndis_string_table = { + .language = 0x0409, /* en-us */ + .strings = rndis_string_defs, +}; + +static struct usb_gadget_strings *rndis_strings[] = { + &rndis_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static struct sk_buff *rndis_add_header(struct sk_buff *skb) +{ + skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); + if (skb) + rndis_add_hdr(skb); + return skb; +} + +static void rndis_response_available(void *_rndis) +{ + struct f_rndis *rndis = _rndis; + struct usb_request *req = rndis->notify_req; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + __le32 *data = req->buf; + int status; + + if (atomic_inc_return(&rndis->notify_count)) + return; + + /* Send RNDIS RESPONSE_AVAILABLE notification; a + * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too + * + * This is the only notification defined by RNDIS. + */ + data[0] = cpu_to_le32(1); + data[1] = cpu_to_le32(0); + + status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&rndis->notify_count); + DBG(cdev, "notify/0 --> %d\n", status); + } +} + +static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + int status = req->status; + + /* after TX: + * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) + * - RNDIS_RESPONSE_AVAILABLE (status/irq) + */ + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + atomic_set(&rndis->notify_count, 0); + break; + default: + DBG(cdev, "RNDIS %s response error %d, %d/%d\n", + ep->name, status, + req->actual, req->length); + /* FALLTHROUGH */ + case 0: + if (ep != rndis->notify) + break; + + /* handle multiple pending RNDIS_RESPONSE_AVAILABLE + * notifications by resending until we're done + */ + if (atomic_dec_and_test(&rndis->notify_count)) + break; + status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&rndis->notify_count); + DBG(cdev, "notify/1 --> %d\n", status); + } + break; + } +} + +static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + int status; + + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ +// spin_lock(&dev->lock); + status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + if (status < 0) + ERROR(cdev, "RNDIS command error %d, %d/%d\n", + status, req->actual, req->length); +// spin_unlock(&dev->lock); +} + +static int +rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + 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); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + /* RNDIS uses the CDC command encapsulation mechanism to implement + * an RPC scheme, with much getting/setting of attributes by OID. + */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SEND_ENCAPSULATED_COMMAND: + if (w_length > req->length || w_value + || w_index != rndis->ctrl_id) + goto invalid; + /* read the request; process it later */ + value = w_length; + req->complete = rndis_command_complete; + req->context = rndis; + /* later, rndis_response_available() sends a notification */ + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (w_value || w_index != rndis->ctrl_id) + goto invalid; + else { + u8 *buf; + u32 n; + + /* return the result */ + buf = rndis_get_next_response(rndis->config, &n); + if (buf) { + memcpy(req->buf, buf, n); + req->complete = rndis_response_complete; + rndis_free_response(rndis->config, buf); + value = n; + } + /* else stalls ... spec says to avoid that */ + } + break; + + default: +invalid: + VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "rndis response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0 */ + + if (intf == rndis->ctrl_id) { + if (rndis->notify->driver_data) { + VDBG(cdev, "reset rndis control %d\n", intf); + usb_ep_disable(rndis->notify); + } else { + VDBG(cdev, "init rndis ctrl %d\n", intf); + rndis->notify_desc = ep_choose(cdev->gadget, + rndis->hs.notify, + rndis->fs.notify); + } + usb_ep_enable(rndis->notify, rndis->notify_desc); + rndis->notify->driver_data = rndis; + + } else if (intf == rndis->data_id) { + struct net_device *net; + + if (rndis->port.in_ep->driver_data) { + DBG(cdev, "reset rndis\n"); + gether_disconnect(&rndis->port); + } else { + DBG(cdev, "init rndis\n"); + rndis->port.in = ep_choose(cdev->gadget, + rndis->hs.in, rndis->fs.in); + rndis->port.out = ep_choose(cdev->gadget, + rndis->hs.out, rndis->fs.out); + } + + /* Avoid ZLPs; they can be troublesome. */ + rndis->port.is_zlp_ok = false; + + /* RNDIS should be in the "RNDIS uninitialized" state, + * either never activated or after rndis_uninit(). + * + * We don't want data to flow here until a nonzero packet + * filter is set, at which point it enters "RNDIS data + * initialized" state ... but we do want the endpoints + * to be activated. It's a strange little state. + * + * REVISIT the RNDIS gadget code has done this wrong for a + * very long time. We need another call to the link layer + * code -- gether_updown(...bool) maybe -- to do it right. + */ + rndis->port.cdc_filter = 0; + + DBG(cdev, "RNDIS RX/TX early activation ... \n"); + net = gether_connect(&rndis->port); + if (IS_ERR(net)) + return PTR_ERR(net); + + rndis_set_param_dev(rndis->config, net, + &rndis->port.cdc_filter); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +static void rndis_disable(struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (!rndis->notify->driver_data) + return; + + DBG(cdev, "rndis deactivated\n"); + + rndis_uninit(rndis->config); + gether_disconnect(&rndis->port); + + usb_ep_disable(rndis->notify); + rndis->notify->driver_data = NULL; +} + +/*-------------------------------------------------------------------------*/ + +/* + * This isn't quite the same mechanism as CDC Ethernet, since the + * notification scheme passes less data, but the same set of link + * states must be tested. A key difference is that altsettings are + * not used to tell whether the link should send packets or not. + */ + +static void rndis_open(struct gether *geth) +{ + struct f_rndis *rndis = func_to_rndis(&geth->func); + struct usb_composite_dev *cdev = geth->func.config->cdev; + + DBG(cdev, "%s\n", __func__); + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, + bitrate(cdev->gadget) / 100); + rndis_signal_connect(rndis->config); +} + +static void rndis_close(struct gether *geth) +{ + struct f_rndis *rndis = func_to_rndis(&geth->func); + + DBG(geth->func.config->cdev, "%s\n", __func__); + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->config); +} + +/*-------------------------------------------------------------------------*/ + +/* ethernet function driver setup/binding */ + +static int __init +rndis_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_rndis *rndis = func_to_rndis(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + rndis->ctrl_id = status; + + rndis_control_intf.bInterfaceNumber = status; + rndis_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + rndis->data_id = status; + + rndis_data_intf.bInterfaceNumber = status; + rndis_union_desc.bSlaveInterface0 = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + rndis->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + rndis->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* NOTE: a status/notification endpoint is, strictly speaking, + * optional. We don't treat it that way though! It's simpler, + * and some newer profiles don't treat it as optional. + */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); + if (!ep) + goto fail; + rndis->notify = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* allocate notification request and buffer */ + rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!rndis->notify_req) + goto fail; + rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!rndis->notify_req->buf) + goto fail; + rndis->notify_req->length = STATUS_BYTECOUNT; + rndis->notify_req->context = rndis; + rndis->notify_req->complete = rndis_response_complete; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(eth_fs_function); + if (!f->descriptors) + goto fail; + + rndis->fs.in = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_in_desc); + rndis->fs.out = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_out_desc); + rndis->fs.notify = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(eth_hs_function); + + if (!f->hs_descriptors) + goto fail; + + rndis->hs.in = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_in_desc); + rndis->hs.out = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_out_desc); + } + + rndis->port.open = rndis_open; + rndis->port.close = rndis_close; + + status = rndis_register(rndis_response_available, rndis); + if (status < 0) + goto fail; + rndis->config = status; + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->config, rndis->ethaddr); + +#if 0 +// FIXME + if (rndis_set_param_vendor(rndis->config, vendorID, + manufacturer)) + goto fail0; +#endif + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + rndis->port.in_ep->name, rndis->port.out_ep->name, + rndis->notify->name); + return 0; + +fail: + if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + if (rndis->notify_req) { + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (rndis->notify) + rndis->notify->driver_data = NULL; + if (rndis->port.out) + rndis->port.out_ep->driver_data = NULL; + if (rndis->port.in) + rndis->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + rndis_deregister(rndis->config); + rndis_exit(); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); + + kfree(rndis); +} + +/* Some controllers can't support RNDIS ... */ +static inline bool can_support_rndis(struct usb_configuration *c) +{ + /* only two endpoints on sa1100 */ + if (gadget_is_sa1100(c->cdev->gadget)) + return false; + + /* everything else is *presumably* fine */ + return true; +} + +/** + * rndis_bind_config - add RNDIS network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_rndis *rndis; + int status; + + if (!can_support_rndis(c) || !ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (rndis_string_defs[0].id == 0) { + + /* ... and setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[0].id = status; + rndis_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[1].id = status; + rndis_data_intf.iInterface = status; + } + + /* allocate and initialize one new instance */ + status = -ENOMEM; + rndis = kzalloc(sizeof *rndis, GFP_KERNEL); + if (!rndis) + goto fail; + + memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); + + /* RNDIS activates when the host changes this filter */ + rndis->port.cdc_filter = 0; + + /* RNDIS has special (and complex) framing */ + rndis->port.header_len = sizeof(struct rndis_packet_msg_type); + rndis->port.wrap = rndis_add_header; + rndis->port.unwrap = rndis_rm_hdr; + + rndis->port.func.name = "rndis"; + rndis->port.func.strings = rndis_strings; + /* descriptors are per-instance copies */ + rndis->port.func.bind = rndis_bind; + rndis->port.func.unbind = rndis_unbind; + rndis->port.func.set_alt = rndis_set_alt; + rndis->port.func.setup = rndis_setup; + rndis->port.func.disable = rndis_disable; + + status = usb_add_function(c, &rndis->port.func); + if (status) { + kfree(rndis); +fail: + rndis_exit(); + } + return status; +} diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c new file mode 100644 index 0000000..fe5674d --- /dev/null +++ b/drivers/usb/gadget/f_serial.c @@ -0,0 +1,294 @@ +/* + * f_serial.c - generic USB serial function 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 +#include + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This function packages a simple "generic serial" port with no real + * control mechanisms, just raw data transfer over two bulk endpoints. + * + * Because it's not standardized, this isn't as interoperable as the + * CDC ACM driver. However, for many purposes it's just as functional + * if you can arrange appropriate host side drivers. + */ + +struct gser_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; +}; + +struct f_gser { + struct gserial port; + u8 data_id; + u8 port_num; + + struct gser_descs fs; + struct gser_descs hs; +}; + +static inline struct f_gser *func_to_gser(struct usb_function *f) +{ + return container_of(f, struct f_gser, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* interface descriptor: */ + +static struct usb_interface_descriptor gser_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *gser_fs_function[] __initdata = { + (struct usb_descriptor_header *) &gser_interface_desc, + (struct usb_descriptor_header *) &gser_fs_in_desc, + (struct usb_descriptor_header *) &gser_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *gser_hs_function[] __initdata = { + (struct usb_descriptor_header *) &gser_interface_desc, + (struct usb_descriptor_header *) &gser_hs_in_desc, + (struct usb_descriptor_header *) &gser_hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string gser_string_defs[] = { + [0].s = "Generic Serial", + { } /* end of list */ +}; + +static struct usb_gadget_strings gser_string_table = { + .language = 0x0409, /* en-us */ + .strings = gser_string_defs, +}; + +static struct usb_gadget_strings *gser_strings[] = { + &gser_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_gser *gser = func_to_gser(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0, so this is an activation or a reset */ + + if (gser->port.in->driver_data) { + DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); + gserial_disconnect(&gser->port); + } else { + DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); + gser->port.in_desc = ep_choose(cdev->gadget, + gser->hs.in, gser->fs.in); + gser->port.out_desc = ep_choose(cdev->gadget, + gser->hs.out, gser->fs.out); + } + gserial_connect(&gser->port, gser->port_num); + return 0; +} + +static void gser_disable(struct usb_function *f) +{ + struct f_gser *gser = func_to_gser(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); + gserial_disconnect(&gser->port); +} + +/*-------------------------------------------------------------------------*/ + +/* serial function driver setup/binding */ + +static int __init +gser_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_gser *gser = func_to_gser(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + gser->data_id = status; + gser_interface_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); + if (!ep) + goto fail; + gser->port.in = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); + if (!ep) + goto fail; + gser->port.out = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(gser_fs_function); + + gser->fs.in = usb_find_endpoint(gser_fs_function, + f->descriptors, &gser_fs_in_desc); + gser->fs.out = usb_find_endpoint(gser_fs_function, + f->descriptors, &gser_fs_out_desc); + + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + gser_hs_in_desc.bEndpointAddress = + gser_fs_in_desc.bEndpointAddress; + gser_hs_out_desc.bEndpointAddress = + gser_fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(gser_hs_function); + + gser->hs.in = usb_find_endpoint(gser_hs_function, + f->hs_descriptors, &gser_hs_in_desc); + gser->hs.out = usb_find_endpoint(gser_hs_function, + f->hs_descriptors, &gser_hs_out_desc); + } + + DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", + gser->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + gser->port.in->name, gser->port.out->name); + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (gser->port.out) + gser->port.out->driver_data = NULL; + if (gser->port.in) + gser->port.in->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +gser_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + kfree(func_to_gser(f)); +} + +/** + * gser_bind_config - add a generic serial function to a configuration + * @c: the configuration to support the serial instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds. Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init gser_bind_config(struct usb_configuration *c, u8 port_num) +{ + struct f_gser *gser; + int status; + + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string ID */ + if (gser_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + gser_string_defs[0].id = status; + } + + /* allocate and initialize one new instance */ + gser = kzalloc(sizeof *gser, GFP_KERNEL); + if (!gser) + return -ENOMEM; + + gser->port_num = port_num; + + gser->port.func.name = "gser"; + gser->port.func.strings = gser_strings; + gser->port.func.bind = gser_bind; + gser->port.func.unbind = gser_unbind; + gser->port.func.set_alt = gser_set_alt; + gser->port.func.disable = gser_disable; + + status = usb_add_function(c, &gser->port.func); + if (status) + kfree(gser); + return status; +} diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c new file mode 100644 index 0000000..f18c3a1 --- /dev/null +++ b/drivers/usb/gadget/f_sourcesink.c @@ -0,0 +1,587 @@ +/* + * f_sourcesink.c - USB peripheral source/sink configuration driver + * + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include "g_zero.h" +#include "gadget_chips.h" + + +/* + * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral + * controller drivers. + * + * This just sinks bulk packets OUT to the peripheral and sources them IN + * to the host, optionally with specific data patterns for integrity tests. + * As such it supports basic functionality and load tests. + * + * In terms of control messaging, this supports all the standard requests + * plus two that support control-OUT tests. If the optional "autoresume" + * mode is enabled, it provides good functional coverage for the "USBCV" + * test harness from USB-IF. + * + * Note that because this doesn't queue more than one request at a time, + * some other function must be used to test queueing logic. The network + * link (g_ether) is the best overall option for that, since its TX and RX + * queues are relatively independent, will receive a range of packet sizes, + * and can often be made to run out completely. Those issues are important + * when stress testing peripheral controller drivers. + * + * + * This is currently packaged as a configuration driver, which can't be + * combined with other functions to make composite devices. However, it + * can be combined with other independent configurations. + */ +struct f_sourcesink { + struct usb_function function; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; + struct timer_list resume; +}; + +static inline struct f_sourcesink *func_to_ss(struct usb_function *f) +{ + return container_of(f, struct f_sourcesink, function); +} + +static unsigned autoresume; +module_param(autoresume, uint, 0); +MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); + +static unsigned pattern; +module_param(pattern, uint, 0); +MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 "); + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor source_sink_intf = { + .bLength = sizeof source_sink_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &fs_sink_desc, + (struct usb_descriptor_header *) &fs_source_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +}; + +/* function-specific strings: */ + +static struct usb_string strings_sourcesink[] = { + [0].s = "source and sink data", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_sourcesink = { + .language = 0x0409, /* en-us */ + .strings = strings_sourcesink, +}; + +static struct usb_gadget_strings *sourcesink_strings[] = { + &stringtab_sourcesink, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void sourcesink_autoresume(unsigned long _c) +{ + struct usb_composite_dev *cdev = (void *)_c; + struct usb_gadget *g = cdev->gadget; + + /* Normally the host would be woken up for something + * more significant than just a timer firing; likely + * because of some direct user request. + */ + if (g->speed != USB_SPEED_UNKNOWN) { + int status = usb_gadget_wakeup(g); + DBG(cdev, "%s --> %d\n", __func__, status); + } +} + +static int __init +sourcesink_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_sourcesink *ss = func_to_ss(f); + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + source_sink_intf.bInterfaceNumber = id; + + /* allocate endpoints */ + ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); + if (!ss->in_ep) { +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; + } + ss->in_ep->driver_data = cdev; /* claim */ + + ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); + if (!ss->out_ep) + goto autoconf_fail; + ss->out_ep->driver_data = cdev; /* claim */ + + setup_timer(&ss->resume, sourcesink_autoresume, + (unsigned long) c->cdev); + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + f->hs_descriptors = hs_source_sink_descs; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, ss->in_ep->name, ss->out_ep->name); + return 0; +} + +static void +sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) +{ + kfree(func_to_ss(f)); +} + +/* optionally require specific source/sink data patterns */ +static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + struct usb_composite_dev *cdev = ss->function.config->cdev; + + for (i = 0; i < req->actual; i++, buf++) { + switch (pattern) { + + /* all-zeroes has no synchronization issues */ + case 0: + if (*buf == 0) + continue; + break; + + /* "mod63" stays in sync with short-terminated transfers, + * OR otherwise when host and gadget agree on how large + * each usb transfer request should be. Resync is done + * with set_interface or set_config. (We *WANT* it to + * get quickly out of sync if controllers or their drivers + * stutter for any reason, including buffer duplcation...) + */ + case 1: + if (*buf == (u8)(i % 63)) + continue; + break; + } + ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf); + usb_ep_set_halt(ss->out_ep); + return -EINVAL; + } + return 0; +} + +static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + + switch (pattern) { + case 0: + memset(req->buf, 0, req->length); + break; + case 1: + for (i = 0; i < req->length; i++) + *buf++ = (u8) (i % 63); + break; + } +} + +static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_sourcesink *ss = ep->driver_data; + struct usb_composite_dev *cdev = ss->function.config->cdev; + int status = req->status; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == ss->out_ep) { + check_read_data(ss, req); + memset(req->buf, 0x55, req->length); + } else + reinit_write_data(ep, req); + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == ss->out_ep) + check_read_data(ss, req); + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough + * buffer. + */ + default: +#if 1 + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +#endif + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) +{ + struct usb_ep *ep; + struct usb_request *req; + int status; + + ep = is_in ? ss->in_ep : ss->out_ep; + req = alloc_ep_req(ep); + if (!req) + return -ENOMEM; + + req->complete = source_sink_complete; + if (is_in) + reinit_write_data(ep, req); + else + memset(req->buf, 0x55, req->length); + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + ERROR(cdev, "start %s %s --> %d\n", + is_in ? "IN" : "OUT", + ep->name, status); + free_ep_req(ep, req); + } + + return status; +} + +static void disable_source_sink(struct f_sourcesink *ss) +{ + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + disable_endpoints(cdev, ss->in_ep, ss->out_ep); + del_timer(&ss->resume); + VDBG(cdev, "%s disabled\n", ss->function.name); +} + +static int +enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) +{ + int result = 0; + const struct usb_endpoint_descriptor *src, *sink; + struct usb_ep *ep; + + src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); + sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); + + /* one endpoint writes (sources) zeroes IN (to the host) */ + ep = ss->in_ep; + result = usb_ep_enable(ep, src); + if (result < 0) + return result; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, true); + if (result < 0) { +fail: + ep = ss->in_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + return result; + } + + /* one endpoint reads (sinks) anything OUT (from the host) */ + ep = ss->out_ep; + result = usb_ep_enable(ep, sink); + if (result < 0) + goto fail; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, false); + if (result < 0) { + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail; + } + + DBG(cdev, "%s enabled\n", ss->function.name); + return result; +} + +static int sourcesink_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt is zero */ + if (ss->in_ep->driver_data) + disable_source_sink(ss); + return enable_source_sink(cdev, ss); +} + +static void sourcesink_disable(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + + disable_source_sink(ss); +} + +static void sourcesink_suspend(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (cdev->gadget->speed == USB_SPEED_UNKNOWN) + return; + + if (autoresume) { + mod_timer(&ss->resume, jiffies + (HZ * autoresume)); + DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); + } else + DBG(cdev, "%s\n", __func__); +} + +static void sourcesink_resume(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "%s\n", __func__); + del_timer(&ss->resume); +} + +/*-------------------------------------------------------------------------*/ + +static int __init sourcesink_bind_config(struct usb_configuration *c) +{ + struct f_sourcesink *ss; + int status; + + ss = kzalloc(sizeof *ss, GFP_KERNEL); + if (!ss) + return -ENOMEM; + + ss->function.name = "source/sink"; + ss->function.descriptors = fs_source_sink_descs; + ss->function.bind = sourcesink_bind; + ss->function.unbind = sourcesink_unbind; + ss->function.set_alt = sourcesink_set_alt; + ss->function.disable = sourcesink_disable; + ss->function.suspend = sourcesink_suspend; + ss->function.resume = sourcesink_resume; + + status = usb_add_function(c, &ss->function); + if (status) + kfree(ss); + return status; +} + +static int sourcesink_setup(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = c->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); + + /* composite driver infrastructure handles everything except + * the two control test requests. + */ + switch (ctrl->bRequest) { + + /* + * These are the same vendor-specific requests supported by + * Intel's USB 2.0 compliance test devices. We exceed that + * device spec by allowing multiple-packet requests. + * + * NOTE: the Control-OUT data stays in req->buf ... better + * would be copying it into a scratch buffer, so that other + * requests may safely intervene. + */ + case 0x5b: /* control WRITE test -- fill the buffer */ + if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* just read that many bytes into the buffer */ + if (w_length > req->length) + break; + value = w_length; + break; + case 0x5c: /* control READ test -- return the buffer */ + if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* expect those bytes are still in the buffer; send back */ + if (w_length > req->length) + break; + value = w_length; + break; + + default: +unknown: + VDBG(c->cdev, + "unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(c->cdev, "source/sinkc response, err %d\n", + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static struct usb_configuration sourcesink_driver = { + .label = "source/sink", + .strings = sourcesink_strings, + .bind = sourcesink_bind_config, + .setup = sourcesink_setup, + .bConfigurationValue = 3, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ + /* .iConfiguration = DYNAMIC */ +}; + +/** + * sourcesink_add - add a source/sink testing configuration to a device + * @cdev: the device to support the configuration + */ +int __init sourcesink_add(struct usb_composite_dev *cdev) +{ + int id; + + /* allocate string ID(s) */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_sourcesink[0].id = id; + + source_sink_intf.iInterface = id; + sourcesink_driver.iConfiguration = id; + + /* support autoresume for remote wakeup testing */ + if (autoresume) + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + sourcesink_driver.descriptors = otg_desc; + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + return usb_add_config(cdev, &sourcesink_driver); +} diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c new file mode 100644 index 0000000..acb8d23 --- /dev/null +++ b/drivers/usb/gadget/f_subset.c @@ -0,0 +1,421 @@ +/* + * f_subset.c -- "CDC Subset" Ethernet link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "u_ether.h" + + +/* + * This function packages a simple "CDC Subset" Ethernet port with no real + * control mechanisms; just raw data transfer over two bulk endpoints. + * The data transfer model is exactly that of CDC Ethernet, which is + * why we call it the "CDC Subset". + * + * Because it's not standardized, this has some interoperability issues. + * They mostly relate to driver binding, since the data transfer model is + * so simple (CDC Ethernet). The original versions of this protocol used + * specific product/vendor IDs: byteswapped IDs for Digital Equipment's + * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported + * daughtercards with USB peripheral connectors. (It was used more often + * with other boards, using the Itsy identifiers.) Linux hosts recognized + * this with CONFIG_USB_ARMLINUX; these devices have only one configuration + * and one interface. + * + * At some point, MCCI defined a (nonconformant) CDC MDLM variant called + * "SAFE", which happens to have a mode which is identical to the "CDC + * Subset" in terms of data transfer and lack of control model. This was + * adopted by later Sharp Zaurus models, and by some other software which + * Linux hosts recognize with CONFIG_USB_NET_ZAURUS. + * + * Because Microsoft's RNDIS drivers are far from robust, we added a few + * descriptors to the CDC Subset code, making this code look like a SAFE + * implementation. This lets you use MCCI's host side MS-Windows drivers + * if you get fed up with RNDIS. It also makes it easier for composite + * drivers to work, since they can use class based binding instead of + * caring about specific product and vendor IDs. + */ + +struct geth_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; +}; + +struct f_gether { + struct gether port; + + char ethaddr[14]; + + struct geth_descs fs; + struct geth_descs hs; +}; + +static inline struct f_gether *func_to_geth(struct usb_function *f) +{ + return container_of(f, struct f_gether, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* + * "Simple" CDC-subset option is a simple vendor-neutral model that most + * full speed controllers can handle: one interface, two bulk endpoints. + * To assist host side drivers, we fancy it up a bit, and add descriptors so + * some host side drivers will understand it as a "SAFE" variant. + * + * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways. + * Data endpoints live in the control interface, there's no data interface. + * And it's not used to talk to a cell phone radio. + */ + +/* interface descriptor: */ + +static struct usb_interface_descriptor subset_data_intf __initdata = { + .bLength = sizeof subset_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { + .bLength = sizeof mdlm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_MDLM_TYPE, + + .bcdVersion = __constant_cpu_to_le16(0x0100), + .bGUID = { + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, + }, +}; + +/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we + * can't really use its struct. All we do here is say that we're using + * the submode of "SAFE" which directly matches the CDC Subset. + */ +static u8 mdlm_detail_desc[] __initdata = { + 6, + USB_DT_CS_INTERFACE, + USB_CDC_MDLM_DETAIL_TYPE, + + 0, /* "SAFE" */ + 0, /* network control capabilities (none) */ + 0, /* network data capabilities ("raw" encapsulation) */ +}; + +static struct usb_cdc_ether_desc ether_desc __initdata = { + .bLength = sizeof ether_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + /* .iMACAddress = DYNAMIC */ + .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = __constant_cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_eth_function[] __initdata = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_eth_function[] __initdata = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string geth_string_defs[] = { + [0].s = "CDC Ethernet Subset/SAFE", + [1].s = NULL /* DYNAMIC */, + { } /* end of list */ +}; + +static struct usb_gadget_strings geth_string_table = { + .language = 0x0409, /* en-us */ + .strings = geth_string_defs, +}; + +static struct usb_gadget_strings *geth_strings[] = { + &geth_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_gether *geth = func_to_geth(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct net_device *net; + + /* we know alt == 0, so this is an activation or a reset */ + + if (geth->port.in_ep->driver_data) { + DBG(cdev, "reset cdc subset\n"); + gether_disconnect(&geth->port); + } + + DBG(cdev, "init + activate cdc subset\n"); + geth->port.in = ep_choose(cdev->gadget, + geth->hs.in, geth->fs.in); + geth->port.out = ep_choose(cdev->gadget, + geth->hs.out, geth->fs.out); + + net = gether_connect(&geth->port); + return IS_ERR(net) ? PTR_ERR(net) : 0; +} + +static void geth_disable(struct usb_function *f) +{ + struct f_gether *geth = func_to_geth(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "net deactivated\n"); + gether_disconnect(&geth->port); +} + +/*-------------------------------------------------------------------------*/ + +/* serial function driver setup/binding */ + +static int __init +geth_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_gether *geth = func_to_geth(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + subset_data_intf.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + geth->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + geth->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(fs_eth_function); + + geth->fs.in = usb_find_endpoint(fs_eth_function, + f->descriptors, &fs_in_desc); + geth->fs.out = usb_find_endpoint(fs_eth_function, + f->descriptors, &fs_out_desc); + + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(hs_eth_function); + + geth->hs.in = usb_find_endpoint(hs_eth_function, + f->hs_descriptors, &hs_in_desc); + geth->hs.out = usb_find_endpoint(hs_eth_function, + f->hs_descriptors, &hs_out_desc); + } + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + geth->port.in_ep->name, geth->port.out_ep->name); + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (geth->port.out) + geth->port.out_ep->driver_data = NULL; + if (geth->port.in) + geth->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +geth_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + geth_string_defs[1].s = NULL; + kfree(func_to_geth(f)); +} + +/** + * geth_bind_config - add CDC Subset network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_gether *geth; + int status; + + if (!ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (geth_string_defs[0].id == 0) { + + /* interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + geth_string_defs[0].id = status; + subset_data_intf.iInterface = status; + + /* MAC address */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + geth_string_defs[1].id = status; + ether_desc.iMACAddress = status; + } + + /* allocate and initialize one new instance */ + geth = kzalloc(sizeof *geth, GFP_KERNEL); + if (!geth) + return -ENOMEM; + + /* export host's Ethernet address in CDC format */ + snprintf(geth->ethaddr, sizeof geth->ethaddr, + "%02X%02X%02X%02X%02X%02X", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + geth_string_defs[1].s = geth->ethaddr; + + geth->port.cdc_filter = DEFAULT_FILTER; + + geth->port.func.name = "cdc_subset"; + geth->port.func.strings = geth_strings; + geth->port.func.bind = geth_bind; + geth->port.func.unbind = geth_unbind; + geth->port.func.set_alt = geth_set_alt; + geth->port.func.disable = geth_disable; + + status = usb_add_function(c, &geth->port.func); + if (status) { + geth_string_defs[1].s = NULL; + kfree(geth); + } + return status; +} diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 47bb9f0..ea2c31d 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL"); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) @@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg) if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", fsg->ep0->name, rc); } return rc; @@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* Save the command for later */ if (fsg->cbbuf_cmnd_size) - WARN(fsg, "CB[I] overwriting previous command\n"); + WARNING(fsg, "CB[I] overwriting previous command\n"); fsg->cbbuf_cmnd_size = req->actual; memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); @@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } } @@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint halt\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_halt -> %d\n", rc); + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); rc = 0; break; } @@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint wedge\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_wedge -> %d\n", rc); + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); rc = 0; break; } @@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg) if (gcnum >= 0) mod_data.release = 0x0300 + gcnum; else { - WARN(fsg, "controller '%s' not recognized\n", + WARNING(fsg, "controller '%s' not recognized\n", fsg->gadget->name); mod_data.release = 0x0399; } @@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; dev_set_drvdata(&curlun->dev, fsg); - snprintf(curlun->dev.bus_id, BUS_ID_SIZE, - "%s-lun%d", gadget->dev.bus_id, i); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); if ((rc = device_register(&curlun->dev)) != 0) { INFO(fsg, "failed to register LUN%d: %d\n", i, rc); diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 1868754..45ad556 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -223,7 +223,7 @@ static int dr_controller_setup(struct fsl_udc *udc) fsl_writel(tmp, &dr_regs->endpointlistaddr); VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", - (int)udc->ep_qh, (int)tmp, + udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); /* Config PHY interface */ @@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (curr_ep->name == NULL) { - WARN("Invalid EP?"); + WARNING("Invalid EP?"); continue; } @@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.name = driver_name; /* Setup gadget.dev and register with kernel */ - strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; ret = device_register(&udc_controller->gadget.dev); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 98b1483..6131752 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h new file mode 100644 index 0000000..dd2f16a --- /dev/null +++ b/drivers/usb/gadget/g_zero.h @@ -0,0 +1,25 @@ +/* + * This header declares the utility functions used by "Gadget Zero", plus + * interfaces to its two single-configuration function drivers. + */ + +#ifndef __G_ZERO_H +#define __G_ZERO_H + +#include + +/* global state */ +extern unsigned buflen; +extern const struct usb_descriptor_header *otg_desc[]; + +/* common utilities */ +struct usb_request *alloc_ep_req(struct usb_ep *ep); +void free_ep_req(struct usb_ep *ep, struct usb_request *req); +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out); + +/* configuration-specific linkup */ +int sourcesink_add(struct usb_composite_dev *cdev); +int loopback_add(struct usb_composite_dev *cdev); + +#endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f7f159c..17d9905 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -11,6 +11,10 @@ * Some are available on 2.4 kernels; several are available, but not * yet pushed in the 2.6 mainline tree. */ + +#ifndef __GADGET_CHIPS_H +#define __GADGET_CHIPS_H + #ifdef CONFIG_USB_GADGET_NET2280 #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) #else @@ -29,8 +33,8 @@ #define gadget_is_dummy(g) 0 #endif -#ifdef CONFIG_USB_GADGET_PXA2XX -#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) +#ifdef CONFIG_USB_GADGET_PXA25X +#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name) #else #define gadget_is_pxa(g) 0 #endif @@ -214,3 +218,28 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x21; return -ENOENT; } + + +/** + * gadget_supports_altsettings - return true if altsettings work + * @gadget: the gadget in question + */ +static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) +{ + /* PXA 21x/25x/26x has no altsettings at all */ + if (gadget_is_pxa(gadget)) + return false; + + /* PXA 27x and 3xx have *broken* altsetting support */ + if (gadget_is_pxa27x(gadget)) + return false; + + /* SH3 hardware just doesn't do altsettings */ + if (gadget_is_sh(gadget)) + return false; + + /* Everything else is *presumably* fine ... */ + return true; +} + +#endif /* __GADGET_CHIPS_H */ diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 7f4d482..ea8651e 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index be6613a..60aa048 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) * usb_gadget_driver_{register,unregister}() must change. */ if (the_controller) { - WARN(dev, "ignoring %s\n", pci_name(pdev)); + WARNING(dev, "ignoring %s\n", pci_name(pdev)); return -EBUSY; } if (!pdev->irq) { @@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.ops = &goku_ops; /* the "gadget" abstracts/virtualizes the controller */ - strcpy(dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index bc4eb1e..566cb23 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -285,7 +285,7 @@ struct goku_udc { #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 69b0a27..f4585d3 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -261,8 +262,6 @@ static const char *CHIP; #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) @@ -483,8 +482,7 @@ ep_release (struct inode *inode, struct file *fd) return 0; } -static int ep_ioctl (struct inode *inode, struct file *fd, - unsigned code, unsigned long value) +static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) { struct ep_data *data = fd->private_data; int status; @@ -740,7 +738,7 @@ static const struct file_operations ep_io_operations = { .read = ep_read, .write = ep_write, - .ioctl = ep_ioctl, + .unlocked_ioctl = ep_ioctl, .release = ep_release, .aio_read = ep_aio_read, @@ -1294,15 +1292,18 @@ out: return mask; } -static int dev_ioctl (struct inode *inode, struct file *fd, - unsigned code, unsigned long value) +static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) { struct dev_data *dev = fd->private_data; struct usb_gadget *gadget = dev->gadget; + long ret = -ENOTTY; - if (gadget->ops->ioctl) - return gadget->ops->ioctl (gadget, code, value); - return -ENOTTY; + if (gadget->ops->ioctl) { + lock_kernel(); + ret = gadget->ops->ioctl (gadget, code, value); + unlock_kernel(); + } + return ret; } /* used after device configuration */ @@ -1314,7 +1315,7 @@ static const struct file_operations ep0_io_operations = { .write = ep0_write, .fasync = ep0_fasync, .poll = ep0_poll, - .ioctl = dev_ioctl, + .unlocked_ioctl = dev_ioctl, .release = dev_release, }; @@ -1501,7 +1502,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } break; -#ifndef CONFIG_USB_GADGET_PXA2XX +#ifndef CONFIG_USB_GADGET_PXA25X /* PXA automagically handles this request too */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != 0x80) @@ -1964,7 +1965,7 @@ static const struct file_operations dev_init_operations = { .open = dev_open, .write = dev_config, .fasync = ep0_fasync, - .ioctl = dev_ioctl, + .unlocked_ioctl = dev_ioctl, .release = dev_release, }; diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index 825abd2..c6e7df0 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = { static void nop_release(struct device *dev) { - DEBUG("%s %s\n", __func__, dev->bus_id); + DEBUG("%s %s\n", __func__, dev_name(dev)); } static struct lh7a40x_udc memory = { diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h index 1ecfd63..ca86120 100644 --- a/drivers/usb/gadget/lh7a40x_udc.h +++ b/drivers/usb/gadget/lh7a40x_udc.h @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index ee6b35f..77b44fb 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.ops = &m66592_gadget_ops; device_initialize(&m66592->gadget.dev); - strcpy(m66592->gadget.dev.bus_id, "gadget"); + dev_set_name(&m66592->gadget.dev, "gadget"); m66592->gadget.is_dualspeed = 1; m66592->gadget.dev.parent = &pdev->dev; m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index 09e3ee4..df886ce 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -1,11 +1,11 @@ /* - * ndis.h - * + * ndis.h + * * ntddndis.h modified by Benedikt Spranger - * - * Thanks to the cygwin development team, + * + * Thanks to the cygwin development team, * espacially to Casper S. Hornstrup - * + * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index e018623..5cfb5eb 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep) * 0122, and 0124; not all cases trigger the warning. */ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARN (ep->dev, "%s lost packet sync!\n", + WARNING (ep->dev, "%s lost packet sync!\n", ep->ep.name); req->req.status = -EOVERFLOW; } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { @@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.is_dualspeed = 1; /* the "gadget" abstracts/virtualizes the controller */ - strcpy (dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index 1f2af39..81a71db 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev) #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 881d74c..bb54cca 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -52,8 +52,9 @@ #include #include -#include -#include +#include +#include +#include #include "omap_udc.h" @@ -135,13 +136,17 @@ static void use_ep(struct omap_ep *ep, u16 select) if (ep->bEndpointAddress & USB_DIR_IN) num |= UDC_EP_DIR; - UDC_EP_NUM_REG = num | select; + omap_writew(num | select, UDC_EP_NUM); /* when select, MUST deselect later !! */ } static inline void deselect_ep(void) { - UDC_EP_NUM_REG &= ~UDC_EP_SEL; + u16 w; + + w = omap_readw(UDC_EP_NUM); + w &= ~UDC_EP_SEL; + omap_writew(w, UDC_EP_NUM); /* 6 wait states before TX will happen */ } @@ -216,7 +221,7 @@ static int omap_ep_enable(struct usb_ep *_ep, ep->has_dma = 0; ep->lch = -1; use_ep(ep, UDC_EP_SEL); - UDC_CTRL_REG = udc->clr_halt; + omap_writew(udc->clr_halt, UDC_CTRL); ep->ackwait = 0; deselect_ep(); @@ -232,7 +237,7 @@ static int omap_ep_enable(struct usb_ep *_ep, if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC && !ep->has_dma && !(ep->bEndpointAddress & USB_DIR_IN)) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } @@ -259,7 +264,7 @@ static int omap_ep_disable(struct usb_ep *_ep) nuke (ep, -ESHUTDOWN); ep->ep.maxpacket = ep->maxpacket; ep->has_dma = 0; - UDC_CTRL_REG = UDC_SET_HALT; + omap_writew(UDC_SET_HALT, UDC_CTRL); list_del_init(&ep->iso); del_timer(&ep->timer); @@ -360,13 +365,13 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max) if (likely((((int)buf) & 1) == 0)) { wp = (u16 *)buf; while (max >= 2) { - UDC_DATA_REG = *wp++; + omap_writew(*wp++, UDC_DATA); max -= 2; } buf = (u8 *)wp; } while (max--) - *(volatile u8 *)&UDC_DATA_REG = *buf++; + omap_writeb(*buf++, UDC_DATA); return len; } @@ -385,13 +390,13 @@ static int write_fifo(struct omap_ep *ep, struct omap_req *req) prefetch(buf); /* PIO-IN isn't double buffered except for iso */ - ep_stat = UDC_STAT_FLG_REG; + ep_stat = omap_readw(UDC_STAT_FLG); if (ep_stat & UDC_FIFO_UNWRITABLE) return 0; count = ep->ep.maxpacket; count = write_packet(buf, req, count); - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1; /* last packet is often short (sometimes a zlp) */ @@ -425,13 +430,13 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail) if (likely((((int)buf) & 1) == 0)) { wp = (u16 *)buf; while (avail >= 2) { - *wp++ = UDC_DATA_REG; + *wp++ = omap_readw(UDC_DATA); avail -= 2; } buf = (u8 *)wp; } while (avail--) - *buf++ = *(volatile u8 *)&UDC_DATA_REG; + *buf++ = omap_readb(UDC_DATA); return len; } @@ -446,7 +451,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) prefetchw(buf); for (;;) { - u16 ep_stat = UDC_STAT_FLG_REG; + u16 ep_stat = omap_readw(UDC_STAT_FLG); is_last = 0; if (ep_stat & FIFO_EMPTY) { @@ -460,7 +465,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) if (ep_stat & UDC_FIFO_FULL) avail = ep->ep.maxpacket; else { - avail = UDC_RXFSTAT_REG; + avail = omap_readw(UDC_RXFSTAT); ep->fnf = ep->double_buf; } count = read_packet(buf, req, avail); @@ -473,7 +478,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) req->req.status = -EOVERFLOW; avail -= count; while (avail--) - (void) *(volatile u8 *)&UDC_DATA_REG; + omap_readw(UDC_DATA); } } else if (req->req.length == req->req.actual) is_last = 1; @@ -491,32 +496,6 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req) /*-------------------------------------------------------------------------*/ -static inline dma_addr_t dma_csac(unsigned lch) -{ - dma_addr_t csac; - - /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is - * read before the DMA controller finished disabling the channel. - */ - csac = OMAP_DMA_CSAC_REG(lch); - if (csac == 0) - csac = OMAP_DMA_CSAC_REG(lch); - return csac; -} - -static inline dma_addr_t dma_cdac(unsigned lch) -{ - dma_addr_t cdac; - - /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is - * read before the DMA controller finished disabling the channel. - */ - cdac = OMAP_DMA_CDAC_REG(lch); - if (cdac == 0) - cdac = OMAP_DMA_CDAC_REG(lch); - return cdac; -} - static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) { dma_addr_t end; @@ -527,7 +506,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) if (cpu_is_omap15xx()) return 0; - end = dma_csac(ep->lch); + end = omap_get_dma_src_pos(ep->lch); if (end == ep->dma_counter) return 0; @@ -537,15 +516,11 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) return end - start; } -#define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ - ? OMAP_DMA_CSAC_REG(x) /* really: CPC */ \ - : dma_cdac(x)) - static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) { dma_addr_t end; - end = DMA_DEST_LAST(ep->lch); + end = omap_get_dma_dst_pos(ep->lch); if (end == ep->dma_counter) return 0; @@ -565,7 +540,7 @@ static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) static void next_in_dma(struct omap_ep *ep, struct omap_req *req) { - u16 txdma_ctrl; + u16 txdma_ctrl, w; unsigned length = req->req.length - req->req.actual; const int sync_mode = cpu_is_omap15xx() ? OMAP_DMA_SYNC_FRAME @@ -596,14 +571,18 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) 0, 0); omap_start_dma(ep->lch); - ep->dma_counter = dma_csac(ep->lch); - UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); - UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; + ep->dma_counter = omap_get_dma_src_pos(ep->lch); + w = omap_readw(UDC_DMA_IRQ_EN); + w |= UDC_TX_DONE_IE(ep->dma_channel); + omap_writew(w, UDC_DMA_IRQ_EN); + omap_writew(UDC_TXN_START | txdma_ctrl, UDC_TXDMA(ep->dma_channel)); req->dma_bytes = length; } static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) { + u16 w; + if (status == 0) { req->req.actual += req->dma_bytes; @@ -620,7 +599,9 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) /* tx completion */ omap_stop_dma(ep->lch); - UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel); + w = omap_readw(UDC_DMA_IRQ_EN); + w &= ~UDC_TX_DONE_IE(ep->dma_channel); + omap_writew(w, UDC_DMA_IRQ_EN); done(ep, req, status); } @@ -628,6 +609,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) { unsigned packets = req->req.length - req->req.actual; int dma_trigger = 0; + u16 w; if (cpu_is_omap24xx()) dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel); @@ -654,12 +636,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 0, 0); - ep->dma_counter = DMA_DEST_LAST(ep->lch); + ep->dma_counter = omap_get_dma_dst_pos(ep->lch); - UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); - UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); - UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf); - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel)); + w = omap_readw(UDC_DMA_IRQ_EN); + w |= UDC_RX_EOT_IE(ep->dma_channel); + omap_writew(w, UDC_DMA_IRQ_EN); + omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); omap_start_dma(ep->lch); } @@ -667,7 +651,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) static void finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) { - u16 count; + u16 count, w; if (status == 0) ep->dma_counter = (u16) (req->req.dma + req->req.actual); @@ -686,13 +670,15 @@ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one) return; /* rx completion */ - UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel); + w = omap_readw(UDC_DMA_IRQ_EN); + w &= ~UDC_RX_EOT_IE(ep->dma_channel); + omap_writew(w, UDC_DMA_IRQ_EN); done(ep, req, status); } static void dma_irq(struct omap_udc *udc, u16 irq_src) { - u16 dman_stat = UDC_DMAN_STAT_REG; + u16 dman_stat = omap_readw(UDC_DMAN_STAT); struct omap_ep *ep; struct omap_req *req; @@ -706,7 +692,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) struct omap_req, queue); finish_in_dma(ep, req, 0); } - UDC_IRQ_SRC_REG = UDC_TXN_DONE; + omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC); if (!list_empty (&ep->queue)) { req = container_of(ep->queue.next, @@ -725,7 +711,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) struct omap_req, queue); finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB); } - UDC_IRQ_SRC_REG = UDC_RXN_EOT; + omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC); if (!list_empty (&ep->queue)) { req = container_of(ep->queue.next, @@ -739,7 +725,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) ep->irqs++; /* omap15xx does this unasked... */ VDBG("%s, RX_CNT irq?\n", ep->ep.name); - UDC_IRQ_SRC_REG = UDC_RXN_CNT; + omap_writew(UDC_RXN_CNT, UDC_IRQ_SRC); } } @@ -762,9 +748,9 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) is_in = ep->bEndpointAddress & USB_DIR_IN; if (is_in) - reg = UDC_TXDMA_CFG_REG; + reg = omap_readw(UDC_TXDMA_CFG); else - reg = UDC_RXDMA_CFG_REG; + reg = omap_readw(UDC_RXDMA_CFG); reg |= UDC_DMA_REQ; /* "pulse" activated */ ep->dma_channel = 0; @@ -792,7 +778,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { - UDC_TXDMA_CFG_REG = reg; + omap_writew(reg, UDC_TXDMA_CFG); /* EMIFF or SDRC */ omap_set_dma_src_burst_mode(ep->lch, OMAP_DMA_DATA_BURST_4); @@ -801,7 +787,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, - (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), + UDC_DATA_DMA, 0, 0); } } else { @@ -813,12 +799,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { - UDC_RXDMA_CFG_REG = reg; + omap_writew(reg, UDC_RXDMA_CFG); /* TIPB */ omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, - (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), + UDC_DATA_DMA, 0, 0); /* EMIFF or SDRC */ omap_set_dma_dest_burst_mode(ep->lch, @@ -834,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) /* channel type P: hw synch (fifo) */ if (cpu_class_is_omap1() && !cpu_is_omap15xx()) - OMAP1_DMA_LCH_CTRL_REG(ep->lch) = 2; + omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P); } just_restart: @@ -860,7 +846,7 @@ just_restart: (is_in ? write_fifo : read_fifo)(ep, req); deselect_ep(); if (!is_in) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } /* IN: 6 wait states before it'll tx */ @@ -881,7 +867,7 @@ static void dma_channel_release(struct omap_ep *ep) else req = NULL; - active = ((1 << 7) & OMAP_DMA_CCR_REG(ep->lch)) != 0; + active = omap_get_dma_active_status(ep->lch); DBG("%s release %s %cxdma%d %p\n", ep->ep.name, active ? "active" : "idle", @@ -894,23 +880,25 @@ static void dma_channel_release(struct omap_ep *ep) /* wait till current packet DMA finishes, and fifo empties */ if (ep->bEndpointAddress & USB_DIR_IN) { - UDC_TXDMA_CFG_REG = (UDC_TXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; + omap_writew((omap_readw(UDC_TXDMA_CFG) & ~mask) | UDC_DMA_REQ, + UDC_TXDMA_CFG); if (req) { finish_in_dma(ep, req, -ECONNRESET); /* clear FIFO; hosts probably won't empty it */ use_ep(ep, UDC_EP_SEL); - UDC_CTRL_REG = UDC_CLR_EP; + omap_writew(UDC_CLR_EP, UDC_CTRL); deselect_ep(); } - while (UDC_TXDMA_CFG_REG & mask) + while (omap_readw(UDC_TXDMA_CFG) & mask) udelay(10); } else { - UDC_RXDMA_CFG_REG = (UDC_RXDMA_CFG_REG & ~mask) | UDC_DMA_REQ; + omap_writew((omap_readw(UDC_RXDMA_CFG) & ~mask) | UDC_DMA_REQ, + UDC_RXDMA_CFG); /* dma empties the fifo */ - while (UDC_RXDMA_CFG_REG & mask) + while (omap_readw(UDC_RXDMA_CFG) & mask) udelay(10); if (req) finish_out_dma(ep, req, -ECONNRESET, 0); @@ -997,9 +985,13 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->req.actual = 0; /* maybe kickstart non-iso i/o queues */ - if (is_iso) - UDC_IRQ_EN_REG |= UDC_SOF_IE; - else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { + if (is_iso) { + u16 w; + + w = omap_readw(UDC_IRQ_EN); + w |= UDC_SOF_IE; + omap_writew(w, UDC_IRQ_EN); + } else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { int is_in; if (ep->bEndpointAddress == 0) { @@ -1017,23 +1009,23 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * requests to non-control endpoints */ if (udc->ep0_set_config) { - u16 irq_en = UDC_IRQ_EN_REG; + u16 irq_en = omap_readw(UDC_IRQ_EN); irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE; if (!udc->ep0_reset_config) irq_en |= UDC_EPN_RX_IE | UDC_EPN_TX_IE; - UDC_IRQ_EN_REG = irq_en; + omap_writew(irq_en, UDC_IRQ_EN); } /* STATUS for zero length DATA stages is * always an IN ... even for IN transfers, * a weird case which seem to stall OMAP. */ - UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR); - UDC_CTRL_REG = UDC_CLR_EP; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_CLR_EP, UDC_CTRL); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(UDC_EP_DIR, UDC_EP_NUM); /* cleanup */ udc->ep0_pending = 0; @@ -1042,11 +1034,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* non-empty DATA stage */ } else if (is_in) { - UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); } else { if (udc->ep0_setup) goto irq_wait; - UDC_EP_NUM_REG = UDC_EP_SEL; + omap_writew(UDC_EP_SEL, UDC_EP_NUM); } } else { is_in = ep->bEndpointAddress & USB_DIR_IN; @@ -1062,7 +1054,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req = NULL; deselect_ep(); if (!is_in) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } /* IN: 6 wait states before it'll tx */ @@ -1129,10 +1121,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) status = -EINVAL; else if (value) { if (ep->udc->ep0_set_config) { - WARN("error changing config?\n"); - UDC_SYSCON2_REG = UDC_CLR_CFG; + WARNING("error changing config?\n"); + omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } - UDC_SYSCON2_REG = UDC_STALL_CMD; + omap_writew(UDC_STALL_CMD, UDC_SYSCON2); ep->udc->ep0_pending = 0; status = 0; } else /* NOP */ @@ -1159,8 +1151,8 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) channel = 0; use_ep(ep, UDC_EP_SEL); - if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) { - UDC_CTRL_REG = UDC_SET_HALT; + if (omap_readw(UDC_STAT_FLG) & UDC_NON_ISO_FIFO_EMPTY) { + omap_writew(UDC_SET_HALT, UDC_CTRL); status = 0; } else status = -EAGAIN; @@ -1170,10 +1162,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) dma_channel_claim(ep, channel); } else { use_ep(ep, 0); - UDC_CTRL_REG = ep->udc->clr_halt; + omap_writew(ep->udc->clr_halt, UDC_CTRL); ep->ackwait = 0; if (!(ep->bEndpointAddress & USB_DIR_IN)) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } } @@ -1205,7 +1197,7 @@ static struct usb_ep_ops omap_ep_ops = { static int omap_get_frame(struct usb_gadget *gadget) { - u16 sof = UDC_SOF_REG; + u16 sof = omap_readw(UDC_SOF); return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC; } @@ -1224,7 +1216,7 @@ static int omap_wakeup(struct usb_gadget *gadget) */ if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) { DBG("remote wakeup...\n"); - UDC_SYSCON2_REG = UDC_RMT_WKP; + omap_writew(UDC_RMT_WKP, UDC_SYSCON2); retval = 0; } @@ -1247,12 +1239,12 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) udc = container_of(gadget, struct omap_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - syscon1 = UDC_SYSCON1_REG; + syscon1 = omap_readw(UDC_SYSCON1); if (is_selfpowered) syscon1 |= UDC_SELF_PWR; else syscon1 &= ~UDC_SELF_PWR; - UDC_SYSCON1_REG = syscon1; + omap_writew(syscon1, UDC_SYSCON1); spin_unlock_irqrestore(&udc->lock, flags); return 0; @@ -1265,18 +1257,36 @@ static int can_pullup(struct omap_udc *udc) static void pullup_enable(struct omap_udc *udc) { - UDC_SYSCON1_REG |= UDC_PULLUP_EN; - if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) - OTG_CTRL_REG |= OTG_BSESSVLD; - UDC_IRQ_EN_REG = UDC_DS_CHG_IE; + u16 w; + + w = omap_readw(UDC_SYSCON1); + w |= UDC_PULLUP_EN; + omap_writew(w, UDC_SYSCON1); + if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { + u32 l; + + l = omap_readl(OTG_CTRL); + l |= OTG_BSESSVLD; + omap_writel(l, OTG_CTRL); + } + omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); } static void pullup_disable(struct omap_udc *udc) { - if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) - OTG_CTRL_REG &= ~OTG_BSESSVLD; - UDC_IRQ_EN_REG = UDC_DS_CHG_IE; - UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; + u16 w; + + if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) { + u32 l; + + l = omap_readl(OTG_CTRL); + l &= ~OTG_BSESSVLD; + omap_writel(l, OTG_CTRL); + } + omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN); + w = omap_readw(UDC_SYSCON1); + w &= ~UDC_PULLUP_EN; + omap_writew(w, UDC_SYSCON1); } static struct omap_udc *udc; @@ -1304,6 +1314,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) { struct omap_udc *udc; unsigned long flags; + u32 l; udc = container_of(gadget, struct omap_udc, gadget); spin_lock_irqsave(&udc->lock, flags); @@ -1311,10 +1322,12 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) udc->vbus_active = (is_active != 0); if (cpu_is_omap15xx()) { /* "software" detect, ignored if !VBUS_MODE_1510 */ + l = omap_readl(FUNC_MUX_CTRL_0); if (is_active) - FUNC_MUX_CTRL_0_REG |= VBUS_CTRL_1510; + l |= VBUS_CTRL_1510; else - FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; + l &= ~VBUS_CTRL_1510; + omap_writel(l, FUNC_MUX_CTRL_0); } if (udc->dc_clk != NULL && is_active) { if (!udc->clk_requested) { @@ -1384,9 +1397,9 @@ static void nuke(struct omap_ep *ep, int status) dma_channel_release(ep); use_ep(ep, 0); - UDC_CTRL_REG = UDC_CLR_EP; + omap_writew(UDC_CLR_EP, UDC_CTRL); if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) - UDC_CTRL_REG = UDC_SET_HALT; + omap_writew(UDC_SET_HALT, UDC_CTRL); while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct omap_req, queue); @@ -1414,8 +1427,8 @@ static void update_otg(struct omap_udc *udc) if (!gadget_is_otg(&udc->gadget)) return; - if (OTG_CTRL_REG & OTG_ID) - devstat = UDC_DEVSTAT_REG; + if (omap_readl(OTG_CTRL) & OTG_ID) + devstat = omap_readw(UDC_DEVSTAT); else devstat = 0; @@ -1426,9 +1439,14 @@ static void update_otg(struct omap_udc *udc) /* Enable HNP early, avoiding races on suspend irq path. * ASSUMES OTG state machine B_BUS_REQ input is true. */ - if (udc->gadget.b_hnp_enable) - OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ) - & ~OTG_PULLUP; + if (udc->gadget.b_hnp_enable) { + u32 l; + + l = omap_readl(OTG_CTRL); + l |= OTG_B_HNPEN | OTG_B_BUSREQ; + l &= ~OTG_PULLUP; + omap_writel(l, OTG_CTRL); + } } static void ep0_irq(struct omap_udc *udc, u16 irq_src) @@ -1446,7 +1464,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) nuke(ep0, 0); if (ack) { - UDC_IRQ_SRC_REG = ack; + omap_writew(ack, UDC_IRQ_SRC); irq_src = UDC_SETUP; } } @@ -1466,9 +1484,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (irq_src & UDC_EP0_TX) { int stat; - UDC_IRQ_SRC_REG = UDC_EP0_TX; - UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; - stat = UDC_STAT_FLG_REG; + omap_writew(UDC_EP0_TX, UDC_IRQ_SRC); + omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); + stat = omap_readw(UDC_STAT_FLG); if (stat & UDC_ACK) { if (udc->ep0_in) { /* write next IN packet from response, @@ -1476,26 +1494,26 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) */ if (req) stat = write_fifo(ep0, req); - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_DIR, UDC_EP_NUM); if (!req && udc->ep0_pending) { - UDC_EP_NUM_REG = UDC_EP_SEL; - UDC_CTRL_REG = UDC_CLR_EP; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = 0; + omap_writew(UDC_EP_SEL, UDC_EP_NUM); + omap_writew(UDC_CLR_EP, UDC_CTRL); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(0, UDC_EP_NUM); udc->ep0_pending = 0; } /* else: 6 wait states before it'll tx */ } else { /* ack status stage of OUT transfer */ - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_DIR, UDC_EP_NUM); if (req) done(ep0, req, 0); } req = NULL; } else if (stat & UDC_STALL) { - UDC_CTRL_REG = UDC_CLR_HALT; - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_CLR_HALT, UDC_CTRL); + omap_writew(UDC_EP_DIR, UDC_EP_NUM); } else { - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_DIR, UDC_EP_NUM); } } @@ -1503,9 +1521,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (irq_src & UDC_EP0_RX) { int stat; - UDC_IRQ_SRC_REG = UDC_EP0_RX; - UDC_EP_NUM_REG = UDC_EP_SEL; - stat = UDC_STAT_FLG_REG; + omap_writew(UDC_EP0_RX, UDC_IRQ_SRC); + omap_writew(UDC_EP_SEL, UDC_EP_NUM); + stat = omap_readw(UDC_STAT_FLG); if (stat & UDC_ACK) { if (!udc->ep0_in) { stat = 0; @@ -1513,34 +1531,35 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) * reactiviting the fifo; stall on errors. */ if (!req || (stat = read_fifo(ep0, req)) < 0) { - UDC_SYSCON2_REG = UDC_STALL_CMD; + omap_writew(UDC_STALL_CMD, UDC_SYSCON2); udc->ep0_pending = 0; stat = 0; } else if (stat == 0) - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = 0; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(0, UDC_EP_NUM); /* activate status stage */ if (stat == 1) { done(ep0, req, 0); /* that may have STALLed ep0... */ - UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; - UDC_CTRL_REG = UDC_CLR_EP; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_SEL | UDC_EP_DIR, + UDC_EP_NUM); + omap_writew(UDC_CLR_EP, UDC_CTRL); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(UDC_EP_DIR, UDC_EP_NUM); udc->ep0_pending = 0; } } else { /* ack status stage of IN transfer */ - UDC_EP_NUM_REG = 0; + omap_writew(0, UDC_EP_NUM); if (req) done(ep0, req, 0); } } else if (stat & UDC_STALL) { - UDC_CTRL_REG = UDC_CLR_HALT; - UDC_EP_NUM_REG = 0; + omap_writew(UDC_CLR_HALT, UDC_CTRL); + omap_writew(0, UDC_EP_NUM); } else { - UDC_EP_NUM_REG = 0; + omap_writew(0, UDC_EP_NUM); } } @@ -1555,14 +1574,14 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* read the (latest) SETUP message */ do { - UDC_EP_NUM_REG = UDC_SETUP_SEL; + omap_writew(UDC_SETUP_SEL, UDC_EP_NUM); /* two bytes at a time */ - u.word[0] = UDC_DATA_REG; - u.word[1] = UDC_DATA_REG; - u.word[2] = UDC_DATA_REG; - u.word[3] = UDC_DATA_REG; - UDC_EP_NUM_REG = 0; - } while (UDC_IRQ_SRC_REG & UDC_SETUP); + u.word[0] = omap_readw(UDC_DATA); + u.word[1] = omap_readw(UDC_DATA); + u.word[2] = omap_readw(UDC_DATA); + u.word[3] = omap_readw(UDC_DATA); + omap_writew(0, UDC_EP_NUM); + } while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP); #define w_value le16_to_cpu(u.r.wValue) #define w_index le16_to_cpu(u.r.wIndex) @@ -1593,9 +1612,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) * later if it fails the request. */ if (udc->ep0_reset_config) - UDC_SYSCON2_REG = UDC_CLR_CFG; + omap_writew(UDC_CLR_CFG, UDC_SYSCON2); else - UDC_SYSCON2_REG = UDC_DEV_CFG; + omap_writew(UDC_DEV_CFG, UDC_SYSCON2); update_otg(udc); goto delegate; case USB_REQ_CLEAR_FEATURE: @@ -1613,10 +1632,10 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) || !ep->desc) goto do_stall; use_ep(ep, 0); - UDC_CTRL_REG = udc->clr_halt; + omap_writew(udc->clr_halt, UDC_CTRL); ep->ackwait = 0; if (!(ep->bEndpointAddress & USB_DIR_IN)) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } /* NOTE: assumes the host behaves sanely, @@ -1649,15 +1668,15 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) } use_ep(ep, 0); /* can't halt if fifo isn't empty... */ - UDC_CTRL_REG = UDC_CLR_EP; - UDC_CTRL_REG = UDC_SET_HALT; + omap_writew(UDC_CLR_EP, UDC_CTRL); + omap_writew(UDC_SET_HALT, UDC_CTRL); VDBG("%s halted by host\n", ep->name); ep0out_status_stage: status = 0; - UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; - UDC_CTRL_REG = UDC_CLR_EP; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_CLR_EP, UDC_CTRL); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(UDC_EP_DIR, UDC_EP_NUM); udc->ep0_pending = 0; break; case USB_REQ_GET_STATUS: @@ -1694,10 +1713,10 @@ intf_status: zero_status: /* return two zero bytes */ - UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; - UDC_DATA_REG = 0; - UDC_CTRL_REG = UDC_SET_FIFO_EN; - UDC_EP_NUM_REG = UDC_EP_DIR; + omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM); + omap_writew(0, UDC_DATA); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); + omap_writew(UDC_EP_DIR, UDC_EP_NUM); status = 0; VDBG("GET_STATUS, interface %d\n", w_index); /* next, status stage */ @@ -1706,8 +1725,8 @@ zero_status: delegate: /* activate the ep0out fifo right away */ if (!udc->ep0_in && w_length) { - UDC_EP_NUM_REG = 0; - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(0, UDC_EP_NUM); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); } /* gadget drivers see class/vendor specific requests, @@ -1746,11 +1765,11 @@ do_stall: u.r.bRequestType, u.r.bRequest, status); if (udc->ep0_set_config) { if (udc->ep0_reset_config) - WARN("error resetting config?\n"); + WARNING("error resetting config?\n"); else - UDC_SYSCON2_REG = UDC_CLR_CFG; + omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } - UDC_SYSCON2_REG = UDC_STALL_CMD; + omap_writew(UDC_STALL_CMD, UDC_SYSCON2); udc->ep0_pending = 0; } } @@ -1764,7 +1783,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) { u16 devstat, change; - devstat = UDC_DEVSTAT_REG; + devstat = omap_readw(UDC_DEVSTAT); change = devstat ^ udc->devstat; udc->devstat = devstat; @@ -1804,7 +1823,8 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) INFO("USB reset done, gadget %s\n", udc->driver->driver.name); /* ep0 traffic is legal from now on */ - UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE; + omap_writew(UDC_DS_CHG_IE | UDC_EP0_IE, + UDC_IRQ_EN); } change &= ~UDC_USB_RESET; } @@ -1848,7 +1868,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) VDBG("devstat %03x, ignore change %03x\n", devstat, change); - UDC_IRQ_SRC_REG = UDC_DS_CHG; + omap_writew(UDC_DS_CHG, UDC_IRQ_SRC); } static irqreturn_t omap_udc_irq(int irq, void *_udc) @@ -1859,7 +1879,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc) unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - irq_src = UDC_IRQ_SRC_REG; + irq_src = omap_readw(UDC_IRQ_SRC); /* Device state change (usb ch9 stuff) */ if (irq_src & UDC_DS_CHG) { @@ -1882,7 +1902,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc) irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); } - irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX); + irq_src &= ~(UDC_IRQ_SOF | UDC_EPN_TX|UDC_EPN_RX); if (irq_src) DBG("udc_irq, unhandled %03x\n", irq_src); spin_unlock_irqrestore(&udc->lock, flags); @@ -1903,7 +1923,7 @@ static void pio_out_timer(unsigned long _ep) spin_lock_irqsave(&ep->udc->lock, flags); if (!list_empty(&ep->queue) && ep->ackwait) { use_ep(ep, UDC_EP_SEL); - stat_flg = UDC_STAT_FLG_REG; + stat_flg = omap_readw(UDC_STAT_FLG); if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) || (ep->double_buf && HALF_FULL(stat_flg)))) { @@ -1913,8 +1933,8 @@ static void pio_out_timer(unsigned long _ep) req = container_of(ep->queue.next, struct omap_req, queue); (void) read_fifo(ep, req); - UDC_EP_NUM_REG = ep->bEndpointAddress; - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(ep->bEndpointAddress, UDC_EP_NUM); + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } else deselect_ep(); @@ -1934,20 +1954,20 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - epn_stat = UDC_EPN_STAT_REG; - irq_src = UDC_IRQ_SRC_REG; + epn_stat = omap_readw(UDC_EPN_STAT); + irq_src = omap_readw(UDC_IRQ_SRC); /* handle OUT first, to avoid some wasteful NAKs */ if (irq_src & UDC_EPN_RX) { epnum = (epn_stat >> 8) & 0x0f; - UDC_IRQ_SRC_REG = UDC_EPN_RX; + omap_writew(UDC_EPN_RX, UDC_IRQ_SRC); status = IRQ_HANDLED; ep = &udc->ep[epnum]; ep->irqs++; - UDC_EP_NUM_REG = epnum | UDC_EP_SEL; + omap_writew(epnum | UDC_EP_SEL, UDC_EP_NUM); ep->fnf = 0; - if ((UDC_STAT_FLG_REG & UDC_ACK)) { + if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { ep->ackwait--; if (!list_empty(&ep->queue)) { int stat; @@ -1959,15 +1979,15 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) } } /* min 6 clock delay before clearing EP_SEL ... */ - epn_stat = UDC_EPN_STAT_REG; - epn_stat = UDC_EPN_STAT_REG; - UDC_EP_NUM_REG = epnum; + epn_stat = omap_readw(UDC_EPN_STAT); + epn_stat = omap_readw(UDC_EPN_STAT); + omap_writew(epnum, UDC_EP_NUM); /* enabling fifo _after_ clearing ACK, contrary to docs, * reduces lossage; timer still needed though (sigh). */ if (ep->fnf) { - UDC_CTRL_REG = UDC_SET_FIFO_EN; + omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); ep->ackwait = 1 + ep->double_buf; } mod_timer(&ep->timer, PIO_OUT_TIMEOUT); @@ -1976,13 +1996,13 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) /* then IN transfers */ else if (irq_src & UDC_EPN_TX) { epnum = epn_stat & 0x0f; - UDC_IRQ_SRC_REG = UDC_EPN_TX; + omap_writew(UDC_EPN_TX, UDC_IRQ_SRC); status = IRQ_HANDLED; ep = &udc->ep[16 + epnum]; ep->irqs++; - UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; - if ((UDC_STAT_FLG_REG & UDC_ACK)) { + omap_writew(epnum | UDC_EP_DIR | UDC_EP_SEL, UDC_EP_NUM); + if (omap_readw(UDC_STAT_FLG) & UDC_ACK) { ep->ackwait = 0; if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, @@ -1991,9 +2011,9 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev) } } /* min 6 clock delay before clearing EP_SEL ... */ - epn_stat = UDC_EPN_STAT_REG; - epn_stat = UDC_EPN_STAT_REG; - UDC_EP_NUM_REG = epnum | UDC_EP_DIR; + epn_stat = omap_readw(UDC_EPN_STAT); + epn_stat = omap_readw(UDC_EPN_STAT); + omap_writew(epnum | UDC_EP_DIR, UDC_EP_NUM); /* then 6 clocks before it'd tx */ } @@ -2021,7 +2041,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) req = list_entry(ep->queue.next, struct omap_req, queue); use_ep(ep, UDC_EP_SEL); - stat = UDC_STAT_FLG_REG; + stat = omap_readw(UDC_STAT_FLG); /* NOTE: like the other controller drivers, this isn't * currently reporting lost or damaged frames. @@ -2053,9 +2073,14 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) if (!list_empty(&ep->queue)) pending = 1; } - if (!pending) - UDC_IRQ_EN_REG &= ~UDC_SOF_IE; - UDC_IRQ_SRC_REG = UDC_SOF; + if (!pending) { + u16 w; + + w = omap_readw(UDC_IRQ_EN); + w &= ~UDC_SOF_IE; + omap_writew(w, UDC_IRQ_EN); + } + omap_writew(UDC_IRQ_SOF, UDC_IRQ_SRC); spin_unlock_irqrestore(&udc->lock, flags); return IRQ_HANDLED; @@ -2104,7 +2129,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) continue; use_ep(ep, 0); - UDC_CTRL_REG = UDC_SET_HALT; + omap_writew(UDC_SET_HALT, UDC_CTRL); } udc->ep0_pending = 0; udc->ep[0].irqs = 0; @@ -2128,7 +2153,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) } DBG("bound to driver %s\n", driver->driver.name); - UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; + omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); /* connect to bus through transceiver */ if (udc->transceiver) { @@ -2225,7 +2250,7 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) else buf[0] = 0; - stat_flg = UDC_STAT_FLG_REG; + stat_flg = omap_readw(UDC_STAT_FLG); seq_printf(s, "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", ep->name, buf, @@ -2286,17 +2311,17 @@ static int proc_otg_show(struct seq_file *s) u32 trans; char *ctrl_name; - tmp = OTG_REV_REG; + tmp = omap_readl(OTG_REV); if (cpu_is_omap24xx()) { ctrl_name = "control_devconf"; - trans = CONTROL_DEVCONF_REG; + trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); } else { ctrl_name = "tranceiver_ctrl"; - trans = USB_TRANSCEIVER_CTRL_REG; + trans = omap_readw(USB_TRANSCEIVER_CTRL); } seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", tmp >> 4, tmp & 0xf, ctrl_name, trans); - tmp = OTG_SYSCON_1_REG; + tmp = omap_readw(OTG_SYSCON_1); seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R), @@ -2308,7 +2333,7 @@ static int proc_otg_show(struct seq_file *s) (tmp & HST_IDLE_EN) ? " !host" : "", (tmp & DEV_IDLE_EN) ? " !dev" : "", (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active"); - tmp = OTG_SYSCON_2_REG; + tmp = omap_readl(OTG_SYSCON_2); seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS " b_ase_brst=%d hmc=%d\n", tmp, (tmp & OTG_EN) ? " otg_en" : "", @@ -2323,7 +2348,7 @@ static int proc_otg_show(struct seq_file *s) (tmp & HMC_TLLATTACH) ? " tllattach" : "", B_ASE_BRST(tmp), OTG_HMC(tmp)); - tmp = OTG_CTRL_REG; + tmp = omap_readl(OTG_CTRL); seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp, (tmp & OTG_ASESSVLD) ? " asess" : "", (tmp & OTG_BSESSEND) ? " bsess_end" : "", @@ -2343,13 +2368,13 @@ static int proc_otg_show(struct seq_file *s) (tmp & OTG_PU_VBUS) ? " pu_vb" : "", (tmp & OTG_PU_ID) ? " pu_id" : "" ); - tmp = OTG_IRQ_EN_REG; + tmp = omap_readw(OTG_IRQ_EN); seq_printf(s, "otg_irq_en %04x" "\n", tmp); - tmp = OTG_IRQ_SRC_REG; + tmp = omap_readw(OTG_IRQ_SRC); seq_printf(s, "otg_irq_src %04x" "\n", tmp); - tmp = OTG_OUTCTRL_REG; + tmp = omap_readw(OTG_OUTCTRL); seq_printf(s, "otg_outctrl %04x" "\n", tmp); - tmp = OTG_TEST_REG; + tmp = omap_readw(OTG_TEST); seq_printf(s, "otg_test %04x" "\n", tmp); return 0; } @@ -2370,7 +2395,7 @@ static int proc_udc_show(struct seq_file *s, void *_) driver_desc, use_dma ? " (dma)" : ""); - tmp = UDC_REV_REG & 0xff; + tmp = omap_readw(UDC_REV) & 0xff; seq_printf(s, "UDC rev %d.%d, fifo mode %d, gadget %s\n" "hmc %d, transceiver %s\n", @@ -2384,16 +2409,16 @@ static int proc_udc_show(struct seq_file *s, void *_) ? "external" : "(none)")); if (cpu_class_is_omap1()) { seq_printf(s, "ULPD control %04x req %04x status %04x\n", - __REG16(ULPD_CLOCK_CTRL), - __REG16(ULPD_SOFT_REQ), - __REG16(ULPD_STATUS_REQ)); + omap_readw(ULPD_CLOCK_CTRL), + omap_readw(ULPD_SOFT_REQ), + omap_readw(ULPD_STATUS_REQ)); } /* OTG controller registers */ if (!cpu_is_omap15xx()) proc_otg_show(s); - tmp = UDC_SYSCON1_REG; + tmp = omap_readw(UDC_SYSCON1); seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "", (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "", @@ -2412,7 +2437,7 @@ static int proc_udc_show(struct seq_file *s, void *_) return 0; } - tmp = UDC_DEVSTAT_REG; + tmp = omap_readw(UDC_DEVSTAT); seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp, (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "", (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "", @@ -2424,20 +2449,20 @@ static int proc_udc_show(struct seq_file *s, void *_) (tmp & UDC_ADD) ? " ADD" : "", (tmp & UDC_DEF) ? " DEF" : "", (tmp & UDC_ATT) ? " ATT" : ""); - seq_printf(s, "sof %04x\n", UDC_SOF_REG); - tmp = UDC_IRQ_EN_REG; + seq_printf(s, "sof %04x\n", omap_readw(UDC_SOF)); + tmp = omap_readw(UDC_IRQ_EN); seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp, (tmp & UDC_SOF_IE) ? " sof" : "", (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "", (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "", (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "", (tmp & UDC_EP0_IE) ? " ep0" : ""); - tmp = UDC_IRQ_SRC_REG; + tmp = omap_readw(UDC_IRQ_SRC); seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp, (tmp & UDC_TXN_DONE) ? " txn_done" : "", (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "", (tmp & UDC_RXN_EOT) ? " rxn_eot" : "", - (tmp & UDC_SOF) ? " sof" : "", + (tmp & UDC_IRQ_SOF) ? " sof" : "", (tmp & UDC_EPN_RX) ? " epn_rx" : "", (tmp & UDC_EPN_TX) ? " epn_tx" : "", (tmp & UDC_DS_CHG) ? " ds_chg" : "", @@ -2447,7 +2472,7 @@ static int proc_udc_show(struct seq_file *s, void *_) if (use_dma) { unsigned i; - tmp = UDC_DMA_IRQ_EN_REG; + tmp = omap_readw(UDC_DMA_IRQ_EN); seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp, (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "", (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "", @@ -2461,29 +2486,29 @@ static int proc_udc_show(struct seq_file *s, void *_) (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "", (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : ""); - tmp = UDC_RXDMA_CFG_REG; + tmp = omap_readw(UDC_RXDMA_CFG); seq_printf(s, "rxdma_cfg %04x\n", tmp); if (tmp) { for (i = 0; i < 3; i++) { if ((tmp & (0x0f << (i * 4))) == 0) continue; seq_printf(s, "rxdma[%d] %04x\n", i, - UDC_RXDMA_REG(i + 1)); + omap_readw(UDC_RXDMA(i + 1))); } } - tmp = UDC_TXDMA_CFG_REG; + tmp = omap_readw(UDC_TXDMA_CFG); seq_printf(s, "txdma_cfg %04x\n", tmp); if (tmp) { for (i = 0; i < 3; i++) { if (!(tmp & (0x0f << (i * 4)))) continue; seq_printf(s, "txdma[%d] %04x\n", i, - UDC_TXDMA_REG(i + 1)); + omap_readw(UDC_TXDMA(i + 1))); } } } - tmp = UDC_DEVSTAT_REG; + tmp = omap_readw(UDC_DEVSTAT); if (tmp & UDC_ATT) { proc_ep_show(s, &udc->ep[0]); if (tmp & UDC_ADD) { @@ -2535,7 +2560,7 @@ static inline void remove_proc_file(void) {} * buffer space among the endpoints we'll be operating. * * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when - * UDC_SYSCON_1_REG.CFG_LOCK is set can now work. We won't use that + * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that * capability yet though. */ static unsigned __init @@ -2597,9 +2622,9 @@ omap_ep_setup(char *name, u8 addr, u8 type, name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf); if (addr & USB_DIR_IN) - UDC_EP_TX_REG(addr & 0xf) = epn_rxtx; + omap_writew(epn_rxtx, UDC_EP_TX(addr & 0xf)); else - UDC_EP_RX_REG(addr) = epn_rxtx; + omap_writew(epn_rxtx, UDC_EP_RX(addr)); /* next endpoint's buffer starts after this one's */ buf += maxp; @@ -2638,15 +2663,15 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) unsigned tmp, buf; /* abolish any previous hardware state */ - UDC_SYSCON1_REG = 0; - UDC_IRQ_EN_REG = 0; - UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; - UDC_DMA_IRQ_EN_REG = 0; - UDC_RXDMA_CFG_REG = 0; - UDC_TXDMA_CFG_REG = 0; + omap_writew(0, UDC_SYSCON1); + omap_writew(0, UDC_IRQ_EN); + omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); + omap_writew(0, UDC_DMA_IRQ_EN); + omap_writew(0, UDC_RXDMA_CFG); + omap_writew(0, UDC_TXDMA_CFG); /* UDC_PULLUP_EN gates the chip clock */ - // OTG_SYSCON_1_REG |= DEV_IDLE_EN; + // OTG_SYSCON_1 |= DEV_IDLE_EN; udc = kzalloc(sizeof(*udc), GFP_KERNEL); if (!udc) @@ -2662,7 +2687,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) udc->gadget.name = driver_name; device_initialize(&udc->gadget.dev); - strcpy (udc->gadget.dev.bus_id, "gadget"); + dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.release = omap_udc_release; udc->gadget.dev.parent = &odev->dev; if (use_dma) @@ -2677,8 +2702,8 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) /* initially disable all non-ep0 endpoints */ for (tmp = 1; tmp < 15; tmp++) { - UDC_EP_RX_REG(tmp) = 0; - UDC_EP_TX_REG(tmp) = 0; + omap_writew(0, UDC_EP_RX(tmp)); + omap_writew(0, UDC_EP_TX(tmp)); } #define OMAP_BULK_EP(name,addr) \ @@ -2763,7 +2788,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) ERR("unsupported fifo_mode #%d\n", fifo_mode); return -ENODEV; } - UDC_SYSCON1_REG = UDC_CFG_LOCK|UDC_SELF_PWR; + omap_writew(UDC_CFG_LOCK|UDC_SELF_PWR, UDC_SYSCON1); INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf); return 0; } @@ -2807,7 +2832,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) } INFO("OMAP UDC rev %d.%d%s\n", - UDC_REV_REG >> 4, UDC_REV_REG & 0xf, + omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf, config->otg ? ", Mini-AB" : ""); /* use the mode given to us by board init code */ @@ -2822,12 +2847,12 @@ static int __init omap_udc_probe(struct platform_device *pdev) * know when to turn PULLUP_EN on/off; and that * means we always "need" the 48MHz clock. */ - u32 tmp = FUNC_MUX_CTRL_0_REG; - - FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; + u32 tmp = omap_readl(FUNC_MUX_CTRL_0); + tmp &= ~VBUS_CTRL_1510; + omap_writel(tmp, FUNC_MUX_CTRL_0); tmp |= VBUS_MODE_1510; tmp &= ~VBUS_CTRL_1510; - FUNC_MUX_CTRL_0_REG = tmp; + omap_writel(tmp, FUNC_MUX_CTRL_0); } } else { /* The transceiver may package some GPIO logic or handle @@ -2907,7 +2932,7 @@ known: #endif /* starting with omap1710 es2.0, clear toggle is a separate bit */ - if (UDC_REV_REG >= 0x61) + if (omap_readw(UDC_REV) >= 0x61) udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE; else udc->clr_halt = UDC_RESET_EP; @@ -3005,7 +3030,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) put_device(udc->transceiver->dev); udc->transceiver = NULL; } - UDC_SYSCON1_REG = 0; + omap_writew(0, UDC_SYSCON1); remove_proc_file(); @@ -3036,7 +3061,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) * * REVISIT we should probably reject suspend requests when there's a host * session active, rather than disconnecting, at least on boards that can - * report VBUS irqs (UDC_DEVSTAT_REG.UDC_ATT). And in any case, we need to + * report VBUS irqs (UDC_DEVSTAT.UDC_ATT). And in any case, we need to * make host resumes and VBUS detection trigger OMAP wakeup events; that * may involve talking to an external transceiver (e.g. isp1301). */ @@ -3045,14 +3070,14 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) { u32 devstat; - devstat = UDC_DEVSTAT_REG; + devstat = omap_readw(UDC_DEVSTAT); /* we're requesting 48 MHz clock if the pullup is enabled * (== we're attached to the host) and we're not suspended, * which would prevent entry to deep sleep... */ if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { - WARN("session active; suspend requires disconnect\n"); + WARNING("session active; suspend requires disconnect\n"); omap_pullup(&udc->gadget, 0); } diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index c6b9cbc..29edc51 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -8,23 +8,22 @@ /* * USB device/endpoint management registers */ -#define UDC_REG(offset) __REG16(UDC_BASE + (offset)) -#define UDC_REV_REG UDC_REG(0x0) /* Revision */ -#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */ +#define UDC_REV (UDC_BASE + 0x0) /* Revision */ +#define UDC_EP_NUM (UDC_BASE + 0x4) /* Which endpoint */ # define UDC_SETUP_SEL (1 << 6) # define UDC_EP_SEL (1 << 5) # define UDC_EP_DIR (1 << 4) /* low 4 bits for endpoint number */ -#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */ -#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */ +#define UDC_DATA (UDC_BASE + 0x08) /* Endpoint FIFO */ +#define UDC_CTRL (UDC_BASE + 0x0C) /* Endpoint control */ # define UDC_CLR_HALT (1 << 7) # define UDC_SET_HALT (1 << 6) # define UDC_CLRDATA_TOGGLE (1 << 3) # define UDC_SET_FIFO_EN (1 << 2) # define UDC_CLR_EP (1 << 1) # define UDC_RESET_EP (1 << 0) -#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */ +#define UDC_STAT_FLG (UDC_BASE + 0x10) /* Endpoint status */ # define UDC_NO_RXPACKET (1 << 15) # define UDC_MISS_IN (1 << 14) # define UDC_DATA_FLUSH (1 << 13) @@ -38,8 +37,8 @@ # define UDC_FIFO_EN (1 << 2) # define UDC_NON_ISO_FIFO_EMPTY (1 << 1) # define UDC_NON_ISO_FIFO_FULL (1 << 0) -#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */ -#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */ +#define UDC_RXFSTAT (UDC_BASE + 0x14) /* OUT bytecount */ +#define UDC_SYSCON1 (UDC_BASE + 0x18) /* System config 1 */ # define UDC_CFG_LOCK (1 << 8) # define UDC_DATA_ENDIAN (1 << 7) # define UDC_DMA_ENDIAN (1 << 6) @@ -48,12 +47,12 @@ # define UDC_SELF_PWR (1 << 2) # define UDC_SOFF_DIS (1 << 1) # define UDC_PULLUP_EN (1 << 0) -#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */ +#define UDC_SYSCON2 (UDC_BASE + 0x1C) /* System config 2 */ # define UDC_RMT_WKP (1 << 6) # define UDC_STALL_CMD (1 << 5) # define UDC_DEV_CFG (1 << 3) # define UDC_CLR_CFG (1 << 2) -#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */ +#define UDC_DEVSTAT (UDC_BASE + 0x20) /* Device status */ # define UDC_B_HNP_ENABLE (1 << 9) # define UDC_A_HNP_SUPPORT (1 << 8) # define UDC_A_ALT_HNP_SUPPORT (1 << 7) @@ -64,26 +63,26 @@ # define UDC_ADD (1 << 2) # define UDC_DEF (1 << 1) # define UDC_ATT (1 << 0) -#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */ +#define UDC_SOF (UDC_BASE + 0x24) /* Start of frame */ # define UDC_FT_LOCK (1 << 12) # define UDC_TS_OK (1 << 11) # define UDC_TS 0x03ff -#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */ +#define UDC_IRQ_EN (UDC_BASE + 0x28) /* Interrupt enable */ # define UDC_SOF_IE (1 << 7) # define UDC_EPN_RX_IE (1 << 5) # define UDC_EPN_TX_IE (1 << 4) # define UDC_DS_CHG_IE (1 << 3) # define UDC_EP0_IE (1 << 0) -#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */ +#define UDC_DMA_IRQ_EN (UDC_BASE + 0x2C) /* DMA irq enable */ /* rx/tx dma channels numbered 1-3 not 0-2 */ # define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2)) # define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3)) # define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4)) -#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */ +#define UDC_IRQ_SRC (UDC_BASE + 0x30) /* Interrupt source */ # define UDC_TXN_DONE (1 << 10) # define UDC_RXN_CNT (1 << 9) # define UDC_RXN_EOT (1 << 8) -# define UDC_SOF (1 << 7) +# define UDC_IRQ_SOF (1 << 7) # define UDC_EPN_RX (1 << 5) # define UDC_EPN_TX (1 << 4) # define UDC_DS_CHG (1 << 3) @@ -91,41 +90,41 @@ # define UDC_EP0_RX (1 << 1) # define UDC_EP0_TX (1 << 0) # define UDC_IRQ_SRC_MASK 0x7bf -#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */ -#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */ +#define UDC_EPN_STAT (UDC_BASE + 0x34) /* EP irq status */ +#define UDC_DMAN_STAT (UDC_BASE + 0x38) /* DMA irq status */ # define UDC_DMA_RX_SB (1 << 12) # define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf) # define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf) /* DMA configuration registers: up to three channels in each direction. */ -#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */ +#define UDC_RXDMA_CFG (UDC_BASE + 0x40) /* 3 eps for RX DMA */ # define UDC_DMA_REQ (1 << 12) -#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */ -#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */ +#define UDC_TXDMA_CFG (UDC_BASE + 0x44) /* 3 eps for TX DMA */ +#define UDC_DATA_DMA (UDC_BASE + 0x48) /* rx/tx fifo addr */ /* rx/tx dma control, numbering channels 1-3 not 0-2 */ -#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan)) +#define UDC_TXDMA(chan) (UDC_BASE + 0x50 - 4 + 4 * (chan)) # define UDC_TXN_EOT (1 << 15) /* bytes vs packets */ # define UDC_TXN_START (1 << 14) /* start transfer */ # define UDC_TXN_TSC 0x03ff /* units in xfer */ -#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan)) +#define UDC_RXDMA(chan) (UDC_BASE + 0x60 - 4 + 4 * (chan)) # define UDC_RXN_STOP (1 << 15) /* enable EOT irq */ # define UDC_RXN_TC 0x00ff /* packets in xfer */ /* * Endpoint configuration registers (used before CFG_LOCK is set) - * UDC_EP_TX_REG(0) is unused + * UDC_EP_TX(0) is unused */ -#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4) +#define UDC_EP_RX(endpoint) (UDC_BASE + 0x80 + (endpoint)*4) # define UDC_EPN_RX_VALID (1 << 15) # define UDC_EPN_RX_DB (1 << 14) /* buffer size in bits 13, 12 */ # define UDC_EPN_RX_ISO (1 << 11) /* buffer pointer in low 11 bits */ -#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4) - /* same bitfields as in RX_REG */ +#define UDC_EP_TX(endpoint) (UDC_BASE + 0xc0 + (endpoint)*4) + /* same bitfields as in RX */ /*-------------------------------------------------------------------------*/ @@ -189,20 +188,20 @@ struct omap_udc { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) /*-------------------------------------------------------------------------*/ -#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0) -#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ +/* MOD_CONF_CTRL_0 */ +#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ -#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0) +/* FUNC_MUX_CTRL_0 */ #define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */ #define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */ -#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) -#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) +#define HMC_1510 ((omap_readl(MOD_CONF_CTRL_0) >> 1) & 0x3f) +#define HMC_1610 (omap_readl(OTG_SYSCON_2) & 0x3f) #define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 76be75e..e009008 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define ERROR(dev, fmt, args...) \ xprintk(dev, KERN_ERR, fmt, ## args) -#define WARN(dev, fmt, args...) \ +#define WARNING(dev, fmt, args...) \ xprintk(dev, KERN_WARNING, fmt, ## args) #define INFO(dev, fmt, args...) \ xprintk(dev, KERN_INFO, fmt, ## args) @@ -462,6 +462,7 @@ printer_open(struct inode *inode, struct file *fd) unsigned long flags; int ret = -EBUSY; + lock_kernel(); dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); spin_lock_irqsave(&dev->lock, flags); @@ -477,7 +478,7 @@ printer_open(struct inode *inode, struct file *fd) spin_unlock_irqrestore(&dev->lock, flags); DBG(dev, "printer_open returned %x\n", ret); - + unlock_kernel(); return ret; } @@ -827,9 +828,8 @@ printer_poll(struct file *fd, poll_table *wait) return status; } -static int -printer_ioctl(struct inode *inode, struct file *fd, unsigned int code, - unsigned long arg) +static long +printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) { struct printer_dev *dev = fd->private_data; unsigned long flags; @@ -868,7 +868,7 @@ static struct file_operations printer_io_operations = { .write = printer_write, .fsync = printer_fsync, .poll = printer_poll, - .ioctl = printer_ioctl, + .unlocked_ioctl = printer_ioctl, .release = printer_close }; @@ -1360,8 +1360,8 @@ printer_bind(struct usb_gadget *gadget) /* Setup the sysfs files for the printer gadget. */ - dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - "g_printer"); + dev->pdev = device_create_drvdata(usb_gadget_class, NULL, + g_printer_devno, NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); goto fail; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c new file mode 100644 index 0000000..da6e93c --- /dev/null +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -0,0 +1,2390 @@ +/* + * Intel PXA25x and IXP4xx on-chip full speed USB device controllers + * + * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) + * Copyright (C) 2003 Robert Schwebel, Pengutronix + * Copyright (C) 2003 Benedikt Spranger, Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003 Joshua Wise + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * This driver is PXA25x only. Grab the right register definitions. + */ +#ifdef CONFIG_ARCH_PXA +#include +#endif + +#include + + +/* + * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x + * series processors. The UDC for the IXP 4xx series is very similar. + * There are fifteen endpoints, in addition to ep0. + * + * Such controller drivers work with a gadget driver. The gadget driver + * returns descriptors, implements configuration and data protocols used + * by the host to interact with this device, and allocates endpoints to + * the different protocol interfaces. The controller driver virtualizes + * usb hardware so that the gadget drivers will be more portable. + * + * This UDC hardware wants to implement a bit too much USB protocol, so + * it constrains the sorts of USB configuration change events that work. + * The errata for these chips are misleading; some "fixed" bugs from + * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + * + * Note that the UDC hardware supports DMA (except on IXP) but that's + * not used here. IN-DMA (to host) is simple enough, when the data is + * suitably aligned (16 bytes) ... the network stack doesn't do that, + * other software can. OUT-DMA is buggy in most chip versions, as well + * as poorly designed (data toggle not automatic). So this driver won't + * bother using DMA. (Mostly-working IN-DMA support was available in + * kernels before 2.6.23, but was never enabled or well tested.) + */ + +#define DRIVER_VERSION "30-June-2007" +#define DRIVER_DESC "PXA 25x USB Device Controller driver" + + +static const char driver_name [] = "pxa25x_udc"; + +static const char ep0name [] = "ep0"; + + +#ifdef CONFIG_ARCH_IXP4XX + +/* cpu-specific register addresses are compiled in to this code */ +#ifdef CONFIG_ARCH_PXA +#error "Can't configure both IXP and PXA" +#endif + +/* IXP doesn't yet support */ +#define clk_get(dev,name) NULL +#define clk_enable(clk) do { } while (0) +#define clk_disable(clk) do { } while (0) +#define clk_put(clk) do { } while (0) + +#endif + +#include "pxa25x_udc.h" + + +#ifdef CONFIG_USB_PXA25X_SMALL +#define SIZE_STR " (small)" +#else +#define SIZE_STR "" +#endif + +/* --------------------------------------------------------------------------- + * endpoint related parts of the api to the usb controller hardware, + * used by gadget driver; and the inner talker-to-hardware core. + * --------------------------------------------------------------------------- + */ + +static void pxa25x_ep_fifo_flush (struct usb_ep *ep); +static void nuke (struct pxa25x_ep *, int status); + +/* one GPIO should be used to detect VBUS from the host */ +static int is_vbus_present(void) +{ + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + + if (mach->gpio_vbus) { + int value = gpio_get_value(mach->gpio_vbus); + return mach->gpio_vbus_inverted ? !value : value; + } + if (mach->udc_is_connected) + return mach->udc_is_connected(); + return 1; +} + +/* one GPIO should control a D+ pullup, so host sees this device (or not) */ +static void pullup_off(void) +{ + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + int off_level = mach->gpio_pullup_inverted; + + if (mach->gpio_pullup) + gpio_set_value(mach->gpio_pullup, off_level); + else if (mach->udc_command) + mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); +} + +static void pullup_on(void) +{ + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + int on_level = !mach->gpio_pullup_inverted; + + if (mach->gpio_pullup) + gpio_set_value(mach->gpio_pullup, on_level); + else if (mach->udc_command) + mach->udc_command(PXA2XX_UDC_CMD_CONNECT); +} + +static void pio_irq_enable(int bEndpointAddress) +{ + bEndpointAddress &= 0xf; + if (bEndpointAddress < 8) + UICR0 &= ~(1 << bEndpointAddress); + else { + bEndpointAddress -= 8; + UICR1 &= ~(1 << bEndpointAddress); + } +} + +static void pio_irq_disable(int bEndpointAddress) +{ + bEndpointAddress &= 0xf; + if (bEndpointAddress < 8) + UICR0 |= 1 << bEndpointAddress; + else { + bEndpointAddress -= 8; + UICR1 |= 1 << bEndpointAddress; + } +} + +/* The UDCCR reg contains mask and interrupt status bits, + * so using '|=' isn't safe as it may ack an interrupt. + */ +#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) + +static inline void udc_set_mask_UDCCR(int mask) +{ + UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); +} + +static inline void udc_clear_mask_UDCCR(int mask) +{ + UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); +} + +static inline void udc_ack_int_UDCCR(int mask) +{ + /* udccr contains the bits we dont want to change */ + __u32 udccr = UDCCR & UDCCR_MASK_BITS; + + UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); +} + +/* + * endpoint enable/disable + * + * we need to verify the descriptors used to enable endpoints. since pxa25x + * endpoint configurations are fixed, and are pretty much always enabled, + * there's not a lot to manage here. + * + * because pxa25x can't selectively initialize bulk (or interrupt) endpoints, + * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except + * for a single interface (with only the default altsetting) and for gadget + * drivers that don't halt endpoints (not reset by set_interface). that also + * means that if you use ISO, you must violate the USB spec rule that all + * iso endpoints must be in non-default altsettings. + */ +static int pxa25x_ep_enable (struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct pxa25x_ep *ep; + struct pxa25x_udc *dev; + + ep = container_of (_ep, struct pxa25x_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep->fifo_size < le16_to_cpu + (desc->wMaxPacketSize)) { + DMSG("%s, bad ep or descriptor\n", __func__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DMSG("%s, %s type mismatch\n", __func__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu (desc->wMaxPacketSize) + != BULK_FIFO_SIZE) + || !desc->wMaxPacketSize) { + DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DMSG("%s, bogus device state\n", __func__); + return -ESHUTDOWN; + } + + ep->desc = desc; + ep->stopped = 0; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + + /* flush fifo (mostly for OUT buffers) */ + pxa25x_ep_fifo_flush (_ep); + + /* ... reset halt state too, if we could ... */ + + DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); + return 0; +} + +static int pxa25x_ep_disable (struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + unsigned long flags; + + ep = container_of (_ep, struct pxa25x_ep, ep); + if (!_ep || !ep->desc) { + DMSG("%s, %s not enabled\n", __func__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + local_irq_save(flags); + + nuke (ep, -ESHUTDOWN); + + /* flush fifo (mostly for IN buffers) */ + pxa25x_ep_fifo_flush (_ep); + + ep->desc = NULL; + ep->stopped = 1; + + local_irq_restore(flags); + DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers + * must still pass correctly initialized endpoints, since other controller + * drivers may care about how it's currently set up (dma issues etc). + */ + +/* + * pxa25x_ep_alloc_request - allocate a request data structure + */ +static struct usb_request * +pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct pxa25x_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + + +/* + * pxa25x_ep_free_request - deallocate a request data structure + */ +static void +pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa25x_request *req; + + req = container_of (_req, struct pxa25x_request, req); + WARN_ON(!list_empty (&req->queue)); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (likely (req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + req->req.complete(&ep->ep, &req->req); + ep->stopped = stopped; +} + + +static inline void ep0_idle (struct pxa25x_udc *dev) +{ + dev->ep0state = EP0_IDLE; +} + +static int +write_packet(volatile u32 *uddr, struct pxa25x_request *req, unsigned max) +{ + u8 *buf; + unsigned length, count; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* how big will this packet be? */ + length = min(req->req.length - req->req.actual, max); + req->req.actual += length; + + count = length; + while (likely(count--)) + *uddr = *buf++; + + return length; +} + +/* + * write to an IN endpoint fifo, as many packets as possible. + * irqs will use this to write the rest later. + * caller guarantees at least one packet buffer is ready (or a zlp). + */ +static int +write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + unsigned max; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + do { + unsigned count; + int is_last, is_short; + + count = write_packet(ep->reg_uddr, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely (count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely (max < ep->fifo_size); + } + + DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* let loose that packet. maybe try writing another one, + * double buffering might work. TSP, TPC, and TFS + * bit values are the same for all normal IN endpoints. + */ + *ep->reg_udccs = UDCCS_BI_TPC; + if (is_short) + *ep->reg_udccs = UDCCS_BI_TSP; + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done (ep, req, 0); + if (list_empty(&ep->queue)) + pio_irq_disable (ep->bEndpointAddress); + return 1; + } + + // TODO experiment: how robust can fifo mode tweaking be? + // double buffering is off in the default fifo mode, which + // prevents TFS from being set here. + + } while (*ep->reg_udccs & UDCCS_BI_TFS); + return 0; +} + +/* caller asserts req->pending (ep0 irq status nyet cleared); starts + * ep0 data stage. these chips want very simple state transitions. + */ +static inline +void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) +{ + UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; + USIR0 = USIR0_IR0; + dev->req_pending = 0; + DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", + __func__, tag, UDCCS0, flags); +} + +static int +write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + unsigned count; + int is_short; + + count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); + ep->dev->stats.write.bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != EP0_FIFO_SIZE); + + DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, + req->req.length - req->req.actual, req); + + if (unlikely (is_short)) { + if (ep->dev->req_pending) + ep0start(ep->dev, UDCCS0_IPR, "short IN"); + else + UDCCS0 = UDCCS0_IPR; + + count = req->req.length; + done (ep, req, 0); + ep0_idle(ep->dev); +#ifndef CONFIG_ARCH_IXP4XX +#if 1 + /* This seems to get rid of lost status irqs in some cases: + * host responds quickly, or next request involves config + * change automagic, or should have been hidden, or ... + * + * FIXME get rid of all udelays possible... + */ + if (count >= EP0_FIFO_SIZE) { + count = 100; + do { + if ((UDCCS0 & UDCCS0_OPR) != 0) { + /* clear OPR, generate ack */ + UDCCS0 = UDCCS0_OPR; + break; + } + count--; + udelay(1); + } while (count); + } +#endif +#endif + } else if (ep->dev->req_pending) + ep0start(ep->dev, 0, "IN"); + return is_short; +} + + +/* + * read_fifo - unload packet(s) from the fifo we use for usb OUT + * transfers and put them into the request. caller should have made + * sure there's at least one packet ready. + * + * returns true if the request completed because of short packet or the + * request buffer having filled (and maybe overran till end-of-packet). + */ +static int +read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + for (;;) { + u32 udccs; + u8 *buf; + unsigned bufferspace, count, is_short; + + /* make sure there's a packet in the FIFO. + * UDCCS_{BO,IO}_RPC are all the same bit value. + * UDCCS_{BO,IO}_RNE are all the same bit value. + */ + udccs = *ep->reg_udccs; + if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) + break; + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely (udccs & UDCCS_BO_RNE)) { + count = 1 + (0x0ff & *ep->reg_ubcr); + req->req.actual += min (count, bufferspace); + } else /* zlp */ + count = 0; + is_short = (count < ep->ep.maxpacket); + DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, udccs, count, + is_short ? "/S" : "", + req, req->req.actual, req->req.length); + while (likely (count-- != 0)) { + u8 byte = (u8) *ep->reg_uddr; + + if (unlikely (bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DMSG("%s overflow %d\n", + ep->ep.name, count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + *ep->reg_udccs = UDCCS_BO_RPC; + /* RPC/RSP/RNE could now reflect the other packet buffer */ + + /* iso is one request per packet */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (udccs & UDCCS_IO_ROF) + req->req.status = -EHOSTUNREACH; + /* more like "is_done" */ + is_short = 1; + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done (ep, req, 0); + if (list_empty(&ep->queue)) + pio_irq_disable (ep->bEndpointAddress); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + } + return 0; +} + +/* + * special ep0 version of the above. no UBCR0 or double buffering; status + * handshaking is magic. most device protocols don't need control-OUT. + * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other + * protocols do use them. + */ +static int +read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) +{ + u8 *buf, byte; + unsigned bufferspace; + + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + + while (UDCCS0 & UDCCS0_RNE) { + byte = (u8) UDDR0; + + if (unlikely (bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DMSG("%s overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + req->req.actual++; + bufferspace--; + } + } + + UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; + + /* completion */ + if (req->req.actual >= req->req.length) + return 1; + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int +pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct pxa25x_request *req; + struct pxa25x_ep *ep; + struct pxa25x_udc *dev; + unsigned long flags; + + req = container_of(_req, struct pxa25x_request, req); + if (unlikely (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + DMSG("%s, bad params\n", __func__); + return -EINVAL; + } + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DMSG("%s, bad ep\n", __func__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely (!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DMSG("%s, bogus device state\n", __func__); + return -ESHUTDOWN; + } + + /* iso is always one packet per request, that's the only way + * we can report per-packet status. that also helps with dma. + */ + if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + && req->req.length > le16_to_cpu + (ep->desc->wMaxPacketSize))) + return -EMSGSIZE; + + DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + local_irq_save(flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + if (ep->desc == NULL/* ep0 */) { + unsigned length = _req->length; + + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + dev->stats.write.ops++; + if (write_ep0_fifo(ep, req)) + req = NULL; + break; + + case EP0_OUT_DATA_PHASE: + dev->stats.read.ops++; + /* messy ... */ + if (dev->req_config) { + DBG(DBG_VERBOSE, "ep0 config ack%s\n", + dev->has_cfr ? "" : " raced"); + if (dev->has_cfr) + UDCCFR = UDCCFR_AREN|UDCCFR_ACM + |UDCCFR_MB1; + done(ep, req, 0); + dev->ep0state = EP0_END_XFER; + local_irq_restore (flags); + return 0; + } + if (dev->req_pending) + ep0start(dev, UDCCS0_IPR, "OUT"); + if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 + && read_ep0_fifo(ep, req))) { + ep0_idle(dev); + done(ep, req, 0); + req = NULL; + } + break; + + default: + DMSG("ep0 i/o, odd state %d\n", dev->ep0state); + local_irq_restore (flags); + return -EL2HLT; + } + /* can the FIFO can satisfy the request immediately? */ + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 + && write_fifo(ep, req)) + req = NULL; + } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 + && read_fifo(ep, req)) { + req = NULL; + } + + if (likely (req && ep->desc)) + pio_irq_enable(ep->bEndpointAddress); + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != NULL)) + list_add_tail(&req->queue, &ep->queue); + local_irq_restore(flags); + + return 0; +} + + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct pxa25x_ep *ep, int status) +{ + struct pxa25x_request *req; + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct pxa25x_request, + queue); + done(ep, req, status); + } + if (ep->desc) + pio_irq_disable (ep->bEndpointAddress); +} + + +/* dequeue JUST ONE request */ +static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa25x_ep *ep; + struct pxa25x_request *req; + unsigned long flags; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct pxa25x_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (unlikely (!_ep + || (!ep->desc && ep->ep.name != ep0name)) + || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + DMSG("%s, bad ep\n", __func__); + return -EINVAL; + } + if (value == 0) { + /* this path (reset toggle+halt) is needed to implement + * SET_INTERFACE on normal hardware. but it can't be + * done from software on the PXA UDC, and the hardware + * forgets to do it as part of SET_INTERFACE automagic. + */ + DMSG("only host can clear %s halt\n", _ep->name); + return -EROFS; + } + + local_irq_save(flags); + + if ((ep->bEndpointAddress & USB_DIR_IN) != 0 + && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 + || !list_empty(&ep->queue))) { + local_irq_restore(flags); + return -EAGAIN; + } + + /* FST bit is the same for control, bulk in, bulk out, interrupt in */ + *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; + + /* ep0 needs special care */ + if (!ep->desc) { + start_watchdog(ep->dev); + ep->dev->req_pending = 0; + ep->dev->ep0state = EP0_STALL; + + /* and bulk/intr endpoints like dropping stalls too */ + } else { + unsigned i; + for (i = 0; i < 1000; i += 20) { + if (*ep->reg_udccs & UDCCS_BI_SST) + break; + udelay(20); + } + } + local_irq_restore(flags); + + DBG(DBG_VERBOSE, "%s halt\n", _ep->name); + return 0; +} + +static int pxa25x_ep_fifo_status(struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep) { + DMSG("%s, bad ep\n", __func__); + return -ENODEV; + } + /* pxa can't report unclaimed bytes from IN fifos */ + if ((ep->bEndpointAddress & USB_DIR_IN) != 0) + return -EOPNOTSUPP; + if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN + || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) + return 0; + else + return (*ep->reg_ubcr & 0xfff) + 1; +} + +static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) +{ + struct pxa25x_ep *ep; + + ep = container_of(_ep, struct pxa25x_ep, ep); + if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { + DMSG("%s, bad ep\n", __func__); + return; + } + + /* toggle and halt bits stay unchanged */ + + /* for OUT, just read and discard the FIFO contents. */ + if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { + while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) + (void) *ep->reg_uddr; + return; + } + + /* most IN status is the same, but ISO can't stall */ + *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR + | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + ? 0 : UDCCS_BI_SST; +} + + +static struct usb_ep_ops pxa25x_ep_ops = { + .enable = pxa25x_ep_enable, + .disable = pxa25x_ep_disable, + + .alloc_request = pxa25x_ep_alloc_request, + .free_request = pxa25x_ep_free_request, + + .queue = pxa25x_ep_queue, + .dequeue = pxa25x_ep_dequeue, + + .set_halt = pxa25x_ep_set_halt, + .fifo_status = pxa25x_ep_fifo_status, + .fifo_flush = pxa25x_ep_fifo_flush, +}; + + +/* --------------------------------------------------------------------------- + * device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int pxa25x_udc_get_frame(struct usb_gadget *_gadget) +{ + return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); +} + +static int pxa25x_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + if ((UDCCS0 & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); + return 0; +} + +static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *); +static void udc_enable (struct pxa25x_udc *); +static void udc_disable(struct pxa25x_udc *); + +/* We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +static int pullup(struct pxa25x_udc *udc) +{ + int is_active = udc->vbus && udc->pullup && !udc->suspended; + DMSG("%s\n", is_active ? "active" : "inactive"); + if (is_active) { + if (!udc->active) { + udc->active = 1; + /* Enable clock for USB device */ + clk_enable(udc->clk); + udc_enable(udc); + } + } else { + if (udc->active) { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + DMSG("disconnect %s\n", udc->driver + ? udc->driver->driver.name + : "(no driver)"); + stop_activity(udc, udc->driver); + } + udc_disable(udc); + /* Disable clock for USB device */ + clk_disable(udc->clk); + udc->active = 0; + } + + } + return 0; +} + +/* VBUS reporting logically comes from a transceiver */ +static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + udc->vbus = (is_active != 0); + DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); + pullup(udc); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + + /* not all boards support pullup control */ + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + return -EOPNOTSUPP; + + udc->pullup = (is_active != 0); + pullup(udc); + return 0; +} + +static const struct usb_gadget_ops pxa25x_udc_ops = { + .get_frame = pxa25x_udc_get_frame, + .wakeup = pxa25x_udc_wakeup, + .vbus_session = pxa25x_udc_vbus_session, + .pullup = pxa25x_udc_pullup, + + // .vbus_draw ... boards may consume current from VBUS, up to + // 100-500mA based on config. the 500uA suspend ceiling means + // that exclusively vbus-powered PXA designs violate USB specs. +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FS + +static int +udc_seq_show(struct seq_file *m, void *_d) +{ + struct pxa25x_udc *dev = m->private; + unsigned long flags; + int i; + u32 tmp; + + local_irq_save(flags); + + /* basic device status */ + seq_printf(m, DRIVER_DESC "\n" + "%s version: %s\nGadget driver: %s\nHost %s\n\n", + driver_name, DRIVER_VERSION SIZE_STR "(pio)", + dev->driver ? dev->driver->driver.name : "(none)", + is_vbus_present() ? "full speed" : "disconnected"); + + /* registers for device and ep0 */ + seq_printf(m, + "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", + UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); + + tmp = UDCCR; + seq_printf(m, + "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, + (tmp & UDCCR_REM) ? " rem" : "", + (tmp & UDCCR_RSTIR) ? " rstir" : "", + (tmp & UDCCR_SRM) ? " srm" : "", + (tmp & UDCCR_SUSIR) ? " susir" : "", + (tmp & UDCCR_RESIR) ? " resir" : "", + (tmp & UDCCR_RSM) ? " rsm" : "", + (tmp & UDCCR_UDA) ? " uda" : "", + (tmp & UDCCR_UDE) ? " ude" : ""); + + tmp = UDCCS0; + seq_printf(m, + "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, + (tmp & UDCCS0_SA) ? " sa" : "", + (tmp & UDCCS0_RNE) ? " rne" : "", + (tmp & UDCCS0_FST) ? " fst" : "", + (tmp & UDCCS0_SST) ? " sst" : "", + (tmp & UDCCS0_DRWF) ? " dwrf" : "", + (tmp & UDCCS0_FTF) ? " ftf" : "", + (tmp & UDCCS0_IPR) ? " ipr" : "", + (tmp & UDCCS0_OPR) ? " opr" : ""); + + if (dev->has_cfr) { + tmp = UDCCFR; + seq_printf(m, + "udccfr %02X =%s%s\n", tmp, + (tmp & UDCCFR_AREN) ? " aren" : "", + (tmp & UDCCFR_ACM) ? " acm" : ""); + } + + if (!is_vbus_present() || !dev->driver) + goto done; + + seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops, + dev->stats.irqs); + + /* dump endpoint queues */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { + struct pxa25x_ep *ep = &dev->ep [i]; + struct pxa25x_request *req; + + if (i != 0) { + const struct usb_endpoint_descriptor *desc; + + desc = ep->desc; + if (!desc) + continue; + tmp = *dev->ep [i].reg_udccs; + seq_printf(m, + "%s max %d %s udccs %02x irqs %lu\n", + ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), + "pio", tmp, ep->pio_irqs); + /* TODO translate all five groups of udccs bits! */ + + } else /* ep0 should only have one transfer queued */ + seq_printf(m, "ep0 max 16 pio irqs %lu\n", + ep->pio_irqs); + + if (list_empty(&ep->queue)) { + seq_printf(m, "\t(nothing queued)\n"); + continue; + } + list_for_each_entry(req, &ep->queue, queue) { + seq_printf(m, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + } + } + +done: + local_irq_restore(flags); + return 0; +} + +static int +udc_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, udc_seq_show, inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = udc_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +#define create_debug_files(dev) \ + do { \ + dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ + S_IRUGO, NULL, dev, &debug_fops); \ + } while (0) +#define remove_debug_files(dev) \ + do { \ + if (dev->debugfs_udc) \ + debugfs_remove(dev->debugfs_udc); \ + } while (0) + +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +#define create_debug_files(dev) do {} while (0) +#define remove_debug_files(dev) do {} while (0) + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct pxa25x_udc *dev) +{ + /* block all irqs */ + udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); + UICR0 = UICR1 = 0xff; + UFNRH = UFNRH_SIM; + + /* if hardware supports it, disconnect from usb */ + pullup_off(); + + udc_clear_mask_UDCCR(UDCCR_UDE); + + ep0_idle (dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct pxa25x_udc *dev) +{ + u32 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD (&dev->gadget.ep_list); + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + dev->ep0state = EP0_IDLE; + + /* basic endpoint records init */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { + struct pxa25x_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = NULL; + ep->stopped = 0; + INIT_LIST_HEAD (&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable (struct pxa25x_udc *dev) +{ + udc_clear_mask_UDCCR(UDCCR_UDE); + + /* try to clear these bits before we enable the udc */ + udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); + + ep0_idle(dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->stats.irqs = 0; + + /* + * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: + * - enable UDC + * - if RESET is already in progress, ack interrupt + * - unmask reset interrupt + */ + udc_set_mask_UDCCR(UDCCR_UDE); + if (!(UDCCR & UDCCR_UDA)) + udc_ack_int_UDCCR(UDCCR_RSTIR); + + if (dev->has_cfr /* UDC_RES2 is defined */) { + /* pxa255 (a0+) can avoid a set_config race that could + * prevent gadget drivers from configuring correctly + */ + UDCCFR = UDCCFR_ACM | UDCCFR_MB1; + } else { + /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) + * which could result in missing packets and interrupts. + * supposedly one bit per endpoint, controlling whether it + * double buffers or not; ACM/AREN bits fit into the holes. + * zero bits (like USIR0_IRx) disable double buffering. + */ + UDC_RES1 = 0x00; + UDC_RES2 = 0x00; + } + + /* enable suspend/resume and reset irqs */ + udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); + + /* enable ep0 irqs */ + UICR0 &= ~UICR0_IM0; + + /* if hardware supports it, pullup D+ and wait for reset */ + pullup_on(); +} + + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct pxa25x_udc *dev = the_controller; + int retval; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + dev->pullup = 1; + + retval = device_add (&dev->gadget.dev); + if (retval) { +fail: + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } + retval = driver->bind(&dev->gadget); + if (retval) { + DMSG("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del (&dev->gadget.dev); + goto fail; + } + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + DMSG("registered gadget driver '%s'\n", driver->driver.name); + pullup(dev); + dump_state(dev); + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void +stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { + struct pxa25x_ep *ep = &dev->ep[i]; + + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + del_timer_sync(&dev->timer); + + /* report disconnect; the driver is already quiesced */ + if (driver) + driver->disconnect(&dev->gadget); + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct pxa25x_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + local_irq_disable(); + dev->pullup = 0; + pullup(dev); + stop_activity(dev, driver); + local_irq_enable(); + + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_del (&dev->gadget.dev); + + DMSG("unregistered gadget driver '%s'\n", driver->driver.name); + dump_state(dev); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_LUBBOCK + +/* Lubbock has separate connect and disconnect irqs. More typical designs + * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. + */ + +static irqreturn_t +lubbock_vbus_irq(int irq, void *_dev) +{ + struct pxa25x_udc *dev = _dev; + int vbus; + + dev->stats.irqs++; + switch (irq) { + case LUBBOCK_USB_IRQ: + vbus = 1; + disable_irq(LUBBOCK_USB_IRQ); + enable_irq(LUBBOCK_USB_DISC_IRQ); + break; + case LUBBOCK_USB_DISC_IRQ: + vbus = 0; + disable_irq(LUBBOCK_USB_DISC_IRQ); + enable_irq(LUBBOCK_USB_IRQ); + break; + default: + return IRQ_NONE; + } + + pxa25x_udc_vbus_session(&dev->gadget, vbus); + return IRQ_HANDLED; +} + +#endif + +static irqreturn_t udc_vbus_irq(int irq, void *_dev) +{ + struct pxa25x_udc *dev = _dev; + int vbus = gpio_get_value(dev->mach->gpio_vbus); + + if (dev->mach->gpio_vbus_inverted) + vbus = !vbus; + + pxa25x_udc_vbus_session(&dev->gadget, vbus); + return IRQ_HANDLED; +} + + +/*-------------------------------------------------------------------------*/ + +static inline void clear_ep_state (struct pxa25x_udc *dev) +{ + unsigned i; + + /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint + * fifos, and pending transactions mustn't be continued in any case. + */ + for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) + nuke(&dev->ep[i], -ECONNABORTED); +} + +static void udc_watchdog(unsigned long _dev) +{ + struct pxa25x_udc *dev = (void *)_dev; + + local_irq_disable(); + if (dev->ep0state == EP0_STALL + && (UDCCS0 & UDCCS0_FST) == 0 + && (UDCCS0 & UDCCS0_SST) == 0) { + UDCCS0 = UDCCS0_FST|UDCCS0_FTF; + DBG(DBG_VERBOSE, "ep0 re-stall\n"); + start_watchdog(dev); + } + local_irq_enable(); +} + +static void handle_ep0 (struct pxa25x_udc *dev) +{ + u32 udccs0 = UDCCS0; + struct pxa25x_ep *ep = &dev->ep [0]; + struct pxa25x_request *req; + union { + struct usb_ctrlrequest r; + u8 raw [8]; + u32 word [2]; + } u; + + if (list_empty(&ep->queue)) + req = NULL; + else + req = list_entry(ep->queue.next, struct pxa25x_request, queue); + + /* clear stall status */ + if (udccs0 & UDCCS0_SST) { + nuke(ep, -EPIPE); + UDCCS0 = UDCCS0_SST; + del_timer(&dev->timer); + ep0_idle(dev); + } + + /* previous request unfinished? non-error iff back-to-back ... */ + if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { + nuke(ep, 0); + del_timer(&dev->timer); + ep0_idle(dev); + } + + switch (dev->ep0state) { + case EP0_IDLE: + /* late-breaking status? */ + udccs0 = UDCCS0; + + /* start control request? */ + if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) + == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { + int i; + + nuke (ep, -EPROTO); + + /* read SETUP packet */ + for (i = 0; i < 8; i++) { + if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { +bad_setup: + DMSG("SETUP %d!\n", i); + goto stall; + } + u.raw [i] = (u8) UDDR0; + } + if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) + goto bad_setup; + +got_setup: + DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + le16_to_cpu(u.r.wValue), + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wLength)); + + /* cope with automagic for some standard requests. */ + dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + switch (u.r.bRequest) { + /* hardware restricts gadget drivers here! */ + case USB_REQ_SET_CONFIGURATION: + if (u.r.bRequestType == USB_RECIP_DEVICE) { + /* reflect hardware's automagic + * up to the gadget driver. + */ +config_change: + dev->req_config = 1; + clear_ep_state(dev); + /* if !has_cfr, there's no synch + * else use AREN (later) not SA|OPR + * USIR0_IR0 acts edge sensitive + */ + } + break; + /* ... and here, even more ... */ + case USB_REQ_SET_INTERFACE: + if (u.r.bRequestType == USB_RECIP_INTERFACE) { + /* udc hardware is broken by design: + * - altsetting may only be zero; + * - hw resets all interfaces' eps; + * - ep reset doesn't include halt(?). + */ + DMSG("broken set_interface (%d/%d)\n", + le16_to_cpu(u.r.wIndex), + le16_to_cpu(u.r.wValue)); + goto config_change; + } + break; + /* hardware was supposed to hide this */ + case USB_REQ_SET_ADDRESS: + if (u.r.bRequestType == USB_RECIP_DEVICE) { + ep0start(dev, 0, "address"); + return; + } + break; + } + + if (u.r.bRequestType & USB_DIR_IN) + dev->ep0state = EP0_IN_DATA_PHASE; + else + dev->ep0state = EP0_OUT_DATA_PHASE; + + i = dev->driver->setup(&dev->gadget, &u.r); + if (i < 0) { + /* hardware automagic preventing STALL... */ + if (dev->req_config) { + /* hardware sometimes neglects to tell + * tell us about config change events, + * so later ones may fail... + */ + WARNING("config change %02x fail %d?\n", + u.r.bRequest, i); + return; + /* TODO experiment: if has_cfr, + * hardware didn't ACK; maybe we + * could actually STALL! + */ + } + DBG(DBG_VERBOSE, "protocol STALL, " + "%02x err %d\n", UDCCS0, i); +stall: + /* the watchdog timer helps deal with cases + * where udc seems to clear FST wrongly, and + * then NAKs instead of STALLing. + */ + ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); + start_watchdog(dev); + dev->ep0state = EP0_STALL; + + /* deferred i/o == no response yet */ + } else if (dev->req_pending) { + if (likely(dev->ep0state == EP0_IN_DATA_PHASE + || dev->req_std || u.r.wLength)) + ep0start(dev, 0, "defer"); + else + ep0start(dev, UDCCS0_IPR, "defer/IPR"); + } + + /* expect at least one data or status stage irq */ + return; + + } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) + == (UDCCS0_OPR|UDCCS0_SA))) { + unsigned i; + + /* pxa210/250 erratum 131 for B0/B1 says RNE lies. + * still observed on a pxa255 a0. + */ + DBG(DBG_VERBOSE, "e131\n"); + nuke(ep, -EPROTO); + + /* read SETUP data, but don't trust it too much */ + for (i = 0; i < 8; i++) + u.raw [i] = (u8) UDDR0; + if ((u.r.bRequestType & USB_RECIP_MASK) + > USB_RECIP_OTHER) + goto stall; + if (u.word [0] == 0 && u.word [1] == 0) + goto stall; + goto got_setup; + } else { + /* some random early IRQ: + * - we acked FST + * - IPR cleared + * - OPR got set, without SA (likely status stage) + */ + UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); + } + break; + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + if (udccs0 & UDCCS0_OPR) { + UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; + DBG(DBG_VERBOSE, "ep0in premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } else /* irq was IPR clearing */ { + if (req) { + /* this IN packet might finish the request */ + (void) write_ep0_fifo(ep, req); + } /* else IN token before response was written */ + } + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + if (udccs0 & UDCCS0_OPR) { + if (req) { + /* this OUT packet might finish the request */ + if (read_ep0_fifo(ep, req)) + done(ep, req, 0); + /* else more OUT packets expected */ + } /* else OUT token before read was issued */ + } else /* irq was IPR clearing */ { + DBG(DBG_VERBOSE, "ep0out premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } + break; + case EP0_END_XFER: + if (req) + done(ep, req, 0); + /* ack control-IN status (maybe in-zlp was skipped) + * also appears after some config change events. + */ + if (udccs0 & UDCCS0_OPR) + UDCCS0 = UDCCS0_OPR; + ep0_idle(dev); + break; + case EP0_STALL: + UDCCS0 = UDCCS0_FST; + break; + } + USIR0 = USIR0_IR0; +} + +static void handle_ep(struct pxa25x_ep *ep) +{ + struct pxa25x_request *req; + int is_in = ep->bEndpointAddress & USB_DIR_IN; + int completed; + u32 udccs, tmp; + + do { + completed = 0; + if (likely (!list_empty(&ep->queue))) + req = list_entry(ep->queue.next, + struct pxa25x_request, queue); + else + req = NULL; + + // TODO check FST handling + + udccs = *ep->reg_udccs; + if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ + tmp = UDCCS_BI_TUR; + if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) + tmp |= UDCCS_BI_SST; + tmp &= udccs; + if (likely (tmp)) + *ep->reg_udccs = tmp; + if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) + completed = write_fifo(ep, req); + + } else { /* irq from RPC (or for ISO, ROF) */ + if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) + tmp = UDCCS_BO_SST | UDCCS_BO_DME; + else + tmp = UDCCS_IO_ROF | UDCCS_IO_DME; + tmp &= udccs; + if (likely(tmp)) + *ep->reg_udccs = tmp; + + /* fifos can hold packets, ready for reading... */ + if (likely(req)) { + completed = read_fifo(ep, req); + } else + pio_irq_disable (ep->bEndpointAddress); + } + ep->pio_irqs++; + } while (completed); +} + +/* + * pxa25x_udc_irq - interrupt handler + * + * avoid delays in ep0 processing. the control handshaking isn't always + * under software control (pxa250c0 and the pxa255 are better), and delays + * could cause usb protocol errors. + */ +static irqreturn_t +pxa25x_udc_irq(int irq, void *_dev) +{ + struct pxa25x_udc *dev = _dev; + int handled; + + dev->stats.irqs++; + do { + u32 udccr = UDCCR; + + handled = 0; + + /* SUSpend Interrupt Request */ + if (unlikely(udccr & UDCCR_SUSIR)) { + udc_ack_int_UDCCR(UDCCR_SUSIR); + handled = 1; + DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() + ? "" : "+disconnect"); + + if (!is_vbus_present()) + stop_activity(dev, dev->driver); + else if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + ep0_idle (dev); + } + + /* RESume Interrupt Request */ + if (unlikely(udccr & UDCCR_RESIR)) { + udc_ack_int_UDCCR(UDCCR_RESIR); + handled = 1; + DBG(DBG_VERBOSE, "USB resume\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume + && is_vbus_present()) + dev->driver->resume(&dev->gadget); + } + + /* ReSeT Interrupt Request - USB reset */ + if (unlikely(udccr & UDCCR_RSTIR)) { + udc_ack_int_UDCCR(UDCCR_RSTIR); + handled = 1; + + if ((UDCCR & UDCCR_UDA) == 0) { + DBG(DBG_VERBOSE, "USB reset start\n"); + + /* reset driver and endpoints, + * in case that's not yet done + */ + stop_activity (dev, dev->driver); + + } else { + DBG(DBG_VERBOSE, "USB reset end\n"); + dev->gadget.speed = USB_SPEED_FULL; + memset(&dev->stats, 0, sizeof dev->stats); + /* driver and endpoints are still reset */ + } + + } else { + u32 usir0 = USIR0 & ~UICR0; + u32 usir1 = USIR1 & ~UICR1; + int i; + + if (unlikely (!usir0 && !usir1)) + continue; + + DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); + + /* control traffic */ + if (usir0 & USIR0_IR0) { + dev->ep[0].pio_irqs++; + handle_ep0(dev); + handled = 1; + } + + /* endpoint data transfers */ + for (i = 0; i < 8; i++) { + u32 tmp = 1 << i; + + if (i && (usir0 & tmp)) { + handle_ep(&dev->ep[i]); + USIR0 |= tmp; + handled = 1; + } + if (usir1 & tmp) { + handle_ep(&dev->ep[i+8]); + USIR1 |= tmp; + handled = 1; + } + } + } + + /* we could also ask for 1 msec SOF (SIR) interrupts */ + + } while (handled); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static void nop_release (struct device *dev) +{ + DMSG("%s %s\n", __func__, dev_name(dev)); +} + +/* this uses load-time allocation and initialization (instead of + * doing it at run-time) to save code, eliminate fault paths, and + * be more obviously correct. + */ +static struct pxa25x_udc memory = { + .gadget = { + .ops = &pxa25x_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &pxa25x_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + .reg_udccs = &UDCCS0, + .reg_uddr = &UDDR0, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS1, + .reg_uddr = &UDDR1, + }, + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS2, + .reg_ubcr = &UBCR2, + .reg_uddr = &UDDR2, + }, +#ifndef CONFIG_USB_PXA25X_SMALL + .ep[3] = { + .ep = { + .name = "ep3in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS3, + .reg_uddr = &UDDR3, + }, + .ep[4] = { + .ep = { + .name = "ep4out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 4, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS4, + .reg_ubcr = &UBCR4, + .reg_uddr = &UDDR4, + }, + .ep[5] = { + .ep = { + .name = "ep5in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 5, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDCCS5, + .reg_uddr = &UDDR5, + }, + + /* second group of endpoints */ + .ep[6] = { + .ep = { + .name = "ep6in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 6, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS6, + .reg_uddr = &UDDR6, + }, + .ep[7] = { + .ep = { + .name = "ep7out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 7, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS7, + .reg_ubcr = &UBCR7, + .reg_uddr = &UDDR7, + }, + .ep[8] = { + .ep = { + .name = "ep8in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 8, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS8, + .reg_uddr = &UDDR8, + }, + .ep[9] = { + .ep = { + .name = "ep9out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 9, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS9, + .reg_ubcr = &UBCR9, + .reg_uddr = &UDDR9, + }, + .ep[10] = { + .ep = { + .name = "ep10in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 10, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDCCS10, + .reg_uddr = &UDDR10, + }, + + /* third group of endpoints */ + .ep[11] = { + .ep = { + .name = "ep11in-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 11, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS11, + .reg_uddr = &UDDR11, + }, + .ep[12] = { + .ep = { + .name = "ep12out-bulk", + .ops = &pxa25x_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = BULK_FIFO_SIZE, + .bEndpointAddress = 12, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .reg_udccs = &UDCCS12, + .reg_ubcr = &UBCR12, + .reg_uddr = &UDDR12, + }, + .ep[13] = { + .ep = { + .name = "ep13in-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 13, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS13, + .reg_uddr = &UDDR13, + }, + .ep[14] = { + .ep = { + .name = "ep14out-iso", + .ops = &pxa25x_ep_ops, + .maxpacket = ISO_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = ISO_FIFO_SIZE, + .bEndpointAddress = 14, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .reg_udccs = &UDCCS14, + .reg_ubcr = &UBCR14, + .reg_uddr = &UDDR14, + }, + .ep[15] = { + .ep = { + .name = "ep15in-int", + .ops = &pxa25x_ep_ops, + .maxpacket = INT_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = INT_FIFO_SIZE, + .bEndpointAddress = USB_DIR_IN | 15, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .reg_udccs = &UDCCS15, + .reg_uddr = &UDDR15, + }, +#endif /* !CONFIG_USB_PXA25X_SMALL */ +}; + +#define CP15R0_VENDOR_MASK 0xffffe000 + +#if defined(CONFIG_ARCH_PXA) +#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ + +#elif defined(CONFIG_ARCH_IXP4XX) +#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */ + +#endif + +#define CP15R0_PROD_MASK 0x000003f0 +#define PXA25x 0x00000100 /* and PXA26x */ +#define PXA210 0x00000120 + +#define CP15R0_REV_MASK 0x0000000f + +#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) + +#define PXA255_A0 0x00000106 /* or PXA260_B1 */ +#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ +#define PXA250_B2 0x00000104 +#define PXA250_B1 0x00000103 /* or PXA260_A0 */ +#define PXA250_B0 0x00000102 +#define PXA250_A1 0x00000101 +#define PXA250_A0 0x00000100 + +#define PXA210_C0 0x00000125 +#define PXA210_B2 0x00000124 +#define PXA210_B1 0x00000123 +#define PXA210_B0 0x00000122 +#define IXP425_A0 0x000001c1 +#define IXP425_B0 0x000001f1 +#define IXP465_AD 0x00000200 + +/* + * probe - binds to the platform device + */ +static int __init pxa25x_udc_probe(struct platform_device *pdev) +{ + struct pxa25x_udc *dev = &memory; + int retval, vbus_irq, irq; + u32 chiprev; + + /* insist on Intel/ARM/XScale */ + asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); + if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { + pr_err("%s: not XScale!\n", driver_name); + return -ENODEV; + } + + /* trigger chiprev-specific logic */ + switch (chiprev & CP15R0_PRODREV_MASK) { +#if defined(CONFIG_ARCH_PXA) + case PXA255_A0: + dev->has_cfr = 1; + break; + case PXA250_A0: + case PXA250_A1: + /* A0/A1 "not released"; ep 13, 15 unusable */ + /* fall through */ + case PXA250_B2: case PXA210_B2: + case PXA250_B1: case PXA210_B1: + case PXA250_B0: case PXA210_B0: + /* OUT-DMA is broken ... */ + /* fall through */ + case PXA250_C0: case PXA210_C0: + break; +#elif defined(CONFIG_ARCH_IXP4XX) + case IXP425_A0: + case IXP425_B0: + case IXP465_AD: + dev->has_cfr = 1; + break; +#endif + default: + pr_err("%s: unrecognized processor: %08x\n", + driver_name, chiprev); + /* iop3xx, ixp4xx, ... */ + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + + dev->clk = clk_get(&pdev->dev, "UDCCLK"); + if (IS_ERR(dev->clk)) { + retval = PTR_ERR(dev->clk); + goto err_clk; + } + + pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, + dev->has_cfr ? "" : " (!cfr)", + SIZE_STR "(pio)" + ); + + /* other non-static parts of init */ + dev->dev = &pdev->dev; + dev->mach = pdev->dev.platform_data; + + if (dev->mach->gpio_vbus) { + if ((retval = gpio_request(dev->mach->gpio_vbus, + "pxa25x_udc GPIO VBUS"))) { + dev_dbg(&pdev->dev, + "can't get vbus gpio %d, err: %d\n", + dev->mach->gpio_vbus, retval); + goto err_gpio_vbus; + } + gpio_direction_input(dev->mach->gpio_vbus); + vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); + } else + vbus_irq = 0; + + if (dev->mach->gpio_pullup) { + if ((retval = gpio_request(dev->mach->gpio_pullup, + "pca25x_udc GPIO PULLUP"))) { + dev_dbg(&pdev->dev, + "can't get pullup gpio %d, err: %d\n", + dev->mach->gpio_pullup, retval); + goto err_gpio_pullup; + } + gpio_direction_output(dev->mach->gpio_pullup, 0); + } + + init_timer(&dev->timer); + dev->timer.function = udc_watchdog; + dev->timer.data = (unsigned long) dev; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_controller = dev; + platform_set_drvdata(pdev, dev); + + udc_disable(dev); + udc_reinit(dev); + + dev->vbus = is_vbus_present(); + + /* irq setup after old hardware state is cleaned up */ + retval = request_irq(irq, pxa25x_udc_irq, + IRQF_DISABLED, driver_name, dev); + if (retval != 0) { + pr_err("%s: can't get irq %d, err %d\n", + driver_name, irq, retval); + goto err_irq1; + } + dev->got_irq = 1; + +#ifdef CONFIG_ARCH_LUBBOCK + if (machine_is_lubbock()) { + retval = request_irq(LUBBOCK_USB_DISC_IRQ, + lubbock_vbus_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + driver_name, dev); + if (retval != 0) { + pr_err("%s: can't get irq %i, err %d\n", + driver_name, LUBBOCK_USB_DISC_IRQ, retval); +lubbock_fail0: + goto err_irq_lub; + } + retval = request_irq(LUBBOCK_USB_IRQ, + lubbock_vbus_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + driver_name, dev); + if (retval != 0) { + pr_err("%s: can't get irq %i, err %d\n", + driver_name, LUBBOCK_USB_IRQ, retval); + free_irq(LUBBOCK_USB_DISC_IRQ, dev); + goto lubbock_fail0; + } + } else +#endif + if (vbus_irq) { + retval = request_irq(vbus_irq, udc_vbus_irq, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + driver_name, dev); + if (retval != 0) { + pr_err("%s: can't get irq %i, err %d\n", + driver_name, vbus_irq, retval); + goto err_vbus_irq; + } + } + create_debug_files(dev); + + return 0; + + err_vbus_irq: +#ifdef CONFIG_ARCH_LUBBOCK + free_irq(LUBBOCK_USB_DISC_IRQ, dev); + err_irq_lub: +#endif + free_irq(irq, dev); + err_irq1: + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + err_gpio_pullup: + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); + err_gpio_vbus: + clk_put(dev->clk); + err_clk: + return retval; +} + +static void pxa25x_udc_shutdown(struct platform_device *_dev) +{ + pullup_off(); +} + +static int __exit pxa25x_udc_remove(struct platform_device *pdev) +{ + struct pxa25x_udc *dev = platform_get_drvdata(pdev); + + if (dev->driver) + return -EBUSY; + + dev->pullup = 0; + pullup(dev); + + remove_debug_files(dev); + + if (dev->got_irq) { + free_irq(platform_get_irq(pdev, 0), dev); + dev->got_irq = 0; + } +#ifdef CONFIG_ARCH_LUBBOCK + if (machine_is_lubbock()) { + free_irq(LUBBOCK_USB_DISC_IRQ, dev); + free_irq(LUBBOCK_USB_IRQ, dev); + } +#endif + if (dev->mach->gpio_vbus) { + free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); + gpio_free(dev->mach->gpio_vbus); + } + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + + clk_put(dev->clk); + + platform_set_drvdata(pdev, NULL); + the_controller = NULL; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* USB suspend (controlled by the host) and system suspend (controlled + * by the PXA) don't necessarily work well together. If USB is active, + * the 48 MHz clock is required; so the system can't enter 33 MHz idle + * mode, or any deeper PM saving state. + * + * For now, we punt and forcibly disconnect from the USB host when PXA + * enters any suspend state. While we're disconnected, we always disable + * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. + * Boards without software pullup control shouldn't use those states. + * VBUS IRQs should probably be ignored so that the PXA device just acts + * "dead" to USB hosts until system resume. + */ +static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct pxa25x_udc *udc = platform_get_drvdata(dev); + unsigned long flags; + + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + WARNING("USB host won't detect disconnect!\n"); + udc->suspended = 1; + + local_irq_save(flags); + pullup(udc); + local_irq_restore(flags); + + return 0; +} + +static int pxa25x_udc_resume(struct platform_device *dev) +{ + struct pxa25x_udc *udc = platform_get_drvdata(dev); + unsigned long flags; + + udc->suspended = 0; + local_irq_save(flags); + pullup(udc); + local_irq_restore(flags); + + return 0; +} + +#else +#define pxa25x_udc_suspend NULL +#define pxa25x_udc_resume NULL +#endif + +/*-------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .shutdown = pxa25x_udc_shutdown, + .remove = __exit_p(pxa25x_udc_remove), + .suspend = pxa25x_udc_suspend, + .resume = pxa25x_udc_resume, + .driver = { + .owner = THIS_MODULE, + .name = "pxa25x-udc", + }, +}; + +static int __init udc_init(void) +{ + pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); + return platform_driver_probe(&udc_driver, pxa25x_udc_probe); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa25x-udc"); diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h new file mode 100644 index 0000000..1d51aa2 --- /dev/null +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -0,0 +1,266 @@ +/* + * Intel PXA25x on-chip full speed USB device controller + * + * Copyright (C) 2003 Robert Schwebel , Pengutronix + * Copyright (C) 2003 David Brownell + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_USB_GADGET_PXA25X_H +#define __LINUX_USB_GADGET_PXA25X_H + +#include + +/*-------------------------------------------------------------------------*/ + +/* pxa25x has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ +#define UFNRH_SIR (1 << 7) /* SOF interrupt request */ +#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */ +#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */ +#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */ +#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */ + +/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ +#define UDCCFR UDC_RES2 /* UDC Control Function Register */ +#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */ +#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */ + +/* latest pxa255 errata define new "must be one" bits in UDCCFR */ +#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM)) + +/*-------------------------------------------------------------------------*/ + +struct pxa25x_udc; + +struct pxa25x_ep { + struct usb_ep ep; + struct pxa25x_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + + unsigned short fifo_size; + u8 bEndpointAddress; + u8 bmAttributes; + + unsigned stopped : 1; + unsigned dma_fixup : 1; + + /* UDCCS = UDC Control/Status for this EP + * UBCR = UDC Byte Count Remaining (contents of OUT fifo) + * UDDR = UDC Endpoint Data Register (the fifo) + * DRCM = DMA Request Channel Map + */ + volatile u32 *reg_udccs; + volatile u32 *reg_ubcr; + volatile u32 *reg_uddr; +}; + +struct pxa25x_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +#define EP0_FIFO_SIZE ((unsigned)16) +#define BULK_FIFO_SIZE ((unsigned)64) +#define ISO_FIFO_SIZE ((unsigned)256) +#define INT_FIFO_SIZE ((unsigned)8) + +struct udc_stats { + struct ep0stats { + unsigned long ops; + unsigned long bytes; + } read, write; + unsigned long irqs; +}; + +#ifdef CONFIG_USB_PXA25X_SMALL +/* when memory's tight, SMALL config saves code+data. */ +#define PXA_UDC_NUM_ENDPOINTS 3 +#endif + +#ifndef PXA_UDC_NUM_ENDPOINTS +#define PXA_UDC_NUM_ENDPOINTS 16 +#endif + +struct pxa25x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + enum ep0_state ep0state; + struct udc_stats stats; + unsigned got_irq : 1, + vbus : 1, + pullup : 1, + has_cfr : 1, + req_pending : 1, + req_std : 1, + req_config : 1, + suspended : 1, + active : 1; + +#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) + struct timer_list timer; + + struct device *dev; + struct clk *clk; + struct pxa2xx_udc_mach_info *mach; + u64 dma_mask; + struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; + +#ifdef CONFIG_USB_GADGET_DEBUG_FS + struct dentry *debugfs_udc; +#endif +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_LUBBOCK +#include +/* lubbock can also report usb connect/disconnect irqs */ +#endif + +static struct pxa25x_udc *the_controller; + +/*-------------------------------------------------------------------------*/ + +/* + * Debugging support vanishes in non-debug builds. DBG_NORMAL should be + * mostly silent during normal use/testing, with no timing side-effects. + */ +#define DBG_NORMAL 1 /* error paths, device state transitions */ +#define DBG_VERBOSE 2 /* add some success path trace info */ +#define DBG_NOISY 3 /* ... even more: request level */ +#define DBG_VERY_NOISY 4 /* ... even more: packet level */ + +#define DMSG(stuff...) pr_debug("udc: " stuff) + +#ifdef DEBUG + +static int is_vbus_present(void); + +static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", "EP0_STALL" +}; + +#ifdef VERBOSE_DEBUG +# define UDC_DEBUG DBG_VERBOSE +#else +# define UDC_DEBUG DBG_NORMAL +#endif + +static void __maybe_unused +dump_udccr(const char *label) +{ + u32 udccr = UDCCR; + DMSG("%s %02X =%s%s%s%s%s%s%s%s\n", + label, udccr, + (udccr & UDCCR_REM) ? " rem" : "", + (udccr & UDCCR_RSTIR) ? " rstir" : "", + (udccr & UDCCR_SRM) ? " srm" : "", + (udccr & UDCCR_SUSIR) ? " susir" : "", + (udccr & UDCCR_RESIR) ? " resir" : "", + (udccr & UDCCR_RSM) ? " rsm" : "", + (udccr & UDCCR_UDA) ? " uda" : "", + (udccr & UDCCR_UDE) ? " ude" : ""); +} + +static void __maybe_unused +dump_udccs0(const char *label) +{ + u32 udccs0 = UDCCS0; + + DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n", + label, state_name[the_controller->ep0state], udccs0, + (udccs0 & UDCCS0_SA) ? " sa" : "", + (udccs0 & UDCCS0_RNE) ? " rne" : "", + (udccs0 & UDCCS0_FST) ? " fst" : "", + (udccs0 & UDCCS0_SST) ? " sst" : "", + (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", + (udccs0 & UDCCS0_FTF) ? " ftf" : "", + (udccs0 & UDCCS0_IPR) ? " ipr" : "", + (udccs0 & UDCCS0_OPR) ? " opr" : ""); +} + +static void __maybe_unused +dump_state(struct pxa25x_udc *dev) +{ + u32 tmp; + unsigned i; + + DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", + is_vbus_present() ? "host " : "disconnected", + state_name[dev->ep0state], + UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); + dump_udccr("udccr"); + if (dev->has_cfr) { + tmp = UDCCFR; + DMSG("udccfr %02X =%s%s\n", tmp, + (tmp & UDCCFR_AREN) ? " aren" : "", + (tmp & UDCCFR_ACM) ? " acm" : ""); + } + + if (!dev->driver) { + DMSG("no gadget driver bound\n"); + return; + } else + DMSG("ep0 driver '%s'\n", dev->driver->driver.name); + + if (!is_vbus_present()) + return; + + dump_udccs0 ("udccs0"); + DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops); + + for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { + if (dev->ep [i].desc == NULL) + continue; + DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); + } +} + +#else + +#define dump_udccr(x) do{}while(0) +#define dump_udccs0(x) do{}while(0) +#define dump_state(x) do{}while(0) + +#define UDC_DEBUG ((unsigned)0) + +#endif + +#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) + +#define ERR(stuff...) pr_err("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) +#define INFO(stuff...) pr_info("udc: " stuff) + + +#endif /* __LINUX_USB_GADGET_PXA25X_H */ diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index e02bfd4..7cbc78a 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -33,13 +33,13 @@ #include #include -#include +#include #include #include #include - -#include +#include /* FIXME: for PSSR */ +#include #include "pxa27x_udc.h" @@ -1575,7 +1575,6 @@ static void udc_enable(struct pxa_udc *udc) { udc_writel(udc, UDCICR0, 0); udc_writel(udc, UDCICR1, 0); - udc_writel(udc, UP2OCR, UP2OCR_HXOE); udc_clear_mask_UDCCR(udc, UDCCR_UDE); clk_enable(udc->clk); @@ -1623,7 +1622,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) struct pxa_udc *udc = the_controller; int retval; - if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind + if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->disconnect || !driver->setup) return -EINVAL; if (!udc) @@ -2360,18 +2359,19 @@ static int pxa_udc_resume(struct platform_device *_dev) * Software must configure the USB OTG pad, UDC, and UHC * to the state they were in before entering sleep mode. */ - PSSR |= PSSR_OTGPH; + if (cpu_is_pxa27x()) + PSSR |= PSSR_OTGPH; return 0; } #endif /* work with hotplug and coldplug */ -MODULE_ALIAS("platform:pxa2xx-udc"); +MODULE_ALIAS("platform:pxa27x-udc"); static struct platform_driver udc_driver = { .driver = { - .name = "pxa2xx-udc", + .name = "pxa27x-udc", .owner = THIS_MODULE, }, .remove = __exit_p(pxa_udc_remove), diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index 97453db..1d1b793 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -484,12 +484,4 @@ static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget) #define ep_warn(ep, fmt, arg...) \ dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg) -/* - * Cannot include pxa-regs.h, as register names are similar. - * So PSSR is redefined here. This should be removed once UDC registers will - * be gone from pxa-regs.h. - */ -#define PSSR __REG(0x40F00004) /* Power Manager Sleep Status */ -#define PSSR_OTGPH (1 << 6) /* OTG Peripheral Hold */ - #endif /* __LINUX_USB_GADGET_PXA27X_H */ diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c deleted file mode 100644 index 08f699b..0000000 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ /dev/null @@ -1,2383 +0,0 @@ -/* - * linux/drivers/usb/gadget/pxa2xx_udc.c - * Intel PXA25x and IXP4xx on-chip full speed USB device controllers - * - * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) - * Copyright (C) 2003 Robert Schwebel, Pengutronix - * Copyright (C) 2003 Benedikt Spranger, Pengutronix - * Copyright (C) 2003 David Brownell - * Copyright (C) 2003 Joshua Wise - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* #define VERBOSE_DEBUG */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - - -/* - * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x - * series processors. The UDC for the IXP 4xx series is very similar. - * There are fifteen endpoints, in addition to ep0. - * - * Such controller drivers work with a gadget driver. The gadget driver - * returns descriptors, implements configuration and data protocols used - * by the host to interact with this device, and allocates endpoints to - * the different protocol interfaces. The controller driver virtualizes - * usb hardware so that the gadget drivers will be more portable. - * - * This UDC hardware wants to implement a bit too much USB protocol, so - * it constrains the sorts of USB configuration change events that work. - * The errata for these chips are misleading; some "fixed" bugs from - * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. - * - * Note that the UDC hardware supports DMA (except on IXP) but that's - * not used here. IN-DMA (to host) is simple enough, when the data is - * suitably aligned (16 bytes) ... the network stack doesn't do that, - * other software can. OUT-DMA is buggy in most chip versions, as well - * as poorly designed (data toggle not automatic). So this driver won't - * bother using DMA. (Mostly-working IN-DMA support was available in - * kernels before 2.6.23, but was never enabled or well tested.) - */ - -#define DRIVER_VERSION "30-June-2007" -#define DRIVER_DESC "PXA 25x USB Device Controller driver" - - -static const char driver_name [] = "pxa2xx_udc"; - -static const char ep0name [] = "ep0"; - - -#ifdef CONFIG_ARCH_IXP4XX - -/* cpu-specific register addresses are compiled in to this code */ -#ifdef CONFIG_ARCH_PXA -#error "Can't configure both IXP and PXA" -#endif - -/* IXP doesn't yet support */ -#define clk_get(dev,name) NULL -#define clk_enable(clk) do { } while (0) -#define clk_disable(clk) do { } while (0) -#define clk_put(clk) do { } while (0) - -#endif - -#include "pxa2xx_udc.h" - - -#ifdef CONFIG_USB_PXA2XX_SMALL -#define SIZE_STR " (small)" -#else -#define SIZE_STR "" -#endif - -/* --------------------------------------------------------------------------- - * endpoint related parts of the api to the usb controller hardware, - * used by gadget driver; and the inner talker-to-hardware core. - * --------------------------------------------------------------------------- - */ - -static void pxa2xx_ep_fifo_flush (struct usb_ep *ep); -static void nuke (struct pxa2xx_ep *, int status); - -/* one GPIO should be used to detect VBUS from the host */ -static int is_vbus_present(void) -{ - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - - if (mach->gpio_vbus) { - int value = gpio_get_value(mach->gpio_vbus); - return mach->gpio_vbus_inverted ? !value : value; - } - if (mach->udc_is_connected) - return mach->udc_is_connected(); - return 1; -} - -/* one GPIO should control a D+ pullup, so host sees this device (or not) */ -static void pullup_off(void) -{ - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - - if (mach->gpio_pullup) - gpio_set_value(mach->gpio_pullup, 0); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); -} - -static void pullup_on(void) -{ - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - - if (mach->gpio_pullup) - gpio_set_value(mach->gpio_pullup, 1); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_CONNECT); -} - -static void pio_irq_enable(int bEndpointAddress) -{ - bEndpointAddress &= 0xf; - if (bEndpointAddress < 8) - UICR0 &= ~(1 << bEndpointAddress); - else { - bEndpointAddress -= 8; - UICR1 &= ~(1 << bEndpointAddress); - } -} - -static void pio_irq_disable(int bEndpointAddress) -{ - bEndpointAddress &= 0xf; - if (bEndpointAddress < 8) - UICR0 |= 1 << bEndpointAddress; - else { - bEndpointAddress -= 8; - UICR1 |= 1 << bEndpointAddress; - } -} - -/* The UDCCR reg contains mask and interrupt status bits, - * so using '|=' isn't safe as it may ack an interrupt. - */ -#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) - -static inline void udc_set_mask_UDCCR(int mask) -{ - UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); -} - -static inline void udc_clear_mask_UDCCR(int mask) -{ - UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); -} - -static inline void udc_ack_int_UDCCR(int mask) -{ - /* udccr contains the bits we dont want to change */ - __u32 udccr = UDCCR & UDCCR_MASK_BITS; - - UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); -} - -/* - * endpoint enable/disable - * - * we need to verify the descriptors used to enable endpoints. since pxa2xx - * endpoint configurations are fixed, and are pretty much always enabled, - * there's not a lot to manage here. - * - * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints, - * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except - * for a single interface (with only the default altsetting) and for gadget - * drivers that don't halt endpoints (not reset by set_interface). that also - * means that if you use ISO, you must violate the USB spec rule that all - * iso endpoints must be in non-default altsettings. - */ -static int pxa2xx_ep_enable (struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct pxa2xx_ep *ep; - struct pxa2xx_udc *dev; - - ep = container_of (_ep, struct pxa2xx_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name - || desc->bDescriptorType != USB_DT_ENDPOINT - || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu - (desc->wMaxPacketSize)) { - DMSG("%s, bad ep or descriptor\n", __func__); - return -EINVAL; - } - - /* xfer types must match, except that interrupt ~= bulk */ - if (ep->bmAttributes != desc->bmAttributes - && ep->bmAttributes != USB_ENDPOINT_XFER_BULK - && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { - DMSG("%s, %s type mismatch\n", __func__, _ep->name); - return -EINVAL; - } - - /* hardware _could_ do smaller, but driver doesn't */ - if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu (desc->wMaxPacketSize) - != BULK_FIFO_SIZE) - || !desc->wMaxPacketSize) { - DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); - return -ERANGE; - } - - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { - DMSG("%s, bogus device state\n", __func__); - return -ESHUTDOWN; - } - - ep->desc = desc; - ep->stopped = 0; - ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); - - /* flush fifo (mostly for OUT buffers) */ - pxa2xx_ep_fifo_flush (_ep); - - /* ... reset halt state too, if we could ... */ - - DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); - return 0; -} - -static int pxa2xx_ep_disable (struct usb_ep *_ep) -{ - struct pxa2xx_ep *ep; - unsigned long flags; - - ep = container_of (_ep, struct pxa2xx_ep, ep); - if (!_ep || !ep->desc) { - DMSG("%s, %s not enabled\n", __func__, - _ep ? ep->ep.name : NULL); - return -EINVAL; - } - local_irq_save(flags); - - nuke (ep, -ESHUTDOWN); - - /* flush fifo (mostly for IN buffers) */ - pxa2xx_ep_fifo_flush (_ep); - - ep->desc = NULL; - ep->stopped = 1; - - local_irq_restore(flags); - DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* for the pxa2xx, these can just wrap kmalloc/kfree. gadget drivers - * must still pass correctly initialized endpoints, since other controller - * drivers may care about how it's currently set up (dma issues etc). - */ - -/* - * pxa2xx_ep_alloc_request - allocate a request data structure - */ -static struct usb_request * -pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct pxa2xx_request *req; - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD (&req->queue); - return &req->req; -} - - -/* - * pxa2xx_ep_free_request - deallocate a request data structure - */ -static void -pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) -{ - struct pxa2xx_request *req; - - req = container_of (_req, struct pxa2xx_request, req); - WARN_ON (!list_empty (&req->queue)); - kfree(req); -} - -/*-------------------------------------------------------------------------*/ - -/* - * done - retire a request; caller blocked irqs - */ -static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status) -{ - unsigned stopped = ep->stopped; - - list_del_init(&req->queue); - - if (likely (req->req.status == -EINPROGRESS)) - req->req.status = status; - else - status = req->req.status; - - if (status && status != -ESHUTDOWN) - DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - req->req.complete(&ep->ep, &req->req); - ep->stopped = stopped; -} - - -static inline void ep0_idle (struct pxa2xx_udc *dev) -{ - dev->ep0state = EP0_IDLE; -} - -static int -write_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max) -{ - u8 *buf; - unsigned length, count; - - buf = req->req.buf + req->req.actual; - prefetch(buf); - - /* how big will this packet be? */ - length = min(req->req.length - req->req.actual, max); - req->req.actual += length; - - count = length; - while (likely(count--)) - *uddr = *buf++; - - return length; -} - -/* - * write to an IN endpoint fifo, as many packets as possible. - * irqs will use this to write the rest later. - * caller guarantees at least one packet buffer is ready (or a zlp). - */ -static int -write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - unsigned max; - - max = le16_to_cpu(ep->desc->wMaxPacketSize); - do { - unsigned count; - int is_last, is_short; - - count = write_packet(ep->reg_uddr, req, max); - - /* last packet is usually short (or a zlp) */ - if (unlikely (count != max)) - is_last = is_short = 1; - else { - if (likely(req->req.length != req->req.actual) - || req->req.zero) - is_last = 0; - else - is_last = 1; - /* interrupt/iso maxpacket may not fill the fifo */ - is_short = unlikely (max < ep->fifo_size); - } - - DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", - ep->ep.name, count, - is_last ? "/L" : "", is_short ? "/S" : "", - req->req.length - req->req.actual, req); - - /* let loose that packet. maybe try writing another one, - * double buffering might work. TSP, TPC, and TFS - * bit values are the same for all normal IN endpoints. - */ - *ep->reg_udccs = UDCCS_BI_TPC; - if (is_short) - *ep->reg_udccs = UDCCS_BI_TSP; - - /* requests complete when all IN data is in the FIFO */ - if (is_last) { - done (ep, req, 0); - if (list_empty(&ep->queue)) - pio_irq_disable (ep->bEndpointAddress); - return 1; - } - - // TODO experiment: how robust can fifo mode tweaking be? - // double buffering is off in the default fifo mode, which - // prevents TFS from being set here. - - } while (*ep->reg_udccs & UDCCS_BI_TFS); - return 0; -} - -/* caller asserts req->pending (ep0 irq status nyet cleared); starts - * ep0 data stage. these chips want very simple state transitions. - */ -static inline -void ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag) -{ - UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; - USIR0 = USIR0_IR0; - dev->req_pending = 0; - DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", - __func__, tag, UDCCS0, flags); -} - -static int -write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - unsigned count; - int is_short; - - count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); - ep->dev->stats.write.bytes += count; - - /* last packet "must be" short (or a zlp) */ - is_short = (count != EP0_FIFO_SIZE); - - DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, - req->req.length - req->req.actual, req); - - if (unlikely (is_short)) { - if (ep->dev->req_pending) - ep0start(ep->dev, UDCCS0_IPR, "short IN"); - else - UDCCS0 = UDCCS0_IPR; - - count = req->req.length; - done (ep, req, 0); - ep0_idle(ep->dev); -#ifndef CONFIG_ARCH_IXP4XX -#if 1 - /* This seems to get rid of lost status irqs in some cases: - * host responds quickly, or next request involves config - * change automagic, or should have been hidden, or ... - * - * FIXME get rid of all udelays possible... - */ - if (count >= EP0_FIFO_SIZE) { - count = 100; - do { - if ((UDCCS0 & UDCCS0_OPR) != 0) { - /* clear OPR, generate ack */ - UDCCS0 = UDCCS0_OPR; - break; - } - count--; - udelay(1); - } while (count); - } -#endif -#endif - } else if (ep->dev->req_pending) - ep0start(ep->dev, 0, "IN"); - return is_short; -} - - -/* - * read_fifo - unload packet(s) from the fifo we use for usb OUT - * transfers and put them into the request. caller should have made - * sure there's at least one packet ready. - * - * returns true if the request completed because of short packet or the - * request buffer having filled (and maybe overran till end-of-packet). - */ -static int -read_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - for (;;) { - u32 udccs; - u8 *buf; - unsigned bufferspace, count, is_short; - - /* make sure there's a packet in the FIFO. - * UDCCS_{BO,IO}_RPC are all the same bit value. - * UDCCS_{BO,IO}_RNE are all the same bit value. - */ - udccs = *ep->reg_udccs; - if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) - break; - buf = req->req.buf + req->req.actual; - prefetchw(buf); - bufferspace = req->req.length - req->req.actual; - - /* read all bytes from this packet */ - if (likely (udccs & UDCCS_BO_RNE)) { - count = 1 + (0x0ff & *ep->reg_ubcr); - req->req.actual += min (count, bufferspace); - } else /* zlp */ - count = 0; - is_short = (count < ep->ep.maxpacket); - DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", - ep->ep.name, udccs, count, - is_short ? "/S" : "", - req, req->req.actual, req->req.length); - while (likely (count-- != 0)) { - u8 byte = (u8) *ep->reg_uddr; - - if (unlikely (bufferspace == 0)) { - /* this happens when the driver's buffer - * is smaller than what the host sent. - * discard the extra data. - */ - if (req->req.status != -EOVERFLOW) - DMSG("%s overflow %d\n", - ep->ep.name, count); - req->req.status = -EOVERFLOW; - } else { - *buf++ = byte; - bufferspace--; - } - } - *ep->reg_udccs = UDCCS_BO_RPC; - /* RPC/RSP/RNE could now reflect the other packet buffer */ - - /* iso is one request per packet */ - if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - if (udccs & UDCCS_IO_ROF) - req->req.status = -EHOSTUNREACH; - /* more like "is_done" */ - is_short = 1; - } - - /* completion */ - if (is_short || req->req.actual == req->req.length) { - done (ep, req, 0); - if (list_empty(&ep->queue)) - pio_irq_disable (ep->bEndpointAddress); - return 1; - } - - /* finished that packet. the next one may be waiting... */ - } - return 0; -} - -/* - * special ep0 version of the above. no UBCR0 or double buffering; status - * handshaking is magic. most device protocols don't need control-OUT. - * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other - * protocols do use them. - */ -static int -read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - u8 *buf, byte; - unsigned bufferspace; - - buf = req->req.buf + req->req.actual; - bufferspace = req->req.length - req->req.actual; - - while (UDCCS0 & UDCCS0_RNE) { - byte = (u8) UDDR0; - - if (unlikely (bufferspace == 0)) { - /* this happens when the driver's buffer - * is smaller than what the host sent. - * discard the extra data. - */ - if (req->req.status != -EOVERFLOW) - DMSG("%s overflow\n", ep->ep.name); - req->req.status = -EOVERFLOW; - } else { - *buf++ = byte; - req->req.actual++; - bufferspace--; - } - } - - UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; - - /* completion */ - if (req->req.actual >= req->req.length) - return 1; - - /* finished that packet. the next one may be waiting... */ - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int -pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct pxa2xx_request *req; - struct pxa2xx_ep *ep; - struct pxa2xx_udc *dev; - unsigned long flags; - - req = container_of(_req, struct pxa2xx_request, req); - if (unlikely (!_req || !_req->complete || !_req->buf - || !list_empty(&req->queue))) { - DMSG("%s, bad params\n", __func__); - return -EINVAL; - } - - ep = container_of(_ep, struct pxa2xx_ep, ep); - if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { - DMSG("%s, bad ep\n", __func__); - return -EINVAL; - } - - dev = ep->dev; - if (unlikely (!dev->driver - || dev->gadget.speed == USB_SPEED_UNKNOWN)) { - DMSG("%s, bogus device state\n", __func__); - return -ESHUTDOWN; - } - - /* iso is always one packet per request, that's the only way - * we can report per-packet status. that also helps with dma. - */ - if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - && req->req.length > le16_to_cpu - (ep->desc->wMaxPacketSize))) - return -EMSGSIZE; - - DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); - - local_irq_save(flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - if (list_empty(&ep->queue) && !ep->stopped) { - if (ep->desc == NULL/* ep0 */) { - unsigned length = _req->length; - - switch (dev->ep0state) { - case EP0_IN_DATA_PHASE: - dev->stats.write.ops++; - if (write_ep0_fifo(ep, req)) - req = NULL; - break; - - case EP0_OUT_DATA_PHASE: - dev->stats.read.ops++; - /* messy ... */ - if (dev->req_config) { - DBG(DBG_VERBOSE, "ep0 config ack%s\n", - dev->has_cfr ? "" : " raced"); - if (dev->has_cfr) - UDCCFR = UDCCFR_AREN|UDCCFR_ACM - |UDCCFR_MB1; - done(ep, req, 0); - dev->ep0state = EP0_END_XFER; - local_irq_restore (flags); - return 0; - } - if (dev->req_pending) - ep0start(dev, UDCCS0_IPR, "OUT"); - if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 - && read_ep0_fifo(ep, req))) { - ep0_idle(dev); - done(ep, req, 0); - req = NULL; - } - break; - - default: - DMSG("ep0 i/o, odd state %d\n", dev->ep0state); - local_irq_restore (flags); - return -EL2HLT; - } - /* can the FIFO can satisfy the request immediately? */ - } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { - if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 - && write_fifo(ep, req)) - req = NULL; - } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 - && read_fifo(ep, req)) { - req = NULL; - } - - if (likely (req && ep->desc)) - pio_irq_enable(ep->bEndpointAddress); - } - - /* pio or dma irq handler advances the queue. */ - if (likely(req != NULL)) - list_add_tail(&req->queue, &ep->queue); - local_irq_restore(flags); - - return 0; -} - - -/* - * nuke - dequeue ALL requests - */ -static void nuke(struct pxa2xx_ep *ep, int status) -{ - struct pxa2xx_request *req; - - /* called with irqs blocked */ - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct pxa2xx_request, - queue); - done(ep, req, status); - } - if (ep->desc) - pio_irq_disable (ep->bEndpointAddress); -} - - -/* dequeue JUST ONE request */ -static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct pxa2xx_ep *ep; - struct pxa2xx_request *req; - unsigned long flags; - - ep = container_of(_ep, struct pxa2xx_ep, ep); - if (!_ep || ep->ep.name == ep0name) - return -EINVAL; - - local_irq_save(flags); - - /* make sure it's actually queued on this endpoint */ - list_for_each_entry (req, &ep->queue, queue) { - if (&req->req == _req) - break; - } - if (&req->req != _req) { - local_irq_restore(flags); - return -EINVAL; - } - - done(ep, req, -ECONNRESET); - - local_irq_restore(flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value) -{ - struct pxa2xx_ep *ep; - unsigned long flags; - - ep = container_of(_ep, struct pxa2xx_ep, ep); - if (unlikely (!_ep - || (!ep->desc && ep->ep.name != ep0name)) - || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - DMSG("%s, bad ep\n", __func__); - return -EINVAL; - } - if (value == 0) { - /* this path (reset toggle+halt) is needed to implement - * SET_INTERFACE on normal hardware. but it can't be - * done from software on the PXA UDC, and the hardware - * forgets to do it as part of SET_INTERFACE automagic. - */ - DMSG("only host can clear %s halt\n", _ep->name); - return -EROFS; - } - - local_irq_save(flags); - - if ((ep->bEndpointAddress & USB_DIR_IN) != 0 - && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 - || !list_empty(&ep->queue))) { - local_irq_restore(flags); - return -EAGAIN; - } - - /* FST bit is the same for control, bulk in, bulk out, interrupt in */ - *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; - - /* ep0 needs special care */ - if (!ep->desc) { - start_watchdog(ep->dev); - ep->dev->req_pending = 0; - ep->dev->ep0state = EP0_STALL; - - /* and bulk/intr endpoints like dropping stalls too */ - } else { - unsigned i; - for (i = 0; i < 1000; i += 20) { - if (*ep->reg_udccs & UDCCS_BI_SST) - break; - udelay(20); - } - } - local_irq_restore(flags); - - DBG(DBG_VERBOSE, "%s halt\n", _ep->name); - return 0; -} - -static int pxa2xx_ep_fifo_status(struct usb_ep *_ep) -{ - struct pxa2xx_ep *ep; - - ep = container_of(_ep, struct pxa2xx_ep, ep); - if (!_ep) { - DMSG("%s, bad ep\n", __func__); - return -ENODEV; - } - /* pxa can't report unclaimed bytes from IN fifos */ - if ((ep->bEndpointAddress & USB_DIR_IN) != 0) - return -EOPNOTSUPP; - if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN - || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) - return 0; - else - return (*ep->reg_ubcr & 0xfff) + 1; -} - -static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep) -{ - struct pxa2xx_ep *ep; - - ep = container_of(_ep, struct pxa2xx_ep, ep); - if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { - DMSG("%s, bad ep\n", __func__); - return; - } - - /* toggle and halt bits stay unchanged */ - - /* for OUT, just read and discard the FIFO contents. */ - if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { - while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) - (void) *ep->reg_uddr; - return; - } - - /* most IN status is the same, but ISO can't stall */ - *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR - | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) - ? 0 : UDCCS_BI_SST; -} - - -static struct usb_ep_ops pxa2xx_ep_ops = { - .enable = pxa2xx_ep_enable, - .disable = pxa2xx_ep_disable, - - .alloc_request = pxa2xx_ep_alloc_request, - .free_request = pxa2xx_ep_free_request, - - .queue = pxa2xx_ep_queue, - .dequeue = pxa2xx_ep_dequeue, - - .set_halt = pxa2xx_ep_set_halt, - .fifo_status = pxa2xx_ep_fifo_status, - .fifo_flush = pxa2xx_ep_fifo_flush, -}; - - -/* --------------------------------------------------------------------------- - * device-scoped parts of the api to the usb controller hardware - * --------------------------------------------------------------------------- - */ - -static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget) -{ - return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); -} - -static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget) -{ - /* host may not have enabled remote wakeup */ - if ((UDCCS0 & UDCCS0_DRWF) == 0) - return -EHOSTUNREACH; - udc_set_mask_UDCCR(UDCCR_RSM); - return 0; -} - -static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *); -static void udc_enable (struct pxa2xx_udc *); -static void udc_disable(struct pxa2xx_udc *); - -/* We disable the UDC -- and its 48 MHz clock -- whenever it's not - * in active use. - */ -static int pullup(struct pxa2xx_udc *udc) -{ - int is_active = udc->vbus && udc->pullup && !udc->suspended; - DMSG("%s\n", is_active ? "active" : "inactive"); - if (is_active) { - if (!udc->active) { - udc->active = 1; - /* Enable clock for USB device */ - clk_enable(udc->clk); - udc_enable(udc); - } - } else { - if (udc->active) { - if (udc->gadget.speed != USB_SPEED_UNKNOWN) { - DMSG("disconnect %s\n", udc->driver - ? udc->driver->driver.name - : "(no driver)"); - stop_activity(udc, udc->driver); - } - udc_disable(udc); - /* Disable clock for USB device */ - clk_disable(udc->clk); - udc->active = 0; - } - - } - return 0; -} - -/* VBUS reporting logically comes from a transceiver */ -static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active) -{ - struct pxa2xx_udc *udc; - - udc = container_of(_gadget, struct pxa2xx_udc, gadget); - udc->vbus = (is_active != 0); - DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); - pullup(udc); - return 0; -} - -/* drivers may have software control over D+ pullup */ -static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active) -{ - struct pxa2xx_udc *udc; - - udc = container_of(_gadget, struct pxa2xx_udc, gadget); - - /* not all boards support pullup control */ - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) - return -EOPNOTSUPP; - - udc->pullup = (is_active != 0); - pullup(udc); - return 0; -} - -static const struct usb_gadget_ops pxa2xx_udc_ops = { - .get_frame = pxa2xx_udc_get_frame, - .wakeup = pxa2xx_udc_wakeup, - .vbus_session = pxa2xx_udc_vbus_session, - .pullup = pxa2xx_udc_pullup, - - // .vbus_draw ... boards may consume current from VBUS, up to - // 100-500mA based on config. the 500uA suspend ceiling means - // that exclusively vbus-powered PXA designs violate USB specs. -}; - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_GADGET_DEBUG_FS - -static int -udc_seq_show(struct seq_file *m, void *_d) -{ - struct pxa2xx_udc *dev = m->private; - unsigned long flags; - int i; - u32 tmp; - - local_irq_save(flags); - - /* basic device status */ - seq_printf(m, DRIVER_DESC "\n" - "%s version: %s\nGadget driver: %s\nHost %s\n\n", - driver_name, DRIVER_VERSION SIZE_STR "(pio)", - dev->driver ? dev->driver->driver.name : "(none)", - is_vbus_present() ? "full speed" : "disconnected"); - - /* registers for device and ep0 */ - seq_printf(m, - "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); - - tmp = UDCCR; - seq_printf(m, - "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, - (tmp & UDCCR_REM) ? " rem" : "", - (tmp & UDCCR_RSTIR) ? " rstir" : "", - (tmp & UDCCR_SRM) ? " srm" : "", - (tmp & UDCCR_SUSIR) ? " susir" : "", - (tmp & UDCCR_RESIR) ? " resir" : "", - (tmp & UDCCR_RSM) ? " rsm" : "", - (tmp & UDCCR_UDA) ? " uda" : "", - (tmp & UDCCR_UDE) ? " ude" : ""); - - tmp = UDCCS0; - seq_printf(m, - "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, - (tmp & UDCCS0_SA) ? " sa" : "", - (tmp & UDCCS0_RNE) ? " rne" : "", - (tmp & UDCCS0_FST) ? " fst" : "", - (tmp & UDCCS0_SST) ? " sst" : "", - (tmp & UDCCS0_DRWF) ? " dwrf" : "", - (tmp & UDCCS0_FTF) ? " ftf" : "", - (tmp & UDCCS0_IPR) ? " ipr" : "", - (tmp & UDCCS0_OPR) ? " opr" : ""); - - if (dev->has_cfr) { - tmp = UDCCFR; - seq_printf(m, - "udccfr %02X =%s%s\n", tmp, - (tmp & UDCCFR_AREN) ? " aren" : "", - (tmp & UDCCFR_ACM) ? " acm" : ""); - } - - if (!is_vbus_present() || !dev->driver) - goto done; - - seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", - dev->stats.write.bytes, dev->stats.write.ops, - dev->stats.read.bytes, dev->stats.read.ops, - dev->stats.irqs); - - /* dump endpoint queues */ - for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { - struct pxa2xx_ep *ep = &dev->ep [i]; - struct pxa2xx_request *req; - - if (i != 0) { - const struct usb_endpoint_descriptor *desc; - - desc = ep->desc; - if (!desc) - continue; - tmp = *dev->ep [i].reg_udccs; - seq_printf(m, - "%s max %d %s udccs %02x irqs %lu\n", - ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), - "pio", tmp, ep->pio_irqs); - /* TODO translate all five groups of udccs bits! */ - - } else /* ep0 should only have one transfer queued */ - seq_printf(m, "ep0 max 16 pio irqs %lu\n", - ep->pio_irqs); - - if (list_empty(&ep->queue)) { - seq_printf(m, "\t(nothing queued)\n"); - continue; - } - list_for_each_entry(req, &ep->queue, queue) { - seq_printf(m, - "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); - } - } - -done: - local_irq_restore(flags); - return 0; -} - -static int -udc_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, udc_seq_show, inode->i_private); -} - -static const struct file_operations debug_fops = { - .open = udc_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -#define create_debug_files(dev) \ - do { \ - dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ - S_IRUGO, NULL, dev, &debug_fops); \ - } while (0) -#define remove_debug_files(dev) \ - do { \ - if (dev->debugfs_udc) \ - debugfs_remove(dev->debugfs_udc); \ - } while (0) - -#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ - -#define create_debug_files(dev) do {} while (0) -#define remove_debug_files(dev) do {} while (0) - -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - -/*-------------------------------------------------------------------------*/ - -/* - * udc_disable - disable USB device controller - */ -static void udc_disable(struct pxa2xx_udc *dev) -{ - /* block all irqs */ - udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); - UICR0 = UICR1 = 0xff; - UFNRH = UFNRH_SIM; - - /* if hardware supports it, disconnect from usb */ - pullup_off(); - - udc_clear_mask_UDCCR(UDCCR_UDE); - - ep0_idle (dev); - dev->gadget.speed = USB_SPEED_UNKNOWN; -} - - -/* - * udc_reinit - initialize software state - */ -static void udc_reinit(struct pxa2xx_udc *dev) -{ - u32 i; - - /* device/ep0 records init */ - INIT_LIST_HEAD (&dev->gadget.ep_list); - INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); - dev->ep0state = EP0_IDLE; - - /* basic endpoint records init */ - for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { - struct pxa2xx_ep *ep = &dev->ep[i]; - - if (i != 0) - list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); - - ep->desc = NULL; - ep->stopped = 0; - INIT_LIST_HEAD (&ep->queue); - ep->pio_irqs = 0; - } - - /* the rest was statically initialized, and is read-only */ -} - -/* until it's enabled, this UDC should be completely invisible - * to any USB host. - */ -static void udc_enable (struct pxa2xx_udc *dev) -{ - udc_clear_mask_UDCCR(UDCCR_UDE); - - /* try to clear these bits before we enable the udc */ - udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); - - ep0_idle(dev); - dev->gadget.speed = USB_SPEED_UNKNOWN; - dev->stats.irqs = 0; - - /* - * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: - * - enable UDC - * - if RESET is already in progress, ack interrupt - * - unmask reset interrupt - */ - udc_set_mask_UDCCR(UDCCR_UDE); - if (!(UDCCR & UDCCR_UDA)) - udc_ack_int_UDCCR(UDCCR_RSTIR); - - if (dev->has_cfr /* UDC_RES2 is defined */) { - /* pxa255 (a0+) can avoid a set_config race that could - * prevent gadget drivers from configuring correctly - */ - UDCCFR = UDCCFR_ACM | UDCCFR_MB1; - } else { - /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) - * which could result in missing packets and interrupts. - * supposedly one bit per endpoint, controlling whether it - * double buffers or not; ACM/AREN bits fit into the holes. - * zero bits (like USIR0_IRx) disable double buffering. - */ - UDC_RES1 = 0x00; - UDC_RES2 = 0x00; - } - - /* enable suspend/resume and reset irqs */ - udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); - - /* enable ep0 irqs */ - UICR0 &= ~UICR0_IM0; - - /* if hardware supports it, pullup D+ and wait for reset */ - pullup_on(); -} - - -/* when a driver is successfully registered, it will receive - * control requests including set_configuration(), which enables - * non-control requests. then usb traffic follows until a - * disconnect is reported. then a host may connect again, or - * the driver might get unbound. - */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) -{ - struct pxa2xx_udc *dev = the_controller; - int retval; - - if (!driver - || driver->speed < USB_SPEED_FULL - || !driver->bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; - - /* first hook up the driver ... */ - dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - dev->pullup = 1; - - retval = device_add (&dev->gadget.dev); - if (retval) { -fail: - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = driver->bind(&dev->gadget); - if (retval) { - DMSG("bind to driver %s --> error %d\n", - driver->driver.name, retval); - device_del (&dev->gadget.dev); - goto fail; - } - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - */ - DMSG("registered gadget driver '%s'\n", driver->driver.name); - pullup(dev); - dump_state(dev); - return 0; -} -EXPORT_SYMBOL(usb_gadget_register_driver); - -static void -stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) -{ - int i; - - /* don't disconnect drivers more than once */ - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - dev->gadget.speed = USB_SPEED_UNKNOWN; - - /* prevent new request submissions, kill any outstanding requests */ - for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { - struct pxa2xx_ep *ep = &dev->ep[i]; - - ep->stopped = 1; - nuke(ep, -ESHUTDOWN); - } - del_timer_sync(&dev->timer); - - /* report disconnect; the driver is already quiesced */ - if (driver) - driver->disconnect(&dev->gadget); - - /* re-init driver-visible data structures */ - udc_reinit(dev); -} - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct pxa2xx_udc *dev = the_controller; - - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; - - local_irq_disable(); - dev->pullup = 0; - pullup(dev); - stop_activity(dev, driver); - local_irq_enable(); - - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; - dev->driver = NULL; - - device_del (&dev->gadget.dev); - - DMSG("unregistered gadget driver '%s'\n", driver->driver.name); - dump_state(dev); - return 0; -} -EXPORT_SYMBOL(usb_gadget_unregister_driver); - - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_LUBBOCK - -/* Lubbock has separate connect and disconnect irqs. More typical designs - * use one GPIO as the VBUS IRQ, and another to control the D+ pullup. - */ - -static irqreturn_t -lubbock_vbus_irq(int irq, void *_dev) -{ - struct pxa2xx_udc *dev = _dev; - int vbus; - - dev->stats.irqs++; - switch (irq) { - case LUBBOCK_USB_IRQ: - vbus = 1; - disable_irq(LUBBOCK_USB_IRQ); - enable_irq(LUBBOCK_USB_DISC_IRQ); - break; - case LUBBOCK_USB_DISC_IRQ: - vbus = 0; - disable_irq(LUBBOCK_USB_DISC_IRQ); - enable_irq(LUBBOCK_USB_IRQ); - break; - default: - return IRQ_NONE; - } - - pxa2xx_udc_vbus_session(&dev->gadget, vbus); - return IRQ_HANDLED; -} - -#endif - -static irqreturn_t udc_vbus_irq(int irq, void *_dev) -{ - struct pxa2xx_udc *dev = _dev; - int vbus = gpio_get_value(dev->mach->gpio_vbus); - - if (dev->mach->gpio_vbus_inverted) - vbus = !vbus; - - pxa2xx_udc_vbus_session(&dev->gadget, vbus); - return IRQ_HANDLED; -} - - -/*-------------------------------------------------------------------------*/ - -static inline void clear_ep_state (struct pxa2xx_udc *dev) -{ - unsigned i; - - /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint - * fifos, and pending transactions mustn't be continued in any case. - */ - for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) - nuke(&dev->ep[i], -ECONNABORTED); -} - -static void udc_watchdog(unsigned long _dev) -{ - struct pxa2xx_udc *dev = (void *)_dev; - - local_irq_disable(); - if (dev->ep0state == EP0_STALL - && (UDCCS0 & UDCCS0_FST) == 0 - && (UDCCS0 & UDCCS0_SST) == 0) { - UDCCS0 = UDCCS0_FST|UDCCS0_FTF; - DBG(DBG_VERBOSE, "ep0 re-stall\n"); - start_watchdog(dev); - } - local_irq_enable(); -} - -static void handle_ep0 (struct pxa2xx_udc *dev) -{ - u32 udccs0 = UDCCS0; - struct pxa2xx_ep *ep = &dev->ep [0]; - struct pxa2xx_request *req; - union { - struct usb_ctrlrequest r; - u8 raw [8]; - u32 word [2]; - } u; - - if (list_empty(&ep->queue)) - req = NULL; - else - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - - /* clear stall status */ - if (udccs0 & UDCCS0_SST) { - nuke(ep, -EPIPE); - UDCCS0 = UDCCS0_SST; - del_timer(&dev->timer); - ep0_idle(dev); - } - - /* previous request unfinished? non-error iff back-to-back ... */ - if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { - nuke(ep, 0); - del_timer(&dev->timer); - ep0_idle(dev); - } - - switch (dev->ep0state) { - case EP0_IDLE: - /* late-breaking status? */ - udccs0 = UDCCS0; - - /* start control request? */ - if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) - == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { - int i; - - nuke (ep, -EPROTO); - - /* read SETUP packet */ - for (i = 0; i < 8; i++) { - if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { -bad_setup: - DMSG("SETUP %d!\n", i); - goto stall; - } - u.raw [i] = (u8) UDDR0; - } - if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) - goto bad_setup; - -got_setup: - DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", - u.r.bRequestType, u.r.bRequest, - le16_to_cpu(u.r.wValue), - le16_to_cpu(u.r.wIndex), - le16_to_cpu(u.r.wLength)); - - /* cope with automagic for some standard requests. */ - dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) - == USB_TYPE_STANDARD; - dev->req_config = 0; - dev->req_pending = 1; - switch (u.r.bRequest) { - /* hardware restricts gadget drivers here! */ - case USB_REQ_SET_CONFIGURATION: - if (u.r.bRequestType == USB_RECIP_DEVICE) { - /* reflect hardware's automagic - * up to the gadget driver. - */ -config_change: - dev->req_config = 1; - clear_ep_state(dev); - /* if !has_cfr, there's no synch - * else use AREN (later) not SA|OPR - * USIR0_IR0 acts edge sensitive - */ - } - break; - /* ... and here, even more ... */ - case USB_REQ_SET_INTERFACE: - if (u.r.bRequestType == USB_RECIP_INTERFACE) { - /* udc hardware is broken by design: - * - altsetting may only be zero; - * - hw resets all interfaces' eps; - * - ep reset doesn't include halt(?). - */ - DMSG("broken set_interface (%d/%d)\n", - le16_to_cpu(u.r.wIndex), - le16_to_cpu(u.r.wValue)); - goto config_change; - } - break; - /* hardware was supposed to hide this */ - case USB_REQ_SET_ADDRESS: - if (u.r.bRequestType == USB_RECIP_DEVICE) { - ep0start(dev, 0, "address"); - return; - } - break; - } - - if (u.r.bRequestType & USB_DIR_IN) - dev->ep0state = EP0_IN_DATA_PHASE; - else - dev->ep0state = EP0_OUT_DATA_PHASE; - - i = dev->driver->setup(&dev->gadget, &u.r); - if (i < 0) { - /* hardware automagic preventing STALL... */ - if (dev->req_config) { - /* hardware sometimes neglects to tell - * tell us about config change events, - * so later ones may fail... - */ - WARN("config change %02x fail %d?\n", - u.r.bRequest, i); - return; - /* TODO experiment: if has_cfr, - * hardware didn't ACK; maybe we - * could actually STALL! - */ - } - DBG(DBG_VERBOSE, "protocol STALL, " - "%02x err %d\n", UDCCS0, i); -stall: - /* the watchdog timer helps deal with cases - * where udc seems to clear FST wrongly, and - * then NAKs instead of STALLing. - */ - ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); - start_watchdog(dev); - dev->ep0state = EP0_STALL; - - /* deferred i/o == no response yet */ - } else if (dev->req_pending) { - if (likely(dev->ep0state == EP0_IN_DATA_PHASE - || dev->req_std || u.r.wLength)) - ep0start(dev, 0, "defer"); - else - ep0start(dev, UDCCS0_IPR, "defer/IPR"); - } - - /* expect at least one data or status stage irq */ - return; - - } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) - == (UDCCS0_OPR|UDCCS0_SA))) { - unsigned i; - - /* pxa210/250 erratum 131 for B0/B1 says RNE lies. - * still observed on a pxa255 a0. - */ - DBG(DBG_VERBOSE, "e131\n"); - nuke(ep, -EPROTO); - - /* read SETUP data, but don't trust it too much */ - for (i = 0; i < 8; i++) - u.raw [i] = (u8) UDDR0; - if ((u.r.bRequestType & USB_RECIP_MASK) - > USB_RECIP_OTHER) - goto stall; - if (u.word [0] == 0 && u.word [1] == 0) - goto stall; - goto got_setup; - } else { - /* some random early IRQ: - * - we acked FST - * - IPR cleared - * - OPR got set, without SA (likely status stage) - */ - UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); - } - break; - case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ - if (udccs0 & UDCCS0_OPR) { - UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; - DBG(DBG_VERBOSE, "ep0in premature status\n"); - if (req) - done(ep, req, 0); - ep0_idle(dev); - } else /* irq was IPR clearing */ { - if (req) { - /* this IN packet might finish the request */ - (void) write_ep0_fifo(ep, req); - } /* else IN token before response was written */ - } - break; - case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ - if (udccs0 & UDCCS0_OPR) { - if (req) { - /* this OUT packet might finish the request */ - if (read_ep0_fifo(ep, req)) - done(ep, req, 0); - /* else more OUT packets expected */ - } /* else OUT token before read was issued */ - } else /* irq was IPR clearing */ { - DBG(DBG_VERBOSE, "ep0out premature status\n"); - if (req) - done(ep, req, 0); - ep0_idle(dev); - } - break; - case EP0_END_XFER: - if (req) - done(ep, req, 0); - /* ack control-IN status (maybe in-zlp was skipped) - * also appears after some config change events. - */ - if (udccs0 & UDCCS0_OPR) - UDCCS0 = UDCCS0_OPR; - ep0_idle(dev); - break; - case EP0_STALL: - UDCCS0 = UDCCS0_FST; - break; - } - USIR0 = USIR0_IR0; -} - -static void handle_ep(struct pxa2xx_ep *ep) -{ - struct pxa2xx_request *req; - int is_in = ep->bEndpointAddress & USB_DIR_IN; - int completed; - u32 udccs, tmp; - - do { - completed = 0; - if (likely (!list_empty(&ep->queue))) - req = list_entry(ep->queue.next, - struct pxa2xx_request, queue); - else - req = NULL; - - // TODO check FST handling - - udccs = *ep->reg_udccs; - if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ - tmp = UDCCS_BI_TUR; - if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) - tmp |= UDCCS_BI_SST; - tmp &= udccs; - if (likely (tmp)) - *ep->reg_udccs = tmp; - if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) - completed = write_fifo(ep, req); - - } else { /* irq from RPC (or for ISO, ROF) */ - if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) - tmp = UDCCS_BO_SST | UDCCS_BO_DME; - else - tmp = UDCCS_IO_ROF | UDCCS_IO_DME; - tmp &= udccs; - if (likely(tmp)) - *ep->reg_udccs = tmp; - - /* fifos can hold packets, ready for reading... */ - if (likely(req)) { - completed = read_fifo(ep, req); - } else - pio_irq_disable (ep->bEndpointAddress); - } - ep->pio_irqs++; - } while (completed); -} - -/* - * pxa2xx_udc_irq - interrupt handler - * - * avoid delays in ep0 processing. the control handshaking isn't always - * under software control (pxa250c0 and the pxa255 are better), and delays - * could cause usb protocol errors. - */ -static irqreturn_t -pxa2xx_udc_irq(int irq, void *_dev) -{ - struct pxa2xx_udc *dev = _dev; - int handled; - - dev->stats.irqs++; - do { - u32 udccr = UDCCR; - - handled = 0; - - /* SUSpend Interrupt Request */ - if (unlikely(udccr & UDCCR_SUSIR)) { - udc_ack_int_UDCCR(UDCCR_SUSIR); - handled = 1; - DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() - ? "" : "+disconnect"); - - if (!is_vbus_present()) - stop_activity(dev, dev->driver); - else if (dev->gadget.speed != USB_SPEED_UNKNOWN - && dev->driver - && dev->driver->suspend) - dev->driver->suspend(&dev->gadget); - ep0_idle (dev); - } - - /* RESume Interrupt Request */ - if (unlikely(udccr & UDCCR_RESIR)) { - udc_ack_int_UDCCR(UDCCR_RESIR); - handled = 1; - DBG(DBG_VERBOSE, "USB resume\n"); - - if (dev->gadget.speed != USB_SPEED_UNKNOWN - && dev->driver - && dev->driver->resume - && is_vbus_present()) - dev->driver->resume(&dev->gadget); - } - - /* ReSeT Interrupt Request - USB reset */ - if (unlikely(udccr & UDCCR_RSTIR)) { - udc_ack_int_UDCCR(UDCCR_RSTIR); - handled = 1; - - if ((UDCCR & UDCCR_UDA) == 0) { - DBG(DBG_VERBOSE, "USB reset start\n"); - - /* reset driver and endpoints, - * in case that's not yet done - */ - stop_activity (dev, dev->driver); - - } else { - DBG(DBG_VERBOSE, "USB reset end\n"); - dev->gadget.speed = USB_SPEED_FULL; - memset(&dev->stats, 0, sizeof dev->stats); - /* driver and endpoints are still reset */ - } - - } else { - u32 usir0 = USIR0 & ~UICR0; - u32 usir1 = USIR1 & ~UICR1; - int i; - - if (unlikely (!usir0 && !usir1)) - continue; - - DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); - - /* control traffic */ - if (usir0 & USIR0_IR0) { - dev->ep[0].pio_irqs++; - handle_ep0(dev); - handled = 1; - } - - /* endpoint data transfers */ - for (i = 0; i < 8; i++) { - u32 tmp = 1 << i; - - if (i && (usir0 & tmp)) { - handle_ep(&dev->ep[i]); - USIR0 |= tmp; - handled = 1; - } - if (usir1 & tmp) { - handle_ep(&dev->ep[i+8]); - USIR1 |= tmp; - handled = 1; - } - } - } - - /* we could also ask for 1 msec SOF (SIR) interrupts */ - - } while (handled); - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -static void nop_release (struct device *dev) -{ - DMSG("%s %s\n", __func__, dev->bus_id); -} - -/* this uses load-time allocation and initialization (instead of - * doing it at run-time) to save code, eliminate fault paths, and - * be more obviously correct. - */ -static struct pxa2xx_udc memory = { - .gadget = { - .ops = &pxa2xx_udc_ops, - .ep0 = &memory.ep[0].ep, - .name = driver_name, - .dev = { - .bus_id = "gadget", - .release = nop_release, - }, - }, - - /* control endpoint */ - .ep[0] = { - .ep = { - .name = ep0name, - .ops = &pxa2xx_ep_ops, - .maxpacket = EP0_FIFO_SIZE, - }, - .dev = &memory, - .reg_udccs = &UDCCS0, - .reg_uddr = &UDDR0, - }, - - /* first group of endpoints */ - .ep[1] = { - .ep = { - .name = "ep1in-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 1, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS1, - .reg_uddr = &UDDR1, - }, - .ep[2] = { - .ep = { - .name = "ep2out-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = 2, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS2, - .reg_ubcr = &UBCR2, - .reg_uddr = &UDDR2, - }, -#ifndef CONFIG_USB_PXA2XX_SMALL - .ep[3] = { - .ep = { - .name = "ep3in-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 3, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS3, - .reg_uddr = &UDDR3, - }, - .ep[4] = { - .ep = { - .name = "ep4out-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = 4, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS4, - .reg_ubcr = &UBCR4, - .reg_uddr = &UDDR4, - }, - .ep[5] = { - .ep = { - .name = "ep5in-int", - .ops = &pxa2xx_ep_ops, - .maxpacket = INT_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = INT_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 5, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS5, - .reg_uddr = &UDDR5, - }, - - /* second group of endpoints */ - .ep[6] = { - .ep = { - .name = "ep6in-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 6, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS6, - .reg_uddr = &UDDR6, - }, - .ep[7] = { - .ep = { - .name = "ep7out-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = 7, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS7, - .reg_ubcr = &UBCR7, - .reg_uddr = &UDDR7, - }, - .ep[8] = { - .ep = { - .name = "ep8in-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 8, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS8, - .reg_uddr = &UDDR8, - }, - .ep[9] = { - .ep = { - .name = "ep9out-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = 9, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS9, - .reg_ubcr = &UBCR9, - .reg_uddr = &UDDR9, - }, - .ep[10] = { - .ep = { - .name = "ep10in-int", - .ops = &pxa2xx_ep_ops, - .maxpacket = INT_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = INT_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 10, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS10, - .reg_uddr = &UDDR10, - }, - - /* third group of endpoints */ - .ep[11] = { - .ep = { - .name = "ep11in-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 11, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS11, - .reg_uddr = &UDDR11, - }, - .ep[12] = { - .ep = { - .name = "ep12out-bulk", - .ops = &pxa2xx_ep_ops, - .maxpacket = BULK_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = BULK_FIFO_SIZE, - .bEndpointAddress = 12, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS12, - .reg_ubcr = &UBCR12, - .reg_uddr = &UDDR12, - }, - .ep[13] = { - .ep = { - .name = "ep13in-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 13, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS13, - .reg_uddr = &UDDR13, - }, - .ep[14] = { - .ep = { - .name = "ep14out-iso", - .ops = &pxa2xx_ep_ops, - .maxpacket = ISO_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = ISO_FIFO_SIZE, - .bEndpointAddress = 14, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS14, - .reg_ubcr = &UBCR14, - .reg_uddr = &UDDR14, - }, - .ep[15] = { - .ep = { - .name = "ep15in-int", - .ops = &pxa2xx_ep_ops, - .maxpacket = INT_FIFO_SIZE, - }, - .dev = &memory, - .fifo_size = INT_FIFO_SIZE, - .bEndpointAddress = USB_DIR_IN | 15, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS15, - .reg_uddr = &UDDR15, - }, -#endif /* !CONFIG_USB_PXA2XX_SMALL */ -}; - -#define CP15R0_VENDOR_MASK 0xffffe000 - -#if defined(CONFIG_ARCH_PXA) -#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ - -#elif defined(CONFIG_ARCH_IXP4XX) -#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */ - -#endif - -#define CP15R0_PROD_MASK 0x000003f0 -#define PXA25x 0x00000100 /* and PXA26x */ -#define PXA210 0x00000120 - -#define CP15R0_REV_MASK 0x0000000f - -#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) - -#define PXA255_A0 0x00000106 /* or PXA260_B1 */ -#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ -#define PXA250_B2 0x00000104 -#define PXA250_B1 0x00000103 /* or PXA260_A0 */ -#define PXA250_B0 0x00000102 -#define PXA250_A1 0x00000101 -#define PXA250_A0 0x00000100 - -#define PXA210_C0 0x00000125 -#define PXA210_B2 0x00000124 -#define PXA210_B1 0x00000123 -#define PXA210_B0 0x00000122 -#define IXP425_A0 0x000001c1 -#define IXP425_B0 0x000001f1 -#define IXP465_AD 0x00000200 - -/* - * probe - binds to the platform device - */ -static int __init pxa2xx_udc_probe(struct platform_device *pdev) -{ - struct pxa2xx_udc *dev = &memory; - int retval, vbus_irq, irq; - u32 chiprev; - - /* insist on Intel/ARM/XScale */ - asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); - if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { - pr_err("%s: not XScale!\n", driver_name); - return -ENODEV; - } - - /* trigger chiprev-specific logic */ - switch (chiprev & CP15R0_PRODREV_MASK) { -#if defined(CONFIG_ARCH_PXA) - case PXA255_A0: - dev->has_cfr = 1; - break; - case PXA250_A0: - case PXA250_A1: - /* A0/A1 "not released"; ep 13, 15 unusable */ - /* fall through */ - case PXA250_B2: case PXA210_B2: - case PXA250_B1: case PXA210_B1: - case PXA250_B0: case PXA210_B0: - /* OUT-DMA is broken ... */ - /* fall through */ - case PXA250_C0: case PXA210_C0: - break; -#elif defined(CONFIG_ARCH_IXP4XX) - case IXP425_A0: - case IXP425_B0: - case IXP465_AD: - dev->has_cfr = 1; - break; -#endif - default: - pr_err("%s: unrecognized processor: %08x\n", - driver_name, chiprev); - /* iop3xx, ixp4xx, ... */ - return -ENODEV; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -ENODEV; - - dev->clk = clk_get(&pdev->dev, "UDCCLK"); - if (IS_ERR(dev->clk)) { - retval = PTR_ERR(dev->clk); - goto err_clk; - } - - pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, - dev->has_cfr ? "" : " (!cfr)", - SIZE_STR "(pio)" - ); - - /* other non-static parts of init */ - dev->dev = &pdev->dev; - dev->mach = pdev->dev.platform_data; - - if (dev->mach->gpio_vbus) { - if ((retval = gpio_request(dev->mach->gpio_vbus, - "pxa2xx_udc GPIO VBUS"))) { - dev_dbg(&pdev->dev, - "can't get vbus gpio %d, err: %d\n", - dev->mach->gpio_vbus, retval); - goto err_gpio_vbus; - } - gpio_direction_input(dev->mach->gpio_vbus); - vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); - } else - vbus_irq = 0; - - if (dev->mach->gpio_pullup) { - if ((retval = gpio_request(dev->mach->gpio_pullup, - "pca2xx_udc GPIO PULLUP"))) { - dev_dbg(&pdev->dev, - "can't get pullup gpio %d, err: %d\n", - dev->mach->gpio_pullup, retval); - goto err_gpio_pullup; - } - gpio_direction_output(dev->mach->gpio_pullup, 0); - } - - init_timer(&dev->timer); - dev->timer.function = udc_watchdog; - dev->timer.data = (unsigned long) dev; - - device_initialize(&dev->gadget.dev); - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - - the_controller = dev; - platform_set_drvdata(pdev, dev); - - udc_disable(dev); - udc_reinit(dev); - - dev->vbus = is_vbus_present(); - - /* irq setup after old hardware state is cleaned up */ - retval = request_irq(irq, pxa2xx_udc_irq, - IRQF_DISABLED, driver_name, dev); - if (retval != 0) { - pr_err("%s: can't get irq %d, err %d\n", - driver_name, irq, retval); - goto err_irq1; - } - dev->got_irq = 1; - -#ifdef CONFIG_ARCH_LUBBOCK - if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, - lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - driver_name, dev); - if (retval != 0) { - pr_err("%s: can't get irq %i, err %d\n", - driver_name, LUBBOCK_USB_DISC_IRQ, retval); -lubbock_fail0: - goto err_irq_lub; - } - retval = request_irq(LUBBOCK_USB_IRQ, - lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - driver_name, dev); - if (retval != 0) { - pr_err("%s: can't get irq %i, err %d\n", - driver_name, LUBBOCK_USB_IRQ, retval); - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - goto lubbock_fail0; - } - } else -#endif - if (vbus_irq) { - retval = request_irq(vbus_irq, udc_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - driver_name, dev); - if (retval != 0) { - pr_err("%s: can't get irq %i, err %d\n", - driver_name, vbus_irq, retval); - goto err_vbus_irq; - } - } - create_debug_files(dev); - - return 0; - - err_vbus_irq: -#ifdef CONFIG_ARCH_LUBBOCK - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - err_irq_lub: -#endif - free_irq(irq, dev); - err_irq1: - if (dev->mach->gpio_pullup) - gpio_free(dev->mach->gpio_pullup); - err_gpio_pullup: - if (dev->mach->gpio_vbus) - gpio_free(dev->mach->gpio_vbus); - err_gpio_vbus: - clk_put(dev->clk); - err_clk: - return retval; -} - -static void pxa2xx_udc_shutdown(struct platform_device *_dev) -{ - pullup_off(); -} - -static int __exit pxa2xx_udc_remove(struct platform_device *pdev) -{ - struct pxa2xx_udc *dev = platform_get_drvdata(pdev); - - if (dev->driver) - return -EBUSY; - - dev->pullup = 0; - pullup(dev); - - remove_debug_files(dev); - - if (dev->got_irq) { - free_irq(platform_get_irq(pdev, 0), dev); - dev->got_irq = 0; - } -#ifdef CONFIG_ARCH_LUBBOCK - if (machine_is_lubbock()) { - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - free_irq(LUBBOCK_USB_IRQ, dev); - } -#endif - if (dev->mach->gpio_vbus) { - free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); - gpio_free(dev->mach->gpio_vbus); - } - if (dev->mach->gpio_pullup) - gpio_free(dev->mach->gpio_pullup); - - clk_put(dev->clk); - - platform_set_drvdata(pdev, NULL); - the_controller = NULL; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM - -/* USB suspend (controlled by the host) and system suspend (controlled - * by the PXA) don't necessarily work well together. If USB is active, - * the 48 MHz clock is required; so the system can't enter 33 MHz idle - * mode, or any deeper PM saving state. - * - * For now, we punt and forcibly disconnect from the USB host when PXA - * enters any suspend state. While we're disconnected, we always disable - * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. - * Boards without software pullup control shouldn't use those states. - * VBUS IRQs should probably be ignored so that the PXA device just acts - * "dead" to USB hosts until system resume. - */ -static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) -{ - struct pxa2xx_udc *udc = platform_get_drvdata(dev); - unsigned long flags; - - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) - WARN("USB host won't detect disconnect!\n"); - udc->suspended = 1; - - local_irq_save(flags); - pullup(udc); - local_irq_restore(flags); - - return 0; -} - -static int pxa2xx_udc_resume(struct platform_device *dev) -{ - struct pxa2xx_udc *udc = platform_get_drvdata(dev); - unsigned long flags; - - udc->suspended = 0; - local_irq_save(flags); - pullup(udc); - local_irq_restore(flags); - - return 0; -} - -#else -#define pxa2xx_udc_suspend NULL -#define pxa2xx_udc_resume NULL -#endif - -/*-------------------------------------------------------------------------*/ - -static struct platform_driver udc_driver = { - .shutdown = pxa2xx_udc_shutdown, - .remove = __exit_p(pxa2xx_udc_remove), - .suspend = pxa2xx_udc_suspend, - .resume = pxa2xx_udc_resume, - .driver = { - .owner = THIS_MODULE, - .name = "pxa2xx-udc", - }, -}; - -static int __init udc_init(void) -{ - pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); - return platform_driver_probe(&udc_driver, pxa2xx_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa2xx-udc"); diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h deleted file mode 100644 index e2c19e8..0000000 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * linux/drivers/usb/gadget/pxa2xx_udc.h - * Intel PXA2xx on-chip full speed USB device controller - * - * Copyright (C) 2003 Robert Schwebel , Pengutronix - * Copyright (C) 2003 David Brownell - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __LINUX_USB_GADGET_PXA2XX_H -#define __LINUX_USB_GADGET_PXA2XX_H - -#include - -/*-------------------------------------------------------------------------*/ - -/* pxa2xx has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ -#define UFNRH_SIR (1 << 7) /* SOF interrupt request */ -#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */ -#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */ -#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */ -#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */ - -/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ -#define UDCCFR UDC_RES2 /* UDC Control Function Register */ -#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */ -#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */ - -/* latest pxa255 errata define new "must be one" bits in UDCCFR */ -#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM)) - -/*-------------------------------------------------------------------------*/ - -struct pxa2xx_udc; - -struct pxa2xx_ep { - struct usb_ep ep; - struct pxa2xx_udc *dev; - - const struct usb_endpoint_descriptor *desc; - struct list_head queue; - unsigned long pio_irqs; - - unsigned short fifo_size; - u8 bEndpointAddress; - u8 bmAttributes; - - unsigned stopped : 1; - unsigned dma_fixup : 1; - - /* UDCCS = UDC Control/Status for this EP - * UBCR = UDC Byte Count Remaining (contents of OUT fifo) - * UDDR = UDC Endpoint Data Register (the fifo) - * DRCM = DMA Request Channel Map - */ - volatile u32 *reg_udccs; - volatile u32 *reg_ubcr; - volatile u32 *reg_uddr; -}; - -struct pxa2xx_request { - struct usb_request req; - struct list_head queue; -}; - -enum ep0_state { - EP0_IDLE, - EP0_IN_DATA_PHASE, - EP0_OUT_DATA_PHASE, - EP0_END_XFER, - EP0_STALL, -}; - -#define EP0_FIFO_SIZE ((unsigned)16) -#define BULK_FIFO_SIZE ((unsigned)64) -#define ISO_FIFO_SIZE ((unsigned)256) -#define INT_FIFO_SIZE ((unsigned)8) - -struct udc_stats { - struct ep0stats { - unsigned long ops; - unsigned long bytes; - } read, write; - unsigned long irqs; -}; - -#ifdef CONFIG_USB_PXA2XX_SMALL -/* when memory's tight, SMALL config saves code+data. */ -#define PXA_UDC_NUM_ENDPOINTS 3 -#endif - -#ifndef PXA_UDC_NUM_ENDPOINTS -#define PXA_UDC_NUM_ENDPOINTS 16 -#endif - -struct pxa2xx_udc { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - - enum ep0_state ep0state; - struct udc_stats stats; - unsigned got_irq : 1, - vbus : 1, - pullup : 1, - has_cfr : 1, - req_pending : 1, - req_std : 1, - req_config : 1, - suspended : 1, - active : 1; - -#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) - struct timer_list timer; - - struct device *dev; - struct clk *clk; - struct pxa2xx_udc_mach_info *mach; - u64 dma_mask; - struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS]; - -#ifdef CONFIG_USB_GADGET_DEBUG_FS - struct dentry *debugfs_udc; -#endif -}; - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_ARCH_LUBBOCK -#include -/* lubbock can also report usb connect/disconnect irqs */ -#endif - -static struct pxa2xx_udc *the_controller; - -/*-------------------------------------------------------------------------*/ - -/* - * Debugging support vanishes in non-debug builds. DBG_NORMAL should be - * mostly silent during normal use/testing, with no timing side-effects. - */ -#define DBG_NORMAL 1 /* error paths, device state transitions */ -#define DBG_VERBOSE 2 /* add some success path trace info */ -#define DBG_NOISY 3 /* ... even more: request level */ -#define DBG_VERY_NOISY 4 /* ... even more: packet level */ - -#define DMSG(stuff...) pr_debug("udc: " stuff) - -#ifdef DEBUG - -static int is_vbus_present(void); - -static const char *state_name[] = { - "EP0_IDLE", - "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", - "EP0_END_XFER", "EP0_STALL" -}; - -#ifdef VERBOSE_DEBUG -# define UDC_DEBUG DBG_VERBOSE -#else -# define UDC_DEBUG DBG_NORMAL -#endif - -static void __maybe_unused -dump_udccr(const char *label) -{ - u32 udccr = UDCCR; - DMSG("%s %02X =%s%s%s%s%s%s%s%s\n", - label, udccr, - (udccr & UDCCR_REM) ? " rem" : "", - (udccr & UDCCR_RSTIR) ? " rstir" : "", - (udccr & UDCCR_SRM) ? " srm" : "", - (udccr & UDCCR_SUSIR) ? " susir" : "", - (udccr & UDCCR_RESIR) ? " resir" : "", - (udccr & UDCCR_RSM) ? " rsm" : "", - (udccr & UDCCR_UDA) ? " uda" : "", - (udccr & UDCCR_UDE) ? " ude" : ""); -} - -static void __maybe_unused -dump_udccs0(const char *label) -{ - u32 udccs0 = UDCCS0; - - DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n", - label, state_name[the_controller->ep0state], udccs0, - (udccs0 & UDCCS0_SA) ? " sa" : "", - (udccs0 & UDCCS0_RNE) ? " rne" : "", - (udccs0 & UDCCS0_FST) ? " fst" : "", - (udccs0 & UDCCS0_SST) ? " sst" : "", - (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", - (udccs0 & UDCCS0_FTF) ? " ftf" : "", - (udccs0 & UDCCS0_IPR) ? " ipr" : "", - (udccs0 & UDCCS0_OPR) ? " opr" : ""); -} - -static void __maybe_unused -dump_state(struct pxa2xx_udc *dev) -{ - u32 tmp; - unsigned i; - - DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - is_vbus_present() ? "host " : "disconnected", - state_name[dev->ep0state], - UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); - dump_udccr("udccr"); - if (dev->has_cfr) { - tmp = UDCCFR; - DMSG("udccfr %02X =%s%s\n", tmp, - (tmp & UDCCFR_AREN) ? " aren" : "", - (tmp & UDCCFR_ACM) ? " acm" : ""); - } - - if (!dev->driver) { - DMSG("no gadget driver bound\n"); - return; - } else - DMSG("ep0 driver '%s'\n", dev->driver->driver.name); - - if (!is_vbus_present()) - return; - - dump_udccs0 ("udccs0"); - DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", - dev->stats.write.bytes, dev->stats.write.ops, - dev->stats.read.bytes, dev->stats.read.ops); - - for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { - if (dev->ep [i].desc == NULL) - continue; - DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); - } -} - -#else - -#define dump_udccr(x) do{}while(0) -#define dump_udccs0(x) do{}while(0) -#define dump_state(x) do{}while(0) - -#define UDC_DEBUG ((unsigned)0) - -#endif - -#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) - -#define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) -#define INFO(stuff...) pr_info("udc: " stuff) - - -#endif /* __LINUX_USB_GADGET_PXA2XX_H */ diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d0677f5..7228e85 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1,8 +1,6 @@ /* * RNDIS MSG parser * - * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * @@ -30,6 +28,7 @@ #include #include #include +#include #include #include @@ -38,9 +37,7 @@ #include -#undef RNDIS_PM -#undef RNDIS_WAKEUP -#undef VERBOSE +#undef VERBOSE_DEBUG #include "rndis.h" @@ -96,9 +93,6 @@ static const u32 oid_supported_list [] = OID_GEN_MAXIMUM_TOTAL_SIZE, OID_GEN_MEDIA_CONNECT_STATUS, OID_GEN_PHYSICAL_MEDIUM, -#if 0 - OID_GEN_RNDIS_CONFIG_PARAMETER, -#endif /* the statistical stuff */ OID_GEN_XMIT_OK, @@ -146,7 +140,14 @@ static const u32 oid_supported_list [] = #endif /* RNDIS_OPTIONAL_STATS */ #ifdef RNDIS_PM - /* PM and wakeup are mandatory for USB: */ + /* PM and wakeup are "mandatory" for USB, but the RNDIS specs + * don't say what they mean ... and the NDIS specs are often + * confusing and/or ambiguous in this context. (That is, more + * so than their specs for the other OIDs.) + * + * FIXME someone who knows what these should do, please + * implement them! + */ /* power management */ OID_PNP_CAPABILITIES, @@ -173,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, __le32 *outbuf; int i, count; rndis_query_cmplt_type *resp; + struct net_device *net; + struct net_device_stats *stats; if (!r) return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; @@ -194,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, outbuf = (__le32 *) &resp[1]; resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + net = rndis_per_dev_params[configNr].dev; + if (net->get_stats) + stats = net->get_stats(net); + else + stats = NULL; + switch (OID) { /* general oids (table 4-1) */ @@ -350,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_XMIT_OK: if (rndis_debug > 1) DBG("%s: OID_GEN_XMIT_OK\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].stats->tx_packets - - rndis_per_dev_params [configNr].stats->tx_errors - - rndis_per_dev_params [configNr].stats->tx_dropped); + if (stats) { + *outbuf = cpu_to_le32(stats->tx_packets + - stats->tx_errors - stats->tx_dropped); retval = 0; } break; @@ -363,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_RCV_OK: if (rndis_debug > 1) DBG("%s: OID_GEN_RCV_OK\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].stats->rx_packets - - rndis_per_dev_params [configNr].stats->rx_errors - - rndis_per_dev_params [configNr].stats->rx_dropped); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_packets + - stats->rx_errors - stats->rx_dropped); retval = 0; } break; @@ -376,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_XMIT_ERROR: if (rndis_debug > 1) DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->tx_errors); retval = 0; } break; @@ -387,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_RCV_ERROR: if (rndis_debug > 1) DBG("%s: OID_GEN_RCV_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_errors); retval = 0; } break; @@ -397,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_NO_BUFFER: DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_dropped); - retval = 0; - } - break; - -#ifdef RNDIS_OPTIONAL_STATS - case OID_GEN_DIRECTED_BYTES_XMIT: - DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); - /* - * Aunt Tilly's size of shoes - * minus antarctica count of penguins - * divided by weight of Alpha Centauri - */ - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - (rndis_per_dev_params [configNr] - .stats->tx_packets - - rndis_per_dev_params [configNr] - .stats->tx_errors - - rndis_per_dev_params [configNr] - .stats->tx_dropped) - * 123); - retval = 0; - } - break; - - case OID_GEN_DIRECTED_FRAMES_XMIT: - DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); - /* dito */ - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - (rndis_per_dev_params [configNr] - .stats->tx_packets - - rndis_per_dev_params [configNr] - .stats->tx_errors - - rndis_per_dev_params [configNr] - .stats->tx_dropped) - / 123); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_BYTES_XMIT: - DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast*1234); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_FRAMES_XMIT: - DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_BYTES_XMIT: - DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_packets/42*255); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_FRAMES_XMIT: - DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_packets/42); - retval = 0; - } - break; - - case OID_GEN_DIRECTED_BYTES_RCV: - DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; - - case OID_GEN_DIRECTED_FRAMES_RCV: - DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; - - case OID_GEN_MULTICAST_BYTES_RCV: - DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast * 1111); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_FRAMES_RCV: - DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_BYTES_RCV: - DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_packets/42*255); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_FRAMES_RCV: - DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_packets/42); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_dropped); retval = 0; } break; - case OID_GEN_RCV_CRC_ERROR: - DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_crc_errors); - retval = 0; - } - break; - - case OID_GEN_TRANSMIT_QUEUE_LENGTH: - DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; -#endif /* RNDIS_OPTIONAL_STATS */ - /* ieee802.3 OIDs (table 4-3) */ /* mandatory */ @@ -592,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_frame_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_frame_errors); retval = 0; } break; @@ -613,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, retval = 0; break; -#ifdef RNDIS_OPTIONAL_STATS - case OID_802_3_XMIT_DEFERRED: - DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_MAX_COLLISIONS: - DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); - /* TODO */ - break; - - case OID_802_3_RCV_OVERRUN: - DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_UNDERRUN: - DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_HEARTBEAT_FAILURE: - DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_TIMES_CRS_LOST: - DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_LATE_COLLISIONS: - DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); - /* TODO */ - break; -#endif /* RNDIS_OPTIONAL_STATS */ - -#ifdef RNDIS_PM - /* power management OIDs (table 4-5) */ - case OID_PNP_CAPABILITIES: - DBG("%s: OID_PNP_CAPABILITIES\n", __func__); - - /* for now, no wakeup capabilities */ - length = sizeof (struct NDIS_PNP_CAPABILITIES); - memset(outbuf, 0, length); - retval = 0; - break; - case OID_PNP_QUERY_POWER: - DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__, - get_unaligned_le32(buf) - 1); - /* only suspend is a real power state, and - * it can't be entered by OID_PNP_SET_POWER... - */ - length = 0; - retval = 0; - break; -#endif - default: pr_warning("%s: query unknown OID 0x%08X\n", __func__, OID); @@ -726,9 +532,6 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, * what makes the packet flow start and stop, like * activating the CDC Ethernet altsetting. */ -#ifdef RNDIS_PM -update_linkstate: -#endif retval = 0; if (*params->filter) { params->state = RNDIS_DATA_INITIALIZED; @@ -747,49 +550,6 @@ update_linkstate: DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); retval = 0; break; -#if 0 - case OID_GEN_RNDIS_CONFIG_PARAMETER: - { - struct rndis_config_parameter *param; - param = (struct rndis_config_parameter *) buf; - DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", - __func__, - min(cpu_to_le32(param->ParameterNameLength),80), - buf + param->ParameterNameOffset); - retval = 0; - } - break; -#endif - -#ifdef RNDIS_PM - case OID_PNP_SET_POWER: - /* The only real power state is USB suspend, and RNDIS requests - * can't enter it; this one isn't really about power. After - * resuming, Windows forces a reset, and then SET_POWER D0. - * FIXME ... then things go batty; Windows wedges itself. - */ - i = get_unaligned_le32(buf); - DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); - switch (i) { - case NdisDeviceStateD0: - *params->filter = params->saved_filter; - goto update_linkstate; - case NdisDeviceStateD3: - case NdisDeviceStateD2: - case NdisDeviceStateD1: - params->saved_filter = *params->filter; - retval = 0; - break; - } - break; - -#ifdef RNDIS_WAKEUP - // no wakeup support advertised, so wakeup OIDs always fail: - // - OID_PNP_ENABLE_WAKE_UP - // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN -#endif - -#endif /* RNDIS_PM */ default: pr_warning("%s: set unknown OID 0x%08X, size %d\n", @@ -807,8 +567,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + if (!params->dev) + return -ENOTSUPP; r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); if (!r) @@ -826,7 +588,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); resp->MaxTransferSize = cpu_to_le32 ( - rndis_per_dev_params [configNr].dev->mtu + params->dev->mtu + sizeof (struct ethhdr) + sizeof (struct rndis_packet_msg_type) + 22); @@ -834,10 +596,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) resp->AFListOffset = __constant_cpu_to_le32 (0); resp->AFListSize = __constant_cpu_to_le32 (0); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -845,9 +604,11 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); - if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + if (!params->dev) + return -ENOTSUPP; /* * we need more memory: @@ -878,9 +639,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) } else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); + params->resp_avail(params->v); return 0; } @@ -889,6 +648,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); if (!r) @@ -898,7 +658,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) BufLength = le32_to_cpu (buf->InformationBufferLength); BufOffset = le32_to_cpu (buf->InformationBufferOffset); -#ifdef VERBOSE +#ifdef VERBOSE_DEBUG DBG("%s: Length: %d\n", __func__, BufLength); DBG("%s: Offset: %d\n", __func__, BufOffset); DBG("%s: InfoBuffer: ", __func__); @@ -919,10 +679,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -930,6 +687,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); if (!r) @@ -942,10 +700,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) /* resent information */ resp->AddressingReset = __constant_cpu_to_le32 (1); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -954,6 +709,7 @@ static int rndis_keepalive_response (int configNr, { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ @@ -968,10 +724,7 @@ static int rndis_keepalive_response (int configNr, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -983,8 +736,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) + if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; r = rndis_add_response (configNr, @@ -1000,9 +754,7 @@ static int rndis_indicate_status_msg (int configNr, u32 status) resp->StatusBufferLength = __constant_cpu_to_le32 (0); resp->StatusBufferOffset = __constant_cpu_to_le32 (0); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); + params->resp_avail(params->v); return 0; } @@ -1029,7 +781,6 @@ void rndis_uninit (int configNr) if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params [configNr].used = 0; rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; /* drain the response queue */ @@ -1142,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *buf) return -ENOTSUPP; } -int rndis_register (int (* rndis_control_ack) (struct net_device *)) +int rndis_register(void (*resp_avail)(void *v), void *v) { u8 i; + if (!resp_avail) + return -EINVAL; + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { if (!rndis_per_dev_params [i].used) { rndis_per_dev_params [i].used = 1; - rndis_per_dev_params [i].ack = rndis_control_ack; + rndis_per_dev_params [i].resp_avail = resp_avail; + rndis_per_dev_params [i].v = v; DBG("%s: configNr = %d\n", __func__, i); return i; } } DBG("failed\n"); - return -1; + return -ENODEV; } void rndis_deregister (int configNr) @@ -1169,16 +924,14 @@ void rndis_deregister (int configNr) return; } -int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats, - u16 *cdc_filter) +int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) { DBG("%s:\n", __func__ ); - if (!dev || !stats) return -1; + if (!dev) + return -EINVAL; if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].dev = dev; - rndis_per_dev_params [configNr].stats = stats; rndis_per_dev_params [configNr].filter = cdc_filter; return 0; @@ -1296,14 +1049,11 @@ int rndis_rm_hdr(struct sk_buff *skb) #ifdef CONFIG_USB_GADGET_DEBUG_FILES -static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, - void *data) +static int rndis_proc_show(struct seq_file *m, void *v) { - char *out = page; - int len; - rndis_params *param = (rndis_params *) data; + rndis_params *param = m->private; - out += snprintf (out, count, + seq_printf(m, "Config Nr. %d\n" "used : %s\n" "state : %s\n" @@ -1326,25 +1076,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int (param->media_state) ? 0 : param->speed*100, (param->media_state) ? "disconnected" : "connected", param->vendorID, param->vendorDescr); - - len = out - page; - len -= off; - - if (len < count) { - *eof = 1; - if (len <= 0) - return 0; - } else - len = count; - - *start = page + off; - return len; + return 0; } -static int rndis_proc_write (struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { - rndis_params *p = data; + rndis_params *p = PDE(file->f_path.dentry->d_inode)->data; u32 speed = 0; int i, fl_speed = 0; @@ -1386,6 +1124,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer, return count; } +static int rndis_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, rndis_proc_show, PDE(inode)->data); +} + +static const struct file_operations rndis_proc_fops = { + .owner = THIS_MODULE, + .open = rndis_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = rndis_proc_write, +}; + #define NAME_TEMPLATE "driver/rndis-%03d" static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; @@ -1403,7 +1155,9 @@ int __init rndis_init (void) sprintf (name, NAME_TEMPLATE, i); if (!(rndis_connect_state [i] - = create_proc_entry (name, 0660, NULL))) + = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, + (void *)(rndis_per_dev_params + i)))) { DBG("%s :remove entries", __func__); while (i) { @@ -1413,11 +1167,6 @@ int __init rndis_init (void) DBG("\n"); return -EIO; } - - rndis_connect_state [i]->write_proc = rndis_proc_write; - rndis_connect_state [i]->read_proc = rndis_proc_read; - rndis_connect_state [i]->data = (void *) - (rndis_per_dev_params + i); #endif rndis_per_dev_params [i].confignr = i; rndis_per_dev_params [i].used = 0; diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 397b149..aac61df 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -1,8 +1,6 @@ /* * RNDIS Definitions for Remote NDIS * - * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * @@ -235,20 +233,19 @@ typedef struct rndis_params const u8 *host_mac; u16 *filter; struct net_device *dev; - struct net_device_stats *stats; u32 vendorID; const char *vendorDescr; - int (*ack) (struct net_device *); + void (*resp_avail)(void *v); + void *v; struct list_head resp_queue; } rndis_params; /* RNDIS Message parser and other useless functions */ int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register (int (*rndis_control_ack) (struct net_device *)); +int rndis_register(void (*resp_avail)(void *v), void *v); void rndis_deregister (int configNr); int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats, u16 *cdc_filter); int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 6b1ef48..29d13eb 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -49,15 +48,14 @@ #include #include #include -#include +#include -#include -#include +#include +#include #include #include -#include #include "s3c2410_udc.h" @@ -888,7 +886,7 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) } } -#include +#include /* * s3c2410_udc_irq - interrupt handler diff --git a/drivers/usb/gadget/sa1100_udc.c b/drivers/usb/gadget/sa1100_udc.c new file mode 100644 index 0000000..3a9f24f --- /dev/null +++ b/drivers/usb/gadget/sa1100_udc.c @@ -0,0 +1,2421 @@ +/* + * SA1100 USB Device Controller (UDC) driver. + * + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation, 2001 + * Copyright (C) David Brownell, 2003 + * Copyright (C) Nick Bane, 2005, 2006, 2007 + * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various + * GPL Copyright authors incl Russel king and Nicolas Pitre + * Working port to 2.6.32-1 by N C Bane + * + * This file provides interrupt routing and overall coordination for the + * sa1100 USB endpoints: ep0, ep1out-bulk, ep2in-bulk, as well as device + * initialization and some parts of USB "Chapter 9" device behavior. + * + * It implements the "USB gadget controller" API, abstracting most hardware + * details so that drivers running on top of this API are mostly independent + * of hardware. A key exception is that ep0 logic needs to understand which + * endpoints a given controller has, and their capabilities. Also, hardware + * that doesn't fully support USB (like sa1100) may need workarounds in the + * protocols implemented by device functions. + * + * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the + * kerneldoc for the API exposed to gadget drivers. + * + */ +//#define DEBUG 1 +//#define VERBOSE 1 + +//#define SA1100_USB_DEBUG +#ifdef SA1100_USB_DEBUG +static int sa1100_usb_debug=0; +#endif + +#define NCB_DMA_FIX +#ifdef NCB_DMA_FIX +// This is a clunky fix for dma alignemnt issues +// It should probably be done better by someone more +// steeped in DMA lore +#include +#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */ +#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */ +static char *send_buffer=NULL; +static char *receive_buffer=NULL; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if CONFIG_PROC_FS +#include +#endif + +#if defined(CONFIG_SA1100_BALLOON) +#include +#endif + +#if defined(CONFIG_SA1100_COLLIE) +#include +#include +#endif + +#define DRIVER_VERSION __DATE__ + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + + +static const char driver_name [] = "sa1100_udc"; +static const char driver_desc [] = "SA-1110 USB Device Controller"; + +static const char ep0name [] = "ep0"; + +#ifdef DEBUG +static char *type_string (u8 bmAttributes) +{ + switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: return "bulk"; + //case USB_ENDPOINT_XFER_ISOC: return "iso"; + case USB_ENDPOINT_XFER_INT: return "intr"; + }; + return "control"; +} +#endif + +#include +struct usb_stats_t { + unsigned long ep0_fifo_write_failures; + unsigned long ep0_bytes_written; + unsigned long ep0_fifo_read_failures; + unsigned long ep0_bytes_read; +}; + +struct usb_info_t { + dma_regs_t *dmaregs_tx, *dmaregs_rx; + int state; + unsigned char address; + struct usb_stats_t stats; +}; + +enum { kError=-1, kEvSuspend=0, kEvReset=1, + kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; +int usbctl_next_state_on_event( int event ) { + return 0; +} +static struct usb_info_t usbd_info; + +/* receiver */ +void ep1_reset(void); +void ep1_stall(void); +int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int)); + +/* xmitter */ +void ep2_reset(void); +void ep2_stall(void); +int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int)); + +/* UDC register utility functions */ +#define UDC_write(reg, val) { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#lx) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} + +#define UDC_set(reg, val) { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#lx) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} + +#define UDC_clear(reg, val) { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#lx) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} + +#define UDC_flip(reg, val) { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#lx) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} + +#include "sa1100_udc.h" + +static struct sa1100_udc *the_controller; +static void nuke (struct sa1100_ep *, int status); +static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status); +static inline void ep0_idle (struct sa1100_udc *dev) +{ + dev->ep0state = EP0_IDLE; +} + +// ep0 handlers + +// 1 == lots of trace noise, 0 = only "important' stuff +#define VERBOSITY 0 + +#if 1 && !defined( ASSERT ) +# define ASSERT(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n",\ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#else +# define ASSERT(expr) +#endif + +#if VERBOSITY +#define PRINTKD(fmt, args...) printk( fmt , ## args) +#else +#define PRINTKD(fmt, args...) +#endif + +/* other subroutines */ +unsigned int (*wrint)(void); +void ep0_int_hndlr( void ); +static void ep0_queue(void *buf, unsigned int req, unsigned int act); +static void write_fifo( void ); +static int read_fifo( struct usb_ctrlrequest * p ); + +/* some voodo helpers 01Mar01ww */ +static void set_cs_bits( __u32 set_bits ); +static void set_de( void ); +static void set_ipr( void ); +static void set_ipr_and_de( void ); +static bool clear_opr( void ); + +/*************************************************************************** +Inline Helpers +***************************************************************************/ + +/* Data extraction from usb_request_t fields */ +enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 }; +static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); } + +static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); } +inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } + +/* following is hook for self-powered flag in GET_STATUS. Some devices + .. might like to override and return real info */ +static inline bool self_powered_hook( void ) { return true; } + +#if VERBOSITY +/* "pcs" == "print control status" */ +static inline void pcs( void ) +{ + __u32 foo = Ser0UDCCS0; + printk( "%8.8X: %s %s %s %s\n", + foo, + foo & UDCCS0_SE ? "SE" : "", + foo & UDCCS0_OPR ? "OPR" : "", + foo & UDCCS0_IPR ? "IPR" : "", + foo & UDCCS0_SST ? "SST" : "" + ); +} +static inline void preq( struct usb_ctrlrequest * pReq ) +{ + static char * tnames[] = { "dev", "intf", "ep", "oth" }; + static char * rnames[] = { "std", "class", "vendor", "???" }; + char * psz; + switch( pReq->bRequest ) { + case USB_REQ_GET_STATUS: psz = "get stat"; break; + case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break; + case USB_REQ_SET_FEATURE: psz = "set feat"; break; + case USB_REQ_SET_ADDRESS: psz = "set addr"; break; + case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break; + case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break; + case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break; + case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break; + case USB_REQ_GET_INTERFACE: psz = "get intf"; break; + case USB_REQ_SET_INTERFACE: psz = "set intf"; break; + default: psz = "unknown"; break; + } + printk( "- [%s: %s req to %s. dir=%s]\n", psz, + rnames[ (pReq->bRequestType >> 5) & 3 ], + tnames[ pReq->bRequestType & 3 ], + ( pReq->bRequestType & 0x80 ) ? "in" : "out" ); +} + +static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) +{ + printk("%s: bRequestType=0x%02x bRequest=0x%02x " + "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n", + prefix, req->bRequestType, req->bRequest, + le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), + le16_to_cpu(req->wLength)); +} +#else +static inline void pcs( void ){} +//static inline void preq( void ){} +static inline void preq( void *x ){} +static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {} +#endif + +/*************************************************************************** +Globals +***************************************************************************/ +static const char pszMe[] = "usbep0: "; + + +/* global write struct to keep write + ..state around across interrupts */ +static struct { + unsigned char *p; + int bytes_left; +} wr; + +/*************************************************************************** +Public Interface +***************************************************************************/ + +/* reset received from HUB (or controller just went nuts and reset by itself!) + so udc core has been reset, track this state here */ +void ep0_reset(void) +{ + /* reset state machine */ + wr.p = NULL; + wr.bytes_left = 0; + usbd_info.address=0; +// needed? + Ser0UDCAR = 0; +} + + +/* handle interrupt for endpoint zero */ + +inline void ep0_clear_write(void) { + wr.p = NULL; + wr.bytes_left = 0; +} + +/* this is a config packet parser based on that from the updated HH 2.6 udc */ +static void ep0_read_packet(void) +{ + unsigned char status_buf[2]; /* returned in GET_STATUS */ + struct usb_ctrlrequest req; + int request_type; + int n; + __u32 address; + __u32 in, out; + + /* reset previous count */ + the_controller->ep0_req_len=-1; + + /* read the setup request */ + n = read_fifo( &req ); + usbctl_dump_request("ep0_read_packet",&req); + + if ( n != sizeof( req ) ) { + printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. " + " Stalling out...\n", + pszMe, sizeof( req ), n ); + /* force stall, serviced out */ + set_cs_bits( UDCCS0_FST | UDCCS0_SO ); + goto sh_sb_end; + } + + /* Is it a standard request? (not vendor or class request) */ + request_type = type_code_from_request( req.bRequestType ); + if ( request_type != 0 ) { + printk( "%ssetup begin: unsupported bRequestType: %d ignored\n", + pszMe, request_type ); + set_cs_bits( UDCCS0_DE | UDCCS0_SO ); + goto sh_sb_end; + } + + /* save requested reply size */ + the_controller->ep0_req_len=le16_to_cpu(req.wLength); + PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len); + +#if VERBOSITY + { + unsigned char * pdb = (unsigned char *) &req; + PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ", + pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7] + ); + preq( &req ); + } +#endif + + /* Handle it */ + switch( req.bRequest ) { + + /* This first bunch have no data phase */ + + case USB_REQ_SET_ADDRESS: + address = (__u32) (req.wValue & 0x7F); + /* when SO and DE sent, UDC will enter status phase and ack, + ..propagating new address to udc core. Next control transfer + ..will be on the new address. You can't see the change in a + ..read back of CAR until then. (about 250us later, on my box). + ..The original Intel driver sets S0 and DE and code to check + ..that address has propagated here. I tried this, but it + ..would only work sometimes! The rest of the time it would + ..never propagate and we'd spin forever. So now I just set + ..it and pray... + */ + Ser0UDCAR = address; + usbd_info.address = address; + usbctl_next_state_on_event( kEvAddress ); + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + printk( "%sI have been assigned address: %d\n", pszMe, address ); + break; + + + case USB_REQ_SET_CONFIGURATION: + if ( req.wValue == 1 ) { + /* configured */ + if (usbctl_next_state_on_event( kEvConfig ) != kError) { + /* (re)set the out and in max packet sizes */ + PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ ); + the_controller->driver->setup(&the_controller->gadget, &req); + in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket ); + out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket ); + Ser0UDCOMP = ( out - 1 ); + Ser0UDCIMP = ( in - 1 ); + // we are configured + usbd_info.state = USB_STATE_CONFIGURED; + // enable rx and tx interrupts + Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM); + + printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in ); + break; + } + } else if ( req.wValue == 0 ) { + /* de-configured */ + if (usbctl_next_state_on_event( kEvDeConfig ) != kError ) + printk( "%sDe-Configured\n", pszMe ); + usbd_info.state = 0; + Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM; + ep1_reset (); + ep2_reset (); + printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__); + } else { + printk( "%ssetup phase: Unknown " + "\"set configuration\" data %d\n", + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + case USB_REQ_CLEAR_FEATURE: + /* could check data length, direction...26Jan01ww */ + if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */ + int ep = windex_to_ep_num( req.wIndex ); + if ( ep == 1 ) { + printk( "%sclear feature \"endpoint halt\" " + " on receiver\n", pszMe ); + ep1_reset(); + } + else if ( ep == 2 ) { + printk( "%sclear feature \"endpoint halt\" " + "on xmitter\n", pszMe ); + ep2_reset(); + } else { + printk( "%sclear feature \"endpoint halt\" " + "on unsupported ep # %d\n", + pszMe, ep ); + } + } else { + printk( "%sUnsupported feature selector (%d) " + "in clear feature. Ignored.\n" , + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + case USB_REQ_SET_FEATURE: + if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */ + int ep = windex_to_ep_num( req.wValue ); + if ( ep == 1 ) { + printk( "%set feature \"endpoint halt\" " + "on receiver\n", pszMe ); + ep1_stall(); + } + else if ( ep == 2 ) { + printk( "%sset feature \"endpoint halt\" " + " on xmitter\n", pszMe ); + ep2_stall(); + } else { + printk( "%sset feature \"endpoint halt\" " + "on unsupported ep # %d\n", + pszMe, ep ); + } + } + else { + printk( "%sUnsupported feature selector " + "(%d) in set feature\n", + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + /* The rest have a data phase that writes back to the host */ + case USB_REQ_GET_STATUS: + /* return status bit flags */ + status_buf[0] = status_buf[1] = 0; + n = request_target(req.bRequestType); + switch( n ) { + case kTargetDevice: + if ( self_powered_hook() ) + status_buf[0] |= 1; + break; + case kTargetInterface: + break; + case kTargetEndpoint: + /* return stalled bit */ + n = windex_to_ep_num( req.wIndex ); + if ( n == 1 ) + status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4; + else if ( n == 2 ) + status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5; + else { + printk( "%sUnknown endpoint (%d) " + "in GET_STATUS\n", pszMe, n ); + } + break; + default: + printk( "%sUnknown target (%d) in GET_STATUS\n", + pszMe, n ); + /* fall thru */ + break; + } + PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength); + ep0_queue( status_buf, req.wLength, sizeof( status_buf )); + break; + case USB_REQ_GET_DESCRIPTOR: + PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ ); + the_controller->driver->setup(&the_controller->gadget, &req); + break; + case USB_REQ_GET_CONFIGURATION: + PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ ); + the_controller->driver->setup(&the_controller->gadget, &req); + break; + case USB_REQ_GET_INTERFACE: + PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ ); + the_controller->driver->setup(&the_controller->gadget, &req); + break; + case USB_REQ_SET_INTERFACE: + PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ ); + the_controller->driver->setup(&the_controller->gadget, &req); + break; + default : + printk("%sunknown request 0x%x\n", pszMe, req.bRequest); + break; + } /* switch( bRequest ) */ + +sh_sb_end: + return; + +} + +void ep0_int_hndlr(void) +{ + u32 cs_reg_in; + + pcs(); + + cs_reg_in = Ser0UDCCS0; + + /* + * If "setup end" has been set, the usb controller has terminated + * a setup transaction before we set DE. This happens during + * enumeration with some hosts. For example, the host will ask for + * our device descriptor and specify a return of 64 bytes. When we + * hand back the first 8, the host will know our max packet size + * and turn around and issue a new setup immediately. This causes + * the UDC to auto-ack the new setup and set SE. We must then + * "unload" (process) the new setup, which is what will happen + * after this preamble is finished executing. + */ + if (cs_reg_in & UDCCS0_SE) { + PRINTKD("UDC: early termination of setup\n"); + + /* + * Clear setup end + */ + set_cs_bits(UDCCS0_SSE); + + /* + * Clear any pending write. + */ + ep0_clear_write(); + } + + /* + * UDC sent a stall due to a protocol violation. + */ + if (cs_reg_in & UDCCS0_SST) { + PRINTKD("UDC: write_preamble: UDC sent stall\n"); + + /* + * Clear sent stall + */ + set_cs_bits(UDCCS0_SST); + + /* + * Clear any pending write. + */ + ep0_clear_write(); + } + + switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) { + case UDCCS0_OPR | UDCCS0_IPR: + PRINTKD("UDC: write_preamble: see OPR. Stopping write to " + "handle new SETUP\n"); + + /* + * very rarely, you can get OPR and + * leftover IPR. Try to clear + */ + UDC_clear(Ser0UDCCS0, UDCCS0_IPR); + + /* + * Clear any pending write. + */ + ep0_clear_write(); + + /*FALLTHROUGH*/ + case UDCCS0_OPR: + /* + * A new setup request is pending. Handle + * it. Note that we don't try to read a + * packet if SE was set and OPR is clear. + */ + ep0_read_packet(); + break; + + case 0: + // if data pending ... + if (wr.p) { + unsigned int cs_bits = 0; + if (wr.bytes_left != 0) { + /* + * More data to go + */ + write_fifo(); + // packet ready + cs_bits |= UDCCS0_IPR; + } + + if (wr.bytes_left == 0) { + /* + * All data sent. + */ + cs_bits |= wrint(); + // a null packet may be following + if (!wrint) + ep0_clear_write(); + } + set_cs_bits(cs_bits); + } + else + PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__); + break; + + case UDCCS0_IPR: + PRINTKD("UDC: IPR set, not writing\n"); + break; + } + + pcs(); + PRINTKD( "-end-\n" ); +} + +static unsigned int ep0_sh_write_data(void) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. And + * we know we don't have to empty packet this + * transfer so just set DE and we are done + */ + PRINTKD("UDC: normal packet ended\n"); + wrint=NULL; + return UDCCS0_DE; +} + +static unsigned int ep0_sh_write_with_empty_packet(void) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. + * We must do short packet suff, so set DE and IPR + */ + PRINTKD("UDC: short packet sent\n"); + wrint=NULL; + return UDCCS0_IPR | UDCCS0_DE; +} + +static unsigned int ep0_sh_write_data_then_empty_packet(void) +{ + PRINTKD("UDC: last packet full. Send empty packet next\n"); + wrint=ep0_sh_write_with_empty_packet; + return 0; +} + +static void ep0_queue(void *buf, unsigned int len, unsigned int req_len) +{ + __u32 cs_reg_bits = UDCCS0_IPR; + + PRINTKD("a=%d r=%d\n", len, req_len); + + if (len == 0) { + // no output packet to wait for + PRINTKD("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__); + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + return; + } + + /* + * thou shalt not enter data phase until + * Out Packet Ready is clear + */ + if (!clear_opr()) { + printk("UDC: SO did not clear OPR\n"); + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + return; + } + + // note data to xmit stored + wr.p=buf; + wr.bytes_left=min(len, req_len); + + // write the first block + write_fifo(); + + // done already? + if (wr.bytes_left == 0) { + /* + * out in one, so data end + */ + cs_reg_bits |= UDCCS0_DE; + ep0_clear_write(); + // rest is a shorter than expected reply? + } else if (len < req_len) { + /* + * we are going to short-change host + * so need nul to not stall + */ + if (len % 8) { + PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left); + wrint=ep0_sh_write_with_empty_packet; + } + // unless we are on a packet boundary. Then send full packet plus null packet. + else { + PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left); + wrint=ep0_sh_write_data_then_empty_packet; + } + } else { + /* + * we have as much or more than requested + */ + PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left); + wrint=ep0_sh_write_data; + } + + /* + * note: IPR was set uncondtionally at start of routine + */ + set_cs_bits(cs_reg_bits); +} + +/* + * write_fifo() + * Stick bytes in the 8 bytes endpoint zero FIFO. + * This version uses a variety of tricks to make sure the bytes + * are written correctly. 1. The count register is checked to + * see if the byte went in, and the write is attempted again + * if not. 2. An overall counter is used to break out so we + * don't hang in those (rare) cases where the UDC reverses + * direction of the FIFO underneath us without notification + * (in response to host aborting a setup transaction early). + * + */ +static void write_fifo( void ) +{ + int bytes_this_time = min(wr.bytes_left, 8); + int bytes_written = 0; + + PRINTKD( "WF=%d: ", bytes_this_time ); + + while( bytes_this_time-- ) { + unsigned int cwc; + int i; + PRINTKD( "%2.2X ", *wr.p ); + cwc = Ser0UDCWC & 15; + i = 10; + do { + Ser0UDCD0 = *wr.p; + udelay( 20 ); /* voodo 28Feb01ww */ + } while( (Ser0UDCWC &15) == cwc && --i ); + + if ( i == 0 ) { + printk( "%swrite_fifo: write failure\n", pszMe ); + usbd_info.stats.ep0_fifo_write_failures++; + } + + wr.p++; + bytes_written++; + } + wr.bytes_left -= bytes_written; + + /* following propagation voodo so maybe caller writing IPR in + ..a moment might actually get it to stick 28Feb01ww */ + udelay( 300 ); + + usbd_info.stats.ep0_bytes_written += bytes_written; + PRINTKD( "L=%d WCR=%8.8lX\n", wr.bytes_left, Ser0UDCWC ); +} +/* + * read_fifo() + * Read 1-8 bytes out of FIFO and put in request. + * Called to do the initial read of setup requests + * from the host. Return number of bytes read. + * + * Like write fifo above, this driver uses multiple + * reads checked agains the count register with an + * overall timeout. + * + */ +static int +read_fifo( struct usb_ctrlrequest * request ) +{ + int bytes_read = 0; + int fifo_count; + + unsigned char * pOut = (unsigned char*) request; + + fifo_count = ( Ser0UDCWC & 0xFF ); + + ASSERT( fifo_count <= 8 ); + PRINTKD( "RF=%d ", fifo_count ); + + while( fifo_count-- ) { + unsigned int cwc; + int i; + + cwc = Ser0UDCWC & 15; + + i = 10; + do { + *pOut = (unsigned char) Ser0UDCD0; + udelay( 20 ); + } while( ( Ser0UDCWC & 15 ) == cwc && --i ); + + if ( i == 0 ) { + printk( "%sread_fifo(): read failure\n", pszMe ); + usbd_info.stats.ep0_fifo_read_failures++; + } + pOut++; + bytes_read++; + } + + PRINTKD( "fc=%d\n", bytes_read ); + usbd_info.stats.ep0_bytes_read++; + return bytes_read; +} + +/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */ + +#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE ) +#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS )) +#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) + +static void set_cs_bits( __u32 bits ) +{ + if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) ) + Ser0UDCCS0 = bits; + else if ( (bits & BOTH_BITS) == BOTH_BITS ) + set_ipr_and_de(); + else if ( bits & UDCCS0_IPR ) + set_ipr(); + else if ( bits & UDCCS0_DE ) + set_de(); +} + +static void set_de( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= UDCCS0_DE; + } else { + PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); + break; + } + if ( Ser0UDCCS0 & UDCCS0_DE ) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8lX)\n", + pszMe, UDCCS0_DE, Ser0UDCCS0 ); + break; + } + } +} + +static void set_ipr( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= UDCCS0_IPR; + } else { + PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); + break; + } + if ( Ser0UDCCS0 & UDCCS0_IPR ) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8lX)\n", + pszMe, UDCCS0_IPR, Ser0UDCCS0 ); + break; + } + } +} + +static void set_ipr_and_de( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= BOTH_BITS; + } else { + PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); + break; + } + if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8lX)\n", + pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); + break; + } + } +} + +static bool clear_opr( void ) +{ + int i = 10000; + bool is_clear; + do { + Ser0UDCCS0 = UDCCS0_SO; + is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); + if ( i-- <= 0 ) { + printk( "%sclear_opr(): failed\n", pszMe ); + break; + } + } while( ! is_clear ); + return is_clear; +} + + + +// ep1 handlers + +static char *ep1_buf; +static int ep1_len; +static void (*ep1_callback)(int flag, int size); +static char *ep1_curdmabuf; +static dma_addr_t ep1_curdmapos; +static int ep1_curdmalen; +static int ep1_remain; +static int ep1_used; + +static dma_regs_t *dmaregs_rx = NULL; +static int rx_pktsize; + +static int naking; + +static void +ep1_start(void) +{ + sa1100_reset_dma(dmaregs_rx); + if (!ep1_curdmalen) { + ep1_curdmalen = rx_pktsize; + if (ep1_curdmalen > ep1_remain) + ep1_curdmalen = ep1_remain; + ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen, + DMA_FROM_DEVICE); + } + + UDC_write( Ser0UDCOMP, ep1_curdmalen-1 ); + + sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen); + + if ( naking ) { + /* turn off NAK of OUT packets, if set */ + UDC_flip( Ser0UDCCS1, UDCCS1_RPC ); + naking = 0; + } +} + +static void +ep1_done(int flag) +{ + int size = ep1_len - ep1_remain; + + if (!ep1_len) + return; + if (ep1_curdmalen) + dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, + DMA_FROM_DEVICE); + ep1_len = ep1_curdmalen = 0; + if (ep1_callback) + ep1_callback(flag, size); +} + +void +ep1_state_change_notify( int new_state ) +{ + +} + +void +ep1_stall( void ) +{ + /* SET_FEATURE force stall at UDC */ + UDC_set( Ser0UDCCS1, UDCCS1_FST ); +} + +int +ep1_init(dma_regs_t *dmaregs) +{ + dmaregs_rx = dmaregs; + sa1100_reset_dma(dmaregs_rx); + ep1_done(-EAGAIN); + return 0; +} + +void +ep1_reset(void) +{ + if (dmaregs_rx) + sa1100_reset_dma(dmaregs_rx); + UDC_clear(Ser0UDCCS1, UDCCS1_FST); + ep1_done(-EINTR); +} + +void ep1_int_hndlr(int udcsr) +{ + dma_addr_t dma_addr; + unsigned int len; + int status = Ser0UDCCS1; + + if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking ); + + if (status & UDCCS1_RPC) { + + if (!ep1_curdmalen) { + printk("usb_recv: RPC for non-existent buffer\n"); + naking=1; + return; + } + + sa1100_stop_dma(dmaregs_rx); + + if (status & UDCCS1_SST) { + printk("usb_recv: stall sent OMP=%ld\n", Ser0UDCOMP); + UDC_flip(Ser0UDCCS1, UDCCS1_SST); + ep1_done(-EIO); // UDC aborted current transfer, so we do + return; + } + + if (status & UDCCS1_RPE) { + printk("usb_recv: RPError %x\n", status); + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + ep1_done(-EIO); + return; + } + + dma_addr=sa1100_get_dma_pos(dmaregs_rx); + dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, + DMA_FROM_DEVICE); + len = dma_addr - ep1_curdmapos; +#ifdef SA1100_USB_DEBUG + if (sa1100_usb_debug) { + int i; + printk("usb rx %d :\n ",len); + if (sa1100_usb_debug>1) { + for (i=0; i= ep1_curdmalen) { + printk("usb_recv: too much data in fifo\n"); + break; + } + *buf++ = Ser0UDCDR; + len++; + } + } else if (Ser0UDCCS1 & UDCCS1_RNE) { + printk("usb_recv: fifo screwed, shouldn't contain data\n"); + len = 0; + } + +#if defined(NCB_DMA_FIX) +// if (len && (ep1_buf != ep1_curdmabuf)) +// memcpy(ep1_buf,ep1_curdmabuf,len); + if (len) + memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len); +#endif + + ep1_curdmalen = 0; /* dma unmap already done */ + ep1_remain -= len; + ep1_used += len; +// ep1_curdmabuf += len; // use same buffer again + naking = 1; +//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain); + if (len && (len == rx_pktsize)) + ep1_start(); + else + ep1_done((len) ? 0 : -EPIPE); + } + /* else, you can get here if we are holding NAK */ +} + +int +sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size)) +{ + unsigned long flags; + char *buf=req->buf; + int len=req->length; + + if (ep1_len) + return -EBUSY; + + local_irq_save(flags); + ep1_buf = buf; + ep1_len = len; + ep1_callback = callback; + ep1_remain = len; + ep1_used = 0; +#ifdef NCB_DMA_FIX +// if (((size_t)buf)&3) + if (1) + ep1_curdmabuf = receive_buffer; + else +#else + ep1_curdmabuf = buf; +#endif + ep1_curdmalen = 0; + ep1_start(); + local_irq_restore(flags); + + return 0; +} + +// ep2 handlers + +static char *ep2_buf; +static int ep2_len; +static void (*ep2_callback)(int status, int size); +static dma_addr_t ep2_dma; +static dma_addr_t ep2_curdmapos; +static int ep2_curdmalen; +static int ep2_remain; +static dma_regs_t *dmaregs_tx = NULL; +static int tx_pktsize; + +/* device state is changing, async */ +void +ep2_state_change_notify( int new_state ) +{ +} + +/* set feature stall executing, async */ +void +ep2_stall( void ) +{ + UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */ +} + +static void +ep2_start(void) +{ + if (!ep2_len) + return; + + ep2_curdmalen = tx_pktsize; + if (ep2_curdmalen > ep2_remain) + ep2_curdmalen = ep2_remain; + + /* must do this _before_ queue buffer.. */ + UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */ + UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); + + Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug + sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen); +} + +static void +ep2_done(int flag) +{ + int size = ep2_len - ep2_remain; + if (ep2_len) { + dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE); + ep2_len = 0; + if (ep2_callback) + ep2_callback(flag, size); + } +} + +int ep2_init(dma_regs_t *dmaregs) +{ + dmaregs_tx = dmaregs; + sa1100_reset_dma(dmaregs_tx); + ep2_done(-EAGAIN); + return 0; +} + +void ep2_reset(void) +{ + UDC_clear(Ser0UDCCS2, UDCCS2_FST); + if (dmaregs_tx) + sa1100_reset_dma(dmaregs_tx); + ep2_done(-EINTR); +} + +void ep2_int_hndlr(int udcsr) +{ + int status = Ser0UDCCS2; + + if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug. + Ser0UDCAR = usbd_info.address; + + if (status & UDCCS2_TPC) { + + UDC_flip(Ser0UDCCS2, UDCCS2_SST); + + sa1100_reset_dma(dmaregs_tx); + + if (status & (UDCCS2_TPE | UDCCS2_TUR)) { + printk("usb_send: transmit error %x\n", status); + ep2_done(-EIO); + } else { + ep2_curdmapos += ep2_curdmalen; + ep2_remain -= ep2_curdmalen; + + if (ep2_remain != 0) + ep2_start(); + else + ep2_done(0); + } + } else { + printk("usb_send: Not TPC: UDCCS2 = %x\n", status); + } +} + +int +sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size)) +{ + char *buf=req->buf; + int len=req->length; + unsigned long flags; + + if (usbd_info.state != USB_STATE_CONFIGURED) { + PRINTKD("%s: return -ENODEV\n",__FUNCTION__); + return -ENODEV; + } + + if (ep2_len) { + PRINTKD("%s: return -EBUSY\n",__FUNCTION__); + return -EBUSY; + } + + local_irq_save(flags); +#ifdef NCB_DMA_FIX + // if misaligned, copy to aligned buffer +// if (((size_t)buf)&3) { + if (1) { + PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len); + memcpy(send_buffer,buf,len); + ep2_buf = send_buffer; + } + else +#endif + ep2_buf = buf; + + ep2_len = len; + ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE); + PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf); + + ep2_callback = callback; + ep2_remain = len; + ep2_curdmapos = ep2_dma; + + PRINTKD("%s: calling ep2_start\n",__FUNCTION__); + ep2_start(); + local_irq_restore(flags); + + return 0; +} +/*-------------------------------------------------------------------------*/ + +static int +sa1100_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct sa1100_udc *dev; + struct sa1100_ep *ep; + u32 max; + int type; + + ep = container_of (_ep, struct sa1100_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) { + PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc); + if (_ep && desc) + PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name, + (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!"); + return -EINVAL; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + max = le16_to_cpu (desc->wMaxPacketSize); + switch (max) { + case 64: case 32: + /* note: maxpacket > 16 means DMA might overrun/underrun */ + case 16: case 8: + break; + default: + if (type == USB_ENDPOINT_XFER_INT && max < 64) + break; + return -EDOM; + } + + switch (type) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + if (ep == &dev->ep[2]) { + if (desc->bEndpointAddress != (USB_DIR_IN|2)) { + PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__); + return -EINVAL; + } + tx_pktsize = max; + Ser0UDCOMP = max - 1; + PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max); + break; + } else if (ep == &dev->ep[1]) { + if (desc->bEndpointAddress != (USB_DIR_OUT|1)) { + PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__); + return -EINVAL; + } + rx_pktsize = max; + Ser0UDCIMP = max - 1; + PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max); + break; + } + // FALLTHROUGH + default: + PRINTKD("%s: Invalid endpoint\n",__FUNCTION__); + return -EINVAL; + } + + _ep->maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + DEBUG (dev, "enabled %s %s max %04x\n", _ep->name, + type_string (desc->bmAttributes), max); + + return 0; +} + +static int sa1100_disable (struct usb_ep *_ep) +{ + struct sa1100_ep *ep; + + ep = container_of (_ep, struct sa1100_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + nuke (ep, -ESHUTDOWN); + + DEBUG (ep->dev, "disabled %s\n", _ep->name); + + ep->desc = NULL; + ep->stopped = 1; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +sa1100_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct sa1100_request *req; + + if (!_ep) + return 0; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + memset (req, 0, sizeof *req); + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + +static void sa1100_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct sa1100_request *req; + + req = container_of (_req, struct sa1100_request, req); + WARN_ON (!list_empty (&req->queue)); + kfree(req); //NCB - see pxa2xx_udc +} + +/*-------------------------------------------------------------------------*/ + +static void done(struct sa1100_ep *ep, struct sa1100_request *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init (&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + req->req.complete (&ep->ep, &req->req); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +/* FIXME move away from the old non-queued api. + * - forces extra work on us + * - stores request state twice + * - doesn't let gadget driver handle dma mapping + * - status codes need mapping + */ + +static int map_status(int status) +{ + switch (status) { + case 0: + case -EIO: /* ep[12]_int_handler */ + return status; + case -EPIPE: /* ep1_int_handler */ + return 0; + // case -EAGAIN: /* ep[12]_init */ + // case -EINTR: /* ep[12]_reset */ + default: + return -ESHUTDOWN; + } +} + +static void tx_callback(int status, int size) +{ + struct sa1100_ep *ep = &the_controller->ep[2]; + struct sa1100_request *req; + + if (list_empty (&ep->queue)) { + if (status != -EAGAIN) + DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", + ep->ep.name, status, size); + return; + } + req = list_entry (ep->queue.next, struct sa1100_request, queue); + req->req.actual = size; + done (ep, req, map_status (status)); + + if (ep->stopped || list_empty (&ep->queue)) + return; + req = list_entry (ep->queue.next, struct sa1100_request, queue); + sa1100_usb_send (&req->req, tx_callback); +} + +static void rx_callback (int status, int size) +{ + struct sa1100_ep *ep = &the_controller->ep[1]; + struct sa1100_request *req; + + if (list_empty (&ep->queue)) { + if (status != -EAGAIN) + DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n", + ep->ep.name, status, size); + return; + } + req = list_entry (ep->queue.next, struct sa1100_request, queue); + req->req.actual = size; + done (ep, req, map_status (status)); + + if (ep->stopped || list_empty (&ep->queue)) + return; + req = list_entry (ep->queue.next, struct sa1100_request, queue); + sa1100_usb_recv (&req->req, rx_callback); +} + + +static int +sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct sa1100_request *req; + struct sa1100_ep *ep; + struct sa1100_udc *dev; + unsigned long flags; + + req = container_of (_req, struct sa1100_request, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty (&req->queue)) + return -EINVAL; + + ep = container_of (_ep, struct sa1100_ep, ep); + if (unlikely(!_ep || (!ep->desc && _ep->name != ep0name))) + return -EINVAL; + + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + // handle ep0 + if (_ep->name == ep0name) { + ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length ); + return 0; + } + + /* sa1100 udc can't write zlps */ + if (ep == &dev->ep[2] && _req->length == 0) + return -ERANGE; + + /* the old sa1100 api doesn't use 'unsigned' for lengths */ + if (_req->length > INT_MAX) + return -ERANGE; + + VDEBUG (dev, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + local_irq_save (flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (list_empty (&ep->queue) && !ep->stopped) { + /* FIXME this does DMA mapping wrong. caller is allowed + * to provide buffers that don't need mapping, but this + * doesn't use them. + */ + if (ep == &ep->dev->ep[2]) { + PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); + sa1100_usb_send (_req, tx_callback); + } + else if (ep == &ep->dev->ep[1]) { + PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length); + sa1100_usb_recv (_req, rx_callback); + } + /* ep0 rx/tx is handled separately */ + } + list_add_tail (&req->queue, &ep->queue); + + local_irq_restore (flags); + + return 0; +} + +/* dequeue ALL requests */ +static void nuke (struct sa1100_ep *ep, int status) +{ + struct sa1100_request *req; + + /* called with irqs blocked */ + while (!list_empty (&ep->queue)) { + req = list_entry (ep->queue.next, + struct sa1100_request, + queue); + done (ep, req, status); + } + if (ep == &ep->dev->ep[1]) + ep1_reset (); + else if (ep == &ep->dev->ep[2]) + ep2_reset (); +} + +/* dequeue JUST ONE request */ +static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req) +{ + struct sa1100_ep *ep; + struct sa1100_request *req; + unsigned long flags; + + ep = container_of (_ep, struct sa1100_ep, ep); + if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req) + return -EINVAL; + + local_irq_save (flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + local_irq_restore(flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int +sa1100_set_halt (struct usb_ep *_ep, int value) +{ + struct sa1100_ep *ep; + + ep = container_of (_ep, struct sa1100_ep, ep); + if (unlikely(!_ep + || (!ep->desc && _ep->name != ep0name)) + || (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); + + /* set/clear, then synch memory views with the device */ + if (value) { + if (ep == &ep->dev->ep[1]) + ep1_stall (); + else + ep2_stall (); + } else { + if (ep == &ep->dev->ep[1]) + ep1_reset (); + else + ep2_reset (); + } + + return 0; +} + +static struct usb_ep_ops sa1100_ep_ops = { + .enable = sa1100_enable, + .disable = sa1100_disable, + + .alloc_request = sa1100_alloc_request, + .free_request = sa1100_free_request, + + .queue = sa1100_queue, + .dequeue = sa1100_dequeue, + + .set_halt = sa1100_set_halt, + // .fifo_status = sa1100_fifo_status, + // .fifo_flush = sa1100_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int sa1100_get_frame (struct usb_gadget *_gadget) +{ + return -EOPNOTSUPP; +} + +static int sa1100_wakeup (struct usb_gadget *_gadget) +{ + struct sa1100_udc *dev; + + if (!_gadget) + return 0; + dev = container_of (_gadget, struct sa1100_udc, gadget); + + // FIXME + + return 0; +} + +static const struct usb_gadget_ops sa1100_ops = { + .get_frame = sa1100_get_frame, + .wakeup = sa1100_wakeup, + + // .set_selfpowered = sa1100_set_selfpowered, +}; + +/*-------------------------------------------------------------------------*/ + +static inline void enable_resume_mask_suspend (void) +{ + int i = 0; + + while (1) { + Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events + udelay (i); + if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR)) + break; + if (++i == 50) { + WARN (&the_controller, "%s Could not set SUSIM %8.8lX\n", + __FUNCTION__, Ser0UDCCR); + break; + } + } + + i = 0; + while (1) { + Ser0UDCCR &= ~UDCCR_RESIM; + udelay (i); + if ( (Ser0UDCCR & UDCCR_RESIM) == 0 + || (Ser0UDCSR & UDCSR_RSTIR)) + break; + if (++i == 50) { + WARN (&the_controller, "%s Could not clear RESIM %8.8lX\n", + __FUNCTION__, Ser0UDCCR); + break; + } + } +} + +static inline void enable_suspend_mask_resume (void) +{ + int i = 0; + while (1) { + Ser0UDCCR |= UDCCR_RESIM; // mask future resume events + udelay (i); + if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR)) + break; + if (++i == 50) { + WARN (&the_controller, "%s could not set RESIM %8.8lX\n", + __FUNCTION__, Ser0UDCCR); + break; + } + } + i = 0; + while (1) { + Ser0UDCCR &= ~UDCCR_SUSIM; + udelay (i); + if ( (Ser0UDCCR & UDCCR_SUSIM) == 0 + || (Ser0UDCSR & UDCSR_RSTIR)) + break; + if (++i == 50) { + WARN (&the_controller, "%s Could not clear SUSIM %8.8lX\n", + __FUNCTION__, Ser0UDCCR); + break; + } + } +} + +// HACK DEBUG 3Mar01ww +// Well, maybe not, it really seems to help! 08Mar01ww +static void core_kicker (void) +{ + u32 car = Ser0UDCAR; + u32 imp = Ser0UDCIMP; + u32 omp = Ser0UDCOMP; + + UDC_set (Ser0UDCCR, UDCCR_UDD); + udelay (300); + UDC_clear (Ser0UDCCR, UDCCR_UDD); + + Ser0UDCAR = car; + Ser0UDCIMP = imp; + Ser0UDCOMP = omp; +} + +static irqreturn_t udc_int_hndlr(int irq, void *_dev) +{ + struct sa1100_udc *dev = _dev; + u32 status = Ser0UDCSR; + + PRINTKD("%s: status = 0x%x and control = 0x%lx\n", __FUNCTION__, + status, Ser0UDCCR); + /* ReSeT Interrupt Request - UDC has been reset */ + if (status & UDCSR_RSTIR) { + PRINTKD("%s: processing UDCSR_RSTIR\n", __FUNCTION__); + if (usbctl_next_state_on_event(kEvReset) != kError) { + /* starting 20ms or so reset sequence now... */ + INFO (dev, "Resetting\n"); + ep0_reset(); // just set state to idle + ep1_reset(); // flush dma, clear false stall + ep2_reset(); // flush dma, clear false stall + } + // mask reset ints, they flood during sequence, enable + // suspend and resume + UDC_set(Ser0UDCCR, UDCCR_REM); // mask reset + UDC_clear(Ser0UDCCR, (UDCCR_SUSIM | UDCCR_RESIM)); // enable suspend and resume + UDC_flip(Ser0UDCSR, status); // clear all pending sources + PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__); + dev->gadget.speed = USB_SPEED_FULL; + return IRQ_HANDLED; // NCB + } + + /* else we have done something other than reset, + * so be sure reset enabled + */ + UDC_clear(Ser0UDCCR, UDCCR_REM); + + /* RESume Interrupt Request */ + if (status & UDCSR_RESIR) { + struct usb_gadget_driver *driver = dev->driver; + + PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__); + if (driver->resume) + driver->resume (&dev->gadget); + core_kicker (); + enable_suspend_mask_resume (); + } + + /* SUSpend Interrupt Request */ + if (status & UDCSR_SUSIR) { + struct usb_gadget_driver *driver = dev->driver; + + PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__); + if (driver->suspend) + driver->suspend (&dev->gadget); + enable_resume_mask_suspend (); + } + + UDC_flip(Ser0UDCSR, status); // clear all pending sources + + if (status & UDCSR_EIR) + PRINTKD("%s: processing ep0_int_hndlr\n",__FUNCTION__); + ep0_int_hndlr(); + + if (status & UDCSR_RIR) { + PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__); + ep1_int_hndlr(status); + } + if (status & UDCSR_TIR) { + PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__); + ep2_int_hndlr(status); + } + + return IRQ_HANDLED; // NCB +} + +/* soft_connect_hook () + * Some devices have platform-specific circuitry to make USB + * not seem to be plugged in, even when it is. This allows + * software to control when a device 'appears' on the USB bus + * (after Linux has booted and this driver has loaded, for + * example). If you have such a circuit, control it here. + */ +#ifdef CONFIG_SA1100_EXTENEX1 +static void soft_connect_hook(int enable) +{ + if (machine_is_extenex1 ()) { + if (enable) { + PPDR |= PPC_USB_SOFT_CON; + PPSR |= PPC_USB_SOFT_CON; + } else { + PPSR &= ~PPC_USB_SOFT_CON; + PPDR &= ~PPC_USB_SOFT_CON; + } + } +} +#elif defined(CONFIG_SA1100_BALLOON) +static void soft_connect_hook(int enable) +{ + if (machine_is_balloon()) { + if (enable) + balloon_cpld_control(BALLOON_UDC_DISCONNECT, 0); + else + balloon_cpld_control(BALLOON_UDC_DISCONNECT, 1); + } +} +#elif defined(CONFIG_SA1100_COLLIE) +extern struct platform_device colliescoop_device; + +static void soft_connect_hook(int enable) +{ + if(enable) + set_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG); + else + reset_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG); +} +#else +#define soft_connect_hook(x) do { } while (0); +#endif + +/* "function" sysfs attribute */ +static ssize_t +show_function(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct sa1100_udc *dev = dev_get_drvdata (_dev); + + if (!dev->driver + || !dev->driver->function + || strlen(dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); + +/* disable the UDC at the source */ +static void udc_disable(struct sa1100_udc *dev) +{ + soft_connect_hook(0); + UDC_set(Ser0UDCCR, UDCCR_UDD); + dev->gadget.speed = USB_SPEED_UNKNOWN; + ep0_idle(dev); +} + +static void udc_reinit(struct sa1100_udc *dev) +{ + u32 i; + + /* Initialize the gadget controller data structure */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + ep0_idle(dev); + for ( i = 0 ; i < 3 ; i++) { + struct sa1100_ep *ep = &dev->ep[i]; + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + ep->desc = NULL; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + } +} + +/* enable the udc at the source */ +static void udc_enable(struct sa1100_udc *dev) +{ + UDC_clear (Ser0UDCCR, UDCCR_UDD); + ep0_idle(dev); +} + +static void ep0_start(struct sa1100_udc *dev) +{ + udc_enable(dev); + udelay(100); + + /* clear stall - receiver seems to start stalled? 19Jan01ww */ + /* also clear other stuff just to be thurough 22Feb01ww */ + UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); + UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); + + /* mask everything */ + Ser0UDCCR = 0xFC; + + /* flush DMA and fire through some -EAGAINs */ + ep1_init(dev->ep[1].dmaregs); + ep2_init(dev->ep[2].dmaregs); + + /* enable any platform specific hardware */ + soft_connect_hook(1); + + /* clear all top-level sources */ + Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | + UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; + + /* EXERIMENT - a short line in the spec says toggling this + * bit diddles the internal state machine in the udc to + * expect a suspend + */ + Ser0UDCCR |= UDCCR_RESIM; + /* END EXPERIMENT 10Feb01ww */ + + /* enable any platform specific hardware */ + soft_connect_hook(1); + + /* Enable interrupts. If you are unplugged you will immediately + * get a suspend interrupt. If you are plugged and have a soft + * connect-circuit, you will get a reset. If you are plugged + * without a soft-connect, I think you also get suspend. In short, + * start with suspend masked and everything else enabled + */ + UDC_write(Ser0UDCCR, UDCCR_SUSIM); +} + + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct sa1100_udc *dev = the_controller; + int retval; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + retval = device_add(&dev->gadget.dev); + if (retval != 0) { + printk(KERN_ERR "Error in device_add() : %d\n",retval); + goto register_error; + } + + retval = driver->bind (&dev->gadget); + if (retval != 0) { + DEBUG(dev, "bind to driver %s --> %d\n", + driver->driver.name, retval); + device_del(&dev->gadget.dev); + goto register_error; + } + + retval = device_create_file(dev->dev, &dev_attr_function); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + ep0_start(dev); + + DEBUG(dev, "%s ready\n", driver->driver.name); + + return 0; + +register_error: + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL (usb_gadget_register_driver); + +static void +stop_activity(struct sa1100_udc *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* mask everything */ + Ser0UDCCR = 0xFC; + + /* stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + for (i = 0; i < 3; i++) { + struct sa1100_ep *ep = &dev->ep[i]; + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + udc_disable (dev); + + /* report disconnect; the driver is already quiesced */ + if (driver) + driver->disconnect(&dev->gadget); + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct sa1100_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + local_irq_disable(); + stop_activity (dev, driver); + local_irq_enable(); + if (driver->unbind) + driver->unbind(&dev->gadget); + dev->driver = 0; + + device_del(&dev->gadget.dev); + device_remove_file(dev->dev, &dev_attr_function); + + DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL (usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +////////////////////////////////////////////////////////////////////////////// +// Proc Filesystem Support +////////////////////////////////////////////////////////////////////////////// + +#if CONFIG_PROC_FS + +#define SAY(fmt,args...) p += sprintf (p, fmt, ## args) +#define SAYV(num) p += sprintf (p, num_fmt, "Value", num) +#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn) +#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v) + +static int usbctl_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + const char * num_fmt = "%25.25s: %8.8lX\n"; + const char * cnt_fmt = "%25.25s: %lu\n"; + const char * yn_fmt = "%25.25s: %s\n"; + const char * yes = "YES"; + const char * no = "NO"; + unsigned long v; + char * p = page; + int len; + + SAY ("SA1100 USB Controller Core\n"); + + SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read); + SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written); + SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures); + SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures); + + SAY ("\n"); + + v = Ser0UDCAR; + SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v); + v = Ser0UDCIMP; + SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v); + v = Ser0UDCOMP; + SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v); + + v = Ser0UDCCR; + SAY ("\nUDC Mask Register\n"); + SAYV (v); + SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no); + SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no); + SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no); + SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no); + + v = Ser0UDCSR; + SAY ("\nUDC Interrupt Request Register\n"); + SAYV (v); + SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no); + SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no); + SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no); + SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no); + SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no); + SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no); + +#ifdef CONFIG_SA1100_EXTENEX1 + SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden"); +#endif + +#if 1 + SAY ("\nDMA Tx registers\n"); + { + dma_regs_t *r=the_controller->ep[2].dmaregs; + SAY (" DDAR"); + SAYV(r->DDAR); + SAY (" DCSR"); + SAYV(r->RdDCSR); + SAY (" DBSA (address buf A) "); + SAYV(r->DBSA); + SAY (" DBTA (transfer count A) "); + SAYV(r->DBTA); + SAY (" DBSB (address buf B) "); + SAYV(r->DBSB); + SAY (" DBTB (transfer count B) "); + SAYV(r->DBTB); + + } + SAY ("\nDMA Rx registers\n"); + { + dma_regs_t *r=the_controller->ep[1].dmaregs; + SAY (" DDAR"); + SAYV(r->DDAR); + SAY (" DCSR"); + SAYV(r->RdDCSR); + SAY (" DBSA (address buf A) "); + SAYV(r->DBSA); + SAY (" DBTA (transfer count A) "); + SAYV(r->DBTA); + SAY (" DBSB (address buf B) "); + SAYV(r->DBSB); + SAY (" DBTB (transfer count B) "); + SAYV(r->DBTB); + + } +#endif +#if 1 + v = Ser0UDCCS0; + SAY ("\nUDC Endpoint Zero Status Register\n"); + SAYV (v); + SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no); + SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no); + SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no); + SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no); + SAYC ("Data End", (v & UDCCS0_DE) ? yes : no); + SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no); + SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no); + + v = Ser0UDCCS1; + SAY ("\nUDC Receiver Status Register\n"); + SAYV (v); + SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no); + SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no); + SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no); + SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no); + SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no); + + v = Ser0UDCCS2; + SAY ("\nUDC Transmitter Status Register\n"); + SAYV (v); + SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no); + SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no); + SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no); + SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no); + SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no); + SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no); +#endif + + len = (p - page) - off; + if (len < 0) + len = 0; + *eof = (len <=count) ? 1 : 0; + *start = page + off; + return len; +} + +static inline void register_proc_entry (void) +{ + create_proc_read_entry (driver_name, 0, NULL, + usbctl_read_proc, NULL); +} + +static inline void unregister_proc_entry (void) +{ + remove_proc_entry (driver_name, NULL); +} + +#else + +#define register_proc_entry() do {} while (0) +#define unregister_proc_entry() do {} while (0) + +#endif /* CONFIG_PROC_FS */ + +/*-------------------------------------------------------------------------*/ + +MODULE_DESCRIPTION ("sa1100_udc"); +MODULE_AUTHOR ("Various"); +MODULE_LICENSE ("GPL"); + +static struct sa1100_udc memory = { + .gadget = { + .ops = &sa1100_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &sa1100_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1out-bulk", + .ops = &sa1100_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + }, + .ep[2] = { + .ep = { + .name = "ep2in-bulk", + .ops = &sa1100_ep_ops, + .maxpacket = BULK_FIFO_SIZE, + }, + .dev = &memory, + } +}; + +static int __init sa1100_udc_probe(struct device *_dev) +{ + struct sa1100_udc *dev = &memory; + int retval = 0; + + /* setup dev */ + dev->dev = _dev; +// dev->mach = _dev->platform_data; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = _dev; + dev->gadget.dev.dma_mask = _dev->dma_mask; + + the_controller = dev; + dev_set_drvdata(_dev, dev); + + /* controller stays disabled until gadget driver is bound */ + udc_disable(dev); + udc_reinit(dev); + +// spin_lock_init(&the_udc.lock); + register_proc_entry(); + + /* setup dma channels and IRQ */ + retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive", + NULL, NULL, &dev->ep[1].dmaregs); + if (retval) { + ERROR(dev, "couldn't get rx dma, err %d\n", retval); + goto err_rx_dma; + } + retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit", + NULL, NULL, &dev->ep[2].dmaregs); + if (retval) { + ERROR(dev, "couldn't get tx dma, err %d\n", retval); + goto err_tx_dma; + } + retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, IRQF_DISABLED, + driver_name, dev); + if (retval) { + ERROR(dev, "couldn't get irq, err %d\n", retval); + goto err_irq; + } + + INFO(dev, "initialized, rx %p tx %p irq %d\n", + dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC); + return 0; + +err_irq: + sa1100_free_dma(dev->ep[2].dmaregs); + usbd_info.dmaregs_rx = 0; +err_tx_dma: + sa1100_free_dma(dev->ep[1].dmaregs); + usbd_info.dmaregs_tx = 0; +err_rx_dma: + return retval; +} + +static int __exit sa1100_udc_remove(struct device *_dev) +{ + struct sa1100_udc *dev = dev_get_drvdata(_dev); + + udc_disable(dev); + unregister_proc_entry(); + usb_gadget_unregister_driver(dev->driver); + sa1100_free_dma(dev->ep[1].dmaregs); + sa1100_free_dma(dev->ep[2].dmaregs); + free_irq(IRQ_Ser0UDC, dev); + dev_set_drvdata(_dev,NULL); + the_controller = NULL; + return 0; +} + +static struct device_driver udc_driver = { + .name = "sa11x0-udc", + .bus = &platform_bus_type, + .probe = sa1100_udc_probe, + .remove = __exit_p(sa1100_udc_remove), +// .suspend = sa1100_udc_suspend, +// .resume = sa1100_udc_resume, + .owner = THIS_MODULE, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); +#ifdef NCB_DMA_FIX + send_buffer = (char*) kzalloc(SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); + receive_buffer = (char*) kzalloc(RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA ); +#endif + return driver_register(&udc_driver); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ +#ifdef NCB_DMA_FIX + if (send_buffer) { + kfree(send_buffer); + send_buffer = NULL; + } + if (receive_buffer) { + kfree(receive_buffer); + receive_buffer = NULL; + } +#endif + driver_unregister(&udc_driver); +} +module_exit(udc_exit); diff --git a/drivers/usb/gadget/sa1100_udc.h b/drivers/usb/gadget/sa1100_udc.h new file mode 100644 index 0000000..acb665a --- /dev/null +++ b/drivers/usb/gadget/sa1100_udc.h @@ -0,0 +1,94 @@ +/* + * internals of "new style" UDC controller + * replaces ARM-specific "sa1100_usb.h". + */ + +struct sa1100_ep { + struct usb_ep ep; + struct sa1100_udc *dev; + //unsigned long irqs; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + dma_regs_t *dmaregs; + unsigned stopped : 1; +}; + +struct sa1100_request { + struct usb_request req; + struct list_head queue; +// NCB unsigned mapped : 1; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +#define EP0_FIFO_SIZE ((unsigned)8) +#define BULK_FIFO_SIZE ((unsigned)64) +//#define ISO_FIFO_SIZE ((unsigned)256) +//#define INT_FIFO_SIZE ((unsigned)8) + +struct udc_stats { + struct ep0stats { + unsigned long ops; + unsigned long bytes; + } read, write; + unsigned long irqs; +}; + +struct sa1100_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + enum ep0_state ep0state; + struct udc_stats stats; +// NCB spinlock_t lock; +// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx; + unsigned got_irq : 1, + vbus : 1, + pullup : 1, + has_cfr : 1, + req_pending : 1, + req_std : 1, + req_config : 1; + struct timer_list timer; + u64 dma_mask; + unsigned char address; + struct sa1100_ep ep[3]; + int ep0_req_len; +}; + +/*-------------------------------------------------------------------------*/ + +#define xprintk(dev,level,fmt,args...) \ + printk(level "%s: " fmt , driver_name , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DEBUG(dev,fmt,args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DEBUG(dev,fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG DEBUG +#else +#define VDEBUG(dev,fmt,args...) \ + do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(dev,fmt,args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +#define WARN(dev,fmt,args...) \ + xprintk(dev , KERN_WARNING , fmt , ## args) +#define INFO(dev,fmt,args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +/*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index fa019fa..b3699af 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -1,15 +1,9 @@ /* - * g_serial.c -- USB gadget serial driver + * serial.c -- USB gadget serial driver * - * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com) - * - * This code is based in part on the Gadget Zero driver, which - * is Copyright (C) 2003 by David Brownell, all rights reserved. - * - * This code also borrows from usbserial.c, which is - * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2000 Peter Berger (pberger@brimson.com) - * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) + * 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, @@ -22,2254 +16,237 @@ #include #include -#include -#include -#include - +#include "u_serial.h" #include "gadget_chips.h" /* Defines */ -#define GS_VERSION_STR "v2.2" -#define GS_VERSION_NUM 0x2200 +#define GS_VERSION_STR "v2.4" +#define GS_VERSION_NUM 0x2400 #define GS_LONG_NAME "Gadget Serial" -#define GS_SHORT_NAME "g_serial" - -#define GS_MAJOR 127 -#define GS_MINOR_START 0 - -/* REVISIT only one port is supported for now; - * see gs_{send,recv}_packet() ... no multiplexing, - * and no support for multiple ACM devices. - */ -#define GS_NUM_PORTS 1 - -#define GS_NUM_CONFIGS 1 -#define GS_NO_CONFIG_ID 0 -#define GS_BULK_CONFIG_ID 1 -#define GS_ACM_CONFIG_ID 2 - -#define GS_MAX_NUM_INTERFACES 2 -#define GS_BULK_INTERFACE_ID 0 -#define GS_CONTROL_INTERFACE_ID 0 -#define GS_DATA_INTERFACE_ID 1 - -#define GS_MAX_DESC_LEN 256 - -#define GS_DEFAULT_READ_Q_SIZE 32 -#define GS_DEFAULT_WRITE_Q_SIZE 32 - -#define GS_DEFAULT_WRITE_BUF_SIZE 8192 -#define GS_TMP_BUF_SIZE 8192 - -#define GS_CLOSE_TIMEOUT 15 - -#define GS_DEFAULT_USE_ACM 0 - -/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults - * expected by "usbser.sys" on MS-Windows. - */ -#define GS_DEFAULT_DTE_RATE 9600 -#define GS_DEFAULT_DATA_BITS 8 -#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY -#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS - -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} - - -/* debug settings */ -#ifdef DEBUG -static int debug = 1; -#else -#define debug 0 -#endif - -#define gs_debug(format, arg...) \ - do { if (debug) pr_debug(format, ## arg); } while (0) -#define gs_debug_level(level, format, arg...) \ - do { if (debug >= level) pr_debug(format, ## arg); } while (0) +#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR +/*-------------------------------------------------------------------------*/ /* Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. - */ +* +* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +* Instead: allocate your own, using normal USB-IF procedures. +*/ #define GS_VENDOR_ID 0x0525 /* NetChip */ #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ -#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ -#define GS_NOTIFY_MAXPACKET 8 +/* string IDs are assigned dynamically */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_DESCRIPTION_IDX 2 -/* circular buffer */ -struct gs_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; +static char manufacturer[50]; -/* the port structure holds info for each port, one for each minor number */ -struct gs_port { - struct gs_dev *port_dev; /* pointer to device struct */ - struct tty_struct *port_tty; /* pointer to tty struct */ - spinlock_t port_lock; - int port_num; - int port_open_count; - int port_in_use; /* open/close in progress */ - wait_queue_head_t port_write_wait;/* waiting to write */ - struct gs_buf *port_write_buf; - struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ - u16 port_handshake_bits; -#define RS232_RTS (1 << 1) -#define RS232_DTE (1 << 0) +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, + [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, + { } /* end of list */ }; -/* the device structure holds info for the USB device */ -struct gs_dev { - struct usb_gadget *dev_gadget; /* gadget device pointer */ - spinlock_t dev_lock; /* lock for set/reset config */ - int dev_config; /* configuration number */ - struct usb_ep *dev_notify_ep; /* address of notify endpoint */ - struct usb_ep *dev_in_ep; /* address of in endpoint */ - struct usb_ep *dev_out_ep; /* address of out endpoint */ - struct usb_endpoint_descriptor /* descriptor of notify ep */ - *dev_notify_ep_desc; - struct usb_endpoint_descriptor /* descriptor of in endpoint */ - *dev_in_ep_desc; - struct usb_endpoint_descriptor /* descriptor of out endpoint */ - *dev_out_ep_desc; - struct usb_request *dev_ctrl_req; /* control request */ - struct list_head dev_req_list; /* list of write requests */ - int dev_sched_port; /* round robin port scheduled */ - struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */ +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, }; - -/* Functions */ - -/* tty driver internals */ -static int gs_send(struct gs_dev *dev); -static int gs_send_packet(struct gs_dev *dev, char *packet, - unsigned int size); -static int gs_recv_packet(struct gs_dev *dev, char *packet, - unsigned int size); -static void gs_read_complete(struct usb_ep *ep, struct usb_request *req); -static void gs_write_complete(struct usb_ep *ep, struct usb_request *req); - -/* gadget driver internals */ -static int gs_set_config(struct gs_dev *dev, unsigned config); -static void gs_reset_config(struct gs_dev *dev); -static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, - u8 type, unsigned int index, int is_otg); - -static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, - gfp_t kmalloc_flags); -static void gs_free_req(struct usb_ep *ep, struct usb_request *req); - -static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags); -static void gs_free_ports(struct gs_dev *dev); - -/* circular buffer */ -static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags); -static void gs_buf_free(struct gs_buf *gb); -static void gs_buf_clear(struct gs_buf *gb); -static unsigned int gs_buf_data_avail(struct gs_buf *gb); -static unsigned int gs_buf_space_avail(struct gs_buf *gb); -static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, - unsigned int count); -static unsigned int gs_buf_get(struct gs_buf *gb, char *buf, - unsigned int count); - - -/* Globals */ - -static struct gs_dev *gs_device; - -static struct mutex gs_open_close_lock[GS_NUM_PORTS]; - - -/*-------------------------------------------------------------------------*/ - -/* USB descriptors */ - -#define GS_MANUFACTURER_STR_ID 1 -#define GS_PRODUCT_STR_ID 2 -#define GS_SERIAL_STR_ID 3 -#define GS_BULK_CONFIG_STR_ID 4 -#define GS_ACM_CONFIG_STR_ID 5 -#define GS_CONTROL_STR_ID 6 -#define GS_DATA_STR_ID 7 - -/* static strings, in UTF-8 */ -static char manufacturer[50]; -static struct usb_string gs_strings[] = { - { GS_MANUFACTURER_STR_ID, manufacturer }, - { GS_PRODUCT_STR_ID, GS_LONG_NAME }, - { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, - { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, - { GS_CONTROL_STR_ID, "Gadget Serial Control" }, - { GS_DATA_STR_ID, "Gadget Serial Data" }, - { } /* end of list */ -}; - -static struct usb_gadget_strings gs_string_table = { - .language = 0x0409, /* en-us */ - .strings = gs_strings, +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -static struct usb_device_descriptor gs_device_desc = { +static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), + /* .bDeviceClass = f(use_acm) */ .bDeviceSubClass = 0, .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), - .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), - .iManufacturer = GS_MANUFACTURER_STR_ID, - .iProduct = GS_PRODUCT_STR_ID, - .bNumConfigurations = GS_NUM_CONFIGS, + /* .idProduct = f(use_acm) */ + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + .bNumConfigurations = 1, }; -static struct usb_otg_descriptor gs_otg_descriptor = { - .bLength = sizeof(gs_otg_descriptor), +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -static struct usb_config_descriptor gs_bulk_config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 1, - .bConfigurationValue = GS_BULK_CONFIG_ID, - .iConfiguration = GS_BULK_CONFIG_STR_ID, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, -}; - -static struct usb_config_descriptor gs_acm_config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 2, - .bConfigurationValue = GS_ACM_CONFIG_ID, - .iConfiguration = GS_ACM_CONFIG_STR_ID, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, -}; - -static const struct usb_interface_descriptor gs_bulk_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_BULK_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_interface_descriptor gs_control_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, - .iInterface = GS_CONTROL_STR_ID, -}; - -static const struct usb_interface_descriptor gs_data_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_DATA_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_cdc_header_desc gs_header_desc = { - .bLength = sizeof(gs_header_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_HEADER_TYPE, - .bcdCDC = __constant_cpu_to_le16(0x0110), -}; - -static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { - .bLength = sizeof(gs_call_mgmt_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - .bmCapabilities = 0, - .bDataInterface = 1, /* index of data interface */ -}; - -static struct usb_cdc_acm_descriptor gs_acm_descriptor = { - .bLength = sizeof(gs_acm_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ACM_TYPE, - .bmCapabilities = (1 << 1), -}; - -static const struct usb_cdc_union_desc gs_union_desc = { - .bLength = sizeof(gs_union_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_UNION_TYPE, - .bMasterInterface0 = 0, /* index of control interface */ - .bSlaveInterface0 = 1, /* index of data interface */ -}; - -static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, -}; - -static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; - -static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; -static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, -}; - -static struct usb_endpoint_descriptor gs_highspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor gs_highspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_qualifier_descriptor gs_qualifier_desc = { - .bLength = sizeof(struct usb_qualifier_descriptor), - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = __constant_cpu_to_le16 (0x0200), - /* assumes ep0 uses the same value for both speeds ... */ - .bNumConfigurations = GS_NUM_CONFIGS, -}; - -static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, - NULL, + /* 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 *gs_acm_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_highspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, NULL, }; - /*-------------------------------------------------------------------------*/ /* Module */ -MODULE_DESCRIPTION(GS_LONG_NAME); +MODULE_DESCRIPTION(GS_VERSION_NAME); MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); -#ifdef DEBUG -module_param(debug, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); -#endif - -static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE; -module_param(read_q_size, uint, S_IRUGO); -MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32"); - -static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE; -module_param(write_q_size, uint, S_IRUGO); -MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32"); +static int use_acm = true; +module_param(use_acm, bool, 0); +MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); -static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE; -module_param(write_buf_size, uint, S_IRUGO); -MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192"); - -static unsigned int use_acm = GS_DEFAULT_USE_ACM; -module_param(use_acm, uint, S_IRUGO); -MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); +static unsigned n_ports = 1; +module_param(n_ports, uint, 0); +MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ -/* TTY Driver */ - -/* - * gs_open - */ -static int gs_open(struct tty_struct *tty, struct file *file) -{ - int port_num; - unsigned long flags; - struct gs_port *port; - struct gs_dev *dev; - struct gs_buf *buf; - struct mutex *mtx; - int ret; - - port_num = tty->index; - - gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file); - - if (port_num < 0 || port_num >= GS_NUM_PORTS) { - pr_err("gs_open: (%d,%p,%p) invalid port number\n", - port_num, tty, file); - return -ENODEV; - } - - dev = gs_device; - - if (dev == NULL) { - pr_err("gs_open: (%d,%p,%p) NULL device pointer\n", - port_num, tty, file); - return -ENODEV; - } - - mtx = &gs_open_close_lock[port_num]; - if (mutex_lock_interruptible(mtx)) { - pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n", - port_num, tty, file); - return -ERESTARTSYS; - } - - spin_lock_irqsave(&dev->dev_lock, flags); - - if (dev->dev_config == GS_NO_CONFIG_ID) { - pr_err("gs_open: (%d,%p,%p) device is not connected\n", - port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; - } - - port = dev->dev_port[port_num]; - - if (port == NULL) { - pr_err("gs_open: (%d,%p,%p) NULL port pointer\n", - port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; - } - - spin_lock(&port->port_lock); - spin_unlock(&dev->dev_lock); - - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n", - port_num, tty, file); - ret = -EIO; - goto exit_unlock_port; - } - - if (port->port_open_count > 0) { - ++port->port_open_count; - gs_debug("gs_open: (%d,%p,%p) already open\n", - port_num, tty, file); - ret = 0; - goto exit_unlock_port; - } - - tty->driver_data = NULL; - - /* mark port as in use, we can drop port lock and sleep if necessary */ - port->port_in_use = 1; - - /* allocate write buffer on first open */ - if (port->port_write_buf == NULL) { - spin_unlock_irqrestore(&port->port_lock, flags); - buf = gs_buf_alloc(write_buf_size, GFP_KERNEL); - spin_lock_irqsave(&port->port_lock, flags); - - /* might have been disconnected while asleep, check */ - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; - } - - if ((port->port_write_buf=buf) == NULL) { - pr_err("gs_open: (%d,%p,%p) cannot allocate " - "port write buffer\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -ENOMEM; - goto exit_unlock_port; - } - - } - - /* wait for carrier detect (not implemented) */ - - /* might have been disconnected while asleep, check */ - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; - } - - tty->driver_data = port; - port->port_tty = tty; - port->port_open_count = 1; - port->port_in_use = 0; - - gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); - - ret = 0; - -exit_unlock_port: - spin_unlock_irqrestore(&port->port_lock, flags); - mutex_unlock(mtx); - return ret; - -exit_unlock_dev: - spin_unlock_irqrestore(&dev->dev_lock, flags); - mutex_unlock(mtx); - return ret; - -} - -/* - * gs_close - */ - -static int gs_write_finished_event_safely(struct gs_port *p) -{ - int cond; - - spin_lock_irq(&(p)->port_lock); - cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); - spin_unlock_irq(&(p)->port_lock); - return cond; -} - -static void gs_close(struct tty_struct *tty, struct file *file) -{ - struct gs_port *port = tty->driver_data; - struct mutex *mtx; - - if (port == NULL) { - pr_err("gs_close: NULL port pointer\n"); - return; - } - - gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); - - mtx = &gs_open_close_lock[port->port_num]; - mutex_lock(mtx); - - spin_lock_irq(&port->port_lock); - - if (port->port_open_count == 0) { - pr_err("gs_close: (%d,%p,%p) port is already closed\n", - port->port_num, tty, file); - goto exit; - } - - if (port->port_open_count > 1) { - --port->port_open_count; - goto exit; - } - - /* free disconnected port on final close */ - if (port->port_dev == NULL) { - kfree(port); - goto exit; - } - - /* mark port as closed but in use, we can drop port lock */ - /* and sleep if necessary */ - port->port_in_use = 1; - port->port_open_count = 0; - - /* wait for write buffer to drain, or */ - /* at most GS_CLOSE_TIMEOUT seconds */ - if (gs_buf_data_avail(port->port_write_buf) > 0) { - spin_unlock_irq(&port->port_lock); - wait_event_interruptible_timeout(port->port_write_wait, - gs_write_finished_event_safely(port), - GS_CLOSE_TIMEOUT * HZ); - spin_lock_irq(&port->port_lock); - } - - /* free disconnected port on final close */ - /* (might have happened during the above sleep) */ - if (port->port_dev == NULL) { - kfree(port); - goto exit; - } - - gs_buf_clear(port->port_write_buf); - - tty->driver_data = NULL; - port->port_tty = NULL; - port->port_in_use = 0; - - gs_debug("gs_close: (%d,%p,%p) completed\n", - port->port_num, tty, file); - -exit: - spin_unlock_irq(&port->port_lock); - mutex_unlock(mtx); -} - -/* - * gs_write - */ -static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int __init serial_bind_config(struct usb_configuration *c) { - unsigned long flags; - struct gs_port *port = tty->driver_data; - int ret; - - if (port == NULL) { - pr_err("gs_write: NULL port pointer\n"); - return -EIO; - } - - gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty, - count); - - if (count == 0) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_write: (%d,%p) port is not connected\n", - port->port_num, tty); - ret = -EIO; - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_write: (%d,%p) port is closed\n", - port->port_num, tty); - ret = -EBADF; - goto exit; - } - - count = gs_buf_put(port->port_write_buf, buf, count); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_send(gs_device); - - gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, - count); + unsigned i; + int status = 0; - return count; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); - return ret; -} - -/* - * gs_put_char - */ -static int gs_put_char(struct tty_struct *tty, unsigned char ch) -{ - unsigned long flags; - struct gs_port *port = tty->driver_data; - int ret = 0; - - if (port == NULL) { - pr_err("gs_put_char: NULL port pointer\n"); - return 0; - } - - gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", - port->port_num, tty, ch, __builtin_return_address(0)); - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_put_char: (%d,%p) port is not connected\n", - port->port_num, tty); - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_put_char: (%d,%p) port is closed\n", - port->port_num, tty); - goto exit; - } - - ret = gs_buf_put(port->port_write_buf, &ch, 1); - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); - return ret; -} - -/* - * gs_flush_chars - */ -static void gs_flush_chars(struct tty_struct *tty) -{ - unsigned long flags; - struct gs_port *port = tty->driver_data; - - if (port == NULL) { - pr_err("gs_flush_chars: NULL port pointer\n"); - return; - } - - gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_flush_chars: (%d,%p) port is not connected\n", - port->port_num, tty); - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_flush_chars: (%d,%p) port is closed\n", - port->port_num, tty); - goto exit; - } - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_send(gs_device); - - return; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); -} - -/* - * gs_write_room - */ -static int gs_write_room(struct tty_struct *tty) -{ - - int room = 0; - unsigned long flags; - struct gs_port *port = tty->driver_data; - - - if (port == NULL) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev != NULL && port->port_open_count > 0 - && port->port_write_buf != NULL) - room = gs_buf_space_avail(port->port_write_buf); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_debug("gs_write_room: (%d,%p) room=%d\n", - port->port_num, tty, room); - - return room; -} - -/* - * gs_chars_in_buffer - */ -static int gs_chars_in_buffer(struct tty_struct *tty) -{ - int chars = 0; - unsigned long flags; - struct gs_port *port = tty->driver_data; - - if (port == NULL) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev != NULL && port->port_open_count > 0 - && port->port_write_buf != NULL) - chars = gs_buf_data_avail(port->port_write_buf); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n", - port->port_num, tty, chars); - - return chars; -} - -/* - * gs_throttle - */ -static void gs_throttle(struct tty_struct *tty) -{ -} - -/* - * gs_unthrottle - */ -static void gs_unthrottle(struct tty_struct *tty) -{ -} - -/* - * gs_break - */ -static void gs_break(struct tty_struct *tty, int break_state) -{ -} - -/* - * gs_ioctl - */ -static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct gs_port *port = tty->driver_data; - - if (port == NULL) { - pr_err("gs_ioctl: NULL port pointer\n"); - return -EIO; + for (i = 0; i < n_ports && status == 0; i++) { + if (use_acm) + status = acm_bind_config(c, i); + else + status = gser_bind_config(c, i); } - - gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", - port->port_num, tty, file, cmd, arg); - - /* handle ioctls */ - - /* could not handle ioctl */ - return -ENOIOCTLCMD; -} - -/* - * gs_set_termios - */ -static void gs_set_termios(struct tty_struct *tty, struct ktermios *old) -{ + return status; } -static const struct tty_operations gs_tty_ops = { - .open = gs_open, - .close = gs_close, - .write = gs_write, - .put_char = gs_put_char, - .flush_chars = gs_flush_chars, - .write_room = gs_write_room, - .ioctl = gs_ioctl, - .set_termios = gs_set_termios, - .throttle = gs_throttle, - .unthrottle = gs_unthrottle, - .break_ctl = gs_break, - .chars_in_buffer = gs_chars_in_buffer, +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, + .bMaxPower = 1, /* 2 mA, minimal */ }; -/*-------------------------------------------------------------------------*/ - -/* -* gs_send -* -* This function finds available write requests, calls -* gs_send_packet to fill these packets with data, and -* continues until either there are no more write requests -* available or no more data to send. This function is -* run whenever data arrives or write requests are available. -*/ -static int gs_send(struct gs_dev *dev) -{ - int ret,len; - unsigned long flags; - struct usb_ep *ep; - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_send: NULL device pointer\n"); - return -ENODEV; - } - - spin_lock_irqsave(&dev->dev_lock, flags); - - ep = dev->dev_in_ep; - - while(!list_empty(&dev->dev_req_list)) { - - req = list_entry(dev->dev_req_list.next, - struct usb_request, list); - - len = gs_send_packet(dev, req->buf, ep->maxpacket); - - if (len > 0) { - gs_debug_level(3, "gs_send: len=%d, 0x%2.2x " - "0x%2.2x 0x%2.2x ...\n", len, - *((unsigned char *)req->buf), - *((unsigned char *)req->buf+1), - *((unsigned char *)req->buf+2)); - list_del(&req->list); - req->length = len; - spin_unlock_irqrestore(&dev->dev_lock, flags); - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err( - "gs_send: cannot queue read request, ret=%d\n", - ret); - spin_lock_irqsave(&dev->dev_lock, flags); - break; - } - spin_lock_irqsave(&dev->dev_lock, flags); - } else { - break; - } - - } - - spin_unlock_irqrestore(&dev->dev_lock, flags); - - return 0; -} - -/* - * gs_send_packet - * - * If there is data to send, a packet is built in the given - * buffer and the size is returned. If there is no data to - * send, 0 is returned. If there is any error a negative - * error number is returned. - * - * Called during USB completion routine, on interrupt time. - * - * We assume that disconnect will not happen until all completion - * routines have completed, so we can assume that the dev_port - * array does not change during the lifetime of this function. - */ -static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size) -{ - unsigned int len; - struct gs_port *port; - - /* TEMPORARY -- only port 0 is supported right now */ - port = dev->dev_port[0]; - - if (port == NULL) { - pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0); - return -EIO; - } - - spin_lock(&port->port_lock); - - len = gs_buf_data_avail(port->port_write_buf); - if (len < size) - size = len; - - if (size == 0) - goto exit; - - size = gs_buf_get(port->port_write_buf, packet, size); - - if (port->port_tty) - wake_up_interruptible(&port->port_tty->write_wait); - -exit: - spin_unlock(&port->port_lock); - return size; -} - -/* - * gs_recv_packet - * - * Called for each USB packet received. Reads the packet - * header and stuffs the data in the appropriate tty buffer. - * Returns 0 if successful, or a negative error number. - * - * Called during USB completion routine, on interrupt time. - * - * We assume that disconnect will not happen until all completion - * routines have completed, so we can assume that the dev_port - * array does not change during the lifetime of this function. - */ -static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size) -{ - unsigned int len; - struct gs_port *port; - int ret; - struct tty_struct *tty; - - /* TEMPORARY -- only port 0 is supported right now */ - port = dev->dev_port[0]; - - if (port == NULL) { - pr_err("gs_recv_packet: port=%d, NULL port pointer\n", - port->port_num); - return -EIO; - } - - spin_lock(&port->port_lock); - - if (port->port_open_count == 0) { - pr_err("gs_recv_packet: port=%d, port is closed\n", - port->port_num); - ret = -EIO; - goto exit; - } - - - tty = port->port_tty; - - if (tty == NULL) { - pr_err("gs_recv_packet: port=%d, NULL tty pointer\n", - port->port_num); - ret = -EIO; - goto exit; - } - - if (port->port_tty->magic != TTY_MAGIC) { - pr_err("gs_recv_packet: port=%d, bad tty magic\n", - port->port_num); - ret = -EIO; - goto exit; - } - - len = tty_buffer_request_room(tty, size); - if (len > 0) { - tty_insert_flip_string(tty, packet, len); - tty_flip_buffer_push(port->port_tty); - wake_up_interruptible(&port->port_tty->read_wait); - } - ret = 0; -exit: - spin_unlock(&port->port_lock); - return ret; -} - -/* -* gs_read_complete -*/ -static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) +static int __init gs_bind(struct usb_composite_dev *cdev) { - int ret; - struct gs_dev *dev = ep->driver_data; - - if (dev == NULL) { - pr_err("gs_read_complete: NULL device pointer\n"); - return; - } + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; - switch(req->status) { - case 0: - /* normal completion */ - gs_recv_packet(dev, req->buf, req->actual); -requeue: - req->length = ep->maxpacket; - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err( - "gs_read_complete: cannot queue read request, ret=%d\n", - ret); - } - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_debug("gs_read_complete: shutdown\n"); - gs_free_req(ep, req); - break; - - default: - /* unexpected */ - pr_err( - "gs_read_complete: unexpected status error, status=%d\n", - req->status); - goto requeue; - break; - } -} - -/* -* gs_write_complete -*/ -static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gs_dev *dev = ep->driver_data; - - if (dev == NULL) { - pr_err("gs_write_complete: NULL device pointer\n"); - return; - } + status = gserial_setup(cdev->gadget, n_ports); + if (status < 0) + return status; - switch(req->status) { - case 0: - /* normal completion */ -requeue: - spin_lock(&dev->dev_lock); - list_add(&req->list, &dev->dev_req_list); - spin_unlock(&dev->dev_lock); - - gs_send(dev); - - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_debug("gs_write_complete: shutdown\n"); - gs_free_req(ep, req); - break; - - default: - pr_err( - "gs_write_complete: unexpected status error, status=%d\n", - req->status); - goto requeue; - break; - } -} + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ -/*-------------------------------------------------------------------------*/ + /* device description: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_MANUFACTURER_IDX].id = status; -/* Gadget Driver */ + device_desc.iManufacturer = status; -/* - * gs_unbind - * - * Called on module unload. Frees the control request and device - * structure. - */ -static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget) -{ - struct gs_dev *dev = get_gadget_data(gadget); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_PRODUCT_IDX].id = status; - gs_device = NULL; + device_desc.iProduct = status; - /* read/write requests already freed, only control request remains */ - if (dev != NULL) { - if (dev->dev_ctrl_req != NULL) { - gs_free_req(gadget->ep0, dev->dev_ctrl_req); - dev->dev_ctrl_req = NULL; - } - gs_reset_config(dev); - gs_free_ports(dev); - kfree(dev); - set_gadget_data(gadget, NULL); - } + /* config description */ + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_DESCRIPTION_IDX].id = status; - pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME, - GS_VERSION_STR); -} - -/* - * gs_bind - * - * Called on module load. Allocates and initializes the device - * structure and a control request. - */ -static int __init gs_bind(struct usb_gadget *gadget) -{ - int ret; - struct usb_ep *ep; - struct gs_dev *dev; - int gcnum; - - /* Some controllers can't support CDC ACM: - * - sh doesn't support multiple interfaces or configs; - * - sa1100 doesn't have a third interrupt endpoint - */ - if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget)) - use_acm = 0; + serial_config_driver.iConfiguration = status; + /* set up other descriptors */ gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) - gs_device_desc.bcdDevice = - cpu_to_le16(GS_VERSION_NUM | gcnum); + 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); - /* unrecognized, but safe unless bulk is REALLY quirky */ - gs_device_desc.bcdDevice = - __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); - } - - dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - - usb_ep_autoconfig_reset(gadget); - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); - if (!ep) - goto autoconf_fail; - dev->dev_in_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); - if (!ep) - goto autoconf_fail; - dev->dev_out_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - - if (use_acm) { - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); - if (!ep) { - pr_err("gs_bind: cannot run ACM on %s\n", gadget->name); - goto autoconf_fail; - } - gs_device_desc.idProduct = __constant_cpu_to_le16( - GS_CDC_PRODUCT_ID), - dev->dev_notify_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - } - - gs_device_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; - gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - - if (gadget_is_dualspeed(gadget)) { - gs_qualifier_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; - /* assume ep0 uses the same packet size for both speeds */ - gs_qualifier_desc.bMaxPacketSize0 = - gs_device_desc.bMaxPacketSize0; - /* assume endpoints are dual-speed */ - gs_highspeed_notify_desc.bEndpointAddress = - gs_fullspeed_notify_desc.bEndpointAddress; - gs_highspeed_in_desc.bEndpointAddress = - gs_fullspeed_in_desc.bEndpointAddress; - gs_highspeed_out_desc.bEndpointAddress = - gs_fullspeed_out_desc.bEndpointAddress; - } - - usb_gadget_set_selfpowered(gadget); - - if (gadget_is_otg(gadget)) { - gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, - gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + device_desc.bcdDevice = + __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099); } - gs_device = dev; - - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - dev->dev_gadget = gadget; - spin_lock_init(&dev->dev_lock); - INIT_LIST_HEAD(&dev->dev_req_list); - set_gadget_data(gadget, dev); - - if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) { - pr_err("gs_bind: cannot allocate ports\n"); - gs_unbind(gadget); - return ret; + if (gadget_is_otg(cdev->gadget)) { + serial_config_driver.descriptors = otg_desc; + serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - /* preallocate control response and buffer */ - dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, - GFP_KERNEL); - if (dev->dev_ctrl_req == NULL) { - gs_unbind(gadget); - return -ENOMEM; - } - gadget->ep0->driver_data = dev; + /* register our configuration */ + status = usb_add_config(cdev, &serial_config_driver); + if (status < 0) + goto fail; - pr_info("gs_bind: %s %s bound\n", - GS_LONG_NAME, GS_VERSION_STR); + INFO(cdev, "%s\n", GS_VERSION_NAME); return 0; -autoconf_fail: - kfree(dev); - pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name); - return -ENODEV; -} - -static int gs_setup_standard(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - switch (ctrl->bRequest) { - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - break; - - switch (wValue >> 8) { - case USB_DT_DEVICE: - ret = min(wLength, - (u16)sizeof(struct usb_device_descriptor)); - memcpy(req->buf, &gs_device_desc, ret); - break; - - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - ret = min(wLength, - (u16)sizeof(struct usb_qualifier_descriptor)); - memcpy(req->buf, &gs_qualifier_desc, ret); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - /* fall through */ - case USB_DT_CONFIG: - ret = gs_build_config_buf(req->buf, gadget, - wValue >> 8, wValue & 0xff, - gadget_is_otg(gadget)); - if (ret >= 0) - ret = min(wLength, (u16)ret); - break; - - case USB_DT_STRING: - /* wIndex == language code. */ - ret = usb_gadget_get_string(&gs_string_table, - wValue & 0xff, req->buf); - if (ret >= 0) - ret = min(wLength, (u16)ret); - break; - } - break; - - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - break; - spin_lock(&dev->dev_lock); - ret = gs_set_config(dev, wValue); - spin_unlock(&dev->dev_lock); - break; - - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - break; - *(u8 *)req->buf = dev->dev_config; - ret = min(wLength, (u16)1); - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->dev_config - || wIndex >= GS_MAX_NUM_INTERFACES) - break; - if (dev->dev_config == GS_BULK_CONFIG_ID - && wIndex != GS_BULK_INTERFACE_ID) - break; - /* no alternate interface settings */ - if (wValue != 0) - break; - spin_lock(&dev->dev_lock); - /* PXA hardware partially handles SET_INTERFACE; - * we need to kluge around that interference. */ - if (gadget_is_pxa(gadget)) { - ret = gs_set_config(dev, use_acm ? - GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); - goto set_interface_done; - } - if (dev->dev_config != GS_BULK_CONFIG_ID - && wIndex == GS_CONTROL_INTERFACE_ID) { - if (dev->dev_notify_ep) { - usb_ep_disable(dev->dev_notify_ep); - usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); - } - } else { - usb_ep_disable(dev->dev_in_ep); - usb_ep_disable(dev->dev_out_ep); - usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc); - usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc); - } - ret = 0; -set_interface_done: - spin_unlock(&dev->dev_lock); - break; - - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) - || dev->dev_config == GS_NO_CONFIG_ID) - break; - if (wIndex >= GS_MAX_NUM_INTERFACES - || (dev->dev_config == GS_BULK_CONFIG_ID - && wIndex != GS_BULK_INTERFACE_ID)) { - ret = -EDOM; - break; - } - /* no alternate interface settings */ - *(u8 *)req->buf = 0; - ret = min(wLength, (u16)1); - break; - - default: - pr_err("gs_setup: unknown standard request, type=%02x, " - "request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - return ret; +fail: + gserial_cleanup(); + return status; } -static void gs_setup_complete_set_line_coding(struct usb_ep *ep, - struct usb_request *req) -{ - struct gs_dev *dev = ep->driver_data; - struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ - - switch (req->status) { - case 0: - /* normal completion */ - if (req->actual != sizeof(port->port_line_coding)) - usb_ep_set_halt(ep); - else if (port) { - struct usb_cdc_line_coding *value = req->buf; - - /* REVISIT: we currently just remember this data. - * If we change that, (a) validate it first, then - * (b) update whatever hardware needs updating. - */ - spin_lock(&port->port_lock); - port->port_line_coding = *value; - spin_unlock(&port->port_lock); - } - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_free_req(ep, req); - break; - - default: - /* unexpected */ - break; - } - return; -} - -static int gs_setup_class(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - switch (ctrl->bRequest) { - case USB_CDC_REQ_SET_LINE_CODING: - if (wLength != sizeof(struct usb_cdc_line_coding)) - break; - ret = wLength; - req->complete = gs_setup_complete_set_line_coding; - break; - - case USB_CDC_REQ_GET_LINE_CODING: - ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding)); - if (port) { - spin_lock(&port->port_lock); - memcpy(req->buf, &port->port_line_coding, ret); - spin_unlock(&port->port_lock); - } - break; - - case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - if (wLength != 0) - break; - ret = 0; - if (port) { - /* REVISIT: we currently just remember this data. - * If we change that, update whatever hardware needs - * updating. - */ - spin_lock(&port->port_lock); - port->port_handshake_bits = wValue; - spin_unlock(&port->port_lock); - } - break; - - default: - /* NOTE: strictly speaking, we should accept AT-commands - * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE. - * But our call management descriptor says we don't handle - * call management, so we should be able to get by without - * handling those "required" commands (except by stalling). - */ - pr_err("gs_setup: unknown class request, " - "type=%02x, request=%02x, value=%04x, " - "index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - return ret; -} - -/* - * gs_setup_complete - */ -static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) { - pr_err("gs_setup_complete: status error, status=%d, " - "actual=%d, length=%d\n", - req->status, req->actual, req->length); - } -} - -/* - * gs_setup - * - * Implements all the control endpoint functionality that's not - * handled in hardware or the hardware driver. - * - * Returns the size of the data sent to the host, or a negative - * error number. - */ -static int gs_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - req->complete = gs_setup_complete; - - switch (ctrl->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_STANDARD: - ret = gs_setup_standard(gadget, ctrl); - break; - - case USB_TYPE_CLASS: - ret = gs_setup_class(gadget, ctrl); - break; - - default: - pr_err("gs_setup: unknown request, type=%02x, request=%02x, " - "value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - /* respond with data transfer before status phase? */ - if (ret >= 0) { - req->length = ret; - req->zero = ret < wLength - && (ret % gadget->ep0->maxpacket) == 0; - ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (ret < 0) { - pr_err("gs_setup: cannot queue response, ret=%d\n", - ret); - req->status = 0; - gs_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (ret < 0) or reports success */ - return ret; -} - -/* - * gs_disconnect - * - * Called when the device is disconnected. Frees the closed - * ports and disconnects open ports. Open ports will be freed - * on close. Then reallocates the ports for the next connection. - */ -static void gs_disconnect(struct usb_gadget *gadget) -{ - unsigned long flags; - struct gs_dev *dev = get_gadget_data(gadget); - - spin_lock_irqsave(&dev->dev_lock, flags); - - gs_reset_config(dev); - - /* free closed ports and disconnect open ports */ - /* (open ports will be freed when closed) */ - gs_free_ports(dev); - - /* re-allocate ports for the next connection */ - if (gs_alloc_ports(dev, GFP_ATOMIC) != 0) - pr_err("gs_disconnect: cannot re-allocate ports\n"); - - spin_unlock_irqrestore(&dev->dev_lock, flags); - - pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME); -} - -static struct usb_gadget_driver gs_gadget_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - .function = GS_LONG_NAME, - .bind = gs_bind, - .unbind = gs_unbind, - .setup = gs_setup, - .disconnect = gs_disconnect, - .driver = { - .name = GS_SHORT_NAME, - .owner = THIS_MODULE, - }, +static struct usb_composite_driver gserial_driver = { + .name = "g_serial", + .dev = &device_desc, + .strings = dev_strings, + .bind = gs_bind, }; -/* - * gs_set_config - * - * Configures the device by enabling device specific - * optimizations, setting up the endpoints, allocating - * read and write requests and queuing read requests. - * - * The device lock must be held when calling this function. - */ -static int gs_set_config(struct gs_dev *dev, unsigned config) +static int __init init(void) { - int i; - int ret = 0; - struct usb_gadget *gadget = dev->dev_gadget; - struct usb_ep *ep; - struct usb_endpoint_descriptor *out, *in, *notify; - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_set_config: NULL device pointer\n"); - return 0; - } - - if (config == dev->dev_config) - return 0; - - gs_reset_config(dev); - - switch (config) { - case GS_NO_CONFIG_ID: - return 0; - case GS_BULK_CONFIG_ID: - if (use_acm) - return -EINVAL; - break; - case GS_ACM_CONFIG_ID: - if (!use_acm) - return -EINVAL; - break; - default: - return -EINVAL; - } - - in = choose_ep_desc(gadget, - &gs_highspeed_in_desc, - &gs_fullspeed_in_desc); - out = choose_ep_desc(gadget, - &gs_highspeed_out_desc, - &gs_fullspeed_out_desc); - notify = dev->dev_notify_ep - ? choose_ep_desc(gadget, - &gs_highspeed_notify_desc, - &gs_fullspeed_notify_desc) - : NULL; - - ret = usb_ep_enable(dev->dev_in_ep, in); - if (ret == 0) { - dev->dev_in_ep_desc = in; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "IN", dev->dev_in_ep->name, ret); - return ret; - } - - ret = usb_ep_enable(dev->dev_out_ep, out); - if (ret == 0) { - dev->dev_out_ep_desc = out; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "OUT", dev->dev_out_ep->name, ret); -fail0: - usb_ep_disable(dev->dev_in_ep); - return ret; - } - - if (notify) { - ret = usb_ep_enable(dev->dev_notify_ep, notify); - if (ret == 0) { - dev->dev_notify_ep_desc = notify; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "NOTIFY", - dev->dev_notify_ep->name, ret); - usb_ep_disable(dev->dev_out_ep); - goto fail0; - } - } - - dev->dev_config = config; - - /* allocate and queue read requests */ - ep = dev->dev_out_ep; - for (i=0; imaxpacket, GFP_ATOMIC))) { - req->complete = gs_read_complete; - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err("gs_set_config: cannot queue read " - "request, ret=%d\n", ret); - } - } else { - pr_err("gs_set_config: cannot allocate " - "read requests\n"); - ret = -ENOMEM; - goto exit_reset_config; - } - } - - /* allocate write requests, and put on free list */ - ep = dev->dev_in_ep; - for (i=0; imaxpacket, GFP_ATOMIC); - if (req) { - req->complete = gs_write_complete; - list_add(&req->list, &dev->dev_req_list); - } else { - pr_err("gs_set_config: cannot allocate " - "write requests\n"); - ret = -ENOMEM; - goto exit_reset_config; - } - } - - /* REVISIT the ACM mode should be able to actually *issue* some - * notifications, for at least serial state change events if - * not also for network connection; say so in bmCapabilities. + /* We *could* export two configs; that'd be much cleaner... + * but neither of these product IDs was defined that way. */ - - pr_info("gs_set_config: %s configured, %s speed %s config\n", - GS_LONG_NAME, - gadget->speed == USB_SPEED_HIGH ? "high" : "full", - config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); - - return 0; - -exit_reset_config: - gs_reset_config(dev); - return ret; -} - -/* - * gs_reset_config - * - * Mark the device as not configured, disable all endpoints, - * which forces completion of pending I/O and frees queued - * requests, and free the remaining write requests on the - * free list. - * - * The device lock must be held when calling this function. - */ -static void gs_reset_config(struct gs_dev *dev) -{ - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_reset_config: NULL device pointer\n"); - return; - } - - if (dev->dev_config == GS_NO_CONFIG_ID) - return; - - dev->dev_config = GS_NO_CONFIG_ID; - - /* free write requests on the free list */ - while(!list_empty(&dev->dev_req_list)) { - req = list_entry(dev->dev_req_list.next, - struct usb_request, list); - list_del(&req->list); - gs_free_req(dev->dev_in_ep, req); - } - - /* disable endpoints, forcing completion of pending i/o; */ - /* completion handlers free their requests in this case */ - if (dev->dev_notify_ep) - usb_ep_disable(dev->dev_notify_ep); - usb_ep_disable(dev->dev_in_ep); - usb_ep_disable(dev->dev_out_ep); -} - -/* - * gs_build_config_buf - * - * Builds the config descriptors in the given buffer and returns the - * length, or a negative error number. - */ -static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, - u8 type, unsigned int index, int is_otg) -{ - int len; - int high_speed = 0; - const struct usb_config_descriptor *config_desc; - const struct usb_descriptor_header **function; - - if (index >= gs_device_desc.bNumConfigurations) - return -EINVAL; - - /* other speed switches high and full speed */ - if (gadget_is_dualspeed(g)) { - high_speed = (g->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - high_speed = !high_speed; - } - if (use_acm) { - config_desc = &gs_acm_config_desc; - function = high_speed - ? gs_acm_highspeed_function - : gs_acm_fullspeed_function; + serial_config_driver.label = "CDC ACM config"; + serial_config_driver.bConfigurationValue = 2; + device_desc.bDeviceClass = USB_CLASS_COMM; + device_desc.idProduct = + __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); } else { - config_desc = &gs_bulk_config_desc; - function = high_speed - ? gs_bulk_highspeed_function - : gs_bulk_fullspeed_function; - } - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); - if (len < 0) - return len; - - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - - return len; -} - -/* - * gs_alloc_req - * - * Allocate a usb_request and its buffer. Returns a pointer to the - * usb_request or NULL if there is an error. - */ -static struct usb_request * -gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags) -{ - struct usb_request *req; - - if (ep == NULL) - return NULL; - - req = usb_ep_alloc_request(ep, kmalloc_flags); - - if (req != NULL) { - req->length = len; - req->buf = kmalloc(len, kmalloc_flags); - if (req->buf == NULL) { - usb_ep_free_request(ep, req); - return NULL; - } + serial_config_driver.label = "Generic Serial config"; + serial_config_driver.bConfigurationValue = 1; + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idProduct = + __constant_cpu_to_le16(GS_PRODUCT_ID); } + strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - return req; + return usb_composite_register(&gserial_driver); } +module_init(init); -/* - * gs_free_req - * - * Free a usb_request and its buffer. - */ -static void gs_free_req(struct usb_ep *ep, struct usb_request *req) +static void __exit cleanup(void) { - if (ep != NULL && req != NULL) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/* - * gs_alloc_ports - * - * Allocate all ports and set the gs_dev struct to point to them. - * Return 0 if successful, or a negative error number. - * - * The device lock is normally held when calling this function. - */ -static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags) -{ - int i; - struct gs_port *port; - - if (dev == NULL) - return -EIO; - - for (i=0; iport_dev = dev; - port->port_num = i; - port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); - port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; - port->port_line_coding.bParityType = GS_DEFAULT_PARITY; - port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; - spin_lock_init(&port->port_lock); - init_waitqueue_head(&port->port_write_wait); - - dev->dev_port[i] = port; - } - - return 0; -} - -/* - * gs_free_ports - * - * Free all closed ports. Open ports are disconnected by - * freeing their write buffers, setting their device pointers - * and the pointers to them in the device to NULL. These - * ports will be freed when closed. - * - * The device lock is normally held when calling this function. - */ -static void gs_free_ports(struct gs_dev *dev) -{ - int i; - unsigned long flags; - struct gs_port *port; - - if (dev == NULL) - return; - - for (i=0; idev_port[i]) != NULL) { - dev->dev_port[i] = NULL; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_write_buf != NULL) { - gs_buf_free(port->port_write_buf); - port->port_write_buf = NULL; - } - - if (port->port_open_count > 0 || port->port_in_use) { - port->port_dev = NULL; - wake_up_interruptible(&port->port_write_wait); - if (port->port_tty) { - tty_hangup(port->port_tty); - } - spin_unlock_irqrestore(&port->port_lock, flags); - } else { - spin_unlock_irqrestore(&port->port_lock, flags); - kfree(port); - } - - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* Circular Buffer */ - -/* - * gs_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) -{ - struct gs_buf *gb; - - if (size == 0) - return NULL; - - gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags); - if (gb == NULL) - return NULL; - - gb->buf_buf = kmalloc(size, kmalloc_flags); - if (gb->buf_buf == NULL) { - kfree(gb); - return NULL; - } - - gb->buf_size = size; - gb->buf_get = gb->buf_put = gb->buf_buf; - - return gb; -} - -/* - * gs_buf_free - * - * Free the buffer and all associated memory. - */ -static void gs_buf_free(struct gs_buf *gb) -{ - if (gb) { - kfree(gb->buf_buf); - kfree(gb); - } -} - -/* - * gs_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void gs_buf_clear(struct gs_buf *gb) -{ - if (gb != NULL) - gb->buf_get = gb->buf_put; - /* equivalent to a get of all data available */ -} - -/* - * gs_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ -static unsigned int gs_buf_data_avail(struct gs_buf *gb) -{ - if (gb != NULL) - return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; - else - return 0; -} - -/* - * gs_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned int gs_buf_space_avail(struct gs_buf *gb) -{ - if (gb != NULL) - return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; - else - return 0; -} - -/* - * gs_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned int -gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) -{ - unsigned int len; - - if (gb == NULL) - return 0; - - len = gs_buf_space_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_put; - if (count > len) { - memcpy(gb->buf_put, buf, len); - memcpy(gb->buf_buf, buf+len, count - len); - gb->buf_put = gb->buf_buf + count - len; - } else { - memcpy(gb->buf_put, buf, count); - if (count < len) - gb->buf_put += count; - else /* count == len */ - gb->buf_put = gb->buf_buf; - } - - return count; -} - -/* - * gs_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned int -gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) -{ - unsigned int len; - - if (gb == NULL) - return 0; - - len = gs_buf_data_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_get; - if (count > len) { - memcpy(buf, gb->buf_get, len); - memcpy(buf+len, gb->buf_buf, count - len); - gb->buf_get = gb->buf_buf + count - len; - } else { - memcpy(buf, gb->buf_get, count); - if (count < len) - gb->buf_get += count; - else /* count == len */ - gb->buf_get = gb->buf_buf; - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - -static struct tty_driver *gs_tty_driver; - -/* - * gs_module_init - * - * Register as a USB gadget driver and a tty driver. - */ -static int __init gs_module_init(void) -{ - int i; - int retval; - - retval = usb_gadget_register_driver(&gs_gadget_driver); - if (retval) { - pr_err("gs_module_init: cannot register gadget driver, " - "ret=%d\n", retval); - return retval; - } - - gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS); - if (!gs_tty_driver) - return -ENOMEM; - gs_tty_driver->owner = THIS_MODULE; - gs_tty_driver->driver_name = GS_SHORT_NAME; - gs_tty_driver->name = "ttygs"; - gs_tty_driver->major = GS_MAJOR; - gs_tty_driver->minor_start = GS_MINOR_START; - gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gs_tty_driver->init_termios = tty_std_termios; - /* must match GS_DEFAULT_DTE_RATE and friends */ - gs_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE; - gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE; - tty_set_operations(gs_tty_driver, &gs_tty_ops); - - for (i = 0; i < GS_NUM_PORTS; i++) - mutex_init(&gs_open_close_lock[i]); - - retval = tty_register_driver(gs_tty_driver); - if (retval) { - usb_gadget_unregister_driver(&gs_gadget_driver); - put_tty_driver(gs_tty_driver); - pr_err("gs_module_init: cannot register tty driver, " - "ret=%d\n", retval); - return retval; - } - - pr_info("gs_module_init: %s %s loaded\n", - GS_LONG_NAME, GS_VERSION_STR); - return 0; -} -module_init(gs_module_init); - -/* - * gs_module_exit - * - * Unregister as a tty driver and a USB gadget driver. - */ -static void __exit gs_module_exit(void) -{ - tty_unregister_driver(gs_tty_driver); - put_tty_driver(gs_tty_driver); - usb_gadget_unregister_driver(&gs_gadget_driver); - - pr_info("gs_module_exit: %s %s unloaded\n", - GS_LONG_NAME, GS_VERSION_STR); + usb_composite_unregister(&gserial_driver); + gserial_cleanup(); } -module_exit(gs_module_exit); +module_exit(cleanup); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c new file mode 100644 index 0000000..3791e62 --- /dev/null +++ b/drivers/usb/gadget/u_ether.c @@ -0,0 +1,964 @@ +/* + * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include + +#include "u_ether.h" + + +/* + * This component encapsulates the Ethernet link glue needed to provide + * one (!) network link through the USB gadget stack, normally "usb0". + * + * The control and data models are handled by the function driver which + * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. + * That includes all descriptor and endpoint management. + * + * Link level addressing is handled by this component using module + * parameters; if no such parameters are provided, random link level + * addresses are used. Each end of the link uses one address. The + * host end address is exported in various ways, and is often recorded + * in configuration databases. + * + * The driver which assembles each configuration using such a link is + * responsible for ensuring that each configuration includes at most one + * instance of is network link. (The network layer provides ways for + * this single "physical" link to be used by multiple virtual links.) + */ + +#define DRIVER_VERSION "29-May-2008" + +struct eth_dev { + /* lock is held while accessing port_usb + * or updating its backlink port_usb->ioport + */ + spinlock_t lock; + struct gether *port_usb; + + struct net_device *net; + struct usb_gadget *gadget; + + spinlock_t req_lock; /* guard {rx,tx}_reqs */ + struct list_head tx_reqs, rx_reqs; + atomic_t tx_qlen; + + unsigned header_len; + struct sk_buff *(*wrap)(struct sk_buff *skb); + int (*unwrap)(struct sk_buff *skb); + + struct work_struct work; + + unsigned long todo; +#define WORK_RX_MEMORY 0 + + bool zlp; + u8 host_mac[ETH_ALEN]; +}; + +/*-------------------------------------------------------------------------*/ + +#define RX_EXTRA 20 /* bytes guarding against rx overflows */ + +#define DEFAULT_QLEN 2 /* double buffering by default */ + + +#ifdef CONFIG_USB_GADGET_DUALSPEED + +static unsigned qmult = 5; +module_param(qmult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); + +#else /* full speed (low speed doesn't do bulk) */ +#define qmult 1 +#endif + +/* for dual-speed hardware, use deeper queues at highspeed */ +static inline int qlen(struct usb_gadget *gadget) +{ + if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) + return qmult * DEFAULT_QLEN; + else + return DEFAULT_QLEN; +} + +/*-------------------------------------------------------------------------*/ + +/* REVISIT there must be a better way than having two sets + * of debug calls ... + */ + +#undef DBG +#undef VDBG +#undef ERROR +#undef INFO + +#define xprintk(d, level, fmt, args...) \ + printk(level "%s: " fmt , (d)->net->name , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DBG(dev, fmt, args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DBG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VDBG DBG +#else +#define VDBG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + +#define ERROR(dev, fmt, args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +#define INFO(dev, fmt, args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +/*-------------------------------------------------------------------------*/ + +/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ + +static int eth_change_mtu(struct net_device *net, int new_mtu) +{ + struct eth_dev *dev = netdev_priv(net); + unsigned long flags; + int status = 0; + + /* don't change MTU on "live" link (peer won't know) */ + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + status = -EBUSY; + else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) + status = -ERANGE; + else + net->mtu = new_mtu; + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) +{ + struct eth_dev *dev = netdev_priv(net); + + strlcpy(p->driver, "g_ether", sizeof p->driver); + strlcpy(p->version, DRIVER_VERSION, sizeof p->version); + strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); + strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); +} + +static u32 eth_get_link(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + return dev->gadget->speed != USB_SPEED_UNKNOWN; +} + +/* REVISIT can also support: + * - WOL (by tracking suspends and issuing remote wakeup) + * - msglevel (implies updated messaging) + * - ... probably more ethtool ops + */ + +static struct ethtool_ops ops = { + .get_drvinfo = eth_get_drvinfo, + .get_link = eth_get_link +}; + +static void defer_kevent(struct eth_dev *dev, int flag) +{ + if (test_and_set_bit(flag, &dev->todo)) + return; + if (!schedule_work(&dev->work)) + ERROR(dev, "kevent %d may have been dropped\n", flag); + else + DBG(dev, "kevent %d scheduled\n", flag); +} + +static void rx_complete(struct usb_ep *ep, struct usb_request *req); + +static int +rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) +{ + struct sk_buff *skb; + int retval = -ENOMEM; + size_t size = 0; + struct usb_ep *out; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + out = dev->port_usb->out_ep; + else + out = NULL; + spin_unlock_irqrestore(&dev->lock, flags); + + if (!out) + return -ENOTCONN; + + + /* Padding up to RX_EXTRA handles minor disagreements with host. + * Normally we use the USB "terminate on short read" convention; + * so allow up to (N*maxpacket), since that memory is normally + * already allocated. Some hardware doesn't deal well with short + * reads (e.g. DMA must be N*maxpacket), so for now don't trim a + * byte off the end (to force hardware errors on overflow). + * + * RNDIS uses internal framing, and explicitly allows senders to + * pad to end-of-packet. That's potentially nice for speed, but + * means receivers can't recover lost synch on their own (because + * new packets don't only start after a short RX). + */ + size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA; + size += dev->port_usb->header_len; + size += out->maxpacket - 1; + size -= size % out->maxpacket; + + skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); + if (skb == NULL) { + DBG(dev, "no rx skb\n"); + goto enomem; + } + + /* Some platforms perform better when IP packets are aligned, + * but on at least one, checksumming fails otherwise. Note: + * RNDIS headers involve variable numbers of LE32 values. + */ + skb_reserve(skb, NET_IP_ALIGN); + + req->buf = skb->data; + req->length = size; + req->complete = rx_complete; + req->context = skb; + + retval = usb_ep_queue(out, req, gfp_flags); + if (retval == -ENOMEM) +enomem: + defer_kevent(dev, WORK_RX_MEMORY); + if (retval) { + DBG(dev, "rx submit --> %d\n", retval); + if (skb) + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->req_lock, flags); + list_add(&req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + } + return retval; +} + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct eth_dev *dev = ep->driver_data; + int status = req->status; + + switch (status) { + + /* normal completion */ + case 0: + skb_put(skb, req->actual); + if (dev->unwrap) + status = dev->unwrap(skb); + if (status < 0 + || ETH_HLEN > skb->len + || skb->len > ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb->len); + break; + } + + skb->protocol = eth_type_trans(skb, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb->len; + + /* no buffer copies needed, unless hardware can't + * use skb buffers. + */ + status = netif_rx(skb); + skb = NULL; + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(dev, "rx shutdown, code %d\n", status); + goto quiesce; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(dev, "rx %s reset\n", ep->name); + defer_kevent(dev, WORK_RX_MEMORY); +quiesce: + dev_kfree_skb_any(skb); + goto clean; + + /* data overrun */ + case -EOVERFLOW: + dev->net->stats.rx_over_errors++; + /* FALLTHROUGH */ + + default: + dev->net->stats.rx_errors++; + DBG(dev, "rx status %d\n", status); + break; + } + + if (skb) + dev_kfree_skb_any(skb); + if (!netif_running(dev->net)) { +clean: + spin_lock(&dev->req_lock); + list_add(&req->list, &dev->rx_reqs); + spin_unlock(&dev->req_lock); + req = NULL; + } + if (req) + rx_submit(dev, req, GFP_ATOMIC); +} + +static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) +{ + unsigned i; + struct usb_request *req; + + if (!n) + return -ENOMEM; + + /* queue/recycle up to N requests */ + i = n; + list_for_each_entry(req, list, list) { + if (i-- == 0) + goto extra; + } + while (i--) { + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return list_empty(list) ? -ENOMEM : 0; + list_add(&req->list, list); + } + return 0; + +extra: + /* free extras */ + for (;;) { + struct list_head *next; + + next = req->list.next; + list_del(&req->list); + usb_ep_free_request(ep, req); + + if (next == list) + break; + + req = container_of(next, struct usb_request, list); + } + return 0; +} + +static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n) +{ + int status; + + spin_lock(&dev->req_lock); + status = prealloc(&dev->tx_reqs, link->in_ep, n); + if (status < 0) + goto fail; + status = prealloc(&dev->rx_reqs, link->out_ep, n); + if (status < 0) + goto fail; + goto done; +fail: + DBG(dev, "can't alloc requests\n"); +done: + spin_unlock(&dev->req_lock); + return status; +} + +static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) +{ + struct usb_request *req; + unsigned long flags; + + /* fill unused rxq slots with some skb */ + spin_lock_irqsave(&dev->req_lock, flags); + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del_init(&req->list); + spin_unlock_irqrestore(&dev->req_lock, flags); + + if (rx_submit(dev, req, gfp_flags) < 0) { + defer_kevent(dev, WORK_RX_MEMORY); + return; + } + + spin_lock_irqsave(&dev->req_lock, flags); + } + spin_unlock_irqrestore(&dev->req_lock, flags); +} + +static void eth_work(struct work_struct *work) +{ + struct eth_dev *dev = container_of(work, struct eth_dev, work); + + if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) { + if (netif_running(dev->net)) + rx_fill(dev, GFP_KERNEL); + } + + if (dev->todo) + DBG(dev, "work done, flags = 0x%lx\n", dev->todo); +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct eth_dev *dev = ep->driver_data; + + switch (req->status) { + default: + dev->net->stats.tx_errors++; + VDBG(dev, "tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + dev->net->stats.tx_bytes += skb->len; + } + dev->net->stats.tx_packets++; + + spin_lock(&dev->req_lock); + list_add(&req->list, &dev->tx_reqs); + spin_unlock(&dev->req_lock); + dev_kfree_skb_any(skb); + + atomic_dec(&dev->tx_qlen); + if (netif_carrier_ok(dev->net)) + netif_wake_queue(dev->net); +} + +static inline int is_promisc(u16 cdc_filter) +{ + return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; +} + +static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + int length = skb->len; + int retval; + struct usb_request *req = NULL; + unsigned long flags; + struct usb_ep *in; + u16 cdc_filter; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + in = dev->port_usb->in_ep; + cdc_filter = dev->port_usb->cdc_filter; + } else { + in = NULL; + cdc_filter = 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + if (!in) { + dev_kfree_skb_any(skb); + return 0; + } + + /* apply outgoing CDC or RNDIS filters */ + if (!is_promisc(cdc_filter)) { + u8 *dest = skb->data; + + if (is_multicast_ether_addr(dest)) { + u16 type; + + /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host + * SET_ETHERNET_MULTICAST_FILTERS requests + */ + if (is_broadcast_ether_addr(dest)) + type = USB_CDC_PACKET_TYPE_BROADCAST; + else + type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; + if (!(cdc_filter & type)) { + dev_kfree_skb_any(skb); + return 0; + } + } + /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ + } + + spin_lock_irqsave(&dev->req_lock, flags); + /* + * this freelist can be empty if an interrupt triggered disconnect() + * and reconfigured the gadget (shutting down this queue) after the + * network stack decided to xmit but before we got the spinlock. + */ + if (list_empty(&dev->tx_reqs)) { + spin_unlock_irqrestore(&dev->req_lock, flags); + return 1; + } + + req = container_of(dev->tx_reqs.next, struct usb_request, list); + list_del(&req->list); + + /* temporarily stop TX queue when the freelist empties */ + if (list_empty(&dev->tx_reqs)) + netif_stop_queue(net); + spin_unlock_irqrestore(&dev->req_lock, flags); + + /* no buffer copies needed, unless the network stack did it + * or the hardware can't use skb buffers. + * or there's not enough space for extra headers we need + */ + if (dev->wrap) { + struct sk_buff *skb_new; + + skb_new = dev->wrap(skb); + if (!skb_new) + goto drop; + + dev_kfree_skb_any(skb); + skb = skb_new; + length = skb->len; + } + req->buf = skb->data; + req->context = skb; + req->complete = tx_complete; + + /* use zlp framing on tx for strict CDC-Ether conformance, + * though any robust network rx path ignores extra padding. + * and some hardware doesn't like to write zlps. + */ + req->zero = 1; + if (!dev->zlp && (length % in->maxpacket) == 0) + length++; + + req->length = length; + + /* throttle highspeed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget)) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) + ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) + : 0; + + retval = usb_ep_queue(in, req, GFP_ATOMIC); + switch (retval) { + default: + DBG(dev, "tx queue err %d\n", retval); + break; + case 0: + net->trans_start = jiffies; + atomic_inc(&dev->tx_qlen); + } + + if (retval) { +drop: + dev->net->stats.tx_dropped++; + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->req_lock, flags); + if (list_empty(&dev->tx_reqs)) + netif_start_queue(net); + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) +{ + DBG(dev, "%s\n", __func__); + + /* fill the rx queue */ + rx_fill(dev, gfp_flags); + + /* and open the tx floodgates */ + atomic_set(&dev->tx_qlen, 0); + netif_wake_queue(dev->net); +} + +static int eth_open(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + struct gether *link; + + DBG(dev, "%s\n", __func__); + if (netif_carrier_ok(dev->net)) + eth_start(dev, GFP_KERNEL); + + spin_lock_irq(&dev->lock); + link = dev->port_usb; + if (link && link->open) + link->open(link); + spin_unlock_irq(&dev->lock); + + return 0; +} + +static int eth_stop(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + unsigned long flags; + + VDBG(dev, "%s\n", __func__); + netif_stop_queue(net); + + DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", + dev->net->stats.rx_packets, dev->net->stats.tx_packets, + dev->net->stats.rx_errors, dev->net->stats.tx_errors + ); + + /* ensure there are no more active requests */ + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + struct gether *link = dev->port_usb; + + if (link->close) + link->close(link); + + /* NOTE: we have no abort-queue primitive we could use + * to cancel all pending I/O. Instead, we disable then + * reenable the endpoints ... this idiom may leave toggle + * wrong, but that's a self-correcting error. + * + * REVISIT: we *COULD* just let the transfers complete at + * their own pace; the network stack can handle old packets. + * For the moment we leave this here, since it works. + */ + usb_ep_disable(link->in_ep); + usb_ep_disable(link->out_ep); + if (netif_carrier_ok(net)) { + DBG(dev, "host still using in/out endpoints\n"); + usb_ep_enable(link->in_ep, link->in); + usb_ep_enable(link->out_ep, link->out); + } + } + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ +static char *dev_addr; +module_param(dev_addr, charp, S_IRUGO); +MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); + +/* this address is invisible to ifconfig */ +static char *host_addr; +module_param(host_addr, charp, S_IRUGO); +MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); + + +static u8 __init nibble(unsigned char c) +{ + if (isdigit(c)) + return c - '0'; + c = toupper(c); + if (isxdigit(c)) + return 10 + c - 'A'; + return 0; +} + +static int __init get_ether_addr(const char *str, u8 *dev_addr) +{ + if (str) { + unsigned i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if ((*str == '.') || (*str == ':')) + str++; + num = nibble(*str++) << 4; + num |= (nibble(*str++)); + dev_addr [i] = num; + } + if (is_valid_ether_addr(dev_addr)) + return 0; + } + random_ether_addr(dev_addr); + return 1; +} + +static struct eth_dev *the_dev; + + +/** + * gether_setup - initialize one ethernet-over-usb link + * @g: gadget to associated with these links + * @ethaddr: NULL, or a buffer in which the ethernet address of the + * host side of the link is recorded + * Context: may sleep + * + * This sets up the single network link that may be exported by a + * gadget driver using this framework. The link layer addresses are + * set up using module parameters. + * + * Returns negative errno, or zero on success + */ +int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +{ + struct eth_dev *dev; + struct net_device *net; + int status; + + if (the_dev) + return -EBUSY; + + net = alloc_etherdev(sizeof *dev); + if (!net) + return -ENOMEM; + + dev = netdev_priv(net); + spin_lock_init(&dev->lock); + spin_lock_init(&dev->req_lock); + INIT_WORK(&dev->work, eth_work); + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + + /* network device setup */ + dev->net = net; + strcpy(net->name, "usb%d"); + + if (get_ether_addr(dev_addr, net->dev_addr)) + dev_warn(&g->dev, + "using random %s ethernet address\n", "self"); + if (get_ether_addr(host_addr, dev->host_mac)) + dev_warn(&g->dev, + "using random %s ethernet address\n", "host"); + + if (ethaddr) + memcpy(ethaddr, dev->host_mac, ETH_ALEN); + + net->change_mtu = eth_change_mtu; + net->hard_start_xmit = eth_start_xmit; + net->open = eth_open; + net->stop = eth_stop; + /* watchdog_timeo, tx_timeout ... */ + /* set_multicast_list */ + SET_ETHTOOL_OPS(net, &ops); + + /* two kinds of host-initiated state changes: + * - iff DATA transfer is active, carrier is "on" + * - tx queueing enabled if open *and* carrier is "on" + */ + netif_stop_queue(net); + netif_carrier_off(net); + + dev->gadget = g; + SET_NETDEV_DEV(net, &g->dev); + + status = register_netdev(net); + if (status < 0) { + dev_dbg(&g->dev, "register_netdev failed, %d\n", status); + free_netdev(net); + } else { + DECLARE_MAC_BUF(tmp); + + INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr)); + INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac)); + + the_dev = dev; + } + + return status; +} + +/** + * gether_cleanup - remove Ethernet-over-USB device + * Context: may sleep + * + * This is called to free all resources allocated by @gether_setup(). + */ +void gether_cleanup(void) +{ + if (!the_dev) + return; + + unregister_netdev(the_dev->net); + free_netdev(the_dev->net); + + /* assuming we used keventd, it must quiesce too */ + flush_scheduled_work(); + + the_dev = NULL; +} + + +/** + * gether_connect - notify network layer that USB link is active + * @link: the USB link, set up with endpoints, descriptors matching + * current device speed, and any framing wrapper(s) set up. + * Context: irqs blocked + * + * This is called to activate endpoints and let the network layer know + * the connection is active ("carrier detect"). It may cause the I/O + * queues to open and start letting network packets flow, but will in + * any case activate the endpoints so that they respond properly to the + * USB host. + * + * Verify net_device pointer returned using IS_ERR(). If it doesn't + * indicate some error code (negative errno), ep->driver_data values + * have been overwritten. + */ +struct net_device *gether_connect(struct gether *link) +{ + struct eth_dev *dev = the_dev; + int result = 0; + + if (!dev) + return ERR_PTR(-EINVAL); + + link->in_ep->driver_data = dev; + result = usb_ep_enable(link->in_ep, link->in); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", + link->in_ep->name, result); + goto fail0; + } + + link->out_ep->driver_data = dev; + result = usb_ep_enable(link->out_ep, link->out); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", + link->out_ep->name, result); + goto fail1; + } + + if (result == 0) + result = alloc_requests(dev, link, qlen(dev->gadget)); + + if (result == 0) { + dev->zlp = link->is_zlp_ok; + DBG(dev, "qlen %d\n", qlen(dev->gadget)); + + dev->header_len = link->header_len; + dev->unwrap = link->unwrap; + dev->wrap = link->wrap; + + spin_lock(&dev->lock); + dev->port_usb = link; + link->ioport = dev; + spin_unlock(&dev->lock); + + netif_carrier_on(dev->net); + if (netif_running(dev->net)) + eth_start(dev, GFP_ATOMIC); + + /* on error, disable any endpoints */ + } else { + (void) usb_ep_disable(link->out_ep); +fail1: + (void) usb_ep_disable(link->in_ep); + } +fail0: + /* caller is responsible for cleanup on error */ + if (result < 0) + return ERR_PTR(result); + return dev->net; +} + +/** + * gether_disconnect - notify network layer that USB link is inactive + * @link: the USB link, on which gether_connect() was called + * Context: irqs blocked + * + * This is called to deactivate endpoints and let the network layer know + * the connection went inactive ("no carrier"). + * + * On return, the state is as if gether_connect() had never been called. + * The endpoints are inactive, and accordingly without active USB I/O. + * Pointers to endpoint descriptors and endpoint private data are nulled. + */ +void gether_disconnect(struct gether *link) +{ + struct eth_dev *dev = link->ioport; + struct usb_request *req; + + WARN_ON(!dev); + if (!dev) + return; + + DBG(dev, "%s\n", __func__); + + netif_stop_queue(dev->net); + netif_carrier_off(dev->net); + + /* disable endpoints, forcing (synchronous) completion + * of all pending i/o. then free the request objects + * and forget about the endpoints. + */ + usb_ep_disable(link->in_ep); + spin_lock(&dev->req_lock); + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, + struct usb_request, list); + list_del(&req->list); + + spin_unlock(&dev->req_lock); + usb_ep_free_request(link->in_ep, req); + spin_lock(&dev->req_lock); + } + spin_unlock(&dev->req_lock); + link->in_ep->driver_data = NULL; + link->in = NULL; + + usb_ep_disable(link->out_ep); + spin_lock(&dev->req_lock); + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + + spin_unlock(&dev->req_lock); + usb_ep_free_request(link->out_ep, req); + spin_lock(&dev->req_lock); + } + spin_unlock(&dev->req_lock); + link->out_ep->driver_data = NULL; + link->out = NULL; + + /* finish forgetting about this USB link episode */ + dev->header_len = 0; + dev->unwrap = NULL; + dev->wrap = NULL; + + spin_lock(&dev->lock); + dev->port_usb = NULL; + link->ioport = NULL; + spin_unlock(&dev->lock); +} diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h new file mode 100644 index 0000000..0d1f7ae --- /dev/null +++ b/drivers/usb/gadget/u_ether.h @@ -0,0 +1,127 @@ +/* + * u_ether.h -- interface to USB gadget "ethernet link" utilities + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __U_ETHER_H +#define __U_ETHER_H + +#include +#include +#include +#include + +#include "gadget_chips.h" + + +/* + * This represents the USB side of an "ethernet" link, managed by a USB + * function which provides control and (maybe) framing. Two functions + * in different configurations could share the same ethernet link/netdev, + * using different host interaction models. + * + * There is a current limitation that only one instance of this link may + * be present in any given configuration. When that's a problem, network + * layer facilities can be used to package multiple logical links on this + * single "physical" one. + */ +struct gether { + struct usb_function func; + + /* updated by gether_{connect,disconnect} */ + struct eth_dev *ioport; + + /* endpoints handle full and/or high speeds */ + struct usb_ep *in_ep; + struct usb_ep *out_ep; + + /* descriptors match device speed at gether_connect() time */ + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + + bool is_zlp_ok; + + u16 cdc_filter; + + /* hooks for added framing, as needed for RNDIS and EEM. + * we currently don't support multiple frames per SKB. + */ + u32 header_len; + struct sk_buff *(*wrap)(struct sk_buff *skb); + int (*unwrap)(struct sk_buff *skb); + + /* called on network open/close */ + void (*open)(struct gether *); + void (*close)(struct gether *); +}; + +#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ + |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ + |USB_CDC_PACKET_TYPE_PROMISCUOUS \ + |USB_CDC_PACKET_TYPE_DIRECTED) + + +/* netdev setup/teardown as directed by the gadget driver */ +int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); +void gether_cleanup(void); + +/* connect/disconnect is handled by individual functions */ +struct net_device *gether_connect(struct gether *); +void gether_disconnect(struct gether *); + +/* Some controllers can't support CDC Ethernet (ECM) ... */ +static inline bool can_support_ecm(struct usb_gadget *gadget) +{ + if (!gadget_supports_altsettings(gadget)) + return false; + + /* SA1100 can do ECM, *without* status endpoint ... but we'll + * only use it in non-ECM mode for backwards compatibility + * (and since we currently require a status endpoint) + */ + if (gadget_is_sa1100(gadget)) + return false; + + /* Everything else is *presumably* fine ... but this is a bit + * chancy, so be **CERTAIN** there are no hardware issues with + * your controller. Add it above if it can't handle CDC. + */ + return true; +} + +/* each configuration may bind one instance of an ethernet link */ +int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + +#ifdef CONFIG_USB_ETH_RNDIS + +int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + +#else + +static inline int +rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + return 0; +} + +#endif + +#endif /* __U_ETHER_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c new file mode 100644 index 0000000..53d5928 --- /dev/null +++ b/drivers/usb/gadget/u_serial.c @@ -0,0 +1,1330 @@ +/* + * u_serial.c - utilities for USB gadget "serial port"/TTY support + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This code also borrows from usbserial.c, which is + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2000 Peter Berger (pberger@brimson.com) + * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) + * + * 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. + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include + +#include "u_serial.h" + + +/* + * This component encapsulates the TTY layer glue needed to provide basic + * "serial port" functionality through the USB gadget stack. Each such + * port is exposed through a /dev/ttyGS* node. + * + * After initialization (gserial_setup), these TTY port devices stay + * available until they are removed (gserial_cleanup). Each one may be + * connected to a USB function (gserial_connect), or disconnected (with + * gserial_disconnect) when the USB host issues a config change event. + * Data can only flow when the port is connected to the host. + * + * A given TTY port can be made available in multiple configurations. + * For example, each one might expose a ttyGS0 node which provides a + * login application. In one case that might use CDC ACM interface 0, + * while another configuration might use interface 3 for that. The + * work to handle that (including descriptor management) is not part + * of this component. + * + * Configurations may expose more than one TTY port. For example, if + * ttyGS0 provides login service, then ttyGS1 might provide dialer access + * for a telephone or fax link. And ttyGS2 might be something that just + * needs a simple byte stream interface for some messaging protocol that + * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. + */ + +#define PREFIX "ttyGS" + +/* + * gserial is the lifecycle interface, used by USB functions + * gs_port is the I/O nexus, used by the tty driver + * tty_struct links to the tty/filesystem framework + * + * gserial <---> gs_port ... links will be null when the USB link is + * inactive; managed by gserial_{connect,disconnect}(). each gserial + * instance can wrap its own USB control protocol. + * gserial->ioport == usb_ep->driver_data ... gs_port + * gs_port->port_usb ... gserial + * + * gs_port <---> tty_struct ... links will be null when the TTY file + * isn't opened; managed by gs_open()/gs_close() + * gserial->port_tty ... tty_struct + * tty_struct->driver_data ... gserial + */ + +/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the + * next layer of buffering. For TX that's a circular buffer; for RX + * consider it a NOP. A third layer is provided by the TTY code. + */ +#define QUEUE_SIZE 16 +#define WRITE_BUF_SIZE 8192 /* TX only */ + +/* circular buffer */ +struct gs_buf { + unsigned buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + +/* + * The port structure holds info for each port, one for each minor number + * (and thus for each /dev/ node). + */ +struct gs_port { + spinlock_t port_lock; /* guard port_* access */ + + struct gserial *port_usb; + struct tty_struct *port_tty; + + unsigned open_count; + bool openclose; /* open/close in progress */ + u8 port_num; + + wait_queue_head_t close_wait; /* wait for last close */ + + struct list_head read_pool; + struct list_head read_queue; + unsigned n_read; + struct tasklet_struct push; + + struct list_head write_pool; + struct gs_buf port_write_buf; + wait_queue_head_t drain_wait; /* wait while writes drain */ + + /* REVISIT this state ... */ + struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ +}; + +/* increase N_PORTS if you need more */ +#define N_PORTS 4 +static struct portmaster { + struct mutex lock; /* protect open/close */ + struct gs_port *port; +} ports[N_PORTS]; +static unsigned n_ports; + +#define GS_CLOSE_TIMEOUT 15 /* seconds */ + + + +#ifdef VERBOSE_DEBUG +#define pr_vdebug(fmt, arg...) \ + pr_debug(fmt, ##arg) +#else +#define pr_vdebug(fmt, arg...) \ + ({ if (0) pr_debug(fmt, ##arg); }) +#endif + +/*-------------------------------------------------------------------------*/ + +/* Circular Buffer */ + +/* + * gs_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static int gs_buf_alloc(struct gs_buf *gb, unsigned size) +{ + gb->buf_buf = kmalloc(size, GFP_KERNEL); + if (gb->buf_buf == NULL) + return -ENOMEM; + + gb->buf_size = size; + gb->buf_put = gb->buf_buf; + gb->buf_get = gb->buf_buf; + + return 0; +} + +/* + * gs_buf_free + * + * Free the buffer and all associated memory. + */ +static void gs_buf_free(struct gs_buf *gb) +{ + kfree(gb->buf_buf); + gb->buf_buf = NULL; +} + +/* + * gs_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void gs_buf_clear(struct gs_buf *gb) +{ + gb->buf_get = gb->buf_put; + /* equivalent to a get of all data available */ +} + +/* + * gs_buf_data_avail + * + * Return the number of bytes of data written into the circular + * buffer. + */ +static unsigned gs_buf_data_avail(struct gs_buf *gb) +{ + return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; +} + +/* + * gs_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ +static unsigned gs_buf_space_avail(struct gs_buf *gb) +{ + return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; +} + +/* + * gs_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ +static unsigned +gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count) +{ + unsigned len; + + len = gs_buf_space_avail(gb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = gb->buf_buf + gb->buf_size - gb->buf_put; + if (count > len) { + memcpy(gb->buf_put, buf, len); + memcpy(gb->buf_buf, buf+len, count - len); + gb->buf_put = gb->buf_buf + count - len; + } else { + memcpy(gb->buf_put, buf, count); + if (count < len) + gb->buf_put += count; + else /* count == len */ + gb->buf_put = gb->buf_buf; + } + + return count; +} + +/* + * gs_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ +static unsigned +gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) +{ + unsigned len; + + len = gs_buf_data_avail(gb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = gb->buf_buf + gb->buf_size - gb->buf_get; + if (count > len) { + memcpy(buf, gb->buf_get, len); + memcpy(buf+len, gb->buf_buf, count - len); + gb->buf_get = gb->buf_buf + count - len; + } else { + memcpy(buf, gb->buf_get, count); + if (count < len) + gb->buf_get += count; + else /* count == len */ + gb->buf_get = gb->buf_buf; + } + + return count; +} + +/*-------------------------------------------------------------------------*/ + +/* I/O glue between TTY (upper) and USB function (lower) driver layers */ + +/* + * gs_alloc_req + * + * Allocate a usb_request and its buffer. Returns a pointer to the + * usb_request or NULL if there is an error. + */ +struct usb_request * +gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, kmalloc_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, kmalloc_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return NULL; + } + } + + return req; +} + +/* + * gs_free_req + * + * Free a usb_request and its buffer. + */ +void gs_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +/* + * gs_send_packet + * + * If there is data to send, a packet is built in the given + * buffer and the size is returned. If there is no data to + * send, 0 is returned. + * + * Called with port_lock held. + */ +static unsigned +gs_send_packet(struct gs_port *port, char *packet, unsigned size) +{ + unsigned len; + + len = gs_buf_data_avail(&port->port_write_buf); + if (len < size) + size = len; + if (size != 0) + size = gs_buf_get(&port->port_write_buf, packet, size); + return size; +} + +/* + * gs_start_tx + * + * This function finds available write requests, calls + * gs_send_packet to fill these packets with data, and + * continues until either there are no more write requests + * available or no more data to send. This function is + * run whenever data arrives or write requests are available. + * + * Context: caller owns port_lock; port_usb is non-null. + */ +static int gs_start_tx(struct gs_port *port) +/* +__releases(&port->port_lock) +__acquires(&port->port_lock) +*/ +{ + struct list_head *pool = &port->write_pool; + struct usb_ep *in = port->port_usb->in; + int status = 0; + bool do_tty_wake = false; + + while (!list_empty(pool)) { + struct usb_request *req; + int len; + + req = list_entry(pool->next, struct usb_request, list); + len = gs_send_packet(port, req->buf, in->maxpacket); + if (len == 0) { + wake_up_interruptible(&port->drain_wait); + break; + } + do_tty_wake = true; + + req->length = len; + list_del(&req->list); + + pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", + port->port_num, len, *((u8 *)req->buf), + *((u8 *)req->buf+1), *((u8 *)req->buf+2)); + + /* Drop lock while we call out of driver; completions + * could be issued while we do so. Disconnection may + * happen too; maybe immediately before we queue this! + * + * NOTE that we may keep sending data for a while after + * the TTY closed (dev->ioport->port_tty is NULL). + */ + spin_unlock(&port->port_lock); + status = usb_ep_queue(in, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + pr_debug("%s: %s %s err %d\n", + __func__, "queue", in->name, status); + list_add(&req->list, pool); + break; + } + + /* abort immediately after disconnect */ + if (!port->port_usb) + break; + } + + if (do_tty_wake && port->port_tty) + tty_wakeup(port->port_tty); + return status; +} + +/* + * Context: caller owns port_lock, and port_usb is set + */ +static unsigned gs_start_rx(struct gs_port *port) +/* +__releases(&port->port_lock) +__acquires(&port->port_lock) +*/ +{ + struct list_head *pool = &port->read_pool; + struct usb_ep *out = port->port_usb->out; + unsigned started = 0; + + while (!list_empty(pool)) { + struct usb_request *req; + int status; + struct tty_struct *tty; + + /* no more rx if closed */ + tty = port->port_tty; + if (!tty) + break; + + req = list_entry(pool->next, struct usb_request, list); + list_del(&req->list); + req->length = out->maxpacket; + + /* drop lock while we call out; the controller driver + * may need to call us back (e.g. for disconnect) + */ + spin_unlock(&port->port_lock); + status = usb_ep_queue(out, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + pr_debug("%s: %s %s err %d\n", + __func__, "queue", out->name, status); + list_add(&req->list, pool); + break; + } + started++; + + /* abort immediately after disconnect */ + if (!port->port_usb) + break; + } + return started; +} + +/* + * RX tasklet takes data out of the RX queue and hands it up to the TTY + * layer until it refuses to take any more data (or is throttled back). + * Then it issues reads for any further data. + * + * If the RX queue becomes full enough that no usb_request is queued, + * the OUT endpoint may begin NAKing as soon as its FIFO fills up. + * So QUEUE_SIZE packets plus however many the FIFO holds (usually two) + * can be buffered before the TTY layer's buffers (currently 64 KB). + */ +static void gs_rx_push(unsigned long _port) +{ + struct gs_port *port = (void *)_port; + struct tty_struct *tty; + struct list_head *queue = &port->read_queue; + bool disconnect = false; + bool do_push = false; + + /* hand any queued data to the tty */ + spin_lock_irq(&port->port_lock); + tty = port->port_tty; + while (!list_empty(queue)) { + struct usb_request *req; + + req = list_first_entry(queue, struct usb_request, list); + + /* discard data if tty was closed */ + if (!tty) + goto recycle; + + /* leave data queued if tty was rx throttled */ + if (test_bit(TTY_THROTTLED, &tty->flags)) + break; + + switch (req->status) { + case -ESHUTDOWN: + disconnect = true; + pr_vdebug(PREFIX "%d: shutdown\n", port->port_num); + break; + + default: + /* presumably a transient fault */ + pr_warning(PREFIX "%d: unexpected RX status %d\n", + port->port_num, req->status); + /* FALLTHROUGH */ + case 0: + /* normal completion */ + break; + } + + /* push data to (open) tty */ + if (req->actual) { + char *packet = req->buf; + unsigned size = req->actual; + unsigned n; + int count; + + /* we may have pushed part of this packet already... */ + n = port->n_read; + if (n) { + packet += n; + size -= n; + } + + count = tty_insert_flip_string(tty, packet, size); + if (count) + do_push = true; + if (count != size) { + /* stop pushing; TTY layer can't handle more */ + port->n_read += count; + pr_vdebug(PREFIX "%d: rx block %d/%d\n", + port->port_num, + count, req->actual); + break; + } + port->n_read = 0; + } +recycle: + list_move(&req->list, &port->read_pool); + } + + /* Push from tty to ldisc; this is immediate with low_latency, and + * may trigger callbacks to this driver ... so drop the spinlock. + */ + if (tty && do_push) { + spin_unlock_irq(&port->port_lock); + tty_flip_buffer_push(tty); + wake_up_interruptible(&tty->read_wait); + spin_lock_irq(&port->port_lock); + + /* tty may have been closed */ + tty = port->port_tty; + } + + + /* We want our data queue to become empty ASAP, keeping data + * in the tty and ldisc (not here). If we couldn't push any + * this time around, there may be trouble unless there's an + * implicit tty_unthrottle() call on its way... + * + * REVISIT we should probably add a timer to keep the tasklet + * from starving ... but it's not clear that case ever happens. + */ + if (!list_empty(queue) && tty) { + if (!test_bit(TTY_THROTTLED, &tty->flags)) { + if (do_push) + tasklet_schedule(&port->push); + else + pr_warning(PREFIX "%d: RX not scheduled?\n", + port->port_num); + } + } + + /* If we're still connected, refill the USB RX queue. */ + if (!disconnect && port->port_usb) + gs_start_rx(port); + + spin_unlock_irq(&port->port_lock); +} + +static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct gs_port *port = ep->driver_data; + + /* Queue all received data until the tty layer is ready for it. */ + spin_lock(&port->port_lock); + list_add_tail(&req->list, &port->read_queue); + tasklet_schedule(&port->push); + spin_unlock(&port->port_lock); +} + +static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct gs_port *port = ep->driver_data; + + spin_lock(&port->port_lock); + list_add(&req->list, &port->write_pool); + + switch (req->status) { + default: + /* presumably a transient fault */ + pr_warning("%s: unexpected %s status %d\n", + __func__, ep->name, req->status); + /* FALL THROUGH */ + case 0: + /* normal completion */ + gs_start_tx(port); + break; + + case -ESHUTDOWN: + /* disconnect */ + pr_vdebug("%s: %s shutdown\n", __func__, ep->name); + break; + } + + spin_unlock(&port->port_lock); +} + +static void gs_free_requests(struct usb_ep *ep, struct list_head *head) +{ + struct usb_request *req; + + while (!list_empty(head)) { + req = list_entry(head->next, struct usb_request, list); + list_del(&req->list); + gs_free_req(ep, req); + } +} + +static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, + void (*fn)(struct usb_ep *, struct usb_request *)) +{ + int i; + struct usb_request *req; + + /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't + * do quite that many this time, don't fail ... we just won't + * be as speedy as we might otherwise be. + */ + for (i = 0; i < QUEUE_SIZE; i++) { + req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); + if (!req) + return list_empty(head) ? -ENOMEM : 0; + req->complete = fn; + list_add_tail(&req->list, head); + } + return 0; +} + +/** + * gs_start_io - start USB I/O streams + * @dev: encapsulates endpoints to use + * Context: holding port_lock; port_tty and port_usb are non-null + * + * We only start I/O when something is connected to both sides of + * this port. If nothing is listening on the host side, we may + * be pointlessly filling up our TX buffers and FIFO. + */ +static int gs_start_io(struct gs_port *port) +{ + struct list_head *head = &port->read_pool; + struct usb_ep *ep = port->port_usb->out; + int status; + unsigned started; + + /* Allocate RX and TX I/O buffers. We can't easily do this much + * earlier (with GFP_KERNEL) because the requests are coupled to + * endpoints, as are the packet sizes we'll be using. Different + * configurations may use different endpoints with a given port; + * and high speed vs full speed changes packet sizes too. + */ + status = gs_alloc_requests(ep, head, gs_read_complete); + if (status) + return status; + + status = gs_alloc_requests(port->port_usb->in, &port->write_pool, + gs_write_complete); + if (status) { + gs_free_requests(ep, head); + return status; + } + + /* queue read requests */ + port->n_read = 0; + started = gs_start_rx(port); + + /* unblock any pending writes into our circular buffer */ + if (started) { + tty_wakeup(port->port_tty); + } else { + gs_free_requests(ep, head); + gs_free_requests(port->port_usb->in, &port->write_pool); + status = -EIO; + } + + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* TTY Driver */ + +/* + * gs_open sets up the link between a gs_port and its associated TTY. + * That link is broken *only* by TTY close(), and all driver methods + * know that. + */ +static int gs_open(struct tty_struct *tty, struct file *file) +{ + int port_num = tty->index; + struct gs_port *port; + int status; + + if (port_num < 0 || port_num >= n_ports) + return -ENXIO; + + do { + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + if (!port) + status = -ENODEV; + else { + spin_lock_irq(&port->port_lock); + + /* already open? Great. */ + if (port->open_count) { + status = 0; + port->open_count++; + + /* currently opening/closing? wait ... */ + } else if (port->openclose) { + status = -EBUSY; + + /* ... else we do the work */ + } else { + status = -EAGAIN; + port->openclose = true; + } + spin_unlock_irq(&port->port_lock); + } + mutex_unlock(&ports[port_num].lock); + + switch (status) { + default: + /* fully handled */ + return status; + case -EAGAIN: + /* must do the work */ + break; + case -EBUSY: + /* wait for EAGAIN task to finish */ + msleep(1); + /* REVISIT could have a waitchannel here, if + * concurrent open performance is important + */ + break; + } + } while (status != -EAGAIN); + + /* Do the "real open" */ + spin_lock_irq(&port->port_lock); + + /* allocate circular buffer on first open */ + if (port->port_write_buf.buf_buf == NULL) { + + spin_unlock_irq(&port->port_lock); + status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); + spin_lock_irq(&port->port_lock); + + if (status) { + pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", + port->port_num, tty, file); + port->openclose = false; + goto exit_unlock_port; + } + } + + /* REVISIT if REMOVED (ports[].port NULL), abort the open + * to let rmmod work faster (but this way isn't wrong). + */ + + /* REVISIT maybe wait for "carrier detect" */ + + tty->driver_data = port; + port->port_tty = tty; + + port->open_count = 1; + port->openclose = false; + + /* low_latency means ldiscs work in tasklet context, without + * needing a workqueue schedule ... easier to keep up. + */ + tty->low_latency = 1; + + /* if connected, start the I/O stream */ + if (port->port_usb) { + struct gserial *gser = port->port_usb; + + pr_debug("gs_open: start ttyGS%d\n", port->port_num); + gs_start_io(port); + + if (gser->connect) + gser->connect(gser); + } + + pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); + + status = 0; + +exit_unlock_port: + spin_unlock_irq(&port->port_lock); + return status; +} + +static int gs_writes_finished(struct gs_port *p) +{ + int cond; + + /* return true on disconnect or empty buffer */ + spin_lock_irq(&p->port_lock); + cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf); + spin_unlock_irq(&p->port_lock); + + return cond; +} + +static void gs_close(struct tty_struct *tty, struct file *file) +{ + struct gs_port *port = tty->driver_data; + struct gserial *gser; + + spin_lock_irq(&port->port_lock); + + if (port->open_count != 1) { + if (port->open_count == 0) + WARN_ON(1); + else + --port->open_count; + goto exit; + } + + pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); + + /* mark port as closing but in use; we can drop port lock + * and sleep if necessary + */ + port->openclose = true; + port->open_count = 0; + + gser = port->port_usb; + if (gser && gser->disconnect) + gser->disconnect(gser); + + /* wait for circular write buffer to drain, disconnect, or at + * most GS_CLOSE_TIMEOUT seconds; then discard the rest + */ + if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { + spin_unlock_irq(&port->port_lock); + wait_event_interruptible_timeout(port->drain_wait, + gs_writes_finished(port), + GS_CLOSE_TIMEOUT * HZ); + spin_lock_irq(&port->port_lock); + gser = port->port_usb; + } + + /* Iff we're disconnected, there can be no I/O in flight so it's + * ok to free the circular buffer; else just scrub it. And don't + * let the push tasklet fire again until we're re-opened. + */ + if (gser == NULL) + gs_buf_free(&port->port_write_buf); + else + gs_buf_clear(&port->port_write_buf); + + tty->driver_data = NULL; + port->port_tty = NULL; + + port->openclose = false; + + pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", + port->port_num, tty, file); + + wake_up_interruptible(&port->close_wait); +exit: + spin_unlock_irq(&port->port_lock); +} + +static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int status; + + pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n", + port->port_num, tty, count); + + spin_lock_irqsave(&port->port_lock, flags); + if (count) + count = gs_buf_put(&port->port_write_buf, buf, count); + /* treat count == 0 as flush_chars() */ + if (port->port_usb) + status = gs_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); + + return count; +} + +static int gs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int status; + + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", + port->port_num, tty, ch, __builtin_return_address(0)); + + spin_lock_irqsave(&port->port_lock, flags); + status = gs_buf_put(&port->port_write_buf, &ch, 1); + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; +} + +static void gs_flush_chars(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + + pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + gs_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int gs_write_room(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int room = 0; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + room = gs_buf_space_avail(&port->port_write_buf); + spin_unlock_irqrestore(&port->port_lock, flags); + + pr_vdebug("gs_write_room: (%d,%p) room=%d\n", + port->port_num, tty, room); + + return room; +} + +static int gs_chars_in_buffer(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int chars = 0; + + spin_lock_irqsave(&port->port_lock, flags); + chars = gs_buf_data_avail(&port->port_write_buf); + spin_unlock_irqrestore(&port->port_lock, flags); + + pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n", + port->port_num, tty, chars); + + return chars; +} + +/* undo side effects of setting TTY_THROTTLED */ +static void gs_unthrottle(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) { + /* Kickstart read queue processing. We don't do xon/xoff, + * rts/cts, or other handshaking with the host, but if the + * read queue backs up enough we'll be NAKing OUT packets. + */ + tasklet_schedule(&port->push); + pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num); + } + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int gs_break_ctl(struct tty_struct *tty, int duration) +{ + struct gs_port *port = tty->driver_data; + int status = 0; + struct gserial *gser; + + pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n", + port->port_num, duration); + + spin_lock_irq(&port->port_lock); + gser = port->port_usb; + if (gser && gser->send_break) + status = gser->send_break(gser, duration); + spin_unlock_irq(&port->port_lock); + + return status; +} + +static const struct tty_operations gs_tty_ops = { + .open = gs_open, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .unthrottle = gs_unthrottle, + .break_ctl = gs_break_ctl, +}; + +/*-------------------------------------------------------------------------*/ + +static struct tty_driver *gs_tty_driver; + +static int __init +gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) +{ + struct gs_port *port; + + port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); + if (port == NULL) + return -ENOMEM; + + spin_lock_init(&port->port_lock); + init_waitqueue_head(&port->close_wait); + init_waitqueue_head(&port->drain_wait); + + tasklet_init(&port->push, gs_rx_push, (unsigned long) port); + + INIT_LIST_HEAD(&port->read_pool); + INIT_LIST_HEAD(&port->read_queue); + INIT_LIST_HEAD(&port->write_pool); + + port->port_num = port_num; + port->port_line_coding = *coding; + + ports[port_num].port = port; + + return 0; +} + +/** + * gserial_setup - initialize TTY driver for one or more ports + * @g: gadget to associate with these ports + * @count: how many ports to support + * Context: may sleep + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports you will be + * exporting through USB. Later, connect them to functions based + * on what configuration is activated by the USB host; and disconnect + * them as appropriate. + * + * An example would be a two-configuration device in which both + * configurations expose port 0, but through different functions. + * One configuration could even expose port 1 while the other + * one doesn't. + * + * Returns negative errno or zero. + */ +int __init gserial_setup(struct usb_gadget *g, unsigned count) +{ + unsigned i; + struct usb_cdc_line_coding coding; + int status; + + if (count == 0 || count > N_PORTS) + return -EINVAL; + + gs_tty_driver = alloc_tty_driver(count); + if (!gs_tty_driver) + return -ENOMEM; + + gs_tty_driver->owner = THIS_MODULE; + gs_tty_driver->driver_name = "g_serial"; + gs_tty_driver->name = PREFIX; + /* uses dynamically assigned dev_t values */ + + gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gs_tty_driver->init_termios = tty_std_termios; + + /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on + * MS-Windows. Otherwise, most of these flags shouldn't affect + * anything unless we were to actually hook up to a serial line. + */ + gs_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + gs_tty_driver->init_termios.c_ispeed = 9600; + gs_tty_driver->init_termios.c_ospeed = 9600; + + coding.dwDTERate = __constant_cpu_to_le32(9600); + coding.bCharFormat = 8; + coding.bParityType = USB_CDC_NO_PARITY; + coding.bDataBits = USB_CDC_1_STOP_BITS; + + tty_set_operations(gs_tty_driver, &gs_tty_ops); + + /* make devices be openable */ + for (i = 0; i < count; i++) { + mutex_init(&ports[i].lock); + status = gs_port_alloc(i, &coding); + if (status) { + count = i; + goto fail; + } + } + n_ports = count; + + /* export the driver ... */ + status = tty_register_driver(gs_tty_driver); + if (status) { + put_tty_driver(gs_tty_driver); + pr_err("%s: cannot register, err %d\n", + __func__, status); + goto fail; + } + + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ + for (i = 0; i < count; i++) { + struct device *tty_dev; + + tty_dev = tty_register_device(gs_tty_driver, i, &g->dev); + if (IS_ERR(tty_dev)) + pr_warning("%s: no classdev for port %d, err %ld\n", + __func__, i, PTR_ERR(tty_dev)); + } + + pr_debug("%s: registered %d ttyGS* device%s\n", __func__, + count, (count == 1) ? "" : "s"); + + return status; +fail: + while (count--) + kfree(ports[count].port); + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; + return status; +} + +static int gs_closed(struct gs_port *port) +{ + int cond; + + spin_lock_irq(&port->port_lock); + cond = (port->open_count == 0) && !port->openclose; + spin_unlock_irq(&port->port_lock); + return cond; +} + +/** + * gserial_cleanup - remove TTY-over-USB driver and devices + * Context: may sleep + * + * This is called to free all resources allocated by @gserial_setup(). + * Accordingly, it may need to wait until some open /dev/ files have + * closed. + * + * The caller must have issued @gserial_disconnect() for any ports + * that had previously been connected, so that there is never any + * I/O pending when it's called. + */ +void gserial_cleanup(void) +{ + unsigned i; + struct gs_port *port; + + if (!gs_tty_driver) + return; + + /* start sysfs and /dev/ttyGS* node removal */ + for (i = 0; i < n_ports; i++) + tty_unregister_device(gs_tty_driver, i); + + for (i = 0; i < n_ports; i++) { + /* prevent new opens */ + mutex_lock(&ports[i].lock); + port = ports[i].port; + ports[i].port = NULL; + mutex_unlock(&ports[i].lock); + + tasklet_kill(&port->push); + + /* wait for old opens to finish */ + wait_event(port->close_wait, gs_closed(port)); + + WARN_ON(port->port_usb != NULL); + + kfree(port); + } + n_ports = 0; + + tty_unregister_driver(gs_tty_driver); + gs_tty_driver = NULL; + + pr_debug("%s: cleaned up ttyGS* support\n", __func__); +} + +/** + * gserial_connect - notify TTY I/O glue that USB link is active + * @gser: the function, set up with endpoints and descriptors + * @port_num: which port is active + * Context: any (usually from irq) + * + * This is called activate endpoints and let the TTY layer know that + * the connection is active ... not unlike "carrier detect". It won't + * necessarily start I/O queues; unless the TTY is held open by any + * task, there would be no point. However, the endpoints will be + * activated so the USB host can perform I/O, subject to basic USB + * hardware flow control. + * + * Caller needs to have set up the endpoints and USB function in @dev + * before calling this, as well as the appropriate (speed-specific) + * endpoint descriptors, and also have set up the TTY driver by calling + * @gserial_setup(). + * + * Returns negative errno or zero. + * On success, ep->driver_data will be overwritten. + */ +int gserial_connect(struct gserial *gser, u8 port_num) +{ + struct gs_port *port; + unsigned long flags; + int status; + + if (!gs_tty_driver || port_num >= n_ports) + return -ENXIO; + + /* we "know" gserial_cleanup() hasn't been called */ + port = ports[port_num].port; + + /* activate the endpoints */ + status = usb_ep_enable(gser->in, gser->in_desc); + if (status < 0) + return status; + gser->in->driver_data = port; + + status = usb_ep_enable(gser->out, gser->out_desc); + if (status < 0) + goto fail_out; + gser->out->driver_data = port; + + /* then tell the tty glue that I/O can work */ + spin_lock_irqsave(&port->port_lock, flags); + gser->ioport = port; + port->port_usb = gser; + + /* REVISIT unclear how best to handle this state... + * we don't really couple it with the Linux TTY. + */ + gser->port_line_coding = port->port_line_coding; + + /* REVISIT if waiting on "carrier detect", signal. */ + + /* if it's already open, start I/O ... and notify the serial + * protocol about open/close status (connect/disconnect). + */ + if (port->open_count) { + pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); + gs_start_io(port); + if (gser->connect) + gser->connect(gser); + } else { + if (gser->disconnect) + gser->disconnect(gser); + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; + +fail_out: + usb_ep_disable(gser->in); + gser->in->driver_data = NULL; + return status; +} + +/** + * gserial_disconnect - notify TTY I/O glue that USB link is inactive + * @gser: the function, on which gserial_connect() was called + * Context: any (usually from irq) + * + * This is called to deactivate endpoints and let the TTY layer know + * that the connection went inactive ... not unlike "hangup". + * + * On return, the state is as if gserial_connect() had never been called; + * there is no active USB I/O on these endpoints. + */ +void gserial_disconnect(struct gserial *gser) +{ + struct gs_port *port = gser->ioport; + unsigned long flags; + + if (!port) + return; + + /* tell the TTY glue not to do I/O here any more */ + spin_lock_irqsave(&port->port_lock, flags); + + /* REVISIT as above: how best to track this? */ + port->port_line_coding = gser->port_line_coding; + + port->port_usb = NULL; + gser->ioport = NULL; + if (port->open_count > 0 || port->openclose) { + wake_up_interruptible(&port->drain_wait); + if (port->port_tty) + tty_hangup(port->port_tty); + } + spin_unlock_irqrestore(&port->port_lock, flags); + + /* disable endpoints, aborting down any active I/O */ + usb_ep_disable(gser->out); + gser->out->driver_data = NULL; + + usb_ep_disable(gser->in); + gser->in->driver_data = NULL; + + /* finally, free any unused/unusable I/O buffers */ + spin_lock_irqsave(&port->port_lock, flags); + if (port->open_count == 0 && !port->openclose) + gs_buf_free(&port->port_write_buf); + gs_free_requests(gser->out, &port->read_pool); + gs_free_requests(gser->out, &port->read_queue); + gs_free_requests(gser->in, &port->write_pool); + spin_unlock_irqrestore(&port->port_lock, flags); +} diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h new file mode 100644 index 0000000..af3910d --- /dev/null +++ b/drivers/usb/gadget/u_serial.h @@ -0,0 +1,66 @@ +/* + * u_serial.h - interface to USB gadget "serial port"/TTY utilities + * + * Copyright (C) 2008 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. + */ + +#ifndef __U_SERIAL_H +#define __U_SERIAL_H + +#include +#include + +/* + * One non-multiplexed "serial" I/O port ... there can be several of these + * on any given USB peripheral device, if it provides enough endpoints. + * + * The "u_serial" utility component exists to do one thing: manage TTY + * style I/O using the USB peripheral endpoints listed here, including + * hookups to sysfs and /dev for each logical "tty" device. + * + * REVISIT at least ACM could support tiocmget() if needed. + * + * REVISIT someday, allow multiplexing several TTYs over these endpoints. + */ +struct gserial { + struct usb_function func; + + /* port is managed by gserial_{connect,disconnect} */ + struct gs_port *ioport; + + struct usb_ep *in; + struct usb_ep *out; + struct usb_endpoint_descriptor *in_desc; + struct usb_endpoint_descriptor *out_desc; + + /* REVISIT avoid this CDC-ACM support harder ... */ + struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ + + /* notification callbacks */ + void (*connect)(struct gserial *p); + void (*disconnect)(struct gserial *p); + int (*send_break)(struct gserial *p, int duration); +}; + +/* utilities to allocate/free request and buffer */ +struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); +void gs_free_req(struct usb_ep *, struct usb_request *req); + +/* port setup/teardown is handled by gadget driver */ +int gserial_setup(struct usb_gadget *g, unsigned n_ports); +void gserial_cleanup(void); + +/* connect/disconnect is handled by individual functions */ +int gserial_connect(struct gserial *, u8 port_num); +void gserial_disconnect(struct gserial *); + +/* functions are bound to configurations by a config or gadget driver */ +int acm_bind_config(struct usb_configuration *c, u8 port_num); +int gser_bind_config(struct usb_configuration *c, u8 port_num); + +#endif /* __U_SERIAL_H */ diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index fce4924..aa0bd4f 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1,8 +1,8 @@ /* * zero.c -- Gadget Zero, for USB development * - * Copyright (C) 2003-2007 David Brownell - * All rights reserved. + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,12 +30,7 @@ * * It supports two similar configurations. One sinks whatever the usb host * writes, and in return sources zeroes. The other loops whatever the host - * writes back, so the host can read it. Module options include: - * - * buflen=N default N=4096, buffer size used - * qlen=N default N=32, how many buffers in the loopback queue - * loopdefault default false, list loopback config first - * autoresume=N default N=0, seconds before triggering remote wakeup + * writes back, so the host can read it. * * Many drivers will only have one configuration, letting them be much * simpler if they also don't support high speed operation (like this @@ -47,94 +42,35 @@ * work with low capability USB controllers without four bulk endpoints. */ +/* + * driver assumes self-powered hardware, and + * has no way for users to trigger remote wakeup. + */ + /* #define VERBOSE_DEBUG */ #include #include #include -#include -#include - +#include "g_zero.h" #include "gadget_chips.h" /*-------------------------------------------------------------------------*/ -#define DRIVER_VERSION "Earth Day 2008" +#define DRIVER_VERSION "Cinco de Mayo 2008" -static const char shortname[] = "zero"; static const char longname[] = "Gadget Zero"; -static const char source_sink[] = "source and sink data"; -static const char loopback[] = "loop input to output"; - -/*-------------------------------------------------------------------------*/ - -/* - * driver assumes self-powered hardware, and - * has no way for users to trigger remote wakeup. - * - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; /* source */ -static const char *EP_OUT_NAME; /* sink */ - -/*-------------------------------------------------------------------------*/ - -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - -struct zero_dev { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - - /* when configured, we have one of two configs: - * - source data (in to host) and sink it (out from host) - * - or loop it back (out from host back in to host) - */ - u8 config; - struct usb_ep *in_ep, *out_ep; - - /* autoresume timer */ - struct timer_list resume; -}; - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - -/*-------------------------------------------------------------------------*/ - -static unsigned buflen = 4096; -static unsigned qlen = 32; -static unsigned pattern = 0; - -module_param(buflen, uint, S_IRUGO); -module_param(qlen, uint, S_IRUGO); -module_param(pattern, uint, S_IRUGO|S_IWUSR); - -/* - * if it's nonzero, autoresume says how many seconds to wait - * before trying to wake up the host after suspend. - */ -static unsigned autoresume = 0; -module_param(autoresume, uint, 0); +unsigned buflen = 4096; +module_param(buflen, uint, 0); /* * Normally the "loopback" configuration is second (index 1) so * it's not the default. Here's where to change that order, to - * work better with hosts where config changes are problematic. - * Or controllers (like superh) that only support one config. + * work better with hosts where config changes are problematic or + * controllers (like original superh) that only support one config. */ static int loopdefault = 0; module_param(loopdefault, bool, S_IRUGO|S_IWUSR); @@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR); /*-------------------------------------------------------------------------*/ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_SOURCE_SINK 250 -#define STRING_LOOPBACK 251 - -/* - * This device advertises two configurations; these numbers work - * on a pxa250 as well as more flexible hardware. - */ -#define CONFIG_SOURCE_SINK 3 -#define CONFIG_LOOPBACK 2 - static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = { .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, .bNumConfigurations = 2, }; -static struct usb_config_descriptor source_sink_config = { - .bLength = sizeof source_sink_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_SOURCE_SINK, - .iConfiguration = STRING_SOURCE_SINK, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, /* self-powered */ -}; - -static struct usb_config_descriptor loopback_config = { - .bLength = sizeof loopback_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_LOOPBACK, - .iConfiguration = STRING_LOOPBACK, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, /* self-powered */ -}; - +#ifdef CONFIG_USB_OTG static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -/* one interface in each configuration */ - -static const struct usb_interface_descriptor source_sink_intf = { - .bLength = sizeof source_sink_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .iInterface = STRING_SOURCE_SINK, -}; - -static const struct usb_interface_descriptor loopback_intf = { - .bLength = sizeof loopback_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .iInterface = STRING_LOOPBACK, -}; - -/* two full speed bulk endpoints; their use is config-dependent */ - -static struct usb_endpoint_descriptor fs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor fs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static const struct usb_descriptor_header *fs_source_sink_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &source_sink_intf, - (struct usb_descriptor_header *) &fs_sink_desc, - (struct usb_descriptor_header *) &fs_source_desc, - NULL, -}; - -static const struct usb_descriptor_header *fs_loopback_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &loopback_intf, - (struct usb_descriptor_header *) &fs_sink_desc, - (struct usb_descriptor_header *) &fs_source_desc, - NULL, -}; - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - * - * that means alternate endpoint descriptors (bigger packets) - * and a "device qualifier" ... plus more construction options - * for the config descriptor. - */ - -static struct usb_endpoint_descriptor hs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor hs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -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, - - .bNumConfigurations = 2, + /* 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 *hs_source_sink_function[] = { +const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &source_sink_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, NULL, }; +#endif -static const struct usb_descriptor_header *hs_loopback_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &loopback_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, - NULL, -}; +/* string IDs are assigned dynamically */ -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 static char manufacturer[50]; /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; - -/* static strings, in UTF-8 */ -static struct usb_string strings[] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, longname, }, - { STRING_SERIAL, serial, }, - { STRING_LOOPBACK, loopback, }, - { STRING_SOURCE_SINK, source_sink, }, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = longname, + [STRING_SERIAL_IDX].s = serial, { } /* end of list */ }; -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -/* - * config descriptors are also handcrafted. these must agree with code - * that sets configurations, and with code managing interfaces and their - * altsettings. other complexity may come from: - * - * - high speed support, including "other speed config" rules - * - multiple configurations - * - interfaces with alternate settings - * - embedded class or vendor-specific descriptors - * - * this handles high speed, and has a second config that could as easily - * have been an alternate interface setting (on most hardware). - * - * NOTE: to demonstrate (and test) more USB capabilities, this driver - * should include an altsetting to test interrupt transfers, including - * high bandwidth modes at high speed. (Maybe work like Intel's test - * device?) - */ -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int is_source_sink; - int len; - const struct usb_descriptor_header **function; - int hs = 0; - - /* two configurations will always be index 0 and index 1 */ - if (index > 1) - return -EINVAL; - is_source_sink = loopdefault ? (index == 1) : (index == 0); - - if (gadget_is_dualspeed(gadget)) { - hs = (gadget->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - } - if (hs) - function = is_source_sink - ? hs_source_sink_function - : hs_loopback_function; - else - function = is_source_sink - ? fs_source_sink_function - : fs_loopback_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(is_source_sink - ? &source_sink_config - : &loopback_config, - buf, USB_BUFSIZ, function); - if (len < 0) - return len; - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; /*-------------------------------------------------------------------------*/ -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +struct usb_request *alloc_ep_req(struct usb_ep *ep) { struct usb_request *req; req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); + req->length = buflen; + req->buf = kmalloc(buflen, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request(ep, req); req = NULL; @@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) return req; } -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +void free_ep_req(struct usb_ep *ep, struct usb_request *req) { kfree(req->buf); usb_ep_free_request(ep, req); } -/*-------------------------------------------------------------------------*/ - -/* - * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals, - * this just sinks bulk packets OUT to the peripheral and sources them IN - * to the host, optionally with specific data patterns. - * - * In terms of control messaging, this supports all the standard requests - * plus two that support control-OUT tests. - * - * Note that because this doesn't queue more than one request at a time, - * some other function must be used to test queueing logic. The network - * link (g_ether) is probably the best option for that. - */ - -/* optionally require specific source/sink data patterns */ - -static int -check_read_data( - struct zero_dev *dev, - struct usb_ep *ep, - struct usb_request *req -) +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) { - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i < req->actual; i++, buf++) { - switch (pattern) { - /* all-zeroes has no synchronization issues */ - case 0: - if (*buf == 0) - continue; - break; - /* mod63 stays in sync with short-terminated transfers, - * or otherwise when host and gadget agree on how large - * each usb transfer request should be. resync is done - * with set_interface or set_config. - */ - case 1: - if (*buf == (u8)(i % 63)) - continue; - break; - } - ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf); - usb_ep_set_halt(ep); - return -EINVAL; + int value; + + if (ep->driver_data) { + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", + ep->name, value); + ep->driver_data = NULL; } - return 0; } -static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out) { - unsigned i; - u8 *buf = req->buf; - - switch (pattern) { - case 0: - memset(req->buf, 0, req->length); - break; - case 1: - for (i = 0; i < req->length; i++) - *buf++ = (u8) (i % 63); - break; - } -} - -/* if there is only one request in the queue, there'll always be an - * irq delay between end of one request and start of the next. - * that prevents using hardware dma queues. - */ -static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct zero_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == dev->out_ep) { - check_read_data(dev, ep, req); - memset(req->buf, 0x55, req->length); - } else - reinit_write_data(ep, req); - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) - check_read_data(dev, ep, req); - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: -#if 1 - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); -#endif - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static struct usb_request *source_sink_start_ep(struct usb_ep *ep) -{ - struct usb_request *req; - int status; - - req = alloc_ep_req(ep, buflen); - if (!req) - return NULL; - - memset(req->buf, 0, req->length); - req->complete = source_sink_complete; - - if (strcmp(ep->name, EP_IN_NAME) == 0) - reinit_write_data(ep, req); - else - memset(req->buf, 0x55, req->length); - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - struct zero_dev *dev = ep->driver_data; - - ERROR(dev, "start %s --> %d\n", ep->name, status); - free_ep_req(ep, req); - req = NULL; - } - - return req; -} - -static int set_source_sink_config(struct zero_dev *dev) -{ - int result = 0; - struct usb_ep *ep; - struct usb_gadget *gadget = dev->gadget; - - gadget_for_each_ep(ep, gadget) { - const struct usb_endpoint_descriptor *d; - - /* one endpoint writes (sources) zeroes in (to the host) */ - if (strcmp(ep->name, EP_IN_NAME) == 0) { - d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - if (source_sink_start_ep(ep) != NULL) { - dev->in_ep = ep; - continue; - } - usb_ep_disable(ep); - result = -EIO; - } - - /* one endpoint reads (sinks) anything out (from the host) */ - } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - if (source_sink_start_ep(ep) != NULL) { - dev->out_ep = ep; - continue; - } - usb_ep_disable(ep); - result = -EIO; - } - - /* ignore any other endpoints */ - } else - continue; - - /* stop on error */ - ERROR(dev, "can't start %s, result %d\n", ep->name, result); - break; - } - if (result == 0) - DBG(dev, "buflen %d\n", buflen); - - /* caller is responsible for cleanup on error */ - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void loopback_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct zero_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == dev->out_ep) { - /* loop this OUT packet back IN to the host */ - req->zero = (req->actual < req->length); - req->length = req->actual; - status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC); - if (status == 0) - return; - - /* "should never get here" */ - ERROR(dev, "can't loop %s to %s: %d\n", - ep->name, dev->in_ep->name, - status); - } - - /* queue the buffer for some later OUT packet */ - req->length = buflen; - status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); - if (status == 0) - return; - - /* "should never get here" */ - /* FALLTHROUGH */ - - default: - ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - /* FALLTHROUGH */ - - /* NOTE: since this driver doesn't maintain an explicit record - * of requests it submitted (just maintains qlen count), we - * rely on the hardware driver to clean up on disconnect or - * endpoint disable. - */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - free_ep_req(ep, req); - return; - } -} - -static int set_loopback_config(struct zero_dev *dev) -{ - int result = 0; - struct usb_ep *ep; - struct usb_gadget *gadget = dev->gadget; - - gadget_for_each_ep(ep, gadget) { - const struct usb_endpoint_descriptor *d; - - /* one endpoint writes data back IN to the host */ - if (strcmp(ep->name, EP_IN_NAME) == 0) { - d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->in_ep = ep; - continue; - } - - /* one endpoint just reads OUT packets */ - } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->out_ep = ep; - continue; - } - - /* ignore any other endpoints */ - } else - continue; - - /* stop on error */ - ERROR(dev, "can't enable %s, result %d\n", ep->name, result); - break; - } - - /* allocate a bunch of read buffers and queue them all at once. - * we buffer at most 'qlen' transfers; fewer if any need more - * than 'buflen' bytes each. - */ - if (result == 0) { - struct usb_request *req; - unsigned i; - - ep = dev->out_ep; - for (i = 0; i < qlen && result == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = loopback_complete; - result = usb_ep_queue(ep, req, GFP_ATOMIC); - if (result) - DBG(dev, "%s queue req --> %d\n", - ep->name, result); - } else - result = -ENOMEM; - } - } - if (result == 0) - DBG(dev, "qlen %d, buflen %d\n", qlen, buflen); - - /* caller is responsible for cleanup on error */ - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void zero_reset_config(struct zero_dev *dev) -{ - if (dev->config == 0) - return; - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - if (dev->in_ep) { - usb_ep_disable(dev->in_ep); - dev->in_ep = NULL; - } - if (dev->out_ep) { - usb_ep_disable(dev->out_ep); - dev->out_ep = NULL; - } - dev->config = 0; - del_timer(&dev->resume); -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int zero_set_config(struct zero_dev *dev, unsigned number) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - - if (number == dev->config) - return 0; - - if (gadget_is_sa1100(gadget) && dev->config) { - /* tx fifo is full, but we can't clear it...*/ - ERROR(dev, "can't change configurations\n"); - return -ESPIPE; - } - zero_reset_config(dev); - - switch (number) { - case CONFIG_SOURCE_SINK: - result = set_source_sink_config(dev); - break; - case CONFIG_LOOPBACK: - result = set_loopback_config(dev); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) - result = -ENODEV; - if (result) - zero_reset_config(dev); - else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed config #%d: %s\n", speed, number, - (number == CONFIG_SOURCE_SINK) - ? source_sink : loopback); - } - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DBG((struct zero_dev *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int -zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) -{ - struct zero_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->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); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - goto unknown; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - value = min(w_length, (u16) sizeof device_desc); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - value = min(w_length, (u16) sizeof dev_qualifier); - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - // FALLTHROUGH - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) - value = min(w_length, (u16) value); - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) - value = min(w_length, (u16) value); - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - goto unknown; - if (gadget->a_hnp_support) - DBG(dev, "HNP available\n"); - else if (gadget->a_alt_hnp_support) - DBG(dev, "HNP needs a different root port\n"); - else - VDBG(dev, "HNP inactive\n"); - spin_lock(&dev->lock); - value = zero_set_config(dev, w_value); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - goto unknown; - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16) 1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) - goto unknown; - spin_lock(&dev->lock); - if (dev->config && w_index == 0 && w_value == 0) { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - zero_reset_config(dev); - zero_set_config(dev, config); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto unknown; - if (!dev->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); - break; - - /* - * These are the same vendor-specific requests supported by - * Intel's USB 2.0 compliance test devices. We exceed that - * device spec by allowing multiple-packet requests. - */ - case 0x5b: /* control WRITE test -- fill the buffer */ - if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) - goto unknown; - if (w_value || w_index) - break; - /* just read that many bytes into the buffer */ - if (w_length > USB_BUFSIZ) - break; - value = w_length; - break; - case 0x5c: /* control READ test -- return the buffer */ - if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) - goto unknown; - if (w_value || w_index) - break; - /* expect those bytes are still in the buffer; send back */ - if (w_length > USB_BUFSIZ - || w_length != req->length) - break; - value = w_length; - break; - - default: -unknown: - VDBG(dev, - "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - zero_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void zero_disconnect(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - zero_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void zero_autoresume(unsigned long _dev) -{ - struct zero_dev *dev = (struct zero_dev *) _dev; - int status; - - /* normally the host would be woken up for something - * more significant than just a timer firing... - */ - if (dev->gadget->speed != USB_SPEED_UNKNOWN) { - status = usb_gadget_wakeup(dev->gadget); - DBG(dev, "wakeup --> %d\n", status); - } + disable_ep(cdev, in); + disable_ep(cdev, out); } /*-------------------------------------------------------------------------*/ -static void zero_unbind(struct usb_gadget *gadget) +static int __init zero_bind(struct usb_composite_dev *cdev) { - struct zero_dev *dev = get_gadget_data(gadget); - - DBG(dev, "unbind\n"); - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - del_timer_sync(&dev->resume); - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int __init zero_bind(struct usb_gadget *gadget) -{ - struct zero_dev *dev; - struct usb_ep *ep; int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int id; - /* FIXME this can't yet work right with SH ... it has only - * one configuration, numbered one. + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. */ - if (gadget_is_sh(gadget)) - return -ENODEV; - - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + /* Register primary, then secondary configuration. Note that + * SH3 only allows one config... */ - usb_ep_autoconfig_reset(gadget); - ep = usb_ep_autoconfig(gadget, &fs_source_desc); - if (!ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; + if (loopdefault) { + loopback_add(cdev); + if (!gadget_is_sh(gadget)) + sourcesink_add(cdev); + } else { + sourcesink_add(cdev); + if (!gadget_is_sh(gadget)) + loopback_add(cdev); } - EP_IN_NAME = ep->name; - ep->driver_data = ep; /* claim */ - - ep = usb_ep_autoconfig(gadget, &fs_sink_desc); - if (!ep) - goto autoconf_fail; - EP_OUT_NAME = ep->name; - ep->driver_data = ep; /* claim */ gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) @@ -1115,144 +241,44 @@ autoconf_fail: else { /* gadget zero 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. + * so just warn about unrcognized controllers -- don't panic. * * things like configuration and altsetting numbering * can need hardware-specific attention though. */ pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); + longname, gadget->name); device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); } - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - spin_lock_init(&dev->lock); - dev->gadget = gadget; - set_gadget_data(gadget, dev); - - init_timer(&dev->resume); - dev->resume.function = zero_autoresume; - dev->resume.data = (unsigned long) dev; - - /* preallocate control response and buffer */ - dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); - if (!dev->req) - goto enomem; - dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); - if (!dev->req->buf) - goto enomem; - - dev->req->complete = zero_setup_complete; - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - - if (gadget_is_dualspeed(gadget)) { - /* assume ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; - } - - if (gadget_is_otg(gadget)) { - otg_descriptor.bmAttributes |= USB_OTG_HNP, - source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - - usb_gadget_set_selfpowered(gadget); - - if (autoresume) { - source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", init_utsname()->sysname, init_utsname()->release, gadget->name); return 0; - -enomem: - zero_unbind(gadget); - return -ENOMEM; } -/*-------------------------------------------------------------------------*/ - -static void zero_suspend(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) - return; - - if (autoresume) { - mod_timer(&dev->resume, jiffies + (HZ * autoresume)); - DBG(dev, "suspend, wakeup in %d seconds\n", autoresume); - } else - DBG(dev, "suspend\n"); -} - -static void zero_resume(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); - del_timer(&dev->resume); -} - - -/*-------------------------------------------------------------------------*/ - -static struct usb_gadget_driver zero_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *) longname, +static struct usb_composite_driver zero_driver = { + .name = "zero", + .dev = &device_desc, + .strings = dev_strings, .bind = zero_bind, - .unbind = __exit_p(zero_unbind), - - .setup = zero_setup, - .disconnect = zero_disconnect, - - .suspend = zero_suspend, - .resume = zero_resume, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - }, }; MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - static int __init init(void) { - return usb_gadget_register_driver(&zero_driver); + return usb_composite_register(&zero_driver); } module_init(init); static void __exit cleanup(void) { - usb_gadget_unregister_driver(&zero_driver); + usb_composite_unregister(&zero_driver); } module_exit(cleanup); - diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h new file mode 100644 index 0000000..c932390 --- /dev/null +++ b/include/linux/usb/composite.h @@ -0,0 +1,338 @@ +/* + * composite.h -- framework for usb gadgets which are composite devices + * + * Copyright (C) 2006-2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LINUX_USB_COMPOSITE_H +#define __LINUX_USB_COMPOSITE_H + +/* + * This framework is an optional layer on top of the USB Gadget interface, + * making it easier to build (a) Composite devices, supporting multiple + * functions within any single configuration, and (b) Multi-configuration + * devices, also supporting multiple functions but without necessarily + * having more than one function per configuration. + * + * Example: a device with a single configuration supporting both network + * link and mass storage functions is a composite device. Those functions + * might alternatively be packaged in individual configurations, but in + * the composite model the host can use both functions at the same time. + */ + +#include +#include + + +struct usb_configuration; + +/** + * struct usb_function - describes one function of a configuration + * @name: For diagnostics, identifies the function. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and by language IDs provided in control requests + * @descriptors: Table of full (or low) speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at full speed (or at low speed). + * @hs_descriptors: Table of high speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at high speed. + * @config: assigned when @usb_add_function() is called; this is the + * configuration with which this function is associated. + * @bind: Before the gadget can register, all of its functions bind() to the + * available resources including string and interface identifiers used + * in interface or class descriptors; endpoints; I/O buffers; and so on. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this function. + * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may + * initialize usb_ep.driver data at this time (when it is used). + * Note that setting an interface to its current altsetting resets + * interface state, and that all interfaces have a disabled state. + * @get_alt: Returns the active altsetting. If this is not provided, + * then only altsetting zero is supported. + * @disable: (REQUIRED) Indicates the function should be disabled. Reasons + * include host resetting or reconfiguring the gadget, and disconnection. + * @setup: Used for interface-specific control requests. + * @suspend: Notifies functions when the host stops sending USB traffic. + * @resume: Notifies functions when the host restarts USB traffic. + * + * A single USB function uses one or more interfaces, and should in most + * cases support operation at both full and high speeds. Each function is + * associated by @usb_add_function() with a one configuration; that function + * causes @bind() to be called so resources can be allocated as part of + * setting up a gadget driver. Those resources include endpoints, which + * should be allocated using @usb_ep_autoconfig(). + * + * To support dual speed operation, a function driver provides descriptors + * for both high and full speed operation. Except in rare cases that don't + * involve bulk endpoints, each speed needs different endpoint descriptors. + * + * Function drivers choose their own strategies for managing instance data. + * The simplest strategy just declares it "static', which means the function + * can only be activated once. If the function needs to be exposed in more + * than one configuration at a given speed, it needs to support multiple + * usb_function structures (one for each configuration). + * + * A more complex strategy might encapsulate a @usb_function structure inside + * a driver-specific instance structure to allows multiple activations. An + * example of multiple activations might be a CDC ACM function that supports + * two or more distinct instances within the same configuration, providing + * several independent logical data links to a USB host. + */ +struct usb_function { + const char *name; + struct usb_gadget_strings **strings; + struct usb_descriptor_header **descriptors; + struct usb_descriptor_header **hs_descriptors; + + struct usb_configuration *config; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching. + * Related: unbind() may kfree() but bind() won't... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *, + struct usb_function *); + void (*unbind)(struct usb_configuration *, + struct usb_function *); + + /* runtime state management */ + int (*set_alt)(struct usb_function *, + unsigned interface, unsigned alt); + int (*get_alt)(struct usb_function *, + unsigned interface); + void (*disable)(struct usb_function *); + int (*setup)(struct usb_function *, + const struct usb_ctrlrequest *); + void (*suspend)(struct usb_function *); + void (*resume)(struct usb_function *); + + /* internals */ + struct list_head list; +}; + +int usb_add_function(struct usb_configuration *, struct usb_function *); + +int usb_interface_id(struct usb_configuration *, struct usb_function *); + +/** + * ep_choose - select descriptor endpoint at current device speed + * @g: gadget, connected and running at some speed + * @hs: descriptor to use for high speed operation + * @fs: descriptor to use for full or low speed operation + */ +static inline struct usb_endpoint_descriptor * +ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + +/** + * struct usb_configuration - represents one gadget configuration + * @label: For diagnostics, describes the configuration. + * @strings: Tables of strings, keyed by identifiers assigned during @bind() + * and by language IDs provided in control requests. + * @descriptors: Table of descriptors preceding all function descriptors. + * Examples include OTG and vendor-specific descriptors. + * @bind: Called from @usb_add_config() to allocate resources unique to this + * configuration and to call @usb_add_function() for each function used. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this configuration. + * @setup: Used to delegate control requests that aren't handled by standard + * device infrastructure or directed at a specific interface. + * @bConfigurationValue: Copied into configuration descriptor. + * @iConfiguration: Copied into configuration descriptor. + * @bmAttributes: Copied into configuration descriptor. + * @bMaxPower: Copied into configuration descriptor. + * @cdev: assigned by @usb_add_config() before calling @bind(); this is + * the device associated with this configuration. + * + * Configurations are building blocks for gadget drivers structured around + * function drivers. Simple USB gadgets require only one function and one + * configuration, and handle dual-speed hardware by always providing the same + * functionality. Slightly more complex gadgets may have more than one + * single-function configuration at a given speed; or have configurations + * that only work at one speed. + * + * Composite devices are, by definition, ones with configurations which + * include more than one function. + * + * The lifecycle of a usb_configuration includes allocation, initialization + * of the fields described above, and calling @usb_add_config() to set up + * internal data and bind it to a specific device. The configuration's + * @bind() method is then used to initialize all the functions and then + * call @usb_add_function() for them. + * + * Those functions would normally be independant of each other, but that's + * not mandatory. CDC WMC devices are an example where functions often + * depend on other functions, with some functions subsidiary to others. + * Such interdependency may be managed in any way, so long as all of the + * descriptors complete by the time the composite driver returns from + * its bind() routine. + */ +struct usb_configuration { + const char *label; + struct usb_gadget_strings **strings; + const struct usb_descriptor_header **descriptors; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *); + void (*unbind)(struct usb_configuration *); + int (*setup)(struct usb_configuration *, + const struct usb_ctrlrequest *); + + /* fields in the config descriptor */ + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; + + struct usb_composite_dev *cdev; + + /* internals */ + struct list_head list; + struct list_head functions; + u8 next_interface_id; + unsigned highspeed:1; + unsigned fullspeed:1; + struct usb_function *interface[MAX_CONFIG_INTERFACES]; +}; + +int usb_add_config(struct usb_composite_dev *, + struct usb_configuration *); + +/** + * struct usb_composite_driver - groups configurations into a gadget + * @name: For diagnostics, identifies the driver. + * @dev: Template descriptor for the device, including default device + * identifiers. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and language IDs provided in control requests + * @bind: (REQUIRED) Used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. + * @unbind: Reverses @bind(); called as a side effect of unregistering + * this driver. + * + * Devices default to reporting self powered operation. Devices which rely + * on bus powered operation should report this in their @bind() method. + * + * Before returning from @bind, various fields in the template descriptor + * may be overridden. These include the idVendor/idProduct/bcdDevice values + * normally to bind the appropriate host side driver, and the three strings + * (iManufacturer, iProduct, iSerialNumber) normally used to provide user + * meaningful device identifiers. (The strings will not be defined unless + * they are defined in @dev and @strings.) The correct ep0 maxpacket size + * is also reported, as defined by the underlying controller driver. + */ +struct usb_composite_driver { + const char *name; + const struct usb_device_descriptor *dev; + struct usb_gadget_strings **strings; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + int (*bind)(struct usb_composite_dev *); + int (*unbind)(struct usb_composite_dev *); +}; + +extern int usb_composite_register(struct usb_composite_driver *); +extern void usb_composite_unregister(struct usb_composite_driver *); + + +/** + * struct usb_composite_device - represents one composite usb gadget + * @gadget: read-only, abstracts the gadget's usb peripheral controller + * @req: used for control responses; buffer is pre-allocated + * @bufsiz: size of buffer pre-allocated in @req + * @config: the currently active configuration + * + * One of these devices is allocated and initialized before the + * associated device driver's bind() is called. + * + * OPEN ISSUE: it appears that some WUSB devices will need to be + * built by combining a normal (wired) gadget with a wireless one. + * This revision of the gadget framework should probably try to make + * sure doing that won't hurt too much. + * + * One notion for how to handle Wireless USB devices involves: + * (a) a second gadget here, discovery mechanism TBD, but likely + * needing separate "register/unregister WUSB gadget" calls; + * (b) updates to usb_gadget to include flags "is it wireless", + * "is it wired", plus (presumably in a wrapper structure) + * bandgroup and PHY info; + * (c) presumably a wireless_ep wrapping a usb_ep, and reporting + * wireless-specific parameters like maxburst and maxsequence; + * (d) configurations that are specific to wireless links; + * (e) function drivers that understand wireless configs and will + * support wireless for (additional) function instances; + * (f) a function to support association setup (like CBAF), not + * necessarily requiring a wireless adapter; + * (g) composite device setup that can create one or more wireless + * configs, including appropriate association setup support; + * (h) more, TBD. + */ +struct usb_composite_dev { + struct usb_gadget *gadget; + struct usb_request *req; + unsigned bufsiz; + + struct usb_configuration *config; + + /* internals */ + struct usb_device_descriptor desc; + struct list_head configs; + struct usb_composite_driver *driver; + u8 next_string_id; + + spinlock_t lock; + + /* REVISIT use and existence of lock ... */ +}; + +extern int usb_string_id(struct usb_composite_dev *c); + +/* messaging utils */ +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#endif /* __LINUX_USB_COMPOSITE_H */ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h new file mode 100644 index 0000000..5b88e36 --- /dev/null +++ b/include/linux/usb/ehci_def.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_USB_EHCI_DEF_H +#define __LINUX_USB_EHCI_DEF_H + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute [8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved [9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status [0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1<<3) /* Stream disable */ +#define USBMODE_BE (1<<2) /* BE/LE endianness select */ +#define USBMODE_CM_HC (3<<0) /* host controller mode */ +#define USBMODE_CM_IDLE (0<<0) /* idle state */ + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + +#endif /* __LINUX_USB_EHCI_DEF_H */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index cf468fb..0460a74 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -33,7 +33,8 @@ struct usb_ep; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). * @complete: Function called when request completes, so this request and - * its buffer may be re-used. + * its buffer may be re-used. The function will always be called with + * interrupts disabled, and it must not sleep. * Reads terminate with a short packet, or when the buffer fills, * whichever comes first. When writes terminate, some data bytes * will usually still be in flight (often in a hardware fifo). @@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep, * (Note that some USB device controllers disallow protocol stall responses * in some cases.) When control responses are deferred (the response is * written after the setup callback returns), then usb_ep_set_halt() may be - * used on ep0 to trigger protocol stalls. + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. * * For periodic endpoints, like interrupt or isochronous ones, the usb host * arranges to poll once per interval, and the gadget driver usually will @@ -858,6 +862,25 @@ int usb_descriptor_fillbuf(void *, unsigned, int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); +/* copy a NULL-terminated vector of descriptors */ +struct usb_descriptor_header **usb_copy_descriptors( + struct usb_descriptor_header **); + +/* return copy of endpoint descriptor given original descriptor set */ +struct usb_endpoint_descriptor *usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match); + +/** + * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() + * @v: vector of descriptors + */ +static inline void usb_free_descriptors(struct usb_descriptor_header **v) +{ + kfree(v); +} + /*-------------------------------------------------------------------------*/ /* utility wrapping a simple endpoint selection policy */ diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h new file mode 100644 index 0000000..e345cea --- /dev/null +++ b/include/linux/usb/irda.h @@ -0,0 +1,151 @@ +/* + * USB IrDA Bridge Device Definition + */ + +#ifndef __LINUX_USB_IRDA_H +#define __LINUX_USB_IRDA_H + +/* This device should use Application-specific class */ + +#define USB_SUBCLASS_IRDA 0x02 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific requests (bRequest field) */ + +#define USB_REQ_CS_IRDA_RECEIVING 1 +#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3 +#define USB_REQ_CS_IRDA_RATE_SNIFF 4 +#define USB_REQ_CS_IRDA_UNICAST_LIST 5 +#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific descriptor */ + +#define USB_DT_CS_IRDA 0x21 + +/*-------------------------------------------------------------------------*/ + +/* Data sizes */ + +#define USB_IRDA_DS_2048 (1 << 5) +#define USB_IRDA_DS_1024 (1 << 4) +#define USB_IRDA_DS_512 (1 << 3) +#define USB_IRDA_DS_256 (1 << 2) +#define USB_IRDA_DS_128 (1 << 1) +#define USB_IRDA_DS_64 (1 << 0) + +/* Window sizes */ + +#define USB_IRDA_WS_7 (1 << 6) +#define USB_IRDA_WS_6 (1 << 5) +#define USB_IRDA_WS_5 (1 << 4) +#define USB_IRDA_WS_4 (1 << 3) +#define USB_IRDA_WS_3 (1 << 2) +#define USB_IRDA_WS_2 (1 << 1) +#define USB_IRDA_WS_1 (1 << 0) + +/* Min turnaround times in usecs */ + +#define USB_IRDA_MTT_0 (1 << 7) +#define USB_IRDA_MTT_10 (1 << 6) +#define USB_IRDA_MTT_50 (1 << 5) +#define USB_IRDA_MTT_100 (1 << 4) +#define USB_IRDA_MTT_500 (1 << 3) +#define USB_IRDA_MTT_1000 (1 << 2) +#define USB_IRDA_MTT_5000 (1 << 1) +#define USB_IRDA_MTT_10000 (1 << 0) + +/* Baud rates */ + +#define USB_IRDA_BR_4000000 (1 << 8) +#define USB_IRDA_BR_1152000 (1 << 7) +#define USB_IRDA_BR_576000 (1 << 6) +#define USB_IRDA_BR_115200 (1 << 5) +#define USB_IRDA_BR_57600 (1 << 4) +#define USB_IRDA_BR_38400 (1 << 3) +#define USB_IRDA_BR_19200 (1 << 2) +#define USB_IRDA_BR_9600 (1 << 1) +#define USB_IRDA_BR_2400 (1 << 0) + +/* Additional BOFs */ + +#define USB_IRDA_AB_0 (1 << 7) +#define USB_IRDA_AB_1 (1 << 6) +#define USB_IRDA_AB_2 (1 << 5) +#define USB_IRDA_AB_3 (1 << 4) +#define USB_IRDA_AB_6 (1 << 3) +#define USB_IRDA_AB_12 (1 << 2) +#define USB_IRDA_AB_24 (1 << 1) +#define USB_IRDA_AB_48 (1 << 0) + +/* IRDA Rate Sniff */ + +#define USB_IRDA_RATE_SNIFF 1 + +/*-------------------------------------------------------------------------*/ + +struct usb_irda_cs_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __le16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* Data Format */ + +#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7) + +/* The following is a 4-bit value used for both + * inbound and outbound headers: + * + * 0 - speed ignored + * 1 - 2400 bps + * 2 - 9600 bps + * 3 - 19200 bps + * 4 - 38400 bps + * 5 - 57600 bps + * 6 - 115200 bps + * 7 - 576000 bps + * 8 - 1.152 Mbps + * 9 - 5 mbps + * 10..15 - Reserved + */ +#define USB_IRDA_STATUS_LINK_SPEED 0x0f + +/* The following is a 4-bit value used only for + * outbound header: + * + * 0 - No change (BOF ignored) + * 1 - 48 BOFs + * 2 - 24 BOFs + * 3 - 12 BOFs + * 4 - 6 BOFs + * 5 - 3 BOFs + * 6 - 2 BOFs + * 7 - 1 BOFs + * 8 - 0 BOFs + * 9..15 - Reserved + */ +#define USB_IRDA_EXTRA_BOFS 0xf0 + +struct usb_irda_inbound_header { + __u8 bmStatus; +}; + +struct usb_irda_outbound_header { + __u8 bmChange; +}; + +#endif /* __LINUX_USB_IRDA_H */ + diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h new file mode 100644 index 0000000..630962c --- /dev/null +++ b/include/linux/usb/musb.h @@ -0,0 +1,98 @@ +/* + * This is used to for host and peripheral modes of the driver for + * Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC. + * + * Board initialization should put one of these into dev->platform_data, + * probably on some platform_device named "musb_hdrc". It encapsulates + * key configuration differences between boards. + */ + +/* The USB role is defined by the connector used on the board, so long as + * standards are being followed. (Developer boards sometimes won't.) + */ +enum musb_mode { + MUSB_UNDEFINED = 0, + MUSB_HOST, /* A or Mini-A connector */ + MUSB_PERIPHERAL, /* B or Mini-B connector */ + MUSB_OTG /* Mini-AB connector */ +}; + +struct clk; + +struct musb_hdrc_eps_bits { + const char name[16]; + u8 bits; +}; + +struct musb_hdrc_config { + /* MUSB configuration-specific details */ + unsigned multipoint:1; /* multipoint device */ + unsigned dyn_fifo:1; /* supports dynamic fifo sizing */ + unsigned soft_con:1; /* soft connect required */ + unsigned utm_16:1; /* utm data witdh is 16 bits */ + unsigned big_endian:1; /* true if CPU uses big-endian */ + unsigned mult_bulk_tx:1; /* Tx ep required for multbulk pkts */ + unsigned mult_bulk_rx:1; /* Rx ep required for multbulk pkts */ + unsigned high_iso_tx:1; /* Tx ep required for HB iso */ + unsigned high_iso_rx:1; /* Rx ep required for HD iso */ + unsigned dma:1; /* supports DMA */ + unsigned vendor_req:1; /* vendor registers required */ + + u8 num_eps; /* number of endpoints _with_ ep0 */ + u8 dma_channels; /* number of dma channels */ + u8 dyn_fifo_size; /* dynamic size in bytes */ + u8 vendor_ctrl; /* vendor control reg width */ + u8 vendor_stat; /* vendor status reg witdh */ + u8 dma_req_chan; /* bitmask for required dma channels */ + u8 ram_bits; /* ram address size */ + + struct musb_hdrc_eps_bits *eps_bits; +}; + +struct musb_hdrc_platform_data { + /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */ + u8 mode; + + /* for clk_get() */ + const char *clock; + + /* (HOST or OTG) switch VBUS on/off */ + int (*set_vbus)(struct device *dev, int is_on); + + /* (HOST or OTG) mA/2 power supplied on (default = 8mA) */ + u8 power; + + /* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */ + u8 min_power; + + /* (HOST or OTG) msec/2 after VBUS on till power good */ + u8 potpgt; + + /* Power the device on or off */ + int (*set_power)(int state); + + /* Turn device clock on or off */ + int (*set_clock)(struct clk *clock, int is_on); + + /* MUSB configuration-specific details */ + struct musb_hdrc_config *config; +}; + + +/* TUSB 6010 support */ + +#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */ +#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */ +#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */ + +#ifdef CONFIG_ARCH_OMAP2 + +extern int __init tusb6010_setup_interface( + struct musb_hdrc_platform_data *data, + unsigned ps_refclk, unsigned waitpin, + unsigned async_cs, unsigned sync_cs, + unsigned irq, unsigned dmachan); + +extern int tusb6010_platform_retime(unsigned is_refclk); + +#endif /* OMAP2 */ diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index 29d6458..0a6e6d4 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -260,7 +260,8 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ extern void rndis_status(struct usbnet *dev, struct urb *urb); -extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf); +extern int +rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen); extern int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags); extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8f891cb..655341d 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -17,7 +17,8 @@ #include #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ -#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ +#define SERIAL_TTY_MINORS 254 /* loads of devices :) */ +#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */ /* The maximum number of ports one device can grab at once */ #define MAX_NUM_PORTS 8 @@ -62,7 +63,7 @@ */ struct usb_serial_port { struct usb_serial *serial; - struct tty_struct *tty; + struct tty_port port; spinlock_t lock; struct mutex mutex; unsigned char number; @@ -89,7 +90,6 @@ struct usb_serial_port { wait_queue_head_t write_wait; struct work_struct work; - int open_count; char throttled; char throttle_req; char console; @@ -217,22 +217,27 @@ struct usb_serial_driver { int (*resume)(struct usb_serial *serial); /* serial function calls */ - int (*open)(struct usb_serial_port *port, struct file *filp); - void (*close)(struct usb_serial_port *port, struct file *filp); - int (*write)(struct usb_serial_port *port, const unsigned char *buf, - int count); - int (*write_room)(struct usb_serial_port *port); - int (*ioctl)(struct usb_serial_port *port, struct file *file, + /* Called by console with tty = NULL and by tty */ + int (*open)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + void (*close)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + int (*write)(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); + /* Called only by the tty layer */ + int (*write_room)(struct tty_struct *tty); + int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - void (*set_termios)(struct usb_serial_port *port, struct ktermios *old); - void (*break_ctl)(struct usb_serial_port *port, int break_state); - int (*chars_in_buffer)(struct usb_serial_port *port); - void (*throttle)(struct usb_serial_port *port); - void (*unthrottle)(struct usb_serial_port *port); - int (*tiocmget)(struct usb_serial_port *port, struct file *file); - int (*tiocmset)(struct usb_serial_port *port, struct file *file, + void (*set_termios)(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); + void (*break_ctl)(struct tty_struct *tty, int break_state); + int (*chars_in_buffer)(struct tty_struct *tty); + void (*throttle)(struct tty_struct *tty); + void (*unthrottle)(struct tty_struct *tty); + int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); - + /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); void (*read_bulk_callback)(struct urb *urb); @@ -270,19 +275,19 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} /* Functions needed by other parts of the usbserial core */ extern struct usb_serial *usb_serial_get_by_index(unsigned int minor); extern void usb_serial_put(struct usb_serial *serial); -extern int usb_serial_generic_open(struct usb_serial_port *port, - struct file *filp); -extern int usb_serial_generic_write(struct usb_serial_port *port, - const unsigned char *buf, int count); -extern void usb_serial_generic_close(struct usb_serial_port *port, - struct file *filp); +extern int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +extern int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +extern void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); extern int usb_serial_generic_resume(struct usb_serial *serial); -extern int usb_serial_generic_write_room(struct usb_serial_port *port); -extern int usb_serial_generic_chars_in_buffer(struct usb_serial_port *port); +extern int usb_serial_generic_write_room(struct tty_struct *tty); +extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty); extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); -extern void usb_serial_generic_throttle(struct usb_serial_port *port); -extern void usb_serial_generic_unthrottle(struct usb_serial_port *port); +extern void usb_serial_generic_throttle(struct tty_struct *tty); +extern void usb_serial_generic_unthrottle(struct tty_struct *tty); extern void usb_serial_generic_shutdown(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void);