diff options
Diffstat (limited to 'drivers/usb/gadget')
38 files changed, 3881 insertions, 531 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f41aa0d..01b8229 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -192,6 +192,16 @@ config USB_FUSB300 help Faraday usb device controller FUSB300 driver +config USB_FOTG210_UDC + tristate "Faraday FOTG210 USB Peripheral Controller" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". + config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 @@ -334,14 +344,6 @@ config USB_MV_U3D # Controllers available in both integrated and discrete versions # -# musb builds in ../musb along with host support -config USB_GADGET_MUSB_HDRC - tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)" - depends on USB_MUSB_HDRC - help - This OTG-capable silicon IP is used in dual designs including - the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin - config USB_M66592 tristate "Renesas M66592 USB Peripheral Controller" help @@ -507,12 +509,36 @@ config USB_F_SS_LB config USB_U_SERIAL tristate +config USB_U_ETHER + tristate + +config USB_U_RNDIS + tristate + config USB_F_SERIAL tristate config USB_F_OBEX tristate +config USB_F_NCM + tristate + +config USB_F_ECM + tristate + +config USB_F_PHONET + tristate + +config USB_F_EEM + tristate + +config USB_F_SUBSET + tristate + +config USB_F_RNDIS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -534,6 +560,121 @@ choice # this first set of drivers all depend on bulk-capable hardware. +config USB_CONFIGFS + tristate "USB functions configurable through configfs" + select USB_LIBCOMPOSITE + help + A Linux USB "gadget" can be set up through configfs. + If this is the case, the USB functions (which from the host's + perspective are seen as interfaces) and configurations are + specified simply by creating appropriate directories in configfs. + Associating functions with configurations is done by creating + appropriate symbolic links. + For more information see Documentation/usb/gadget-configfs.txt. + +config USB_CONFIGFS_SERIAL + boolean "Generic serial bulk in/out" + depends on USB_CONFIGFS + depends on TTY + select USB_U_SERIAL + select USB_F_SERIAL + help + The function talks to the Linux-USB generic serial driver. + +config USB_CONFIGFS_ACM + boolean "Abstract Control Model (CDC ACM)" + depends on USB_CONFIGFS + depends on TTY + select USB_U_SERIAL + select USB_F_ACM + help + ACM serial link. This function can be used to interoperate with + MS-Windows hosts or with the Linux-USB "cdc-acm" driver. + +config USB_CONFIGFS_OBEX + boolean "Object Exchange Model (CDC OBEX)" + depends on USB_CONFIGFS + depends on TTY + select USB_U_SERIAL + select USB_F_OBEX + help + You will need a user space OBEX server talking to /dev/ttyGS*, + since the kernel itself doesn't implement the OBEX protocol. + +config USB_CONFIGFS_NCM + boolean "Network Control Model (CDC NCM)" + depends on USB_CONFIGFS + depends on NET + select USB_U_ETHER + select USB_F_NCM + help + NCM is an advanced protocol for Ethernet encapsulation, allows + grouping of several ethernet frames into one USB transfer and + different alignment possibilities. + +config USB_CONFIGFS_ECM + boolean "Ethernet Control Model (CDC ECM)" + depends on USB_CONFIGFS + depends on NET + select USB_U_ETHER + select USB_F_ECM + help + The "Communication Device Class" (CDC) Ethernet Control Model. + That protocol is often avoided with pure Ethernet adapters, in + favor of simpler vendor-specific hardware, but is widely + supported by firmware for smart network devices. + +config USB_CONFIGFS_ECM_SUBSET + boolean "Ethernet Control Model (CDC ECM) subset" + depends on USB_CONFIGFS + depends on NET + select USB_U_ETHER + select USB_F_SUBSET + help + On hardware that can't implement the full protocol, + a simple CDC subset is used, placing fewer demands on USB. + +config USB_CONFIGFS_RNDIS + bool "RNDIS" + depends on USB_CONFIGFS + depends on NET + select USB_U_ETHER + select USB_F_RNDIS + help + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + To make MS-Windows work with this, use Documentation/usb/linux.inf + as the "driver info file". For versions of MS-Windows older than + XP, you'll need to download drivers from Microsoft's website; a URL + is given in comments found in that info file. + +config USB_CONFIGFS_EEM + bool "Ethernet Emulation Model (EEM)" + depends on USB_CONFIGFS + depends on NET + select USB_U_ETHER + select USB_F_EEM + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + +config USB_CONFIGFS_PHONET + boolean "Phonet protocol" + depends on USB_CONFIGFS + depends on NET + depends on PHONET + select USB_U_ETHER + select USB_F_PHONET + help + The Phonet protocol implementation for USB device. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE @@ -603,6 +744,10 @@ config USB_ETH tristate "Ethernet Gadget (with CDC Ethernet support)" depends on NET select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_U_RNDIS + select USB_F_ECM + select USB_F_SUBSET select CRC32 help This driver implements Ethernet style communication, in one of @@ -639,6 +784,7 @@ config USB_ETH_RNDIS bool "RNDIS support" depends on USB_ETH select USB_LIBCOMPOSITE + select USB_F_RNDIS default y help Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, @@ -658,6 +804,7 @@ config USB_ETH_EEM bool "Ethernet Emulation Model (EEM) support" depends on USB_ETH select USB_LIBCOMPOSITE + select USB_F_EEM default n help CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM @@ -675,6 +822,8 @@ config USB_G_NCM tristate "Network Control Model (NCM) support" depends on NET select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_NCM select CRC32 help This driver implements USB CDC NCM subclass standard. NCM is @@ -718,6 +867,7 @@ config USB_FUNCTIONFS config USB_FUNCTIONFS_ETH bool "Include configuration with CDC ECM (Ethernet)" depends on USB_FUNCTIONFS && NET + select USB_U_ETHER help Include a configuration with CDC ECM function (Ethernet) and the Function Filesystem. @@ -725,6 +875,8 @@ config USB_FUNCTIONFS_ETH config USB_FUNCTIONFS_RNDIS bool "Include configuration with RNDIS (Ethernet)" depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_U_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. @@ -825,7 +977,9 @@ config USB_CDC_COMPOSITE depends on NET select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_U_ETHER select USB_F_ACM + select USB_F_ECM help This driver provides two functions in one configuration: a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. @@ -842,7 +996,11 @@ config USB_G_NOKIA depends on PHONET select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_U_ETHER select USB_F_ACM + select USB_F_OBEX + select USB_F_PHONET + select USB_F_ECM help The Nokia composite gadget provides support for acm, obex and phonet in only one composite gadget driver. @@ -869,6 +1027,8 @@ config USB_G_MULTI select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_U_ETHER + select USB_U_RNDIS select USB_F_ACM help The Multifunction Composite Gadget provides Ethernet (RNDIS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6afd166..bad08e6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_MV_UDC) += mv_udc.o mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o # USB Functions @@ -45,6 +46,21 @@ usb_f_serial-y := f_serial.o obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o usb_f_obex-y := f_obex.o obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o +obj-$(CONFIG_USB_U_ETHER) += u_ether.o +u_rndis-y := rndis.o +obj-$(CONFIG_USB_U_RNDIS) += u_rndis.o +usb_f_ncm-y := f_ncm.o +obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o +usb_f_ecm-y := f_ecm.o +obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o +usb_f_phonet-y := f_phonet.o +obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o +usb_f_eem-y := f_eem.o +obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o +usb_f_ecm_subset-y := f_subset.o +obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o +usb_f_rndis-y := f_rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o # # USB gadget drivers diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 2c52551..5a5acf2 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -15,6 +15,7 @@ #include "u_ether.h" #include "u_serial.h" +#include "u_ecm.h" #define DRIVER_DESC "CDC Composite Gadget" @@ -32,18 +33,9 @@ #define CDC_VENDOR_NUM 0x0525 /* NetChip */ #define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "f_ecm.c" -#include "u_ether.c" +USB_ETHERNET_MODULE_PARAMETERS(); /*-------------------------------------------------------------------------*/ @@ -102,12 +94,13 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; -static struct eth_dev *the_dev; /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm; static struct usb_function_instance *fi_serial; +static struct usb_function *f_ecm; +static struct usb_function_instance *fi_ecm; + /* * We _always_ have both CDC ECM and CDC ACM functions. */ @@ -120,13 +113,27 @@ static int __init cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - status = ecm_bind_config(c, hostaddr, the_dev); - if (status < 0) - return status; + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) { + status = PTR_ERR(fi_ecm); + goto err_func_ecm; + } + + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) { + status = PTR_ERR(f_ecm); + goto err_get_ecm; + } + + status = usb_add_function(c, f_ecm); + if (status) + goto err_add_ecm; fi_serial = usb_get_function_instance("acm"); - if (IS_ERR(fi_serial)) - return PTR_ERR(fi_serial); + if (IS_ERR(fi_serial)) { + status = PTR_ERR(fi_serial); + goto err_get_acm; + } f_acm = usb_get_function(fi_serial); if (IS_ERR(f_acm)) { @@ -136,12 +143,21 @@ static int __init cdc_do_config(struct usb_configuration *c) status = usb_add_function(c, f_acm); if (status) - goto err_conf; + goto err_add_acm; + return 0; -err_conf: + +err_add_acm: usb_put_function(f_acm); err_func_acm: usb_put_function_instance(fi_serial); +err_get_acm: + usb_remove_function(c, f_ecm); +err_add_ecm: + usb_put_function(f_ecm); +err_get_ecm: + usb_put_function_instance(fi_ecm); +err_func_ecm: return status; } @@ -157,6 +173,7 @@ static struct usb_configuration cdc_config_driver = { static int __init cdc_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct f_ecm_opts *ecm_opts; int status; if (!can_support_ecm(cdev->gadget)) { @@ -165,10 +182,23 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return -EINVAL; } - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, hostaddr); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) + return PTR_ERR(fi_ecm); + + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + + gether_set_qmult(ecm_opts->net, qmult); + if (!gether_set_host_addr(ecm_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(ecm_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); + + fi_serial = usb_get_function_instance("acm"); + if (IS_ERR(fi_serial)) { + status = PTR_ERR(fi_serial); + goto fail; + } /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. @@ -192,7 +222,9 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return 0; fail1: - gether_cleanup(the_dev); + usb_put_function_instance(fi_serial); +fail: + usb_put_function_instance(fi_ecm); return status; } @@ -200,7 +232,10 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev) { usb_put_function(f_acm); usb_put_function_instance(fi_serial); - gether_cleanup(the_dev); + if (!IS_ERR_OR_NULL(f_ecm)) + usb_put_function(f_ecm); + if (!IS_ERR_OR_NULL(fi_ecm)) + usb_put_function_instance(fi_ecm); return 0; } diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 56c8eca..f48712f 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -14,6 +14,7 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> +#include <linux/netdevice.h> #if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS @@ -91,27 +92,23 @@ static inline bool has_rndis(void) #endif } -/*-------------------------------------------------------------------------*/ +#include <linux/module.h> -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "f_ecm.c" -#include "f_subset.c" +#include "u_ecm.h" +#include "u_gether.h" #ifdef USB_ETH_RNDIS -#include "f_rndis.c" -#include "rndis.c" +#include "u_rndis.h" +#include "rndis.h" +#else +#define rndis_borrow_net(...) do {} while (0) #endif -#include "f_eem.c" -#include "u_ether.c" +#include "u_eem.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ @@ -206,8 +203,18 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; -static struct eth_dev *the_dev; +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_ecm; + +static struct usb_function_instance *fi_eem; +static struct usb_function *f_eem; + +static struct usb_function_instance *fi_geth; +static struct usb_function *f_geth; + +static struct usb_function_instance *fi_rndis; +static struct usb_function *f_rndis; + /*-------------------------------------------------------------------------*/ /* @@ -217,6 +224,8 @@ static struct eth_dev *the_dev; */ static int __init rndis_do_config(struct usb_configuration *c) { + int status; + /* FIXME alloc iConfiguration string, set it in c->strings */ if (gadget_is_otg(c->cdev->gadget)) { @@ -224,7 +233,15 @@ static int __init rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return rndis_bind_config(c, hostaddr, the_dev); + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); + + status = usb_add_function(c, f_rndis); + if (status < 0) + usb_put_function(f_rndis); + + return status; } static struct usb_configuration rndis_config_driver = { @@ -249,6 +266,8 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); */ static int __init eth_do_config(struct usb_configuration *c) { + int status = 0; + /* FIXME alloc iConfiguration string, set it in c->strings */ if (gadget_is_otg(c->cdev->gadget)) { @@ -256,12 +275,38 @@ static int __init eth_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - if (use_eem) - return eem_bind_config(c, the_dev); - else if (can_support_ecm(c->cdev->gadget)) - return ecm_bind_config(c, hostaddr, the_dev); - else - return geth_bind_config(c, hostaddr, the_dev); + if (use_eem) { + f_eem = usb_get_function(fi_eem); + if (IS_ERR(f_eem)) + return PTR_ERR(f_eem); + + status = usb_add_function(c, f_eem); + if (status < 0) + usb_put_function(f_eem); + + return status; + } else if (can_support_ecm(c->cdev->gadget)) { + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) + return PTR_ERR(f_ecm); + + status = usb_add_function(c, f_ecm); + if (status < 0) + usb_put_function(f_ecm); + + return status; + } else { + f_geth = usb_get_function(fi_geth); + if (IS_ERR(f_geth)) + return PTR_ERR(f_geth); + + status = usb_add_function(c, f_geth); + if (status < 0) + usb_put_function(f_geth); + + return status; + } + } static struct usb_configuration eth_config_driver = { @@ -276,24 +321,50 @@ static struct usb_configuration eth_config_driver = { static int __init eth_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct f_eem_opts *eem_opts = NULL; + struct f_ecm_opts *ecm_opts = NULL; + struct f_gether_opts *geth_opts = NULL; + struct net_device *net; int status; - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, hostaddr); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); - /* set up main config label and device descriptor */ if (use_eem) { /* EEM */ + fi_eem = usb_get_function_instance("eem"); + if (IS_ERR(fi_eem)) + return PTR_ERR(fi_eem); + + eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); + + net = eem_opts->net; + eth_config_driver.label = "CDC Ethernet (EEM)"; device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM); - } else if (can_support_ecm(cdev->gadget)) { + } else if (can_support_ecm(gadget)) { /* ECM */ + + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) + return PTR_ERR(fi_ecm); + + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + + net = ecm_opts->net; + eth_config_driver.label = "CDC Ethernet (ECM)"; } else { /* CDC Subset */ + + fi_geth = usb_get_function_instance("geth"); + if (IS_ERR(fi_geth)) + return PTR_ERR(fi_geth); + + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + + net = geth_opts->net; + eth_config_driver.label = "CDC Subset/SAFE"; device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); @@ -302,8 +373,34 @@ static int __init eth_bind(struct usb_composite_dev *cdev) device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } + gether_set_qmult(net, qmult); + if (!gether_set_host_addr(net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); + if (has_rndis()) { /* RNDIS plus ECM-or-Subset */ + gether_set_gadget(net, cdev->gadget); + status = gether_register_netdev(net); + if (status) + goto fail; + + if (use_eem) + eem_opts->bound = true; + else if (can_support_ecm(gadget)) + ecm_opts->bound = true; + else + geth_opts->bound = true; + + fi_rndis = usb_get_function_instance("rndis"); + if (IS_ERR(fi_rndis)) { + status = PTR_ERR(fi_rndis); + goto fail; + } + + rndis_borrow_net(fi_rndis, net); + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); device_desc.bNumConfigurations = 2; @@ -315,7 +412,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev) status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - goto fail; + goto fail1; device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; @@ -324,12 +421,12 @@ static int __init eth_bind(struct usb_composite_dev *cdev) status = usb_add_config(cdev, &rndis_config_driver, rndis_do_config); if (status < 0) - goto fail; + goto fail1; } status = usb_add_config(cdev, ð_config_driver, eth_do_config); if (status < 0) - goto fail; + goto fail1; usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", @@ -337,14 +434,29 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; +fail1: + if (has_rndis()) + usb_put_function_instance(fi_rndis); fail: - gether_cleanup(the_dev); + if (use_eem) + usb_put_function_instance(fi_eem); + else if (can_support_ecm(gadget)) + usb_put_function_instance(fi_ecm); + else + usb_put_function_instance(fi_geth); return status; } static int __exit eth_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(the_dev); + if (has_rndis()) + usb_put_function_instance(fi_rndis); + if (use_eem) + usb_put_function_instance(fi_eem); + else if (can_support_ecm(cdev->gadget)) + usb_put_function_instance(fi_ecm); + else + usb_put_function_instance(fi_geth); return 0; } diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index abf8a31..5d3561e 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -14,10 +14,13 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include "u_ether.h" +#include "u_ether_configfs.h" +#include "u_ecm.h" /* @@ -684,9 +687,44 @@ 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); + struct usb_string *us; int status; struct usb_ep *ep; +#ifndef USBF_ECM_INCLUDED + struct f_ecm_opts *ecm_opts; + + if (!can_support_ecm(cdev->gadget)) + return -EINVAL; + + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to ecm_opts->bound access + */ + if (!ecm_opts->bound) { + mutex_lock(&ecm_opts->lock); + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + mutex_unlock(&ecm_opts->lock); + if (status) + return status; + ecm_opts->bound = true; + } +#endif + us = usb_gstrings_attach(cdev, ecm_strings, + ARRAY_SIZE(ecm_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + ecm_control_intf.iInterface = us[0].id; + ecm_data_intf.iInterface = us[2].id; + ecm_desc.iMACAddress = us[1].id; + ecm_iad_descriptor.iFunction = us[3].id; + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -796,14 +834,15 @@ fail: return status; } +#ifdef USBF_ECM_INCLUDED + static void -ecm_unbind(struct usb_configuration *c, struct usb_function *f) +ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ecm *ecm = func_to_ecm(f); DBG(c->cdev, "ecm unbind\n"); - ecm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); @@ -834,17 +873,6 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], if (!can_support_ecm(c->cdev->gadget) || !ethaddr) return -EINVAL; - if (ecm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, ecm_string_defs); - if (status) - return status; - - ecm_control_intf.iInterface = ecm_string_defs[0].id; - ecm_data_intf.iInterface = ecm_string_defs[2].id; - ecm_desc.iMACAddress = ecm_string_defs[1].id; - ecm_iad_descriptor.iFunction = ecm_string_defs[3].id; - } - /* allocate and initialize one new instance */ ecm = kzalloc(sizeof *ecm, GFP_KERNEL); if (!ecm) @@ -858,10 +886,9 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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.unbind = ecm_old_unbind; ecm->port.func.set_alt = ecm_set_alt; ecm->port.func.get_alt = ecm_get_alt; ecm->port.func.setup = ecm_setup; @@ -872,3 +899,143 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(ecm); return status; } + +#else + +static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ecm_opts, + func_inst.group); +} + +/* f_ecm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ecm); + +/* f_ecm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm); + +/* f_ecm_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm); + +/* f_ecm_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm); + +/* f_ecm_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm); + +static struct configfs_attribute *ecm_attrs[] = { + &f_ecm_opts_dev_addr.attr, + &f_ecm_opts_host_addr.attr, + &f_ecm_opts_qmult.attr, + &f_ecm_opts_ifname.attr, + NULL, +}; + +static struct config_item_type ecm_func_type = { + .ct_item_ops = &ecm_item_ops, + .ct_attrs = ecm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ecm_free_inst(struct usb_function_instance *f) +{ + struct f_ecm_opts *opts; + + opts = container_of(f, struct f_ecm_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *ecm_alloc_inst(void) +{ + struct f_ecm_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ecm_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_PTR(PTR_ERR(opts->net)); + + config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type); + + return &opts->func_inst; +} + +static void ecm_free(struct usb_function *f) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; + + ecm = func_to_ecm(f); + opts = container_of(f->fi, struct f_ecm_opts, func_inst); + kfree(ecm); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); +} + +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"); + + usb_free_all_descriptors(f); + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); +} + +struct usb_function *ecm_alloc(struct usb_function_instance *fi) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; + int status; + + /* allocate and initialize one new instance */ + ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); + if (!ecm) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_ecm_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; + + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr, + sizeof(ecm->ethaddr)); + if (status < 12) { + kfree(ecm); + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } + ecm_string_defs[1].s = ecm->ethaddr; + + ecm->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); + ecm->port.cdc_filter = DEFAULT_FILTER; + + ecm->port.func.name = "cdc_ethernet"; + /* 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; + ecm->port.func.free_func = ecm_free; + + return &ecm->port.func; +} + +DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index f4e0bbe..90ee802 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -12,12 +12,15 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/crc32.h> #include <linux/slab.h> #include "u_ether.h" +#include "u_ether_configfs.h" +#include "u_eem.h" #define EEM_HLEN 2 @@ -40,7 +43,7 @@ static inline struct f_eem *func_to_eem(struct usb_function *f) /* interface descriptor: */ -static struct usb_interface_descriptor eem_intf __initdata = { +static struct usb_interface_descriptor eem_intf = { .bLength = sizeof eem_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -54,7 +57,7 @@ static struct usb_interface_descriptor eem_intf __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = { +static struct usb_endpoint_descriptor eem_fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -62,7 +65,7 @@ static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = { +static struct usb_endpoint_descriptor eem_fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -70,7 +73,7 @@ static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *eem_fs_function[] __initdata = { +static struct usb_descriptor_header *eem_fs_function[] = { /* CDC EEM control descriptors */ (struct usb_descriptor_header *) &eem_intf, (struct usb_descriptor_header *) &eem_fs_in_desc, @@ -80,7 +83,7 @@ static struct usb_descriptor_header *eem_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = { +static struct usb_endpoint_descriptor eem_hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -89,7 +92,7 @@ static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = { +static struct usb_endpoint_descriptor eem_hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -98,7 +101,7 @@ static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *eem_hs_function[] __initdata = { +static struct usb_descriptor_header *eem_hs_function[] = { /* CDC EEM control descriptors */ (struct usb_descriptor_header *) &eem_intf, (struct usb_descriptor_header *) &eem_hs_in_desc, @@ -108,7 +111,7 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = { /* super speed support: */ -static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = { +static struct usb_endpoint_descriptor eem_ss_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -117,7 +120,7 @@ static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = { +static struct usb_endpoint_descriptor eem_ss_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -126,7 +129,7 @@ static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = { +static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = { .bLength = sizeof eem_ss_bulk_comp_desc, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -135,7 +138,7 @@ static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = { /* .bmAttributes = 0, */ }; -static struct usb_descriptor_header *eem_ss_function[] __initdata = { +static struct usb_descriptor_header *eem_ss_function[] = { /* CDC EEM control descriptors */ (struct usb_descriptor_header *) &eem_intf, (struct usb_descriptor_header *) &eem_ss_in_desc, @@ -242,14 +245,40 @@ static void eem_disable(struct usb_function *f) /* EEM function driver setup/binding */ -static int __init -eem_bind(struct usb_configuration *c, struct usb_function *f) +static int eem_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_eem *eem = func_to_eem(f); + struct usb_string *us; int status; struct usb_ep *ep; + struct f_eem_opts *eem_opts; + + eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to eem_opts->bound access + */ + if (!eem_opts->bound) { + mutex_lock(&eem_opts->lock); + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + mutex_unlock(&eem_opts->lock); + if (status) + return status; + eem_opts->bound = true; + } + + us = usb_gstrings_attach(cdev, eem_strings, + ARRAY_SIZE(eem_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + eem_intf.iInterface = us[0].id; + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -307,17 +336,6 @@ fail: return status; } -static void -eem_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_eem *eem = func_to_eem(f); - - DBG(c->cdev, "eem unbind\n"); - - usb_free_all_descriptors(f); - kfree(eem); -} - static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) { struct sk_buff *skb = (struct sk_buff *)req->context; @@ -518,55 +536,124 @@ error: return status; } -/** - * eem_bind_config - add CDC Ethernet (EEM) network link to a configuration - * @c: the configuration to support the network link - * 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 eem_bind_config(struct usb_configuration *c, struct eth_dev *dev) +static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item) { - struct f_eem *eem; - int status; + return container_of(to_config_group(item), struct f_eem_opts, + func_inst.group); +} - /* maybe allocate device-global string IDs */ - if (eem_string_defs[0].id == 0) { +/* f_eem_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(eem); - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - eem_string_defs[0].id = status; - eem_intf.iInterface = status; - } +/* f_eem_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem); + +/* f_eem_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem); + +/* f_eem_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem); + +/* f_eem_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem); + +static struct configfs_attribute *eem_attrs[] = { + &f_eem_opts_dev_addr.attr, + &f_eem_opts_host_addr.attr, + &f_eem_opts_qmult.attr, + &f_eem_opts_ifname.attr, + NULL, +}; + +static struct config_item_type eem_func_type = { + .ct_item_ops = &eem_item_ops, + .ct_attrs = eem_attrs, + .ct_owner = THIS_MODULE, +}; + +static void eem_free_inst(struct usb_function_instance *f) +{ + struct f_eem_opts *opts; + + opts = container_of(f, struct f_eem_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *eem_alloc_inst(void) +{ + struct f_eem_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = eem_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type); + + return &opts->func_inst; +} + +static void eem_free(struct usb_function *f) +{ + struct f_eem *eem; + struct f_eem_opts *opts; + + eem = func_to_eem(f); + opts = container_of(f->fi, struct f_eem_opts, func_inst); + kfree(eem); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); +} + +static void eem_unbind(struct usb_configuration *c, struct usb_function *f) +{ + DBG(c->cdev, "eem unbind\n"); + + usb_free_all_descriptors(f); +} + +struct usb_function *eem_alloc(struct usb_function_instance *fi) +{ + struct f_eem *eem; + struct f_eem_opts *opts; /* allocate and initialize one new instance */ - eem = kzalloc(sizeof *eem, GFP_KERNEL); + eem = kzalloc(sizeof(*eem), GFP_KERNEL); if (!eem) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - eem->port.ioport = dev; + opts = container_of(fi, struct f_eem_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; + + eem->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); eem->port.cdc_filter = DEFAULT_FILTER; eem->port.func.name = "cdc_eem"; - eem->port.func.strings = eem_strings; /* descriptors are per-instance copies */ eem->port.func.bind = eem_bind; eem->port.func.unbind = eem_unbind; eem->port.func.set_alt = eem_set_alt; eem->port.func.setup = eem_setup; eem->port.func.disable = eem_disable; + eem->port.func.free_func = eem_free; eem->port.wrap = eem_wrap; eem->port.unwrap = eem_unwrap; eem->port.header_len = EEM_HLEN; - status = usb_add_function(c, &eem->port.func); - if (status) - kfree(eem); - return status; + return &eem->port.func; } +DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 97666e8..56f1fd1 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -413,6 +413,7 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) /* Caller must hold fsg->lock */ static void wakeup_thread(struct fsg_common *common) { + smp_wmb(); /* ensure the write of bh->state is complete */ /* Tell the main thread that something has happened */ common->thread_wakeup_needed = 1; if (common->thread_task) @@ -632,6 +633,7 @@ static int sleep_thread(struct fsg_common *common) } __set_current_state(TASK_RUNNING); common->thread_wakeup_needed = 0; + smp_rmb(); /* ensure the latest bh->state is visible */ return rc; } @@ -2745,8 +2747,8 @@ buffhds_first_it: "%-8s%-16s%04x", cfg->vendor_name ?: "Linux", /* Assume product name dependent on the first LUN */ cfg->product_name ?: (common->luns->cdrom - ? "File-Stor Gadget" - : "File-CD Gadget"), + ? "File-CD Gadget" + : "File-Stor Gadget"), i); /* diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index ee19bc8..952177f 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -16,6 +16,7 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/crc32.h> @@ -23,6 +24,8 @@ #include <linux/usb/cdc.h> #include "u_ether.h" +#include "u_ether_configfs.h" +#include "u_ncm.h" /* * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. @@ -125,7 +128,7 @@ static struct usb_cdc_ncm_ntb_parameters ntb_parameters = { #define NCM_STATUS_INTERVAL_MS 32 #define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */ -static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = { +static struct usb_interface_assoc_descriptor ncm_iad_desc = { .bLength = sizeof ncm_iad_desc, .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, @@ -139,7 +142,7 @@ static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = { /* interface descriptor: */ -static struct usb_interface_descriptor ncm_control_intf __initdata = { +static struct usb_interface_descriptor ncm_control_intf = { .bLength = sizeof ncm_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -151,7 +154,7 @@ static struct usb_interface_descriptor ncm_control_intf __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc ncm_header_desc __initdata = { +static struct usb_cdc_header_desc ncm_header_desc = { .bLength = sizeof ncm_header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -159,7 +162,7 @@ static struct usb_cdc_header_desc ncm_header_desc __initdata = { .bcdCDC = cpu_to_le16(0x0110), }; -static struct usb_cdc_union_desc ncm_union_desc __initdata = { +static struct usb_cdc_union_desc ncm_union_desc = { .bLength = sizeof(ncm_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -167,7 +170,7 @@ static struct usb_cdc_union_desc ncm_union_desc __initdata = { /* .bSlaveInterface0 = DYNAMIC */ }; -static struct usb_cdc_ether_desc ecm_desc __initdata = { +static struct usb_cdc_ether_desc ecm_desc = { .bLength = sizeof ecm_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, @@ -182,7 +185,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = { #define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE) -static struct usb_cdc_ncm_desc ncm_desc __initdata = { +static struct usb_cdc_ncm_desc ncm_desc = { .bLength = sizeof ncm_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_NCM_TYPE, @@ -194,7 +197,7 @@ static struct usb_cdc_ncm_desc ncm_desc __initdata = { /* the default data interface has no endpoints ... */ -static struct usb_interface_descriptor ncm_data_nop_intf __initdata = { +static struct usb_interface_descriptor ncm_data_nop_intf = { .bLength = sizeof ncm_data_nop_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -209,7 +212,7 @@ static struct usb_interface_descriptor ncm_data_nop_intf __initdata = { /* ... but the "real" data interface has two bulk endpoints */ -static struct usb_interface_descriptor ncm_data_intf __initdata = { +static struct usb_interface_descriptor ncm_data_intf = { .bLength = sizeof ncm_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -224,7 +227,7 @@ static struct usb_interface_descriptor ncm_data_intf __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = { +static struct usb_endpoint_descriptor fs_ncm_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -234,7 +237,7 @@ static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = { .bInterval = NCM_STATUS_INTERVAL_MS, }; -static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_ncm_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -242,7 +245,7 @@ static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_ncm_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -250,7 +253,7 @@ static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *ncm_fs_function[] __initdata = { +static struct usb_descriptor_header *ncm_fs_function[] = { (struct usb_descriptor_header *) &ncm_iad_desc, /* CDC NCM control descriptors */ (struct usb_descriptor_header *) &ncm_control_intf, @@ -269,7 +272,7 @@ static struct usb_descriptor_header *ncm_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = { +static struct usb_endpoint_descriptor hs_ncm_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -278,7 +281,7 @@ static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = { .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT), .bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS), }; -static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = { +static struct usb_endpoint_descriptor hs_ncm_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -287,7 +290,7 @@ static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_ncm_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -296,7 +299,7 @@ static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *ncm_hs_function[] __initdata = { +static struct usb_descriptor_header *ncm_hs_function[] = { (struct usb_descriptor_header *) &ncm_iad_desc, /* CDC NCM control descriptors */ (struct usb_descriptor_header *) &ncm_control_intf, @@ -1152,13 +1155,44 @@ static void ncm_close(struct gether *geth) /* ethernet function driver setup/binding */ -static int __init -ncm_bind(struct usb_configuration *c, struct usb_function *f) +static int ncm_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_ncm *ncm = func_to_ncm(f); + struct usb_string *us; int status; struct usb_ep *ep; + struct f_ncm_opts *ncm_opts; + + if (!can_support_ecm(cdev->gadget)) + return -EINVAL; + + ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to ncm_opts->bound access + */ + if (!ncm_opts->bound) { + mutex_lock(&ncm_opts->lock); + gether_set_gadget(ncm_opts->net, cdev->gadget); + status = gether_register_netdev(ncm_opts->net); + mutex_unlock(&ncm_opts->lock); + if (status) + return status; + ncm_opts->bound = true; + } + us = usb_gstrings_attach(cdev, ncm_strings, + ARRAY_SIZE(ncm_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; + ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; + ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; + ecm_desc.iMACAddress = us[STRING_MAC_IDX].id; + ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id; /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); @@ -1259,74 +1293,128 @@ fail: return status; } -static void -ncm_unbind(struct usb_configuration *c, struct usb_function *f) +static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) { - struct f_ncm *ncm = func_to_ncm(f); + return container_of(to_config_group(item), struct f_ncm_opts, + func_inst.group); +} - DBG(c->cdev, "ncm unbind\n"); +/* f_ncm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ncm); - ncm_string_defs[0].id = 0; - usb_free_all_descriptors(f); +/* f_ncm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm); - kfree(ncm->notify_req->buf); - usb_ep_free_request(ncm->notify, ncm->notify_req); +/* f_ncm_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm); +/* f_ncm_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); + +/* f_ncm_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); + +static struct configfs_attribute *ncm_attrs[] = { + &f_ncm_opts_dev_addr.attr, + &f_ncm_opts_host_addr.attr, + &f_ncm_opts_qmult.attr, + &f_ncm_opts_ifname.attr, + NULL, +}; + +static struct config_item_type ncm_func_type = { + .ct_item_ops = &ncm_item_ops, + .ct_attrs = ncm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ncm_free_inst(struct usb_function_instance *f) +{ + struct f_ncm_opts *opts; + + opts = container_of(f, struct f_ncm_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *ncm_alloc_inst(void) +{ + struct f_ncm_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ncm_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_PTR(PTR_ERR(opts->net)); + + config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); + + return &opts->func_inst; +} + +static void ncm_free(struct usb_function *f) +{ + struct f_ncm *ncm; + struct f_ncm_opts *opts; + + ncm = func_to_ncm(f); + opts = container_of(f->fi, struct f_ncm_opts, func_inst); kfree(ncm); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); } -/** - * ncm_bind_config - add CDC 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 ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev) +static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) { - struct f_ncm *ncm; - int status; + struct f_ncm *ncm = func_to_ncm(f); - if (!can_support_ecm(c->cdev->gadget) || !ethaddr) - return -EINVAL; + DBG(c->cdev, "ncm unbind\n"); - if (ncm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, ncm_string_defs); - if (status < 0) - return status; - ncm_control_intf.iInterface = - ncm_string_defs[STRING_CTRL_IDX].id; + usb_free_all_descriptors(f); - status = ncm_string_defs[STRING_DATA_IDX].id; - ncm_data_nop_intf.iInterface = status; - ncm_data_intf.iInterface = status; + kfree(ncm->notify_req->buf); + usb_ep_free_request(ncm->notify, ncm->notify_req); +} - ecm_desc.iMACAddress = ncm_string_defs[STRING_MAC_IDX].id; - ncm_iad_desc.iFunction = ncm_string_defs[STRING_IAD_IDX].id; - } +struct usb_function *ncm_alloc(struct usb_function_instance *fi) +{ + struct f_ncm *ncm; + struct f_ncm_opts *opts; + int status; /* allocate and initialize one new instance */ - ncm = kzalloc(sizeof *ncm, GFP_KERNEL); + ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); if (!ncm) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_ncm_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; /* export host's Ethernet address in CDC format */ - snprintf(ncm->ethaddr, sizeof ncm->ethaddr, "%pm", ethaddr); + status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, + sizeof(ncm->ethaddr)); + if (status < 12) { /* strlen("01234567890a") */ + kfree(ncm); + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; spin_lock_init(&ncm->lock); ncm_reset_values(ncm); - ncm->port.ioport = dev; + ncm->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.func.name = "cdc_network"; - ncm->port.func.strings = ncm_strings; /* descriptors are per-instance copies */ ncm->port.func.bind = ncm_bind; ncm->port.func.unbind = ncm_unbind; @@ -1334,12 +1422,14 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ncm->port.func.get_alt = ncm_get_alt; ncm->port.func.setup = ncm_setup; ncm->port.func.disable = ncm_disable; + ncm->port.func.free_func = ncm_free; ncm->port.wrap = ncm_wrap_ntb; ncm->port.unwrap = ncm_unwrap_ntb; - status = usb_add_function(c, &ncm->port.func); - if (status) - kfree(ncm); - return status; + return &ncm->port.func; } + +DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yauheni Kaliuta"); diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 8aa2be5..ad39f1d 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -309,23 +309,20 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_obex *obex = func_to_obex(f); + struct usb_string *us; int status; struct usb_ep *ep; if (!can_support_obex(c)) return -EINVAL; - if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { - status = usb_string_ids_tab(c->cdev, obex_string_defs); - if (status < 0) - return status; - obex_control_intf.iInterface = - obex_string_defs[OBEX_CTRL_IDX].id; - - status = obex_string_defs[OBEX_DATA_IDX].id; - obex_data_nop_intf.iInterface = status; - obex_data_intf.iInterface = status; - } + us = usb_gstrings_attach(cdev, obex_strings, + ARRAY_SIZE(obex_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id; + obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id; + obex_data_intf.iInterface = us[OBEX_DATA_IDX].id; /* allocate instance-specific interface IDs, and patch descriptors */ @@ -406,57 +403,6 @@ fail: return status; } -#ifdef USBF_OBEX_INCLUDED - -static void -obex_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - obex_string_defs[OBEX_CTRL_IDX].id = 0; - usb_free_all_descriptors(f); - kfree(func_to_obex(f)); -} - -/** - * obex_bind_config - add a CDC OBEX function to a configuration - * @c: the configuration to support the CDC OBEX instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -int __init obex_bind_config(struct usb_configuration *c, u8 port_num) -{ - struct f_obex *obex; - int status; - - /* allocate and initialize one new instance */ - obex = kzalloc(sizeof *obex, GFP_KERNEL); - if (!obex) - return -ENOMEM; - - obex->port_num = port_num; - - obex->port.connect = obex_connect; - obex->port.disconnect = obex_disconnect; - - obex->port.func.name = "obex"; - obex->port.func.strings = obex_strings; - /* descriptors are per-instance copies */ - obex->port.func.bind = obex_bind; - obex->port.func.unbind = obex_old_unbind; - obex->port.func.set_alt = obex_set_alt; - obex->port.func.get_alt = obex_get_alt; - obex->port.func.disable = obex_disable; - - status = usb_add_function(c, &obex->port.func); - if (status) - kfree(obex); - - return status; -} - -#else - static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) { return container_of(to_config_group(item), struct f_serial_opts, @@ -550,7 +496,6 @@ static void obex_free(struct usb_function *f) static void obex_unbind(struct usb_configuration *c, struct usb_function *f) { - obex_string_defs[OBEX_CTRL_IDX].id = 0; usb_free_all_descriptors(f); } @@ -572,7 +517,6 @@ struct usb_function *obex_alloc(struct usb_function_instance *fi) obex->port.disconnect = obex_disconnect; obex->port.func.name = "obex"; - obex->port.func.strings = obex_strings; /* descriptors are per-instance copies */ obex->port.func.bind = obex_bind; obex->port.func.unbind = obex_unbind; @@ -585,8 +529,5 @@ struct usb_function *obex_alloc(struct usb_function_instance *fi) } DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); - -#endif - MODULE_AUTHOR("Felipe Balbi"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index b21ab55..7944fb0 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -13,6 +13,7 @@ #include <linux/mm.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/netdevice.h> @@ -25,6 +26,7 @@ #include <linux/usb/composite.h> #include "u_phonet.h" +#include "u_ether.h" #define PN_MEDIA_USB 0x1B #define MAXPACKET 512 @@ -478,8 +480,7 @@ static void pn_disconnect(struct usb_function *f) /*-------------------------------------------------------------------------*/ -static __init -int pn_bind(struct usb_configuration *c, struct usb_function *f) +static int pn_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct usb_gadget *gadget = cdev->gadget; @@ -487,6 +488,27 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; int status, i; +#ifndef USBF_PHONET_INCLUDED + struct f_phonet_opts *phonet_opts; + + phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to phonet_opts->bound access + */ + if (!phonet_opts->bound) { + gphonet_set_gadget(phonet_opts->net, gadget); + status = gphonet_register_netdev(phonet_opts->net); + if (status) + return status; + phonet_opts->bound = true; + } +#endif + /* Reserve interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -560,8 +582,98 @@ err: return status; } -static void -pn_unbind(struct usb_configuration *c, struct usb_function *f) +static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_phonet_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_phonet_opts); +static ssize_t f_phonet_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_phonet_opts *opts = to_f_phonet_opts(item); + struct f_phonet_opts_attribute *f_phonet_opts_attr = + container_of(attr, struct f_phonet_opts_attribute, attr); + ssize_t ret = 0; + + if (f_phonet_opts_attr->show) + ret = f_phonet_opts_attr->show(opts, page); + return ret; +} + +static void phonet_attr_release(struct config_item *item) +{ + struct f_phonet_opts *opts = to_f_phonet_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations phonet_item_ops = { + .release = phonet_attr_release, + .show_attribute = f_phonet_attr_show, +}; + +static ssize_t f_phonet_ifname_show(struct f_phonet_opts *opts, char *page) +{ + return gether_get_ifname(opts->net, page, PAGE_SIZE); +} + +static struct f_phonet_opts_attribute f_phonet_ifname = + __CONFIGFS_ATTR_RO(ifname, f_phonet_ifname_show); + +static struct configfs_attribute *phonet_attrs[] = { + &f_phonet_ifname.attr, + NULL, +}; + +static struct config_item_type phonet_func_type = { + .ct_item_ops = &phonet_item_ops, + .ct_attrs = phonet_attrs, + .ct_owner = THIS_MODULE, +}; + +static void phonet_free_inst(struct usb_function_instance *f) +{ + struct f_phonet_opts *opts; + + opts = container_of(f, struct f_phonet_opts, func_inst); + if (opts->bound) + gphonet_cleanup(opts->net); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *phonet_alloc_inst(void) +{ + struct f_phonet_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = phonet_free_inst; + opts->net = gphonet_setup_default(); + if (IS_ERR(opts->net)) + return ERR_PTR(PTR_ERR(opts->net)); + + config_group_init_type_name(&opts->func_inst.group, "", + &phonet_func_type); + + return &opts->func_inst; +} + +static void phonet_free(struct usb_function *f) +{ + struct f_phonet *phonet; + + phonet = func_to_pn(f); + kfree(phonet); +} + +static void pn_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_phonet *fp = func_to_pn(f); int i; @@ -574,61 +686,72 @@ pn_unbind(struct usb_configuration *c, struct usb_function *f) usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); usb_free_all_descriptors(f); - kfree(fp); } -/*-------------------------------------------------------------------------*/ - -static struct net_device *dev; - -int __init phonet_bind_config(struct usb_configuration *c) +struct usb_function *phonet_alloc(struct usb_function_instance *fi) { struct f_phonet *fp; - int err, size; + struct f_phonet_opts *opts; + int size; size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *)); fp = kzalloc(size, GFP_KERNEL); if (!fp) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_phonet_opts, func_inst); - fp->dev = dev; + fp->dev = opts->net; fp->function.name = "phonet"; fp->function.bind = pn_bind; fp->function.unbind = pn_unbind; fp->function.set_alt = pn_set_alt; fp->function.get_alt = pn_get_alt; fp->function.disable = pn_disconnect; + fp->function.free_func = phonet_free; spin_lock_init(&fp->rx.lock); - err = usb_add_function(c, &fp->function); - if (err) - kfree(fp); - return err; + return &fp->function; } -int __init gphonet_setup(struct usb_gadget *gadget) +struct net_device *gphonet_setup_default(void) { + struct net_device *dev; struct phonet_port *port; - int err; /* Create net device */ - BUG_ON(dev); dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); if (!dev) - return -ENOMEM; + return ERR_PTR(-ENOMEM); port = netdev_priv(dev); spin_lock_init(&port->lock); netif_carrier_off(dev); - SET_NETDEV_DEV(dev, &gadget->dev); - err = register_netdev(dev); - if (err) - free_netdev(dev); - return err; + return dev; +} + +void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g) +{ + SET_NETDEV_DEV(net, &g->dev); +} + +int gphonet_register_netdev(struct net_device *net) +{ + int status; + + status = register_netdev(net); + if (status) + free_netdev(net); + + return status; } -void gphonet_cleanup(void) +void gphonet_cleanup(struct net_device *dev) { unregister_netdev(dev); } + +DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc); +MODULE_AUTHOR("Rémi Denis-Courmont"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 36e8c44..191df35 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -17,15 +17,17 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/atomic.h> #include "u_ether.h" +#include "u_ether_configfs.h" +#include "u_rndis.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 @@ -655,6 +657,13 @@ static void rndis_close(struct gether *geth) /*-------------------------------------------------------------------------*/ +/* Some controllers can't support RNDIS ... */ +static inline bool can_support_rndis(struct usb_configuration *c) +{ + /* everything else is *presumably* fine */ + return true; +} + /* ethernet function driver setup/binding */ static int @@ -662,9 +671,41 @@ 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); + struct usb_string *us; int status; struct usb_ep *ep; +#ifndef USB_FRNDIS_INCLUDED + struct f_rndis_opts *rndis_opts; + + if (!can_support_rndis(c)) + return -EINVAL; + + rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to rndis_opts->bound access + */ + if (!rndis_opts->bound) { + gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); + if (status) + return status; + rndis_opts->bound = true; + } +#endif + us = usb_gstrings_attach(cdev, rndis_strings, + ARRAY_SIZE(rndis_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + rndis_control_intf.iInterface = us[0].id; + rndis_data_intf.iInterface = us[1].id; + rndis_iad_descriptor.iFunction = us[2].id; + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -741,10 +782,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; +#ifdef USB_FRNDIS_INCLUDED status = rndis_register(rndis_response_available, rndis); if (status < 0) goto fail; rndis->config = status; +#endif rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); @@ -787,15 +830,15 @@ fail: return status; } +#ifdef USB_FRNDIS_INCLUDED + static void -rndis_unbind(struct usb_configuration *c, struct usb_function *f) +rndis_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); rndis_deregister(rndis->config); - rndis_exit(); - rndis_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(rndis->notify_req->buf); @@ -804,13 +847,6 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis); } -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis(struct usb_configuration *c) -{ - /* everything else is *presumably* fine */ - return true; -} - int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], u32 vendorID, const char *manufacturer, struct eth_dev *dev) @@ -818,24 +854,6 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct f_rndis *rndis; int status; - if (!can_support_rndis(c) || !ethaddr) - return -EINVAL; - - if (rndis_string_defs[0].id == 0) { - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - - status = usb_string_ids_tab(c->cdev, rndis_string_defs); - if (status) - return status; - - rndis_control_intf.iInterface = rndis_string_defs[0].id; - rndis_data_intf.iInterface = rndis_string_defs[1].id; - rndis_iad_descriptor.iFunction = rndis_string_defs[2].id; - } - /* allocate and initialize one new instance */ status = -ENOMEM; rndis = kzalloc(sizeof *rndis, GFP_KERNEL); @@ -856,19 +874,178 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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.unbind = rndis_old_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) { + if (status) kfree(rndis); fail: - rndis_exit(); - } return status; } + +#else + +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) +{ + struct f_rndis_opts *opts; + + opts = container_of(f, struct f_rndis_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + opts->borrowed_net = opts->bound = true; + opts->net = net; +} +EXPORT_SYMBOL(rndis_borrow_net); + +static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_rndis_opts, + func_inst.group); +} + +/* f_rndis_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(rndis); + +/* f_rndis_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis); + +/* f_rndis_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis); + +/* f_rndis_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis); + +/* f_rndis_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis); + +static struct configfs_attribute *rndis_attrs[] = { + &f_rndis_opts_dev_addr.attr, + &f_rndis_opts_host_addr.attr, + &f_rndis_opts_qmult.attr, + &f_rndis_opts_ifname.attr, + NULL, +}; + +static struct config_item_type rndis_func_type = { + .ct_item_ops = &rndis_item_ops, + .ct_attrs = rndis_attrs, + .ct_owner = THIS_MODULE, +}; + +static void rndis_free_inst(struct usb_function_instance *f) +{ + struct f_rndis_opts *opts; + + opts = container_of(f, struct f_rndis_opts, func_inst); + if (!opts->borrowed_net) { + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + } + kfree(opts); +} + +static struct usb_function_instance *rndis_alloc_inst(void) +{ + struct f_rndis_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = rndis_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + config_group_init_type_name(&opts->func_inst.group, "", + &rndis_func_type); + + return &opts->func_inst; +} + +static void rndis_free(struct usb_function *f) +{ + struct f_rndis *rndis; + struct f_rndis_opts *opts; + + rndis = func_to_rndis(f); + rndis_deregister(rndis->config); + opts = container_of(f->fi, struct f_rndis_opts, func_inst); + kfree(rndis); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); +} + +static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + usb_free_all_descriptors(f); + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); +} + +static struct usb_function *rndis_alloc(struct usb_function_instance *fi) +{ + struct f_rndis *rndis; + struct f_rndis_opts *opts; + int status; + + /* allocate and initialize one new instance */ + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + if (!rndis) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_rndis_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; + + gether_get_host_addr_u8(opts->net, rndis->ethaddr); + rndis->vendorID = opts->vendor_id; + rndis->manufacturer = opts->manufacturer; + + rndis->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); + /* 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"; + /* 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; + rndis->port.func.free_func = rndis_free; + + status = rndis_register(rndis_response_available, rndis); + if (status < 0) { + kfree(rndis); + return ERR_PTR(status); + } + rndis->config = status; + + return &rndis->port.func; +} + +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 7be04b3..5601e1d 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -12,11 +12,13 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include "u_ether.h" - +#include "u_ether_configfs.h" +#include "u_gether.h" /* * This function packages a simple "CDC Subset" Ethernet port with no real @@ -295,9 +297,40 @@ 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); + struct usb_string *us; int status; struct usb_ep *ep; +#ifndef USB_FSUBSET_INCLUDED + struct f_gether_opts *gether_opts; + + gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to gether_opts->bound access + */ + if (!gether_opts->bound) { + mutex_lock(&gether_opts->lock); + gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); + mutex_unlock(&gether_opts->lock); + if (status) + return status; + gether_opts->bound = true; + } +#endif + us = usb_gstrings_attach(cdev, geth_strings, + ARRAY_SIZE(geth_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + + subset_data_intf.iInterface = us[0].id; + ether_desc.iMACAddress = us[1].id; + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -360,8 +393,10 @@ fail: return status; } +#ifdef USB_FSUBSET_INCLUDED + static void -geth_unbind(struct usb_configuration *c, struct usb_function *f) +geth_old_unbind(struct usb_configuration *c, struct usb_function *f) { geth_string_defs[0].id = 0; usb_free_all_descriptors(f); @@ -387,18 +422,6 @@ int 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) { - status = usb_string_ids_tab(c->cdev, geth_string_defs); - if (status < 0) - return status; - subset_data_intf.iInterface = geth_string_defs[0].id; - ether_desc.iMACAddress = geth_string_defs[1].id; - } - /* allocate and initialize one new instance */ geth = kzalloc(sizeof *geth, GFP_KERNEL); if (!geth) @@ -412,9 +435,8 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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.unbind = geth_old_unbind; geth->port.func.set_alt = geth_set_alt; geth->port.func.disable = geth_disable; @@ -423,3 +445,130 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(geth); return status; } + +#else + +static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_gether_opts, + func_inst.group); +} + +/* f_gether_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(gether); + +/* f_gether_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether); + +/* f_gether_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether); + +/* f_gether_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether); + +/* f_gether_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether); + +static struct configfs_attribute *gether_attrs[] = { + &f_gether_opts_dev_addr.attr, + &f_gether_opts_host_addr.attr, + &f_gether_opts_qmult.attr, + &f_gether_opts_ifname.attr, + NULL, +}; + +static struct config_item_type gether_func_type = { + .ct_item_ops = &gether_item_ops, + .ct_attrs = gether_attrs, + .ct_owner = THIS_MODULE, +}; + +static void geth_free_inst(struct usb_function_instance *f) +{ + struct f_gether_opts *opts; + + opts = container_of(f, struct f_gether_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *geth_alloc_inst(void) +{ + struct f_gether_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = geth_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + config_group_init_type_name(&opts->func_inst.group, "", + &gether_func_type); + + return &opts->func_inst; +} + +static void geth_free(struct usb_function *f) +{ + struct f_gether *eth; + + eth = func_to_geth(f); + kfree(eth); +} + +static void geth_unbind(struct usb_configuration *c, struct usb_function *f) +{ + geth_string_defs[0].id = 0; + usb_free_all_descriptors(f); +} + +static struct usb_function *geth_alloc(struct usb_function_instance *fi) +{ + struct f_gether *geth; + struct f_gether_opts *opts; + int status; + + /* allocate and initialize one new instance */ + geth = kzalloc(sizeof(*geth), GFP_KERNEL); + if (!geth) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_gether_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt++; + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(opts->net, geth->ethaddr, + sizeof(geth->ethaddr)); + if (status < 12) { + kfree(geth); + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } + geth_string_defs[1].s = geth->ethaddr; + + geth->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); + geth->port.cdc_filter = DEFAULT_FILTER; + + geth->port.func.name = "cdc_subset"; + 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; + geth->port.func.free_func = geth_free; + + return &geth->port.func; +} + +DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index 03c1fb6..2f23566 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/f_uac2.c @@ -90,6 +90,7 @@ struct uac2_req { }; struct uac2_rtd_params { + struct snd_uac2_chip *uac2; /* parent chip */ bool ep_enabled; /* if the ep is enabled */ /* Size of the ring buffer */ size_t dma_bytes; @@ -169,18 +170,6 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p) } static inline -struct snd_uac2_chip *prm_to_uac2(struct uac2_rtd_params *r) -{ - struct snd_uac2_chip *uac2 = container_of(r, - struct snd_uac2_chip, c_prm); - - if (&uac2->c_prm != r) - uac2 = container_of(r, struct snd_uac2_chip, p_prm); - - return uac2; -} - -static inline uint num_channels(uint chanmask) { uint num = 0; @@ -204,7 +193,7 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) struct uac2_req *ur = req->context; struct snd_pcm_substream *substream; struct uac2_rtd_params *prm = ur->pp; - struct snd_uac2_chip *uac2 = prm_to_uac2(prm); + struct snd_uac2_chip *uac2 = prm->uac2; /* i/f shutting down */ if (!prm->ep_enabled) @@ -894,7 +883,7 @@ struct cntrl_range_lay3 { static inline void free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) { - struct snd_uac2_chip *uac2 = prm_to_uac2(prm); + struct snd_uac2_chip *uac2 = prm->uac2; int i; prm->ep_enabled = false; @@ -970,6 +959,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) } agdev->in_ep->driver_data = agdev; + uac2->p_prm.uac2 = uac2; + uac2->c_prm.uac2 = uac2; + hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize; hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 38dcedd..5f91c7a 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -156,8 +156,6 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ - .wMaxPacketSize = 0, - .bInterval = 0, }; static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { @@ -169,8 +167,6 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ - .wMaxPacketSize = 0, - .bInterval = 0, }; static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { @@ -183,17 +179,14 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { /* The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ - .wMaxPacketSize = 0, - .bInterval = 0, }; static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - /* The following 3 values can be tweaked if necessary. */ - .bMaxBurst = 0, - .bmAttributes = 0, - .wBytesPerInterval = cpu_to_le16(1024), + /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be + * initialized from module parameters. + */ }; static const struct usb_descriptor_header * const uvc_fs_streaming[] = { diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c new file mode 100644 index 0000000..cce5535 --- /dev/null +++ b/drivers/usb/gadget/fotg210-udc.c @@ -0,0 +1,1219 @@ +/* + * FOTG210 UDC Driver supports Bulk transfer so far + * + * Copyright (C) 2013 Faraday Technology Corporation + * + * Author : Yuan-Hsin Chen <yhchen@faraday-tech.com> + * + * 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; version 2 of the License. + */ + +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "fotg210.h" + +#define DRIVER_DESC "FOTG210 USB Device Controller Driver" +#define DRIVER_VERSION "30-April-2013" + +static const char udc_name[] = "fotg210_udc"; +static const char * const fotg210_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4"}; + +static void fotg210_disable_fifo_int(struct fotg210_ep *ep) +{ + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value |= DMISGR1_MF_IN_INT(ep->epnum - 1); + else + value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +} + +static void fotg210_enable_fifo_int(struct fotg210_ep *ep) +{ + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); + else + value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +} + +static void fotg210_set_cxdone(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_DONE; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, + int status) +{ + list_del_init(&req->queue); + + /* don't modify queue heads during completion callback */ + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + spin_unlock(&ep->fotg210->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->fotg210->lock); + + if (ep->epnum) { + if (list_empty(&ep->queue)) + fotg210_disable_fifo_int(ep); + } else { + fotg210_set_cxdone(ep->fotg210); + } +} + +static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, + u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + /* Driver should map an ep to a fifo and then map the fifo + * to the ep. What a brain-damaged design! + */ + + /* map a fifo to an ep */ + val = ioread32(fotg210->reg + FOTG210_EPMAP); + val &= ~EPMAP_FIFONOMSK(epnum, dir_in); + val |= EPMAP_FIFONO(epnum, dir_in); + iowrite32(val, fotg210->reg + FOTG210_EPMAP); + + /* map the ep to the fifo */ + val = ioread32(fotg210->reg + FOTG210_FIFOMAP); + val &= ~FIFOMAP_EPNOMSK(epnum); + val |= FIFOMAP_EPNO(epnum); + iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); + + /* enable fifo */ + val = ioread32(fotg210->reg + FOTG210_FIFOCF); + val |= FIFOCF_FIFO_EN(epnum - 1); + iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +} + +static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + val = ioread32(fotg210->reg + FOTG210_FIFOMAP); + val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); + iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +} + +static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + val = ioread32(fotg210->reg + FOTG210_FIFOCF); + val |= FIFOCF_TYPE(type, epnum - 1); + iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +} + +static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, + u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : + FOTG210_OUTEPMPSR(epnum); + + val = ioread32(fotg210->reg + offset); + val |= INOUTEPMPSR_MPS(mps); + iowrite32(val, fotg210->reg + offset); +} + +static int fotg210_config_ep(struct fotg210_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + + fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); + fotg210_set_tfrtype(ep, ep->epnum, ep->type); + fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); + fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); + + fotg210->ep[ep->epnum] = ep; + + return 0; +} + +static int fotg210_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep; + + ep = container_of(_ep, struct fotg210_ep, ep); + + ep->desc = desc; + ep->epnum = usb_endpoint_num(desc); + ep->type = usb_endpoint_type(desc); + ep->dir_in = usb_endpoint_dir_in(desc); + ep->ep.maxpacket = usb_endpoint_maxp(desc); + + return fotg210_config_ep(ep, desc); +} + +static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) +{ + struct fotg210_ep *ep = fotg210->ep[epnum]; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(epnum); + + /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ + * bit. Controller wouldn't clear this bit. WTF!!! + */ + + value = ioread32(reg); + value |= INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); + + value = ioread32(reg); + value &= ~INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); +} + +static int fotg210_ep_release(struct fotg210_ep *ep) +{ + if (!ep->epnum) + return 0; + ep->epnum = 0; + ep->stall = 0; + ep->wedged = 0; + + fotg210_reset_tseq(ep->fotg210, ep->epnum); + + return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + BUG_ON(!_ep); + + ep = container_of(_ep, struct fotg210_ep, ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + spin_lock_irqsave(&ep->fotg210->lock, flags); + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + } + + return fotg210_ep_release(ep); +} + +static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct fotg210_request *req; + + req = kzalloc(sizeof(struct fotg210_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fotg210_ep_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + kfree(req); +} + +static void fotg210_enable_dma(struct fotg210_ep *ep, + dma_addr_t d, u32 len) +{ + u32 value; + struct fotg210_udc *fotg210 = ep->fotg210; + + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_DMACPSR1); + value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); + value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); + + /* set device DMA target FIFO number */ + value = ioread32(fotg210->reg + FOTG210_DMATFNR); + if (ep->epnum) + value |= DMATFNR_ACC_FN(ep->epnum - 1); + else + value |= DMATFNR_ACC_CXF; + iowrite32(value, fotg210->reg + FOTG210_DMATFNR); + + /* set DMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); + + /* enable MDMA_EROR and MDMA_CMPLT interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMISGR2); + value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); + iowrite32(value, fotg210->reg + FOTG210_DMISGR2); + + /* start DMA */ + value = ioread32(fotg210->reg + FOTG210_DMACPSR1); + value |= DMACPSR1_DMA_START; + iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +} + +static void fotg210_disable_dma(struct fotg210_ep *ep) +{ + iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); +} + +static void fotg210_wait_dma_done(struct fotg210_ep *ep) +{ + u32 value; + + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); + if ((value & DISGR2_USBRST_INT) || + (value & DISGR2_DMA_ERROR)) + goto dma_reset; + } while (!(value & DISGR2_DMA_CMPLT)); + + value &= ~DISGR2_DMA_CMPLT; + iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); + return; + +dma_reset: + value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); + value |= DMACPSR1_DMA_ABORT; + iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); + + /* reset fifo */ + if (ep->epnum) { + value = ioread32(ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + value |= FIBCR_FFRST; + iowrite32(value, ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + } else { + value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); + value |= DCFESR_CX_CLR; + iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); + } +} + +static void fotg210_start_dma(struct fotg210_ep *ep, + struct fotg210_request *req) +{ + dma_addr_t d; + u8 *buffer; + u32 length; + + if (ep->epnum) { + if (ep->dir_in) { + buffer = req->req.buf; + length = req->req.length; + } else { + buffer = req->req.buf + req->req.actual; + length = ioread32(ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + length &= FIBCR_BCFX; + } + } else { + buffer = req->req.buf + req->req.actual; + if (req->req.length - req->req.actual > ep->ep.maxpacket) + length = ep->ep.maxpacket; + else + length = req->req.length; + } + + d = dma_map_single(NULL, buffer, length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(NULL, d)) { + pr_err("dma_mapping_error\n"); + return; + } + + dma_sync_single_for_device(NULL, d, length, + ep->dir_in ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + + fotg210_enable_dma(ep, d, length); + + /* check if dma is done */ + fotg210_wait_dma_done(ep); + + fotg210_disable_dma(ep); + + /* update actual transfer length */ + req->req.actual += length; + + dma_unmap_single(NULL, d, length, DMA_TO_DEVICE); +} + +static void fotg210_ep0_queue(struct fotg210_ep *ep, + struct fotg210_request *req) +{ + if (!req->req.length) { + fotg210_done(ep, req, 0); + return; + } + if (ep->dir_in) { /* if IN */ + if (req->req.length) { + fotg210_start_dma(ep, req); + } else { + pr_err("%s : req->req.length = 0x%x\n", + __func__, req->req.length); + } + if ((req->req.length == req->req.actual) || + (req->req.actual < ep->ep.maxpacket)) + fotg210_done(ep, req, 0); + } else { /* OUT */ + if (!req->req.length) { + fotg210_done(ep, req, 0); + } else { + u32 value = ioread32(ep->fotg210->reg + + FOTG210_DMISGR0); + + value &= ~DMISGR0_MCX_OUT_INT; + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); + } + } +} + +static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (!ep->epnum) /* ep0 */ + fotg210_ep0_queue(ep, req); + else if (request && !ep->stall) + fotg210_enable_fifo_int(ep); + + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + spin_lock_irqsave(&ep->fotg210->lock, flags); + if (!list_empty(&ep->queue)) + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static void fotg210_set_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + /* check if IN FIFO is empty before stall */ + if (ep->dir_in) { + do { + value = ioread32(fotg210->reg + FOTG210_DCFESR); + } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); + } + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value |= INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static void fotg210_clear_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value &= ~INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +{ + struct fotg210_ep *ep; + struct fotg210_udc *fotg210; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + + fotg210 = ep->fotg210; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (value) { + fotg210_set_epnstall(ep); + ep->stall = 1; + if (wedge) + ep->wedged = 1; + } else { + fotg210_reset_tseq(fotg210, ep->epnum); + fotg210_clear_epnstall(ep); + ep->stall = 0; + ep->wedged = 0; + if (!list_empty(&ep->queue)) + fotg210_enable_fifo_int(ep); + } + + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + return ret; +} + +static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) +{ + return fotg210_set_halt_and_wedge(_ep, value, 0); +} + +static int fotg210_ep_set_wedge(struct usb_ep *_ep) +{ + return fotg210_set_halt_and_wedge(_ep, 1, 1); +} + +static void fotg210_ep_fifo_flush(struct usb_ep *_ep) +{ +} + +static struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, + + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + + .set_halt = fotg210_ep_set_halt, + .fifo_flush = fotg210_ep_fifo_flush, + .set_wedge = fotg210_ep_set_wedge, +}; + +static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); + + value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 + | TX0BYTE_EP4); + iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); +} + +static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); + + value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 + | RX0BYTE_EP4); + iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); +} + +/* read 8-byte setup packet only */ +static void fotg210_rdsetupp(struct fotg210_udc *fotg210, + u8 *buffer) +{ + int i = 0; + u8 *tmp = buffer; + u32 data; + u32 length = 8; + + iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + *(tmp + 3) = (data >> 24) & 0xFF; + tmp = tmp + 4; + } + + switch (length % 4) { + case 1: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } + + iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); +} + +static void fotg210_set_configuration(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + value |= DAR_AFT_CONF; + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + value |= (addr & 0x7F); + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_cxstall(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_STL; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_request_error(struct fotg210_udc *fotg210) +{ + fotg210_set_cxstall(fotg210); + pr_err("request error!!\n"); +} + +static void fotg210_set_address(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + if (ctrl->wValue >= 0x0100) { + fotg210_request_error(fotg210); + } else { + fotg210_set_dev_addr(fotg210, ctrl->wValue); + fotg210_set_cxdone(fotg210); + } +} + +static void fotg210_set_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: { + u8 epnum; + epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + if (epnum) + fotg210_set_epnstall(fotg210->ep[epnum]); + else + fotg210_set_cxstall(fotg210); + fotg210_set_cxdone(fotg210); + } + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static void fotg210_clear_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + struct fotg210_ep *ep = + fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: + if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { + if (ep->wedged) { + fotg210_set_cxdone(fotg210); + break; + } + if (ep->stall) + fotg210_set_halt_and_wedge(&ep->ep, 0, 0); + } + fotg210_set_cxdone(fotg210); + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static int fotg210_is_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + return value & INOUTEPMPSR_STL_EP ? 1 : 0; +} + +static void fotg210_get_status(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 epnum; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210->ep0_data = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + fotg210->ep0_data = 0; + break; + case USB_RECIP_ENDPOINT: + epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; + if (epnum) + fotg210->ep0_data = + fotg210_is_epnstall(fotg210->ep[epnum]) + << USB_ENDPOINT_HALT; + else + fotg210_request_error(fotg210); + break; + + default: + fotg210_request_error(fotg210); + return; /* exit */ + } + + fotg210->ep0_req->buf = &fotg210->ep0_data; + fotg210->ep0_req->length = 2; + + spin_unlock(&fotg210->lock); + fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL); + spin_lock(&fotg210->lock); +} + +static int fotg210_setup_packet(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 *p = (u8 *)ctrl; + u8 ret = 0; + + fotg210_rdsetupp(fotg210, p); + + fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; + + if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + fotg210->gadget.speed = value & DMCR_HS_EN ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + fotg210_get_status(fotg210, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + fotg210_clear_feature(fotg210, ctrl); + break; + case USB_REQ_SET_FEATURE: + fotg210_set_feature(fotg210, ctrl); + break; + case USB_REQ_SET_ADDRESS: + fotg210_set_address(fotg210, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + fotg210_set_configuration(fotg210); + ret = 1; + break; + default: + ret = 1; + break; + } + } else { + ret = 1; + } + + return ret; +} + +static void fotg210_ep0out(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + + if (!list_empty(&ep->queue) && !ep->dir_in) { + struct fotg210_request *req; + + req = list_first_entry(&ep->queue, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + + if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + fotg210_done(ep, req, 0); + } else { + pr_err("%s : empty queue\n", __func__); + } +} + +static void fotg210_ep0in(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + + if ((!list_empty(&ep->queue)) && (ep->dir_in)) { + struct fotg210_request *req; + + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + + if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + fotg210_done(ep, req, 0); + } else { + fotg210_set_cxdone(fotg210); + } +} + +static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); + + value &= ~DISGR0_CX_COMABT_INT; + iowrite32(value, fotg210->reg + FOTG210_DISGR0); +} + +static void fotg210_in_fifo_handler(struct fotg210_ep *ep) +{ + struct fotg210_request *req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + fotg210_done(ep, req, 0); +} + +static void fotg210_out_fifo_handler(struct fotg210_ep *ep) +{ + struct fotg210_request *req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + fotg210_start_dma(ep, req); + + /* finish out transfer */ + if (req->req.length == req->req.actual || + req->req.actual < ep->ep.maxpacket) + fotg210_done(ep, req, 0); +} + +static irqreturn_t fotg210_irq(int irq, void *_fotg210) +{ + struct fotg210_udc *fotg210 = _fotg210; + u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); + u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); + + int_grp &= ~int_msk; + + spin_lock(&fotg210->lock); + + if (int_grp & DIGR_INT_G2) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); + u32 value; + + int_grp2 &= ~int_msk2; + + if (int_grp2 & DISGR2_USBRST_INT) { + value = ioread32(reg); + value &= ~DISGR2_USBRST_INT; + iowrite32(value, reg); + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { + value = ioread32(reg); + value &= ~DISGR2_SUSP_INT; + iowrite32(value, reg); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { + value = ioread32(reg); + value &= ~DISGR2_RESM_INT; + iowrite32(value, reg); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ERR_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ABORT_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_TX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + fotg210_clear_rx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_RX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 received 0 byte\n"); + } + if (int_grp2 & DISGR2_DMA_ERROR) { + value = ioread32(reg); + value &= ~DISGR2_DMA_ERROR; + iowrite32(value, reg); + } + } + + if (int_grp & DIGR_INT_G0) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR0; + u32 int_grp0 = ioread32(reg); + u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); + struct usb_ctrlrequest ctrl; + + int_grp0 &= ~int_msk0; + + /* the highest priority in this source register */ + if (int_grp0 & DISGR0_CX_COMABT_INT) { + fotg210_clear_comabt_int(fotg210); + pr_info("fotg210 CX command abort\n"); + } + + if (int_grp0 & DISGR0_CX_SETUP_INT) { + if (fotg210_setup_packet(fotg210, &ctrl)) { + spin_unlock(&fotg210->lock); + if (fotg210->driver->setup(&fotg210->gadget, + &ctrl) < 0) + fotg210_set_cxstall(fotg210); + spin_lock(&fotg210->lock); + } + } + if (int_grp0 & DISGR0_CX_COMEND_INT) + pr_info("fotg210 cmd end\n"); + + if (int_grp0 & DISGR0_CX_IN_INT) + fotg210_ep0in(fotg210); + + if (int_grp0 & DISGR0_CX_OUT_INT) + fotg210_ep0out(fotg210); + + if (int_grp0 & DISGR0_CX_COMFAIL_INT) { + fotg210_set_cxstall(fotg210); + pr_info("fotg210 ep0 fail\n"); + } + } + + if (int_grp & DIGR_INT_G1) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR1; + u32 int_grp1 = ioread32(reg); + u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); + int fifo; + + int_grp1 &= ~int_msk1; + + for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { + if (int_grp1 & DISGR1_IN_INT(fifo)) + fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); + + if ((int_grp1 & DISGR1_OUT_INT(fifo)) || + (int_grp1 & DISGR1_SPK_INT(fifo))) + fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); + } + } + + spin_unlock(&fotg210->lock); + + return IRQ_HANDLED; +} + +static void fotg210_disable_unplug(struct fotg210_udc *fotg210) +{ + u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); + + reg &= ~PHYTMSR_UNPLUG; + iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); +} + +static int fotg210_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + u32 value; + + /* hook up the driver */ + driver->driver.bus = NULL; + fotg210->driver = driver; + + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; + iowrite32(value, fotg210->reg + FOTG210_DMCR); + + return 0; +} + +static void fotg210_init(struct fotg210_udc *fotg210) +{ + u32 value; + + /* disable global interrupt and set int polarity to active high */ + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + fotg210->reg + FOTG210_GMIR); + + /* disable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value &= ~DMCR_GLINT_EN; + iowrite32(value, fotg210->reg + FOTG210_DMCR); + + /* disable all fifo interrupt */ + iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); + + /* disable cmd end */ + value = ioread32(fotg210->reg + FOTG210_DMISGR0); + value |= DMISGR0_MCX_COMEND; + iowrite32(value, fotg210->reg + FOTG210_DMISGR0); +} + +static int fotg210_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + unsigned long flags; + + spin_lock_irqsave(&fotg210->lock, flags); + + fotg210_init(fotg210); + fotg210->driver = NULL; + + spin_unlock_irqrestore(&fotg210->lock, flags); + + return 0; +} + +static struct usb_gadget_ops fotg210_gadget_ops = { + .udc_start = fotg210_udc_start, + .udc_stop = fotg210_udc_stop, +}; + +static int __exit fotg210_udc_remove(struct platform_device *pdev) +{ + struct fotg210_udc *fotg210 = dev_get_drvdata(&pdev->dev); + + usb_del_gadget_udc(&fotg210->gadget); + iounmap(fotg210->reg); + free_irq(platform_get_irq(pdev, 0), fotg210); + + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + kfree(fotg210); + + return 0; +} + +static int __init fotg210_udc_probe(struct platform_device *pdev) +{ + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + int ret = 0; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("platform_get_resource error.\n"); + return -ENODEV; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { + pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); + return -ENODEV; + } + + ret = -ENOMEM; + + /* initialize udc */ + fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + if (fotg210 == NULL) { + pr_err("kzalloc error\n"); + goto err_alloc; + } + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (_ep[i] == NULL) { + pr_err("_ep kzalloc error\n"); + goto err_alloc; + } + fotg210->ep[i] = _ep[i]; + } + + fotg210->reg = ioremap(res->start, resource_size(res)); + if (fotg210->reg == NULL) { + pr_err("ioremap error.\n"); + goto err_map; + } + + spin_lock_init(&fotg210->lock); + + dev_set_drvdata(&pdev->dev, fotg210); + + fotg210->gadget.ops = &fotg210_gadget_ops; + + fotg210->gadget.max_speed = USB_SPEED_HIGH; + fotg210->gadget.dev.parent = &pdev->dev; + fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; + fotg210->gadget.name = udc_name; + + INIT_LIST_HEAD(&fotg210->gadget.ep_list); + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + struct fotg210_ep *ep = fotg210->ep[i]; + + if (i) { + INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); + list_add_tail(&fotg210->ep[i]->ep.ep_list, + &fotg210->gadget.ep_list); + } + ep->fotg210 = fotg210; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = fotg210_ep_name[i]; + ep->ep.ops = &fotg210_ep_ops; + } + fotg210->ep[0]->ep.maxpacket = 0x40; + fotg210->gadget.ep0 = &fotg210->ep[0]->ep; + INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); + + fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, + GFP_KERNEL); + if (fotg210->ep0_req == NULL) + goto err_req; + + fotg210_init(fotg210); + + fotg210_disable_unplug(fotg210); + + ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { + pr_err("request_irq error (%d)\n", ret); + goto err_irq; + } + + ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); + if (ret) + goto err_add_udc; + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + + return 0; + +err_add_udc: +err_irq: + free_irq(ires->start, fotg210); + +err_req: + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + +err_map: + if (fotg210->reg) + iounmap(fotg210->reg); + +err_alloc: + kfree(fotg210); + + return ret; +} + +static struct platform_driver fotg210_driver = { + .driver = { + .name = (char *)udc_name, + .owner = THIS_MODULE, + }, + .probe = fotg210_udc_probe, + .remove = fotg210_udc_remove, +}; + +module_platform_driver(fotg210_driver); + +MODULE_AUTHOR("Yuan-Hsin Chen <yhchen@faraday-tech.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/fotg210.h new file mode 100644 index 0000000..bbf991b --- /dev/null +++ b/drivers/usb/gadget/fotg210.h @@ -0,0 +1,253 @@ +/* + * Faraday FOTG210 USB OTG controller + * + * Copyright (C) 2013 Faraday Technology Corporation + * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com> + * + * 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. + */ + +#include <linux/kernel.h> + +#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */ +#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */ + +/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */ +#define FOTG210_GMIR 0xC4 +#define GMIR_INT_POLARITY 0x8 /*Active High*/ +#define GMIR_MHC_INT 0x4 +#define GMIR_MOTG_INT 0x2 +#define GMIR_MDEV_INT 0x1 + +/* Device Main Control Register(0x100) */ +#define FOTG210_DMCR 0x100 +#define DMCR_HS_EN (1 << 6) +#define DMCR_CHIP_EN (1 << 5) +#define DMCR_SFRST (1 << 4) +#define DMCR_GOSUSP (1 << 3) +#define DMCR_GLINT_EN (1 << 2) +#define DMCR_HALF_SPEED (1 << 1) +#define DMCR_CAP_RMWAKUP (1 << 0) + +/* Device Address Register(0x104) */ +#define FOTG210_DAR 0x104 +#define DAR_AFT_CONF (1 << 7) + +/* Device Test Register(0x108) */ +#define FOTG210_DTR 0x108 +#define DTR_TST_CLRFF (1 << 0) + +/* PHY Test Mode Selector register(0x114) */ +#define FOTG210_PHYTMSR 0x114 +#define PHYTMSR_TST_PKT (1 << 4) +#define PHYTMSR_TST_SE0NAK (1 << 3) +#define PHYTMSR_TST_KSTA (1 << 2) +#define PHYTMSR_TST_JSTA (1 << 1) +#define PHYTMSR_UNPLUG (1 << 0) + +/* Cx configuration and FIFO Empty Status register(0x120) */ +#define FOTG210_DCFESR 0x120 +#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo)) +#define DCFESR_CX_EMP (1 << 5) +#define DCFESR_CX_CLR (1 << 3) +#define DCFESR_CX_STL (1 << 2) +#define DCFESR_TST_PKDONE (1 << 1) +#define DCFESR_CX_DONE (1 << 0) + +/* Device IDLE Counter Register(0x124) */ +#define FOTG210_DICR 0x124 + +/* Device Mask of Interrupt Group Register (0x130) */ +#define FOTG210_DMIGR 0x130 +#define DMIGR_MINT_G0 (1 << 0) + +/* Device Mask of Interrupt Source Group 0(0x134) */ +#define FOTG210_DMISGR0 0x134 +#define DMISGR0_MCX_COMEND (1 << 3) +#define DMISGR0_MCX_OUT_INT (1 << 2) +#define DMISGR0_MCX_IN_INT (1 << 1) +#define DMISGR0_MCX_SETUP_INT (1 << 0) + +/* Device Mask of Interrupt Source Group 1 Register(0x138)*/ +#define FOTG210_DMISGR1 0x138 +#define DMISGR1_MF3_IN_INT (1 << 19) +#define DMISGR1_MF2_IN_INT (1 << 18) +#define DMISGR1_MF1_IN_INT (1 << 17) +#define DMISGR1_MF0_IN_INT (1 << 16) +#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo))) +#define DMISGR1_MF3_SPK_INT (1 << 7) +#define DMISGR1_MF3_OUT_INT (1 << 6) +#define DMISGR1_MF2_SPK_INT (1 << 5) +#define DMISGR1_MF2_OUT_INT (1 << 4) +#define DMISGR1_MF1_SPK_INT (1 << 3) +#define DMISGR1_MF1_OUT_INT (1 << 2) +#define DMISGR1_MF0_SPK_INT (1 << 1) +#define DMISGR1_MF0_OUT_INT (1 << 0) +#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2) + +/* Device Mask of Interrupt Source Group 2 Register (0x13C) */ +#define FOTG210_DMISGR2 0x13C +#define DMISGR2_MDMA_ERROR (1 << 8) +#define DMISGR2_MDMA_CMPLT (1 << 7) + +/* Device Interrupt group Register (0x140) */ +#define FOTG210_DIGR 0x140 +#define DIGR_INT_G2 (1 << 2) +#define DIGR_INT_G1 (1 << 1) +#define DIGR_INT_G0 (1 << 0) + +/* Device Interrupt Source Group 0 Register (0x144) */ +#define FOTG210_DISGR0 0x144 +#define DISGR0_CX_COMABT_INT (1 << 5) +#define DISGR0_CX_COMFAIL_INT (1 << 4) +#define DISGR0_CX_COMEND_INT (1 << 3) +#define DISGR0_CX_OUT_INT (1 << 2) +#define DISGR0_CX_IN_INT (1 << 1) +#define DISGR0_CX_SETUP_INT (1 << 0) + +/* Device Interrupt Source Group 1 Register (0x148) */ +#define FOTG210_DISGR1 0x148 +#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2)) +#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2)) +#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo)) + +/* Device Interrupt Source Group 2 Register (0x14C) */ +#define FOTG210_DISGR2 0x14C +#define DISGR2_DMA_ERROR (1 << 8) +#define DISGR2_DMA_CMPLT (1 << 7) +#define DISGR2_RX0BYTE_INT (1 << 6) +#define DISGR2_TX0BYTE_INT (1 << 5) +#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4) +#define DISGR2_ISO_SEQ_ERR_INT (1 << 3) +#define DISGR2_RESM_INT (1 << 2) +#define DISGR2_SUSP_INT (1 << 1) +#define DISGR2_USBRST_INT (1 << 0) + +/* Device Receive Zero-Length Data Packet Register (0x150)*/ +#define FOTG210_RX0BYTE 0x150 +#define RX0BYTE_EP8 (1 << 7) +#define RX0BYTE_EP7 (1 << 6) +#define RX0BYTE_EP6 (1 << 5) +#define RX0BYTE_EP5 (1 << 4) +#define RX0BYTE_EP4 (1 << 3) +#define RX0BYTE_EP3 (1 << 2) +#define RX0BYTE_EP2 (1 << 1) +#define RX0BYTE_EP1 (1 << 0) + +/* Device Transfer Zero-Length Data Packet Register (0x154)*/ +#define FOTG210_TX0BYTE 0x154 +#define TX0BYTE_EP8 (1 << 7) +#define TX0BYTE_EP7 (1 << 6) +#define TX0BYTE_EP6 (1 << 5) +#define TX0BYTE_EP5 (1 << 4) +#define TX0BYTE_EP4 (1 << 3) +#define TX0BYTE_EP3 (1 << 2) +#define TX0BYTE_EP2 (1 << 1) +#define TX0BYTE_EP1 (1 << 0) + +/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */ +#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1)) +#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF) +#define INOUTEPMPSR_STL_EP (1 << 11) +#define INOUTEPMPSR_RESET_TSEQ (1 << 12) + +/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */ +#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1)) + +/* Device Endpoint 1~4 Map Register (0x1A0) */ +#define FOTG210_EPMAP 0x1A0 +#define EPMAP_FIFONO(ep, dir) \ + ((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) +#define EPMAP_FIFONOMSK(ep, dir) \ + ((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4)) + +/* Device FIFO Map Register (0x1A8) */ +#define FOTG210_FIFOMAP 0x1A8 +#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8) +#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8) +#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8) +#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8) +#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8) +#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8) + +/* Device FIFO Confuguration Register (0x1AC) */ +#define FOTG210_FIFOCF 0x1AC +#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8) +#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2) +#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2) +#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2) +#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4) +#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4) +#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5) + +/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */ +#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4) +#define FIBCR_BCFX 0x7FF +#define FIBCR_FFRST (1 << 12) + +/* Device DMA Target FIFO Number Register (0x1C0) */ +#define FOTG210_DMATFNR 0x1C0 +#define DMATFNR_ACC_CXF (1 << 4) +#define DMATFNR_ACC_F3 (1 << 3) +#define DMATFNR_ACC_F2 (1 << 2) +#define DMATFNR_ACC_F1 (1 << 1) +#define DMATFNR_ACC_F0 (1 << 0) +#define DMATFNR_ACC_FN(fifo) (1 << (fifo)) +#define DMATFNR_DISDMA 0 + +/* Device DMA Controller Parameter setting 1 Register (0x1C8) */ +#define FOTG210_DMACPSR1 0x1C8 +#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8) +#define DMACPSR1_DMA_ABORT (1 << 3) +#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1) +#define DMACPSR1_DMA_START (1 << 0) + +/* Device DMA Controller Parameter setting 2 Register (0x1CC) */ +#define FOTG210_DMACPSR2 0x1CC + +/* Device DMA Controller Parameter setting 3 Register (0x1CC) */ +#define FOTG210_CXPORT 0x1D0 + +struct fotg210_request { + struct usb_request req; + struct list_head queue; +}; + +struct fotg210_ep { + struct usb_ep ep; + struct fotg210_udc *fotg210; + + struct list_head queue; + unsigned stall:1; + unsigned wedged:1; + unsigned use_dma:1; + + unsigned char epnum; + unsigned char type; + unsigned char dir_in; + unsigned int maxp; + const struct usb_endpoint_descriptor *desc; +}; + +struct fotg210_udc { + spinlock_t lock; /* protect the struct */ + void __iomem *reg; + + unsigned long irq_trigger; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct fotg210_ep *ep[FOTG210_MAX_NUM_EP]; + + struct usb_request *ep0_req; /* for internal request */ + __le16 ep0_data; + u8 ep0_dir; /* 0/0x80 out/in */ + + u8 reenum; /* if re-enumeration */ +}; + +#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget) diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 9a7ee33..f3bb363 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2589,7 +2589,7 @@ static int qe_udc_probe(struct platform_device *ofdev) if (ret) goto err6; - dev_set_drvdata(&ofdev->dev, udc); + platform_set_drvdata(ofdev, udc); dev_info(udc->dev, "%s USB controller initialized as device\n", (udc->soc_type == PORT_QE) ? "QE" : "CPM"); @@ -2640,7 +2640,7 @@ static int qe_udc_resume(struct platform_device *dev) static int qe_udc_remove(struct platform_device *ofdev) { - struct qe_udc *udc = dev_get_drvdata(&ofdev->dev); + struct qe_udc *udc = platform_get_drvdata(ofdev); struct qe_ep *ep; unsigned int size; DECLARE_COMPLETION(done); diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index b8632d4..c83f3e1 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1347,7 +1347,7 @@ static const struct usb_gadget_ops fusb300_gadget_ops = { static int __exit fusb300_remove(struct platform_device *pdev) { - struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev); + struct fusb300 *fusb300 = platform_get_drvdata(pdev); usb_del_gadget_udc(&fusb300->gadget); iounmap(fusb300->reg); @@ -1416,7 +1416,7 @@ static int __init fusb300_probe(struct platform_device *pdev) spin_lock_init(&fusb300->lock); - dev_set_drvdata(&pdev->dev, fusb300); + platform_set_drvdata(pdev, fusb300); fusb300->gadget.ops = &fusb300_gadget_ops; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 787a78e..5327c82 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -28,15 +28,18 @@ # define USB_ETH_RNDIS y # endif +#define USBF_ECM_INCLUDED # include "f_ecm.c" +#define USB_FSUBSET_INCLUDED # include "f_subset.c" # ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" -# include "rndis.c" +# include "rndis.h" # endif -# include "u_ether.c" +# include "u_ether.h" -static u8 gfs_hostaddr[ETH_ALEN]; +static u8 gfs_host_mac[ETH_ALEN]; static struct eth_dev *the_dev; # ifdef CONFIG_USB_FUNCTIONFS_ETH static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], @@ -45,7 +48,7 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], #else # define the_dev NULL # define gether_cleanup(dev) do { } while (0) -# define gfs_hostaddr NULL +# define gfs_host_mac NULL struct eth_dev; #endif @@ -73,6 +76,8 @@ struct gfs_ffs_obj { USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + static struct usb_device_descriptor gfs_dev_desc = { .bLength = sizeof gfs_dev_desc, .bDescriptorType = USB_DT_DEVICE, @@ -350,7 +355,8 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (missing_funcs) return -ENODEV; #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS - the_dev = gether_setup(cdev->gadget, gfs_hostaddr); + the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac, + qmult); #endif if (IS_ERR(the_dev)) { ret = PTR_ERR(the_dev); @@ -446,7 +452,7 @@ static int gfs_do_config(struct usb_configuration *c) } if (gc->eth) { - ret = gc->eth(c, gfs_hostaddr, the_dev); + ret = gc->eth(c, gfs_host_mac, the_dev); if (unlikely(ret < 0)) return ret; } diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 51cfe72..46ba983 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1533,7 +1533,7 @@ static const struct usb_gadget_ops m66592_gadget_ops = { static int __exit m66592_remove(struct platform_device *pdev) { - struct m66592 *m66592 = dev_get_drvdata(&pdev->dev); + struct m66592 *m66592 = platform_get_drvdata(pdev); usb_del_gadget_udc(&m66592->gadget); @@ -1602,7 +1602,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK; spin_lock_init(&m66592->lock); - dev_set_drvdata(&pdev->dev, m66592); + platform_set_drvdata(pdev, m66592); m66592->gadget.ops = &m66592_gadget_ops; m66592->gadget.max_speed = USB_SPEED_HIGH; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 4a45e80..032b96a 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -43,16 +43,19 @@ MODULE_LICENSE("GPL"); */ #include "f_mass_storage.c" +#define USBF_ECM_INCLUDED #include "f_ecm.c" -#include "f_subset.c" #ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" -# include "rndis.c" +# include "rndis.h" #endif -#include "u_ether.c" +#include "u_ether.h" USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + /***************************** Device Descriptor ****************************/ #define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */ @@ -133,7 +136,7 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); static struct fsg_common fsg_common; -static u8 hostaddr[ETH_ALEN]; +static u8 host_mac[ETH_ALEN]; static struct usb_function_instance *fi_acm; static struct eth_dev *the_dev; @@ -152,7 +155,7 @@ static __init int rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = rndis_bind_config(c, hostaddr, the_dev); + ret = rndis_bind_config(c, host_mac, the_dev); if (ret < 0) return ret; @@ -216,7 +219,7 @@ static __init int cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = ecm_bind_config(c, hostaddr, the_dev); + ret = ecm_bind_config(c, host_mac, the_dev); if (ret < 0) return ret; @@ -280,7 +283,8 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) } /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, hostaddr); + the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, + qmult); if (IS_ERR(the_dev)) return PTR_ERR(the_dev); diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 58288e9..07fdb3e 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1786,8 +1786,6 @@ static int mv_u3d_remove(struct platform_device *dev) clk_put(u3d->clk); - platform_set_drvdata(dev, NULL); - kfree(u3d); return 0; @@ -1997,7 +1995,6 @@ err_map_cap_regs: err_get_cap_regs: err_get_clk: clk_put(u3d->clk); - platform_set_drvdata(dev, NULL); kfree(u3d); err_alloc_private: err_pdata: @@ -2053,7 +2050,7 @@ static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); static void mv_u3d_shutdown(struct platform_device *dev) { - struct mv_u3d *u3d = dev_get_drvdata(&dev->dev); + struct mv_u3d *u3d = platform_get_drvdata(dev); u32 tmp; tmp = ioread32(&u3d->op_regs->usbcmd); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 3b02fd4..81956fe 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -24,23 +24,12 @@ #include <linux/usb/composite.h> #include "u_ether.h" +#include "u_ncm.h" #define DRIVER_DESC "NCM Gadget" /*-------------------------------------------------------------------------*/ -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "f_ncm.c" -#include "u_ether.c" - -/*-------------------------------------------------------------------------*/ - /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ @@ -54,6 +43,8 @@ /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -111,13 +102,15 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -struct eth_dev *the_dev; -static u8 hostaddr[ETH_ALEN]; +static struct usb_function_instance *f_ncm_inst; +static struct usb_function *f_ncm; /*-------------------------------------------------------------------------*/ static int __init ncm_do_config(struct usb_configuration *c) { + int status; + /* FIXME alloc iConfiguration string, set it in c->strings */ if (gadget_is_otg(c->cdev->gadget)) { @@ -125,7 +118,19 @@ static int __init ncm_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return ncm_bind_config(c, hostaddr, the_dev); + f_ncm = usb_get_function(f_ncm_inst); + if (IS_ERR(f_ncm)) { + status = PTR_ERR(f_ncm); + return status; + } + + status = usb_add_function(c, f_ncm); + if (status < 0) { + usb_put_function(f_ncm); + return status; + } + + return 0; } static struct usb_configuration ncm_config_driver = { @@ -141,12 +146,20 @@ static struct usb_configuration ncm_config_driver = { static int __init gncm_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct f_ncm_opts *ncm_opts; int status; - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, hostaddr); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); + f_ncm_inst = usb_get_function_instance("ncm"); + if (IS_ERR(f_ncm_inst)) + return PTR_ERR(f_ncm_inst); + + ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst); + + gether_set_qmult(ncm_opts->net, qmult); + if (!gether_set_host_addr(ncm_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(ncm_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. @@ -169,13 +182,16 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) return 0; fail: - gether_cleanup(the_dev); + usb_put_function_instance(f_ncm_inst); return status; } static int __exit gncm_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(the_dev); + if (!IS_ERR_OR_NULL(f_ncm)) + usb_put_function(f_ncm); + if (!IS_ERR_OR_NULL(f_ncm_inst)) + usb_put_function_instance(f_ncm_inst); return 0; } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 3b344b4..0a8099a 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -16,11 +16,13 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include "u_serial.h" #include "u_ether.h" #include "u_phonet.h" +#include "u_ecm.h" #include "gadget_chips.h" /* Defines */ @@ -28,24 +30,10 @@ #define NOKIA_VERSION_NUM 0x0211 #define NOKIA_LONG_NAME "N900 (PC-Suite Mode)" -/*-------------------------------------------------------------------------*/ - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#define USBF_OBEX_INCLUDED -#include "f_ecm.c" -#include "f_obex.c" -#include "f_phonet.c" -#include "u_ether.c" - -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + #define NOKIA_VENDOR_ID 0x0421 /* Nokia */ #define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */ @@ -98,16 +86,15 @@ MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm_cfg1; static struct usb_function *f_acm_cfg2; -static u8 hostaddr[ETH_ALEN]; -static struct eth_dev *the_dev; - -enum { - TTY_PORT_OBEX0, - TTY_PORT_OBEX1, - TTY_PORTS_MAX, -}; +static struct usb_function *f_ecm_cfg1; +static struct usb_function *f_ecm_cfg2; +static struct usb_function *f_obex1_cfg1; +static struct usb_function *f_obex2_cfg1; +static struct usb_function *f_obex1_cfg2; +static struct usb_function *f_obex2_cfg2; +static struct usb_function *f_phonet_cfg1; +static struct usb_function *f_phonet_cfg2; -static unsigned char tty_lines[TTY_PORTS_MAX]; static struct usb_configuration nokia_config_500ma_driver = { .label = "Bus Powered", @@ -126,47 +113,114 @@ static struct usb_configuration nokia_config_100ma_driver = { }; static struct usb_function_instance *fi_acm; +static struct usb_function_instance *fi_ecm; +static struct usb_function_instance *fi_obex1; +static struct usb_function_instance *fi_obex2; +static struct usb_function_instance *fi_phonet; static int __init nokia_bind_config(struct usb_configuration *c) { struct usb_function *f_acm; + struct usb_function *f_phonet = NULL; + struct usb_function *f_obex1 = NULL; + struct usb_function *f_ecm; + struct usb_function *f_obex2 = NULL; int status = 0; + int obex1_stat = 0; + int obex2_stat = 0; + int phonet_stat = 0; + + if (!IS_ERR(fi_phonet)) { + f_phonet = usb_get_function(fi_phonet); + if (IS_ERR(f_phonet)) + pr_debug("could not get phonet function\n"); + } - status = phonet_bind_config(c); - if (status) - printk(KERN_DEBUG "could not bind phonet config\n"); - - status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX0]); - if (status) - printk(KERN_DEBUG "could not bind obex config %d\n", 0); + if (!IS_ERR(fi_obex1)) { + f_obex1 = usb_get_function(fi_obex1); + if (IS_ERR(f_obex1)) + pr_debug("could not get obex function 0\n"); + } - status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX1]); - if (status) - printk(KERN_DEBUG "could not bind obex config %d\n", 0); + if (!IS_ERR(fi_obex2)) { + f_obex2 = usb_get_function(fi_obex2); + if (IS_ERR(f_obex2)) + pr_debug("could not get obex function 1\n"); + } f_acm = usb_get_function(fi_acm); - if (IS_ERR(f_acm)) - return PTR_ERR(f_acm); + if (IS_ERR(f_acm)) { + status = PTR_ERR(f_acm); + goto err_get_acm; + } + + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) { + status = PTR_ERR(f_ecm); + goto err_get_ecm; + } + + if (!IS_ERR_OR_NULL(f_phonet)) { + phonet_stat = usb_add_function(c, f_phonet); + if (phonet_stat) + pr_debug("could not add phonet function\n"); + } + + if (!IS_ERR_OR_NULL(f_obex1)) { + obex1_stat = usb_add_function(c, f_obex1); + if (obex1_stat) + pr_debug("could not add obex function 0\n"); + } + + if (!IS_ERR_OR_NULL(f_obex2)) { + obex2_stat = usb_add_function(c, f_obex2); + if (obex2_stat) + pr_debug("could not add obex function 1\n"); + } status = usb_add_function(c, f_acm); if (status) goto err_conf; - status = ecm_bind_config(c, hostaddr, the_dev); + status = usb_add_function(c, f_ecm); if (status) { pr_debug("could not bind ecm config %d\n", status); goto err_ecm; } - if (c == &nokia_config_500ma_driver) + if (c == &nokia_config_500ma_driver) { f_acm_cfg1 = f_acm; - else + f_ecm_cfg1 = f_ecm; + f_phonet_cfg1 = f_phonet; + f_obex1_cfg1 = f_obex1; + f_obex2_cfg1 = f_obex2; + } else { f_acm_cfg2 = f_acm; + f_ecm_cfg2 = f_ecm; + f_phonet_cfg2 = f_phonet; + f_obex1_cfg2 = f_obex1; + f_obex2_cfg2 = f_obex2; + } return status; err_ecm: usb_remove_function(c, f_acm); err_conf: + if (!obex2_stat) + usb_remove_function(c, f_obex2); + if (!obex1_stat) + usb_remove_function(c, f_obex1); + if (!phonet_stat) + usb_remove_function(c, f_phonet); + usb_put_function(f_ecm); +err_get_ecm: usb_put_function(f_acm); +err_get_acm: + if (!IS_ERR_OR_NULL(f_obex2)) + usb_put_function(f_obex2); + if (!IS_ERR_OR_NULL(f_obex1)) + usb_put_function(f_obex1); + if (!IS_ERR_OR_NULL(f_phonet)) + usb_put_function(f_phonet); return status; } @@ -174,23 +228,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; int status; - int cur_line; - - status = gphonet_setup(cdev->gadget); - if (status < 0) - goto err_phonet; - - for (cur_line = 0; cur_line < TTY_PORTS_MAX; cur_line++) { - status = gserial_alloc_line(&tty_lines[cur_line]); - if (status) - goto err_ether; - } - - the_dev = gether_setup(cdev->gadget, hostaddr); - if (IS_ERR(the_dev)) { - status = PTR_ERR(the_dev); - goto err_ether; - } status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) @@ -201,18 +238,40 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) nokia_config_500ma_driver.iConfiguration = status; nokia_config_100ma_driver.iConfiguration = status; - if (!gadget_supports_altsettings(gadget)) + if (!gadget_supports_altsettings(gadget)) { + status = -ENODEV; goto err_usb; + } + + fi_phonet = usb_get_function_instance("phonet"); + if (IS_ERR(fi_phonet)) + pr_debug("could not find phonet function\n"); + + fi_obex1 = usb_get_function_instance("obex"); + if (IS_ERR(fi_obex1)) + pr_debug("could not find obex function 1\n"); + + fi_obex2 = usb_get_function_instance("obex"); + if (IS_ERR(fi_obex2)) + pr_debug("could not find obex function 2\n"); fi_acm = usb_get_function_instance("acm"); - if (IS_ERR(fi_acm)) - goto err_usb; + if (IS_ERR(fi_acm)) { + status = PTR_ERR(fi_acm); + goto err_obex2_inst; + } + + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) { + status = PTR_ERR(fi_ecm); + goto err_acm_inst; + } /* finally register the configuration */ status = usb_add_config(cdev, &nokia_config_500ma_driver, nokia_bind_config); if (status < 0) - goto err_acm_inst; + goto err_ecm_inst; status = usb_add_config(cdev, &nokia_config_100ma_driver, nokia_bind_config); @@ -226,33 +285,55 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) err_put_cfg1: usb_put_function(f_acm_cfg1); + if (!IS_ERR_OR_NULL(f_obex1_cfg1)) + usb_put_function(f_obex1_cfg1); + if (!IS_ERR_OR_NULL(f_obex2_cfg1)) + usb_put_function(f_obex2_cfg1); + if (!IS_ERR_OR_NULL(f_phonet_cfg1)) + usb_put_function(f_phonet_cfg1); + usb_put_function(f_ecm_cfg1); +err_ecm_inst: + usb_put_function_instance(fi_ecm); err_acm_inst: usb_put_function_instance(fi_acm); +err_obex2_inst: + if (!IS_ERR(fi_obex2)) + usb_put_function_instance(fi_obex2); + if (!IS_ERR(fi_obex1)) + usb_put_function_instance(fi_obex1); + if (!IS_ERR(fi_phonet)) + usb_put_function_instance(fi_phonet); err_usb: - gether_cleanup(the_dev); -err_ether: - cur_line--; - while (cur_line >= 0) - gserial_free_line(tty_lines[cur_line--]); - - gphonet_cleanup(); -err_phonet: return status; } static int __exit nokia_unbind(struct usb_composite_dev *cdev) { - int i; - + if (!IS_ERR_OR_NULL(f_obex1_cfg2)) + usb_put_function(f_obex1_cfg2); + if (!IS_ERR_OR_NULL(f_obex2_cfg2)) + usb_put_function(f_obex2_cfg2); + if (!IS_ERR_OR_NULL(f_obex1_cfg1)) + usb_put_function(f_obex1_cfg1); + if (!IS_ERR_OR_NULL(f_obex2_cfg1)) + usb_put_function(f_obex2_cfg1); + if (!IS_ERR_OR_NULL(f_phonet_cfg1)) + usb_put_function(f_phonet_cfg1); + if (!IS_ERR_OR_NULL(f_phonet_cfg2)) + usb_put_function(f_phonet_cfg2); usb_put_function(f_acm_cfg1); usb_put_function(f_acm_cfg2); + usb_put_function(f_ecm_cfg1); + usb_put_function(f_ecm_cfg2); + + usb_put_function_instance(fi_ecm); + if (!IS_ERR(fi_obex2)) + usb_put_function_instance(fi_obex2); + if (!IS_ERR(fi_obex1)) + usb_put_function_instance(fi_obex1); + if (!IS_ERR(fi_phonet)) + usb_put_function_instance(fi_phonet); usb_put_function_instance(fi_acm); - gphonet_cleanup(); - - for (i = 0; i < TTY_PORTS_MAX; i++) - gserial_free_line(tty_lines[i]); - - gether_cleanup(the_dev); return 0; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 6b4c7d9..41cea95 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2505,7 +2505,6 @@ static int pxa_udc_remove(struct platform_device *_dev) usb_put_phy(udc->transceiver); udc->transceiver = NULL; - platform_set_drvdata(_dev, NULL); the_controller = NULL; clk_put(udc->clk); iounmap(udc->regs); diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 7ff7d9c..c6af649 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1469,11 +1469,11 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) u16 savepipe; u16 mask0; + spin_lock(&r8a66597->lock); + if (r8a66597_is_sudmac(r8a66597)) r8a66597_sudmac_irq(r8a66597); - spin_lock(&r8a66597->lock); - intsts0 = r8a66597_read(r8a66597, INTSTS0); intenb0 = r8a66597_read(r8a66597, INTENB0); @@ -1822,7 +1822,7 @@ static const struct usb_gadget_ops r8a66597_gadget_ops = { static int __exit r8a66597_remove(struct platform_device *pdev) { - struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); + struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); @@ -1909,7 +1909,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) } spin_lock_init(&r8a66597->lock); - dev_set_drvdata(&pdev->dev, r8a66597); + platform_set_drvdata(pdev, r8a66597); r8a66597->pdata = pdev->dev.platform_data; r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 1e4cfb0..3e3ea72 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -761,6 +761,7 @@ int rndis_signal_connect(int configNr) return rndis_indicate_status_msg(configNr, RNDIS_STATUS_MEDIA_CONNECT); } +EXPORT_SYMBOL(rndis_signal_connect); int rndis_signal_disconnect(int configNr) { @@ -769,6 +770,7 @@ int rndis_signal_disconnect(int configNr) return rndis_indicate_status_msg(configNr, RNDIS_STATUS_MEDIA_DISCONNECT); } +EXPORT_SYMBOL(rndis_signal_disconnect); void rndis_uninit(int configNr) { @@ -783,11 +785,13 @@ void rndis_uninit(int configNr) while ((buf = rndis_get_next_response(configNr, &length))) rndis_free_response(configNr, buf); } +EXPORT_SYMBOL(rndis_uninit); void rndis_set_host_mac(int configNr, const u8 *addr) { rndis_per_dev_params[configNr].host_mac = addr; } +EXPORT_SYMBOL(rndis_set_host_mac); /* * Message Parser @@ -870,6 +874,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return -ENOTSUPP; } +EXPORT_SYMBOL(rndis_msg_parser); int rndis_register(void (*resp_avail)(void *v), void *v) { @@ -891,6 +896,7 @@ int rndis_register(void (*resp_avail)(void *v), void *v) return -ENODEV; } +EXPORT_SYMBOL(rndis_register); void rndis_deregister(int configNr) { @@ -899,6 +905,7 @@ void rndis_deregister(int configNr) if (configNr >= RNDIS_MAX_CONFIGS) return; rndis_per_dev_params[configNr].used = 0; } +EXPORT_SYMBOL(rndis_deregister); int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) { @@ -912,6 +919,7 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) return 0; } +EXPORT_SYMBOL(rndis_set_param_dev); int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) { @@ -924,6 +932,7 @@ int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) return 0; } +EXPORT_SYMBOL(rndis_set_param_vendor); int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) { @@ -935,6 +944,7 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) return 0; } +EXPORT_SYMBOL(rndis_set_param_medium); void rndis_add_hdr(struct sk_buff *skb) { @@ -949,6 +959,7 @@ void rndis_add_hdr(struct sk_buff *skb) header->DataOffset = cpu_to_le32(36); header->DataLength = cpu_to_le32(skb->len - sizeof(*header)); } +EXPORT_SYMBOL(rndis_add_hdr); void rndis_free_response(int configNr, u8 *buf) { @@ -965,6 +976,7 @@ void rndis_free_response(int configNr, u8 *buf) } } } +EXPORT_SYMBOL(rndis_free_response); u8 *rndis_get_next_response(int configNr, u32 *length) { @@ -986,6 +998,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length) return NULL; } +EXPORT_SYMBOL(rndis_get_next_response); static rndis_resp_t *rndis_add_response(int configNr, u32 length) { @@ -1029,6 +1042,7 @@ int rndis_rm_hdr(struct gether *port, skb_queue_tail(list, skb); return 0; } +EXPORT_SYMBOL(rndis_rm_hdr); #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -1160,6 +1174,7 @@ int rndis_init(void) return 0; } +module_init(rndis_init); void rndis_exit(void) { @@ -1173,3 +1188,6 @@ void rndis_exit(void) } #endif } +module_exit(rndis_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 0647f2f..0f4abb4 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -16,6 +16,7 @@ #define _LINUX_RNDIS_H #include <linux/rndis.h> +#include "u_ether.h" #include "ndis.h" #define RNDIS_MAXIMUM_FRAME_SIZE 1518 @@ -216,7 +217,4 @@ int rndis_signal_disconnect (int configNr); int rndis_state (int configNr); extern void rndis_set_host_mac (int configNr, const u8 *addr); -int rndis_init(void); -void rndis_exit (void); - #endif /* _LINUX_RNDIS_H */ diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/u_ecm.h new file mode 100644 index 0000000..262cc03 --- /dev/null +++ b/drivers/usb/gadget/u_ecm.h @@ -0,0 +1,36 @@ +/* + * u_ecm.h + * + * Utility definitions for the ecm function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_ECM_H +#define U_ECM_H + +#include <linux/usb/composite.h> + +struct f_ecm_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_ECM_H */ diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/u_eem.h new file mode 100644 index 0000000..e3ae978 --- /dev/null +++ b/drivers/usb/gadget/u_eem.h @@ -0,0 +1,36 @@ +/* + * u_eem.h + * + * Utility definitions for the eem function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_EEM_H +#define U_EEM_H + +#include <linux/usb/composite.h> + +struct f_eem_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_EEM_H */ diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 4b76124..2aae0d6 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -63,6 +63,8 @@ struct eth_dev { struct sk_buff_head rx_frames; + unsigned qmult; + unsigned header_len; struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); int (*unwrap)(struct gether *, @@ -76,6 +78,7 @@ struct eth_dev { bool zlp; u8 host_mac[ETH_ALEN]; + u8 dev_mac[ETH_ALEN]; }; /*-------------------------------------------------------------------------*/ @@ -84,12 +87,8 @@ struct eth_dev { #define DEFAULT_QLEN 2 /* double buffering by default */ -static unsigned qmult = 5; -module_param(qmult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed"); - /* for dual-speed hardware, use deeper queues at high/super speed */ -static inline int qlen(struct usb_gadget *gadget) +static inline int qlen(struct usb_gadget *gadget, unsigned qmult) { if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || gadget->speed == USB_SPEED_SUPER)) @@ -588,7 +587,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (gadget_is_dualspeed(dev->gadget)) req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH || dev->gadget->speed == USB_SPEED_SUPER) - ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) + ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0) : 0; retval = usb_ep_queue(in, req, GFP_ATOMIC); @@ -697,16 +696,6 @@ static int eth_stop(struct net_device *net) /*-------------------------------------------------------------------------*/ -/* 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 int get_ether_addr(const char *str, u8 *dev_addr) { if (str) { @@ -728,6 +717,17 @@ static int get_ether_addr(const char *str, u8 *dev_addr) return 1; } +static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len) +{ + if (len < 18) + return -EINVAL; + + snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x", + dev_addr[0], dev_addr[1], dev_addr[2], + dev_addr[3], dev_addr[4], dev_addr[5]); + return 18; +} + static const struct net_device_ops eth_netdev_ops = { .ndo_open = eth_open, .ndo_stop = eth_stop, @@ -755,8 +755,9 @@ static struct device_type gadget_type = { * * Returns negative errno, or zero on success */ -struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], - const char *netname) +struct eth_dev *gether_setup_name(struct usb_gadget *g, + const char *dev_addr, const char *host_addr, + u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname) { struct eth_dev *dev; struct net_device *net; @@ -777,6 +778,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], /* network device setup */ dev->net = net; + dev->qmult = qmult; snprintf(net->name, sizeof(net->name), "%s%%d", netname); if (get_ether_addr(dev_addr, net->dev_addr)) @@ -806,7 +808,8 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], INFO(dev, "MAC %pM\n", net->dev_addr); INFO(dev, "HOST MAC %pM\n", dev->host_mac); - /* two kinds of host-initiated state changes: + /* + * two kinds of host-initiated state changes: * - iff DATA transfer is active, carrier is "on" * - tx queueing enabled if open *and* carrier is "on" */ @@ -815,6 +818,186 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], return dev; } +EXPORT_SYMBOL(gether_setup_name); + +struct net_device *gether_setup_name_default(const char *netname) +{ + struct net_device *net; + struct eth_dev *dev; + + net = alloc_etherdev(sizeof(*dev)); + if (!net) + return ERR_PTR(-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); + + skb_queue_head_init(&dev->rx_frames); + + /* network device setup */ + dev->net = net; + dev->qmult = QMULT_DEFAULT; + snprintf(net->name, sizeof(net->name), "%s%%d", netname); + + eth_random_addr(dev->dev_mac); + pr_warn("using random %s ethernet address\n", "self"); + eth_random_addr(dev->host_mac); + pr_warn("using random %s ethernet address\n", "host"); + + net->netdev_ops = ð_netdev_ops; + + SET_ETHTOOL_OPS(net, &ops); + SET_NETDEV_DEVTYPE(net, &gadget_type); + + return net; +} +EXPORT_SYMBOL(gether_setup_name_default); + +int gether_register_netdev(struct net_device *net) +{ + struct eth_dev *dev; + struct usb_gadget *g; + struct sockaddr sa; + int status; + + if (!net->dev.parent) + return -EINVAL; + dev = netdev_priv(net); + g = dev->gadget; + status = register_netdev(net); + if (status < 0) { + dev_dbg(&g->dev, "register_netdev failed, %d\n", status); + return status; + } else { + INFO(dev, "HOST MAC %pM\n", dev->host_mac); + + /* 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_carrier_off(net); + } + sa.sa_family = net->type; + memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN); + rtnl_lock(); + status = dev_set_mac_address(net, &sa); + rtnl_unlock(); + if (status) + pr_warn("cannot set self ethernet address: %d\n", status); + else + INFO(dev, "MAC %pM\n", dev->dev_mac); + + return status; +} +EXPORT_SYMBOL(gether_register_netdev); + +void gether_set_gadget(struct net_device *net, struct usb_gadget *g) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + dev->gadget = g; + SET_NETDEV_DEV(net, &g->dev); +} +EXPORT_SYMBOL(gether_set_gadget); + +int gether_set_dev_addr(struct net_device *net, const char *dev_addr) +{ + struct eth_dev *dev; + u8 new_addr[ETH_ALEN]; + + dev = netdev_priv(net); + if (get_ether_addr(dev_addr, new_addr)) + return -EINVAL; + memcpy(dev->dev_mac, new_addr, ETH_ALEN); + return 0; +} +EXPORT_SYMBOL(gether_set_dev_addr); + +int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + return get_ether_addr_str(dev->dev_mac, dev_addr, len); +} +EXPORT_SYMBOL(gether_get_dev_addr); + +int gether_set_host_addr(struct net_device *net, const char *host_addr) +{ + struct eth_dev *dev; + u8 new_addr[ETH_ALEN]; + + dev = netdev_priv(net); + if (get_ether_addr(host_addr, new_addr)) + return -EINVAL; + memcpy(dev->host_mac, new_addr, ETH_ALEN); + return 0; +} +EXPORT_SYMBOL(gether_set_host_addr); + +int gether_get_host_addr(struct net_device *net, char *host_addr, int len) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + return get_ether_addr_str(dev->host_mac, host_addr, len); +} +EXPORT_SYMBOL(gether_get_host_addr); + +int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len) +{ + struct eth_dev *dev; + + if (len < 13) + return -EINVAL; + + dev = netdev_priv(net); + snprintf(host_addr, len, "%pm", dev->host_mac); + + return strlen(host_addr); +} +EXPORT_SYMBOL(gether_get_host_addr_cdc); + +void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + memcpy(host_mac, dev->host_mac, ETH_ALEN); +} +EXPORT_SYMBOL(gether_get_host_addr_u8); + +void gether_set_qmult(struct net_device *net, unsigned qmult) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + dev->qmult = qmult; +} +EXPORT_SYMBOL(gether_set_qmult); + +unsigned gether_get_qmult(struct net_device *net) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + return dev->qmult; +} +EXPORT_SYMBOL(gether_get_qmult); + +int gether_get_ifname(struct net_device *net, char *name, int len) +{ + rtnl_lock(); + strlcpy(name, netdev_name(net), len); + rtnl_unlock(); + return strlen(name); +} +EXPORT_SYMBOL(gether_get_ifname); /** * gether_cleanup - remove Ethernet-over-USB device @@ -831,6 +1014,7 @@ void gether_cleanup(struct eth_dev *dev) flush_work(&dev->work); free_netdev(dev->net); } +EXPORT_SYMBOL(gether_cleanup); /** * gether_connect - notify network layer that USB link is active @@ -873,11 +1057,12 @@ struct net_device *gether_connect(struct gether *link) } if (result == 0) - result = alloc_requests(dev, link, qlen(dev->gadget)); + result = alloc_requests(dev, link, qlen(dev->gadget, + dev->qmult)); if (result == 0) { dev->zlp = link->is_zlp_ok; - DBG(dev, "qlen %d\n", qlen(dev->gadget)); + DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult)); dev->header_len = link->header_len; dev->unwrap = link->unwrap; @@ -910,6 +1095,7 @@ fail0: return ERR_PTR(result); return dev->net; } +EXPORT_SYMBOL(gether_connect); /** * gether_disconnect - notify network layer that USB link is inactive @@ -980,3 +1166,7 @@ void gether_disconnect(struct gether *link) dev->port_usb = NULL; spin_unlock(&dev->lock); } +EXPORT_SYMBOL(gether_disconnect); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 0252233..fb23d1f 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -21,6 +21,26 @@ #include "gadget_chips.h" +#define QMULT_DEFAULT 5 + +/* + * dev_addr: initial value + * changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" + * host_addr: this address is invisible to ifconfig + */ +#define USB_ETHERNET_MODULE_PARAMETERS() \ + static unsigned qmult = QMULT_DEFAULT; \ + module_param(qmult, uint, S_IRUGO|S_IWUSR); \ + MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\ + \ + static char *dev_addr; \ + module_param(dev_addr, charp, S_IRUGO); \ + MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); \ + \ + static char *host_addr; \ + module_param(host_addr, charp, S_IRUGO); \ + MODULE_PARM_DESC(host_addr, "Host Ethernet Address") + struct eth_dev; /* @@ -71,8 +91,9 @@ struct gether { |USB_CDC_PACKET_TYPE_DIRECTED) /* variant of gether_setup that allows customizing network device name */ -struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], - const char *netname); +struct eth_dev *gether_setup_name(struct usb_gadget *g, + const char *dev_addr, const char *host_addr, + u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname); /* netdev setup/teardown as directed by the gadget driver */ /* gether_setup - initialize one ethernet-over-usb link @@ -88,11 +109,145 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], * Returns negative errno, or zero on success */ static inline struct eth_dev *gether_setup(struct usb_gadget *g, - u8 ethaddr[ETH_ALEN]) + const char *dev_addr, const char *host_addr, + u8 ethaddr[ETH_ALEN], unsigned qmult) { - return gether_setup_name(g, ethaddr, "usb"); + return gether_setup_name(g, dev_addr, host_addr, ethaddr, qmult, "usb"); } +/* + * variant of gether_setup_default that allows customizing + * network device name + */ +struct net_device *gether_setup_name_default(const char *netname); + +/* + * gether_register_netdev - register the net device + * @net: net device to register + * + * Registers the net device associated with this ethernet-over-usb link + * + */ +int gether_register_netdev(struct net_device *net); + +/* gether_setup_default - initialize one ethernet-over-usb link + * 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 to random values. + * + * Returns negative errno, or zero on success + */ +static inline struct net_device *gether_setup_default(void) +{ + return gether_setup_name_default("usb"); +} + +/** + * gether_set_gadget - initialize one ethernet-over-usb link with a gadget + * @net: device representing this link + * @g: the gadget to initialize with + * + * This associates one ethernet-over-usb link with a gadget. + */ +void gether_set_gadget(struct net_device *net, struct usb_gadget *g); + +/** + * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address + * @net: device representing this link + * @dev_addr: eth address of this device + * + * This sets the device-side Ethernet address of this ethernet-over-usb link + * if dev_addr is correct. + * Returns negative errno if the new address is incorrect. + */ +int gether_set_dev_addr(struct net_device *net, const char *dev_addr); + +/** + * gether_get_dev_addr - get an ethernet-over-usb link eth address + * @net: device representing this link + * @dev_addr: place to store device's eth address + * @len: length of the @dev_addr buffer + * + * This gets the device-side Ethernet address of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len); + +/** + * gether_set_host_addr - initialize an ethernet-over-usb link with host address + * @net: device representing this link + * @host_addr: eth address of the host + * + * This sets the host-side Ethernet address of this ethernet-over-usb link + * if host_addr is correct. + * Returns negative errno if the new address is incorrect. + */ +int gether_set_host_addr(struct net_device *net, const char *host_addr); + +/** + * gether_get_host_addr - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_addr: place to store eth address of the host + * @len: length of the @host_addr buffer + * + * This gets the host-side Ethernet address of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_host_addr(struct net_device *net, char *host_addr, int len); + +/** + * gether_get_host_addr_cdc - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_addr: place to store eth address of the host + * @len: length of the @host_addr buffer + * + * This gets the CDC formatted host-side Ethernet address of this + * ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len); + +/** + * gether_get_host_addr_u8 - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_mac: place to store the eth address of the host + * + * This gets the binary formatted host-side Ethernet address of this + * ethernet-over-usb link. + */ +void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]); + +/** + * gether_set_qmult - initialize an ethernet-over-usb link with a multiplier + * @net: device representing this link + * @qmult: queue multiplier + * + * This sets the queue length multiplier of this ethernet-over-usb link. + * For higher speeds use longer queues. + */ +void gether_set_qmult(struct net_device *net, unsigned qmult); + +/** + * gether_get_qmult - get an ethernet-over-usb link multiplier + * @net: device representing this link + * + * This gets the queue length multiplier of this ethernet-over-usb link. + */ +unsigned gether_get_qmult(struct net_device *net); + +/** + * gether_get_ifname - get an ethernet-over-usb link interface name + * @net: device representing this link + * @name: place to store the interface name + * @len: length of the @name buffer + * + * This gets the interface name of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_ifname(struct net_device *net, char *name, int len); + void gether_cleanup(struct eth_dev *dev); /* connect/disconnect is handled by individual functions */ @@ -117,9 +272,6 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev); int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev); -int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev); -int eem_bind_config(struct usb_configuration *c, struct eth_dev *dev); #ifdef USB_ETH_RNDIS diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/u_ether_configfs.h new file mode 100644 index 0000000..bcbd301 --- /dev/null +++ b/drivers/usb/gadget/u_ether_configfs.h @@ -0,0 +1,164 @@ +/* + * u_ether_configfs.h + * + * Utility definitions for configfs support in USB Ethernet functions + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __U_ETHER_CONFIGFS_H +#define __U_ETHER_CONFIGFS_H + +#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \ + CONFIGFS_ATTR_STRUCT(f_##_f_##_opts); \ + CONFIGFS_ATTR_OPS(f_##_f_##_opts); \ + \ + static void _f_##_attr_release(struct config_item *item) \ + { \ + struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ + \ + usb_put_function_instance(&opts->func_inst); \ + } \ + \ + static struct configfs_item_operations _f_##_item_ops = { \ + .release = _f_##_attr_release, \ + .show_attribute = f_##_f_##_opts_attr_show, \ + .store_attribute = f_##_f_##_opts_attr_store, \ + } + +#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_) \ + static ssize_t _f_##_opts_dev_addr_show(struct f_##_f_##_opts *opts, \ + char *page) \ + { \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ + } \ + \ + static ssize_t _f_##_opts_dev_addr_store(struct f_##_f_##_opts *opts, \ + const char *page, size_t len)\ + { \ + int ret; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + mutex_unlock(&opts->lock); \ + return -EBUSY; \ + } \ + \ + ret = gether_set_dev_addr(opts->net, page); \ + mutex_unlock(&opts->lock); \ + if (!ret) \ + ret = len; \ + return ret; \ + } \ + \ + static struct f_##_f_##_opts_attribute f_##_f_##_opts_dev_addr = \ + __CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, \ + _f_##_opts_dev_addr_show, \ + _f_##_opts_dev_addr_store) + +#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_) \ + static ssize_t _f_##_opts_host_addr_show(struct f_##_f_##_opts *opts, \ + char *page) \ + { \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ + } \ + \ + static ssize_t _f_##_opts_host_addr_store(struct f_##_f_##_opts *opts, \ + const char *page, size_t len)\ + { \ + int ret; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + mutex_unlock(&opts->lock); \ + return -EBUSY; \ + } \ + \ + ret = gether_set_host_addr(opts->net, page); \ + mutex_unlock(&opts->lock); \ + if (!ret) \ + ret = len; \ + return ret; \ + } \ + \ + static struct f_##_f_##_opts_attribute f_##_f_##_opts_host_addr = \ + __CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, \ + _f_##_opts_host_addr_show, \ + _f_##_opts_host_addr_store) + +#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_) \ + static ssize_t _f_##_opts_qmult_show(struct f_##_f_##_opts *opts, \ + char *page) \ + { \ + unsigned qmult; \ + \ + mutex_lock(&opts->lock); \ + qmult = gether_get_qmult(opts->net); \ + mutex_unlock(&opts->lock); \ + return sprintf(page, "%d", qmult); \ + } \ + \ + static ssize_t _f_##_opts_qmult_store(struct f_##_f_##_opts *opts, \ + const char *page, size_t len)\ + { \ + u8 val; \ + int ret; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto out; \ + } \ + \ + ret = kstrtou8(page, 0, &val); \ + if (ret) \ + goto out; \ + \ + gether_set_qmult(opts->net, val); \ + ret = len; \ +out: \ + mutex_unlock(&opts->lock); \ + return ret; \ + } \ + \ + static struct f_##_f_##_opts_attribute f_##_f_##_opts_qmult = \ + __CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, \ + _f_##_opts_qmult_show, \ + _f_##_opts_qmult_store) + +#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_) \ + static ssize_t _f_##_opts_ifname_show(struct f_##_f_##_opts *opts, \ + char *page) \ + { \ + int ret; \ + \ + mutex_lock(&opts->lock); \ + ret = gether_get_ifname(opts->net, page, PAGE_SIZE); \ + mutex_unlock(&opts->lock); \ + \ + return ret; \ + } \ + \ + static struct f_##_f_##_opts_attribute f_##_f_##_opts_ifname = \ + __CONFIGFS_ATTR_RO(ifname, _f_##_opts_ifname_show) + +#endif /* __U_ETHER_CONFIGFS_H */ diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h new file mode 100644 index 0000000..d407842 --- /dev/null +++ b/drivers/usb/gadget/u_gether.h @@ -0,0 +1,36 @@ +/* + * u_gether.h + * + * Utility definitions for the subset function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_GETHER_H +#define U_GETHER_H + +#include <linux/usb/composite.h> + +struct f_gether_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_GETHER_H */ diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/u_ncm.h new file mode 100644 index 0000000..ce0f3a7 --- /dev/null +++ b/drivers/usb/gadget/u_ncm.h @@ -0,0 +1,36 @@ +/* + * u_ncm.h + * + * Utility definitions for the ncm function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_NCM_H +#define U_NCM_H + +#include <linux/usb/composite.h> + +struct f_ncm_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_NCM_H */ diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h index 09a7525..98ced18 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/u_phonet.h @@ -14,8 +14,16 @@ #include <linux/usb/composite.h> #include <linux/usb/cdc.h> -int gphonet_setup(struct usb_gadget *gadget); -int phonet_bind_config(struct usb_configuration *c); -void gphonet_cleanup(void); +struct f_phonet_opts { + struct usb_function_instance func_inst; + bool bound; + struct net_device *net; +}; + +struct net_device *gphonet_setup_default(void); +void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g); +int gphonet_register_netdev(struct net_device *net); +int phonet_bind_config(struct usb_configuration *c, struct net_device *dev); +void gphonet_cleanup(struct net_device *dev); #endif /* __U_PHONET_H */ diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h new file mode 100644 index 0000000..c62ba82 --- /dev/null +++ b/drivers/usb/gadget/u_rndis.h @@ -0,0 +1,41 @@ +/* + * u_rndis.h + * + * Utility definitions for the subset function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_RNDIS_H +#define U_RNDIS_H + +#include <linux/usb/composite.h> + +struct f_rndis_opts { + struct usb_function_instance func_inst; + u32 vendor_id; + const char *manufacturer; + struct net_device *net; + bool bound; + bool borrowed_net; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); + +#endif /* U_RNDIS_H */ diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 7ce27e3..e617047 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -103,10 +103,26 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&queue->irqlock, flags); } +static void uvc_wait_prepare(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_unlock(&queue->mutex); +} + +static void uvc_wait_finish(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_lock(&queue->mutex); +} + static struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, + .wait_prepare = uvc_wait_prepare, + .wait_finish = uvc_wait_finish, }; static int uvc_queue_init(struct uvc_video_queue *queue, |