diff options
Diffstat (limited to 'drivers/usb/gadget')
113 files changed, 15602 insertions, 11502 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 14625fd2cec..ba18e9c110c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -58,6 +58,20 @@ config USB_GADGET_DEBUG trying to track down. Never enable these messages for a production build. +config USB_GADGET_VERBOSE + bool "Verbose debugging Messages (DEVELOPMENT)" + depends on USB_GADGET_DEBUG + help + Many controller and gadget drivers will print verbose debugging + messages if you use this option to ask for those messages. + + Avoid enabling these messages, even if you're actively + debugging such a driver. Many drivers will emit so many + messages that the driver timings are affected, which will + either create new failure modes or remove the one you're + trying to track down. Never enable these messages for a + production build. + config USB_GADGET_DEBUG_FILES boolean "Debugging information files (DEVELOPMENT)" depends on PROC_FS @@ -143,7 +157,7 @@ config USB_AT91 config USB_LPC32XX tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX && I2C select USB_ISP1301 help This option selects the USB device controller in the LPC32xx SoC. @@ -154,7 +168,7 @@ config USB_LPC32XX config USB_ATMEL_USBA tristate "Atmel USBA" - depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 + depends on AVR32 || ARCH_AT91 help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. @@ -187,15 +201,32 @@ config USB_FSL_USB2 config USB_FUSB300 tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT + depends on !PHYS_ADDR_T_64BIT && HAS_DMA help Faraday usb device controller FUSB300 driver +config USB_FOTG210_UDC + depends on HAS_DMA + 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_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + VHDL IP core library. + config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 - select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG - select USB_OTG_UTILS if ARCH_OMAP + depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3) help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 @@ -210,7 +241,6 @@ config USB_OMAP config USB_PXA25X tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX - select USB_OTG_UTILS help Intel's PXA 25x series XScale ARM-5TE processors include an integrated full speed USB 1.1 device controller. The @@ -235,6 +265,7 @@ config USB_PXA25X_SMALL config USB_R8A66597 tristate "Renesas R8A66597 USB Peripheral Controller" + depends on HAS_DMA help R8A66597 is a discrete USB host and peripheral controller chip that supports both full and high speed USB 2.0 data transfers. @@ -258,8 +289,6 @@ config USB_RENESAS_USBHS_UDC config USB_PXA27X tristate "PXA 27x" - depends on ARCH_PXA && (PXA27x || PXA3xx) - select USB_OTG_UTILS help Intel's PXA 27x series XScale ARM v5TE processors include an integrated full speed USB 1.1 device controller. @@ -271,27 +300,6 @@ config USB_PXA27X dynamically linked module called "pxa27x_udc" and force all gadget drivers to also be dynamically linked. -config USB_S3C_HSOTG - tristate "S3C HS/OtG USB Device controller" - depends on S3C_DEV_USB_HSOTG - help - The Samsung S3C64XX USB2.0 high-speed gadget controller - integrated into the S3C64XX series SoC. - -config USB_IMX - tristate "Freescale i.MX1 USB Peripheral Controller" - depends on ARCH_MXC - help - Freescale's i.MX1 includes an integrated full speed - USB 1.1 device controller. - - It has Six fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "imx_udc" and force all - gadget drivers to also be dynamically linked. - config USB_S3C2410 tristate "S3C2410 USB Device Controller" depends on ARCH_S3C24XX @@ -319,16 +327,15 @@ config USB_S3C_HSUDC config USB_MV_UDC tristate "Marvell USB2.0 Device Controller" + depends on HAS_DMA help Marvell Socs (including PXA and MMP series) include a high speed USB2.0 OTG controller, which can be configured as high speed or full speed USB peripheral. config USB_MV_U3D + depends on HAS_DMA tristate "MARVELL PXA2128 USB 3.0 controller" - depends on CPU_MMP3 - select USB_GADGET_DUALSPEED - select USB_GADGET_SUPERSPEED help MARVELL PXA2128 Processor series include a super speed USB3.0 device controller, which support super speed USB peripheral. @@ -337,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 @@ -401,7 +400,7 @@ config USB_NET2272 config USB_NET2272_DMA boolean "Support external DMA controller" - depends on USB_NET2272 + depends on USB_NET2272 && HAS_DMA help The NET2272 part can optionally support an external DMA controller, but your board has to have support in the @@ -498,8 +497,51 @@ endmenu # composite based drivers config USB_LIBCOMPOSITE tristate + select CONFIGFS_FS depends on USB_GADGET +config USB_F_ACM + tristate + +config USB_F_SS_LB + tristate + +config USB_U_SERIAL + tristate + +config USB_U_ETHER + 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 + +config USB_F_MASS_STORAGE + tristate + +config USB_F_FS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -521,9 +563,161 @@ 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_CONFIGFS_MASS_STORAGE + boolean "Mass storage" + depends on USB_CONFIGFS + depends on BLOCK + select USB_F_MASS_STORAGE + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + +config USB_CONFIGFS_F_LB_SS + boolean "Loopback and sourcesink function (for testing)" + depends on USB_CONFIGFS + select USB_F_SS_LB + help + Loopback function loops back a configurable number of transfers. + Sourcesink function either sinks and sources bulk data. + It also implements control requests, for "chapter 9" conformance. + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + +config USB_CONFIGFS_F_FS + boolean "Function filesystem (FunctionFS)" + depends on USB_CONFIGFS + select USB_F_FS + help + The Function Filesystem (FunctionFS) lets one create USB + composite functions in user space in the same way GadgetFS + lets one create USB gadgets in user space. This allows creation + of composite gadgets such that some of the functions are + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE + select USB_F_SS_LB help Gadget Zero is a two-configuration device. It either sinks and sources bulk data; or it loops back a configurable number of @@ -589,6 +783,9 @@ config USB_ETH tristate "Ethernet Gadget (with CDC Ethernet support)" depends on NET select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET select CRC32 help This driver implements Ethernet style communication, in one of @@ -625,6 +822,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, @@ -644,6 +842,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 @@ -661,6 +860,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 @@ -686,6 +887,7 @@ config USB_GADGETFS config USB_FUNCTIONFS tristate "Function Filesystem" select USB_LIBCOMPOSITE + select USB_F_FS select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) help The Function Filesystem (FunctionFS) lets one create USB @@ -704,6 +906,9 @@ config USB_FUNCTIONFS config USB_FUNCTIONFS_ETH bool "Include configuration with CDC ECM (Ethernet)" depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET help Include a configuration with CDC ECM function (Ethernet) and the Function Filesystem. @@ -711,6 +916,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_F_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. @@ -725,6 +932,7 @@ config USB_MASS_STORAGE tristate "Mass Storage Gadget" depends on BLOCK select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE help The Mass Storage Gadget acts as a USB Mass Storage disk drive. As its storage repository it can use a regular file or a block @@ -750,6 +958,11 @@ config USB_GADGET_TARGET config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + depends on TTY + select USB_U_SERIAL + select USB_F_ACM + select USB_F_SERIAL + select USB_F_OBEX select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. @@ -799,10 +1012,16 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +if TTY + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" 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. @@ -818,6 +1037,12 @@ config USB_G_NOKIA tristate "Nokia composite gadget" 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. @@ -829,6 +1054,9 @@ config USB_G_ACM_MS tristate "CDC Composite Device (ACM and mass storage)" depends on BLOCK select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_F_ACM + select USB_F_MASS_STORAGE help This driver provides two functions in one configuration: a mass storage, and a CDC ACM (serial port) link. @@ -841,6 +1069,10 @@ config USB_G_MULTI depends on BLOCK && NET select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_MASS_STORAGE help The Multifunction Composite Gadget provides Ethernet (RNDIS and/or CDC Ethernet), mass storage and ACM serial link @@ -859,6 +1091,7 @@ config USB_G_MULTI config USB_G_MULTI_RNDIS bool "RNDIS + CDC Serial + Storage configuration" depends on USB_G_MULTI + select USB_F_RNDIS default y help This option enables a configuration with RNDIS, CDC Serial and @@ -872,6 +1105,7 @@ config USB_G_MULTI_CDC bool "CDC Ethernet + CDC Serial + Storage configuration" depends on USB_G_MULTI default n + select USB_F_ECM help This option enables a configuration with CDC Ethernet (ECM), CDC Serial and Mass Storage functions available in the Multifunction @@ -879,6 +1113,8 @@ config USB_G_MULTI_CDC If unsure, say "y". +endif # TTY + config USB_G_HID tristate "HID Gadget" select USB_LIBCOMPOSITE @@ -895,6 +1131,7 @@ config USB_G_HID # Standalone / single function gadgets config USB_G_DBGP tristate "EHCI Debug Device Gadget" + depends on TTY select USB_LIBCOMPOSITE help This gadget emulates an EHCI Debug device. This is useful when you want @@ -916,6 +1153,7 @@ config USB_G_DBGP_PRINTK config USB_G_DBGP_SERIAL depends on USB_G_DBGP + select USB_U_SERIAL bool "serial" help Userland can interact using /dev/ttyGSxxx. @@ -928,6 +1166,7 @@ config USB_G_WEBCAM tristate "USB Webcam Gadget" depends on VIDEO_DEV select USB_LIBCOMPOSITE + select VIDEOBUF2_VMALLOC help The Webcam Gadget acts as a composite USB Audio and Video Class device. It provides a userspace API to process UVC control requests diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 8b4acfd92aa..49514ea60a9 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,19 +1,19 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o -libcomposite-y += composite.o +libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o -obj-$(CONFIG_USB_IMX) += imx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o @@ -26,14 +26,43 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o 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 +obj-$(CONFIG_USB_GR_UDC) += gr_udc.o + +# USB Functions +usb_f_acm-y := f_acm.o +obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +usb_f_ss_lb-y := f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL) += u_serial.o +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 +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 rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o +usb_f_mass_storage-y := f_mass_storage.o storage_common.o +obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o # # USB gadget drivers diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 5a7f289805f..a252444cc0a 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -31,19 +31,7 @@ #define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ #define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ -/*-------------------------------------------------------------------------*/ - -/* - * 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 "u_serial.c" -#include "f_acm.c" -#include "f_mass_storage.c" +#include "f_mass_storage.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -107,17 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; -FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES -static struct fsg_common fsg_common; +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); /*-------------------------------------------------------------------------*/ +static struct usb_function *f_acm; +static struct usb_function_instance *f_acm_inst; + +static struct usb_function_instance *fi_msg; +static struct usb_function *f_msg; /* * We _always_ have both ACM and mass storage functions. */ static int __init acm_ms_do_config(struct usb_configuration *c) { + struct fsg_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -125,16 +131,38 @@ static int __init acm_ms_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } + opts = fsg_opts_from_func_inst(fi_msg); - status = acm_bind_config(c, 0); - if (status < 0) - return status; + f_acm = usb_get_function(f_acm_inst); + if (IS_ERR(f_acm)) + return PTR_ERR(f_acm); - status = fsg_bind_config(c->cdev, c, &fsg_common); + f_msg = usb_get_function(fi_msg); + if (IS_ERR(f_msg)) { + status = PTR_ERR(f_msg); + goto put_acm; + } + + status = usb_add_function(c, f_acm); if (status < 0) - return status; + goto put_msg; + + status = fsg_common_run_thread(opts->common); + if (status) + goto remove_acm; + + status = usb_add_function(c, f_msg); + if (status) + goto remove_acm; return 0; +remove_acm: + usb_remove_function(c, f_acm); +put_msg: + usb_put_function(f_msg); +put_acm: + usb_put_function(f_acm); + return status; } static struct usb_configuration acm_ms_config_driver = { @@ -149,54 +177,84 @@ static struct usb_configuration acm_ms_config_driver = { static int __init acm_ms_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + struct fsg_opts *opts; + struct fsg_config config; int status; - void *retp; - /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); - if (status < 0) - return status; + f_acm_inst = usb_get_function_instance("acm"); + if (IS_ERR(f_acm_inst)) + return PTR_ERR(f_acm_inst); - /* set up mass storage function */ - retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); - if (IS_ERR(retp)) { - status = PTR_ERR(retp); - goto fail0; + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) { + status = PTR_ERR(fi_msg); + goto fail_get_msg; } + /* set up mass storage function */ + fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); + opts = fsg_opts_from_func_inst(fi_msg); + + opts->no_configfs = true; + status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); + if (status) + goto fail; + + status = fsg_common_set_nluns(opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(opts->common, true); + status = fsg_common_create_luns(opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(opts->common, config.vendor_name, + config.product_name); /* * Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - goto fail1; + goto fail_string_ids; device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration */ status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); if (status < 0) - goto fail1; + goto fail_string_ids; usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); - fsg_common_put(&fsg_common); return 0; /* error recovery */ -fail1: - fsg_common_put(&fsg_common); -fail0: - gserial_cleanup(); +fail_string_ids: + fsg_common_remove_luns(opts->common); +fail_set_cdev: + fsg_common_free_luns(opts->common); +fail_set_nluns: + fsg_common_free_buffers(opts->common); +fail: + usb_put_function_instance(fi_msg); +fail_get_msg: + usb_put_function_instance(f_acm_inst); return status; } static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); - + usb_put_function(f_msg); + usb_put_function_instance(fi_msg); + usb_put_function(f_acm); + usb_put_function_instance(f_acm_inst); return 0; } diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index d9f6b937249..41b062eb4de 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -40,7 +40,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -446,7 +445,7 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep) ep->ep.ops = &udc_ep_ops; INIT_LIST_HEAD(&ep->queue); - ep->ep.maxpacket = (u16) ~0; + usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0); /* set NAK */ tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_SNAK); @@ -1122,7 +1121,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) goto finished; } if (ep->dma) { - retval = prep_dma(ep, req, gfp); + retval = prep_dma(ep, req, GFP_ATOMIC); if (retval != 0) goto finished; /* write desc pointer to enable DMA */ @@ -1190,7 +1189,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) * for PPB modes, because of chain creation reasons */ if (ep->in) { - retval = prep_dma(ep, req, gfp); + retval = prep_dma(ep, req, GFP_ATOMIC); if (retval != 0) goto finished; } @@ -1400,15 +1399,16 @@ static int udc_wakeup(struct usb_gadget *gadget) return 0; } -static int amd5536_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int amd5536_stop(struct usb_gadget_driver *driver); +static int amd5536_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int amd5536_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); /* gadget operations */ static const struct usb_gadget_ops udc_ops = { .wakeup = udc_wakeup, .get_frame = udc_get_frame, - .start = amd5536_start, - .stop = amd5536_stop, + .udc_start = amd5536_udc_start, + .udc_stop = amd5536_udc_stop, }; /* Setups endpoint parameters, adds endpoints to linked list */ @@ -1563,12 +1563,15 @@ static void udc_setup_endpoints(struct udc *dev) } /* EP0 max packet */ if (dev->gadget.speed == USB_SPEED_FULL) { - dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IX].ep.maxpacket = - UDC_FS_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep, + UDC_FS_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep, + UDC_FS_EP0OUT_MAX_PKT_SIZE); } else if (dev->gadget.speed == USB_SPEED_HIGH) { - dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep, + UDC_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep, + UDC_EP0OUT_MAX_PKT_SIZE); } /* @@ -1913,26 +1916,14 @@ static int setup_ep0(struct udc *dev) } /* Called by gadget driver to register itself */ -static int amd5536_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int amd5536_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct udc *dev = udc; - int retval; + struct udc *dev = to_amd5536_udc(g); u32 tmp; - if (!driver || !bind || !driver->setup - || driver->max_speed < USB_SPEED_HIGH) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; - driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - - retval = bind(&dev->gadget, driver); /* Some gadget drivers use both ep0 directions. * NOTE: to gadget driver, ep0 is just one endpoint... @@ -1940,14 +1931,6 @@ static int amd5536_start(struct usb_gadget_driver *driver, dev->ep[UDC_EP0OUT_IX].ep.driver_data = dev->ep[UDC_EP0IN_IX].ep.driver_data; - if (retval) { - DBG(dev, "binding to %s returning %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - /* get ready for ep0 traffic */ setup_ep0(dev); @@ -1969,14 +1952,9 @@ __acquires(dev->lock) { int tmp; - if (dev->gadget.speed != USB_SPEED_UNKNOWN) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - /* empty queues and init hardware */ udc_basic_init(dev); + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) empty_req_queue(&dev->ep[tmp]); @@ -1984,24 +1962,18 @@ __acquires(dev->lock) } /* Called by gadget driver to unregister itself */ -static int amd5536_stop(struct usb_gadget_driver *driver) +static int amd5536_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct udc *dev = udc; - unsigned long flags; + struct udc *dev = to_amd5536_udc(g); + unsigned long flags; u32 tmp; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; - spin_lock_irqsave(&dev->lock, flags); udc_mask_unused_interrupts(dev); shutdown(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; dev->driver = NULL; /* set SD */ @@ -2009,9 +1981,6 @@ static int amd5536_stop(struct usb_gadget_driver *driver) tmp |= AMD_BIT(UDC_DEVCTL_SD); writel(tmp, &dev->regs->ctl); - - DBG(dev, "%s: unregistered\n", driver->driver.name); - return 0; } @@ -3111,9 +3080,6 @@ static void udc_pci_remove(struct pci_dev *pdev) if (dev->active) pci_disable_device(pdev); - device_unregister(&dev->gadget.dev); - pci_set_drvdata(pdev, NULL); - udc_remove(dev); } @@ -3276,8 +3242,6 @@ static int udc_pci_probe( dev->phys_addr = resource; dev->irq = pdev->irq; dev->pdev = pdev; - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; /* general probing */ if (udc_probe(dev) == 0) @@ -3304,7 +3268,6 @@ static int udc_probe(struct udc *dev) dev->gadget.ops = &udc_ops; dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; @@ -3328,17 +3291,11 @@ static int udc_probe(struct udc *dev) "driver version: %s(for Geode5536 B1)\n", tmp); udc = dev; - retval = usb_add_gadget_udc(&udc->pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, + gadget_release); if (retval) goto finished; - retval = device_register(&dev->gadget.dev); - if (retval) { - usb_del_gadget_udc(&dev->gadget); - put_device(&dev->gadget.dev); - goto finished; - } - /* timer init */ init_timer(&udc_timer); udc_timer.function = udc_timer_function; @@ -3383,7 +3340,7 @@ static int udc_remote_wakeup(struct udc *dev) } /* PCI device parameters */ -static DEFINE_PCI_DEVICE_TABLE(pci_id) = { +static const struct pci_device_id pci_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index 14af87d65ca..6744d3b8310 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -472,7 +472,6 @@ struct udc_request { /* flags */ unsigned dma_going : 1, - dma_mapping : 1, dma_done : 1; /* phys. address */ dma_addr_t td_phys; @@ -563,6 +562,8 @@ struct udc { u16 cur_alt; }; +#define to_amd5536_udc(g) (container_of((g), struct udc, gadget)) + /* setup request data */ union udc_setup_data { u32 data[2]; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index f4a21f6f081..cfd18bcca72 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -21,7 +21,6 @@ #include <linux/ioport.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> @@ -221,7 +220,7 @@ static int proc_udc_show(struct seq_file *s, void *unused) static int proc_udc_open(struct inode *inode, struct file *file) { - return single_open(file, proc_udc_show, PDE(inode)->data); + return single_open(file, proc_udc_show, PDE_DATA(inode)); } static const struct file_operations proc_ops = { @@ -834,7 +833,7 @@ static void udc_reinit(struct at91_udc *udc) ep->ep.desc = NULL; ep->stopped = 0; ep->fifo_bank = 0; - ep->ep.maxpacket = ep->maxpacket; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); @@ -870,8 +869,13 @@ static void clk_on(struct at91_udc *udc) if (udc->clocked) return; udc->clocked = 1; - clk_enable(udc->iclk); - clk_enable(udc->fclk); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + clk_set_rate(udc->uclk, 48000000); + clk_prepare_enable(udc->uclk); + } + clk_prepare_enable(udc->iclk); + clk_prepare_enable(udc->fclk); } static void clk_off(struct at91_udc *udc) @@ -880,8 +884,10 @@ static void clk_off(struct at91_udc *udc) return; udc->clocked = 0; udc->gadget.speed = USB_SPEED_UNKNOWN; - clk_disable(udc->fclk); - clk_disable(udc->iclk); + clk_disable_unprepare(udc->fclk); + clk_disable_unprepare(udc->iclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_disable_unprepare(udc->uclk); } /* @@ -1621,8 +1627,7 @@ static void at91_vbus_timer(unsigned long data) * bus such as i2c or spi which may sleep, so schedule some work * to read the vbus gpio */ - if (!work_pending(&udc->vbus_timer_work)) - schedule_work(&udc->vbus_timer_work); + schedule_work(&udc->vbus_timer_work); } static int at91_start(struct usb_gadget *gadget, @@ -1632,7 +1637,6 @@ static int at91_start(struct usb_gadget *gadget, udc = container_of(gadget, struct at91_udc, gadget); udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->pdev->dev.of_node; udc->enabled = 1; udc->selfpowered = 1; @@ -1653,7 +1657,6 @@ static int at91_stop(struct usb_gadget *gadget, at91_udp_write(udc, AT91_UDP_IDR, ~0); spin_unlock_irqrestore(&udc->lock, flags); - udc->gadget.dev.driver = NULL; udc->driver = NULL; DBG("unbound from %s\n", driver->driver.name); @@ -1700,22 +1703,12 @@ static int at91udc_probe(struct platform_device *pdev) int retval; struct resource *res; - if (!dev->platform_data && !pdev->dev.of_node) { + if (!dev_get_platdata(dev) && !pdev->dev.of_node) { /* small (so we copy it) but critical! */ DBG("missing platform_data\n"); return -ENODEV; } - if (pdev->num_resources != 2) { - DBG("invalid num_resources\n"); - return -ENODEV; - } - if ((pdev->resource[0].flags != IORESOURCE_MEM) - || (pdev->resource[1].flags != IORESOURCE_IRQ)) { - DBG("invalid resource type\n"); - return -ENODEV; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; @@ -1728,10 +1721,10 @@ static int at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; - if (pdev->dev.of_node) + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) at91udc_of_init(udc, pdev->dev.of_node); else - memcpy(&udc->board, dev->platform_data, + memcpy(&udc->board, dev_get_platdata(dev), sizeof(struct at91_udc_data)); udc->pdev = pdev; udc->enabled = 0; @@ -1739,7 +1732,7 @@ static int at91udc_probe(struct platform_device *pdev) /* rm9200 needs manual D+ pullup; off by default */ if (cpu_is_at91rm9200()) { - if (gpio_is_valid(udc->board.pullup_pin)) { + if (!gpio_is_valid(udc->board.pullup_pin)) { DBG("no D+ pullup?\n"); retval = -ENODEV; goto fail0; @@ -1777,26 +1770,24 @@ static int at91udc_probe(struct platform_device *pdev) /* get interface and function clocks */ udc->iclk = clk_get(dev, "udc_clk"); udc->fclk = clk_get(dev, "udpck"); - if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) + udc->uclk = clk_get(dev, "usb_clk"); + if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk) || + (IS_ENABLED(CONFIG_COMMON_CLK) && IS_ERR(udc->uclk))) { DBG("clocks missing\n"); retval = -ENODEV; - /* NOTE: we "know" here that refcounts on these are NOPs */ - goto fail0b; - } - - retval = device_register(&udc->gadget.dev); - if (retval < 0) { - put_device(&udc->gadget.dev); - goto fail0b; + goto fail1; } /* don't do anything until we have both gadget driver and VBUS */ - clk_enable(udc->iclk); + retval = clk_prepare_enable(udc->iclk); + if (retval) + goto fail1; at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff); /* Clear all pending interrupts - UDP may be used by bootloader. */ at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff); - clk_disable(udc->iclk); + clk_disable_unprepare(udc->iclk); /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); @@ -1858,8 +1849,12 @@ fail3: fail2: free_irq(udc->udp_irq, udc); fail1: - device_unregister(&udc->gadget.dev); -fail0b: + if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk)) + clk_put(udc->uclk); + if (!IS_ERR(udc->fclk)) + clk_put(udc->fclk); + if (!IS_ERR(udc->iclk)) + clk_put(udc->iclk); iounmap(udc->udp_baseaddr); fail0a: if (cpu_is_at91rm9200()) @@ -1893,8 +1888,6 @@ static int __exit at91udc_remove(struct platform_device *pdev) gpio_free(udc->board.vbus_pin); } free_irq(udc->udp_irq, udc); - device_unregister(&udc->gadget.dev); - iounmap(udc->udp_baseaddr); if (cpu_is_at91rm9200()) @@ -1905,6 +1898,8 @@ static int __exit at91udc_remove(struct platform_device *pdev) clk_put(udc->iclk); clk_put(udc->fclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_put(udc->uclk); return 0; } @@ -1982,17 +1977,7 @@ static struct platform_driver at91_udc_driver = { }, }; -static int __init udc_init_module(void) -{ - return platform_driver_probe(&at91_udc_driver, at91udc_probe); -} -module_init(udc_init_module); - -static void __exit udc_exit_module(void) -{ - platform_driver_unregister(&at91_udc_driver); -} -module_exit(udc_exit_module); +module_platform_driver_probe(at91_udc_driver, at91udc_probe); MODULE_DESCRIPTION("AT91 udc driver"); MODULE_AUTHOR("Thomas Rathbone, David Brownell"); diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index e647d1c2ada..01752466338 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -126,7 +126,7 @@ struct at91_udc { unsigned active_suspend:1; u8 addr; struct at91_udc_data board; - struct clk *iclk, *fclk; + struct clk *iclk, *fclk, *uclk; struct platform_device *pdev; struct proc_dir_entry *pde; void __iomem *udp_baseaddr; diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index a7aed84d98c..76023ce449a 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -22,15 +22,13 @@ #include <linux/usb/atmel_usba_udc.h> #include <linux/delay.h> #include <linux/platform_data/atmel.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <asm/gpio.h> #include "atmel_usba_udc.h" - -static struct usba_udc the_udc; -static struct usba_ep *usba_ep; - #ifdef CONFIG_USB_GADGET_DEBUG_FS #include <linux/debugfs.h> #include <linux/uaccess.h> @@ -93,7 +91,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - mutex_lock(&file->f_dentry->d_inode->i_mutex); + mutex_lock(&file_inode(file)->i_mutex); list_for_each_entry_safe(req, tmp_req, queue, queue) { len = snprintf(tmpbuf, sizeof(tmpbuf), "%8p %08x %c%c%c %5d %c%c%c\n", @@ -120,7 +118,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf, nbytes -= len; buf += len; } - mutex_unlock(&file->f_dentry->d_inode->i_mutex); + mutex_unlock(&file_inode(file)->i_mutex); return actual; } @@ -168,13 +166,13 @@ out: static ssize_t regs_dbg_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file_inode(file); int ret; mutex_lock(&inode->i_mutex); ret = simple_read_from_buffer(buf, nbytes, ppos, file->private_data, - file->f_dentry->d_inode->i_size); + file_inode(file)->i_size); mutex_unlock(&inode->i_mutex); return ret; @@ -328,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc) #if defined(CONFIG_ARCH_AT91SAM9RL) -#include <mach/at91_pmc.h> +#include <linux/clk/at91_pmc.h> static void toggle_bias(int is_on) { @@ -489,13 +487,8 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status) if (req->req.status == -EINPROGRESS) req->req.status = status; - if (req->mapped) { - dma_unmap_single( - &udc->pdev->dev, req->req.dma, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + if (req->using_dma) + usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); DBG(DBG_GADGET | DBG_REQ, "%s: req %p complete: status %d, actual %u\n", @@ -684,7 +677,6 @@ usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) return NULL; INIT_LIST_HEAD(&req->queue); - req->req.dma = DMA_ADDR_INVALID; return &req->req; } @@ -717,20 +709,11 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, return -EINVAL; } - req->using_dma = 1; - - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single( - &udc->pdev->dev, req->req.buf, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device( - &udc->pdev->dev, req->req.dma, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 0; - } + ret = usb_gadget_map_request(&udc->gadget, &req->req, ep->is_in); + if (ret) + return ret; + req->using_dma = 1; req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length) | USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE | USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE; @@ -1029,16 +1012,13 @@ static void nop_release(struct device *dev) } -static struct usba_udc the_udc = { - .gadget = { - .ops = &usba_udc_ops, - .ep_list = LIST_HEAD_INIT(the_udc.gadget.ep_list), - .max_speed = USB_SPEED_HIGH, - .name = "atmel_usba_udc", - .dev = { - .init_name = "gadget", - .release = nop_release, - }, +static struct usb_gadget usba_gadget_template = { + .ops = &usba_udc_ops, + .max_speed = USB_SPEED_HIGH, + .name = "atmel_usba_udc", + .dev = { + .init_name = "gadget", + .release = nop_release, }, }; @@ -1162,7 +1142,7 @@ static int do_test_mode(struct usba_udc *udc) * Test_SE0_NAK: Force high-speed mode and set up ep0 * for Bulk IN transfers */ - ep = &usba_ep[0]; + ep = &udc->usba_ep[0]; usba_writel(udc, TST, USBA_BF(SPEED_CFG, USBA_SPEED_CFG_FORCE_HIGH)); usba_ep_writel(ep, CFG, @@ -1180,7 +1160,7 @@ static int do_test_mode(struct usba_udc *udc) break; case 0x0400: /* Test_Packet */ - ep = &usba_ep[0]; + ep = &udc->usba_ep[0]; usba_ep_writel(ep, CFG, USBA_BF(EPT_SIZE, USBA_EPT_SIZE_64) | USBA_EPT_DIR_IN @@ -1681,21 +1661,21 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; - for (i = 1; i < USBA_NR_ENDPOINTS; i++) + for (i = 1; i < USBA_NR_DMAS; i++) if (dma_status & (1 << i)) - usba_dma_irq(udc, &usba_ep[i]); + usba_dma_irq(udc, &udc->usba_ep[i]); } ep_status = USBA_BFEXT(EPT_INT, status); if (ep_status) { int i; - for (i = 0; i < USBA_NR_ENDPOINTS; i++) + for (i = 0; i < udc->num_ep; i++) if (ep_status & (1 << i)) { - if (ep_is_control(&usba_ep[i])) - usba_control_irq(udc, &usba_ep[i]); + if (ep_is_control(&udc->usba_ep[i])) + usba_control_irq(udc, &udc->usba_ep[i]); else - usba_ep_irq(udc, &usba_ep[i]); + usba_ep_irq(udc, &udc->usba_ep[i]); } } @@ -1706,7 +1686,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) reset_all_endpoints(udc); if (udc->gadget.speed != USB_SPEED_UNKNOWN - && udc->driver->disconnect) { + && udc->driver && udc->driver->disconnect) { udc->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock(&udc->lock); udc->driver->disconnect(&udc->gadget); @@ -1720,7 +1700,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) DBG(DBG_BUS, "%s bus reset detected\n", usb_speed_string(udc->gadget.speed)); - ep0 = &usba_ep[0]; + ep0 = &udc->usba_ep[0]; ep0->ep.desc = &usba_ep0_desc; ep0->state = WAIT_FOR_SETUP; usba_ep_writel(ep0, CFG, @@ -1792,6 +1772,7 @@ out: static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { + int ret; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); unsigned long flags; @@ -1799,11 +1780,16 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); - clk_enable(udc->pclk); - clk_enable(udc->hclk); + ret = clk_prepare_enable(udc->pclk); + if (ret) + return ret; + ret = clk_prepare_enable(udc->hclk); + if (ret) { + clk_disable_unprepare(udc->pclk); + return ret; + } DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); @@ -1841,42 +1827,187 @@ static int atmel_usba_stop(struct usb_gadget *gadget, toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - udc->gadget.dev.driver = NULL; - udc->driver = NULL; + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); - clk_disable(udc->hclk); - clk_disable(udc->pclk); + DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name); - DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); + udc->driver = NULL; return 0; } +#ifdef CONFIG_OF +static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, + struct usba_udc *udc) +{ + u32 val; + const char *name; + enum of_gpio_flags flags; + struct device_node *np = pdev->dev.of_node; + struct device_node *pp; + int i, ret; + struct usba_ep *eps, *ep; + + udc->num_ep = 0; + + udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, + &flags); + udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + pp = NULL; + while ((pp = of_get_next_child(np, pp))) + udc->num_ep++; + + eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep, + GFP_KERNEL); + if (!eps) + return ERR_PTR(-ENOMEM); + + udc->gadget.ep0 = &eps[0].ep; + + INIT_LIST_HEAD(&eps[0].ep.ep_list); + + pp = NULL; + i = 0; + while ((pp = of_get_next_child(np, pp))) { + ep = &eps[i]; + + ret = of_property_read_u32(pp, "reg", &val); + if (ret) { + dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret); + goto err; + } + ep->index = val; + + ret = of_property_read_u32(pp, "atmel,fifo-size", &val); + if (ret) { + dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret); + goto err; + } + ep->fifo_size = val; + + ret = of_property_read_u32(pp, "atmel,nb-banks", &val); + if (ret) { + dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret); + goto err; + } + ep->nr_banks = val; + + ep->can_dma = of_property_read_bool(pp, "atmel,can-dma"); + ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc"); + + ret = of_property_read_string(pp, "name", &name); + ep->ep.name = name; + + ep->ep_regs = udc->regs + USBA_EPT_BASE(i); + ep->dma_regs = udc->regs + USBA_DMA_BASE(i); + ep->fifo = udc->fifo + USBA_FIFO_BASE(i); + ep->ep.ops = &usba_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); + ep->udc = udc; + INIT_LIST_HEAD(&ep->queue); + + if (i) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + + i++; + } + + if (i == 0) { + dev_err(&pdev->dev, "of_probe: no endpoint specified\n"); + ret = -EINVAL; + goto err; + } + + return eps; +err: + return ERR_PTR(ret); +} +#else +static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, + struct usba_udc *udc) +{ + return ERR_PTR(-ENOSYS); +} +#endif + +static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, + struct usba_udc *udc) +{ + struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct usba_ep *eps; + int i; + + if (!pdata) + return ERR_PTR(-ENXIO); + + eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * pdata->num_ep, + GFP_KERNEL); + if (!eps) + return ERR_PTR(-ENOMEM); + + udc->gadget.ep0 = &eps[0].ep; + + udc->vbus_pin = pdata->vbus_pin; + udc->vbus_pin_inverted = pdata->vbus_pin_inverted; + udc->num_ep = pdata->num_ep; + + INIT_LIST_HEAD(&eps[0].ep.ep_list); + + for (i = 0; i < pdata->num_ep; i++) { + struct usba_ep *ep = &eps[i]; + + ep->ep_regs = udc->regs + USBA_EPT_BASE(i); + ep->dma_regs = udc->regs + USBA_DMA_BASE(i); + ep->fifo = udc->fifo + USBA_FIFO_BASE(i); + ep->ep.ops = &usba_ep_ops; + ep->ep.name = pdata->ep[i].name; + ep->fifo_size = pdata->ep[i].fifo_size; + usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); + ep->udc = udc; + INIT_LIST_HEAD(&ep->queue); + ep->nr_banks = pdata->ep[i].nr_banks; + ep->index = pdata->ep[i].index; + ep->can_dma = pdata->ep[i].can_dma; + ep->can_isoc = pdata->ep[i].can_isoc; + + if (i) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + } + + return eps; +} + static int __init usba_udc_probe(struct platform_device *pdev) { - struct usba_platform_data *pdata = pdev->dev.platform_data; struct resource *regs, *fifo; struct clk *pclk, *hclk; - struct usba_udc *udc = &the_udc; + struct usba_udc *udc; int irq, ret, i; + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + udc->gadget = usba_gadget_template; + INIT_LIST_HEAD(&udc->gadget.ep_list); + regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); - if (!regs || !fifo || !pdata) + if (!regs || !fifo) return -ENXIO; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - pclk = clk_get(&pdev->dev, "pclk"); + pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(pclk)) return PTR_ERR(pclk); - hclk = clk_get(&pdev->dev, "hclk"); - if (IS_ERR(hclk)) { - ret = PTR_ERR(hclk); - goto err_get_hclk; - } + hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) + return PTR_ERR(hclk); spin_lock_init(&udc->lock); udc->pdev = pdev; @@ -1885,99 +2016,57 @@ static int __init usba_udc_probe(struct platform_device *pdev) udc->vbus_pin = -ENODEV; ret = -ENOMEM; - udc->regs = ioremap(regs->start, resource_size(regs)); + udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!udc->regs) { dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); - goto err_map_regs; + return ret; } dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", (unsigned long)regs->start, udc->regs); - udc->fifo = ioremap(fifo->start, resource_size(fifo)); + udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo)); if (!udc->fifo) { dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); - goto err_map_fifo; + return ret; } dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", (unsigned long)fifo->start, udc->fifo); - device_initialize(&udc->gadget.dev); - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - platform_set_drvdata(pdev, udc); /* Make sure we start from a clean slate */ - clk_enable(pclk); + ret = clk_prepare_enable(pclk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n"); + return ret; + } toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - clk_disable(pclk); - - usba_ep = kzalloc(sizeof(struct usba_ep) * pdata->num_ep, - GFP_KERNEL); - if (!usba_ep) - goto err_alloc_ep; - - the_udc.gadget.ep0 = &usba_ep[0].ep; - - INIT_LIST_HEAD(&usba_ep[0].ep.ep_list); - usba_ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0); - usba_ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0); - usba_ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0); - usba_ep[0].ep.ops = &usba_ep_ops; - usba_ep[0].ep.name = pdata->ep[0].name; - usba_ep[0].ep.maxpacket = pdata->ep[0].fifo_size; - usba_ep[0].udc = &the_udc; - INIT_LIST_HEAD(&usba_ep[0].queue); - usba_ep[0].fifo_size = pdata->ep[0].fifo_size; - usba_ep[0].nr_banks = pdata->ep[0].nr_banks; - usba_ep[0].index = pdata->ep[0].index; - usba_ep[0].can_dma = pdata->ep[0].can_dma; - usba_ep[0].can_isoc = pdata->ep[0].can_isoc; - - for (i = 1; i < pdata->num_ep; i++) { - struct usba_ep *ep = &usba_ep[i]; + clk_disable_unprepare(pclk); - ep->ep_regs = udc->regs + USBA_EPT_BASE(i); - ep->dma_regs = udc->regs + USBA_DMA_BASE(i); - ep->fifo = udc->fifo + USBA_FIFO_BASE(i); - ep->ep.ops = &usba_ep_ops; - ep->ep.name = pdata->ep[i].name; - ep->ep.maxpacket = pdata->ep[i].fifo_size; - ep->udc = &the_udc; - INIT_LIST_HEAD(&ep->queue); - ep->fifo_size = pdata->ep[i].fifo_size; - ep->nr_banks = pdata->ep[i].nr_banks; - ep->index = pdata->ep[i].index; - ep->can_dma = pdata->ep[i].can_dma; - ep->can_isoc = pdata->ep[i].can_isoc; + if (pdev->dev.of_node) + udc->usba_ep = atmel_udc_of_init(pdev, udc); + else + udc->usba_ep = usba_udc_pdata(pdev, udc); - list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - } + if (IS_ERR(udc->usba_ep)) + return PTR_ERR(udc->usba_ep); - ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc); + ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0, + "atmel_usba_udc", udc); if (ret) { dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n", irq, ret); - goto err_request_irq; + return ret; } udc->irq = irq; - ret = device_add(&udc->gadget.dev); - if (ret) { - dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret); - goto err_device_add; - } - - if (gpio_is_valid(pdata->vbus_pin)) { - if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) { - udc->vbus_pin = pdata->vbus_pin; - udc->vbus_pin_inverted = pdata->vbus_pin_inverted; - - ret = request_irq(gpio_to_irq(udc->vbus_pin), + if (gpio_is_valid(udc->vbus_pin)) { + if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { + ret = devm_request_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), usba_vbus_irq, 0, "atmel_usba_udc", udc); if (ret) { - gpio_free(udc->vbus_pin); udc->vbus_pin = -ENODEV; dev_warn(&udc->pdev->dev, "failed to request vbus irq; " @@ -1993,90 +2082,50 @@ static int __init usba_udc_probe(struct platform_device *pdev) ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (ret) - goto err_add_udc; + return ret; usba_init_debugfs(udc); - for (i = 1; i < pdata->num_ep; i++) - usba_ep_init_debugfs(udc, &usba_ep[i]); + for (i = 1; i < udc->num_ep; i++) + usba_ep_init_debugfs(udc, &udc->usba_ep[i]); return 0; - -err_add_udc: - if (gpio_is_valid(pdata->vbus_pin)) { - free_irq(gpio_to_irq(udc->vbus_pin), udc); - gpio_free(udc->vbus_pin); - } - - device_unregister(&udc->gadget.dev); - -err_device_add: - free_irq(irq, udc); -err_request_irq: - kfree(usba_ep); -err_alloc_ep: - iounmap(udc->fifo); -err_map_fifo: - iounmap(udc->regs); -err_map_regs: - clk_put(hclk); -err_get_hclk: - clk_put(pclk); - - platform_set_drvdata(pdev, NULL); - - return ret; } static int __exit usba_udc_remove(struct platform_device *pdev) { struct usba_udc *udc; int i; - struct usba_platform_data *pdata = pdev->dev.platform_data; udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); - for (i = 1; i < pdata->num_ep; i++) - usba_ep_cleanup_debugfs(&usba_ep[i]); + for (i = 1; i < udc->num_ep; i++) + usba_ep_cleanup_debugfs(&udc->usba_ep[i]); usba_cleanup_debugfs(udc); - if (gpio_is_valid(udc->vbus_pin)) { - free_irq(gpio_to_irq(udc->vbus_pin), udc); - gpio_free(udc->vbus_pin); - } - - free_irq(udc->irq, udc); - kfree(usba_ep); - iounmap(udc->fifo); - iounmap(udc->regs); - clk_put(udc->hclk); - clk_put(udc->pclk); - - device_unregister(&udc->gadget.dev); - return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id atmel_udc_dt_ids[] = { + { .compatible = "atmel,at91sam9rl-udc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); +#endif + static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { .name = "atmel_usba_udc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_udc_dt_ids), }, }; -static int __init udc_init(void) -{ - return platform_driver_probe(&udc_driver, usba_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); +module_platform_driver_probe(udc_driver, usba_udc_probe); MODULE_DESCRIPTION("Atmel USBA UDC driver"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h index 9791259cbda..a70706e8cb0 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/atmel_usba_udc.h @@ -210,18 +210,12 @@ #define USBA_FIFO_BASE(x) ((x) << 16) /* Synth parameters */ -#define USBA_NR_ENDPOINTS 7 +#define USBA_NR_DMAS 7 #define EP0_FIFO_SIZE 64 #define EP0_EPT_SIZE USBA_EPT_SIZE_64 #define EP0_NR_BANKS 1 -/* - * REVISIT: Try to eliminate this value. Can we rely on req->mapped to - * provide this information? - */ -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - #define FIFO_IOMEM_ID 0 #define CTRL_IOMEM_ID 1 @@ -323,8 +317,10 @@ struct usba_udc { int irq; int vbus_pin; int vbus_pin_inverted; + int num_ep; struct clk *pclk; struct clk *hclk; + struct usba_ep *usba_ep; u16 devstatus; diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index 47a49931361..e969eb809a8 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -19,7 +19,6 @@ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/kconfig.h> @@ -361,24 +360,30 @@ static inline void usb_dma_writel(struct bcm63xx_udc *udc, u32 val, u32 off) bcm_writel(val, udc->iudma_regs + off); } -static inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off) +static inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off, int chan) { - return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off); + return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off + + (ENETDMA_CHAN_WIDTH * chan)); } -static inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off) +static inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off, + int chan) { - bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off); + bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off + + (ENETDMA_CHAN_WIDTH * chan)); } -static inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off) +static inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off, int chan) { - return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off); + return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off + + (ENETDMA_CHAN_WIDTH * chan)); } -static inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off) +static inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off, + int chan) { - bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off); + bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off + + (ENETDMA_CHAN_WIDTH * chan)); } static inline void set_clocks(struct bcm63xx_udc *udc, bool is_enabled) @@ -549,7 +554,7 @@ static void bcm63xx_ep_setup(struct bcm63xx_udc *udc) if (idx < 0) continue; - udc->bep[idx].ep.maxpacket = max_pkt; + usb_ep_set_maxpacket_limit(&udc->bep[idx].ep, max_pkt); val = (idx << USBD_CSR_EP_LOG_SHIFT) | (cfg->dir << USBD_CSR_EP_DIR_SHIFT) | @@ -639,7 +644,7 @@ static void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma, } while (!last_bd); usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK, - ENETDMAC_CHANCFG_REG(iudma->ch_idx)); + ENETDMAC_CHANCFG_REG, iudma->ch_idx); } /** @@ -695,9 +700,9 @@ static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma) bcm63xx_fifo_reset_ep(udc, max(0, iudma->ep_num)); /* stop DMA, then wait for the hardware to wrap up */ - usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG(ch_idx)); + usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG, ch_idx); - while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)) & + while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx) & ENETDMAC_CHANCFG_EN_MASK) { udelay(1); @@ -714,10 +719,10 @@ static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma) dev_warn(udc->dev, "forcibly halting IUDMA channel %d\n", ch_idx); usb_dmac_writel(udc, ENETDMAC_CHANCFG_BUFHALT_MASK, - ENETDMAC_CHANCFG_REG(ch_idx)); + ENETDMAC_CHANCFG_REG, ch_idx); } } - usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG(ch_idx)); + usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG, ch_idx); /* don't leave "live" HW-owned entries for the next guy to step on */ for (d = iudma->bd_ring; d <= iudma->end_bd; d++) @@ -729,11 +734,11 @@ static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma) /* set up IRQs, UBUS burst size, and BD base for this channel */ usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK, - ENETDMAC_IRMASK_REG(ch_idx)); - usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG(ch_idx)); + ENETDMAC_IRMASK_REG, ch_idx); + usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG, ch_idx); - usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG(ch_idx)); - usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG(ch_idx)); + usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG, ch_idx); + usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG, ch_idx); } /** @@ -943,7 +948,7 @@ static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc) bep->ep.ops = &bcm63xx_udc_ep_ops; list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list); bep->halted = 0; - bep->ep.maxpacket = BCM63XX_MAX_CTRL_PKT; + usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT); bep->udc = udc; bep->ep.desc = NULL; INIT_LIST_HEAD(&bep->queue); @@ -1819,7 +1824,6 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget, udc->driver = driver; driver->driver.bus = NULL; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->dev->of_node; spin_unlock_irqrestore(&udc->lock, flags); @@ -1841,7 +1845,6 @@ static int bcm63xx_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&udc->lock, flags); udc->driver = NULL; - udc->gadget.dev.driver = NULL; /* * If we switch the PHY too abruptly after dropping D+, the host @@ -2038,7 +2041,7 @@ static irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id) spin_lock(&udc->lock); usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK, - ENETDMAC_IR_REG(iudma->ch_idx)); + ENETDMAC_IR_REG, iudma->ch_idx); bep = iudma->bep; rc = iudma_read(udc, iudma); @@ -2178,18 +2181,18 @@ static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p) seq_printf(s, " [ep%d]:\n", max_t(int, iudma_defaults[ch_idx].ep_num, 0)); seq_printf(s, " cfg: %08x; irqstat: %08x; irqmask: %08x; maxburst: %08x\n", - usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)), - usb_dmac_readl(udc, ENETDMAC_IR_REG(ch_idx)), - usb_dmac_readl(udc, ENETDMAC_IRMASK_REG(ch_idx)), - usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG(ch_idx))); + usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx), + usb_dmac_readl(udc, ENETDMAC_IR_REG, ch_idx), + usb_dmac_readl(udc, ENETDMAC_IRMASK_REG, ch_idx), + usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG, ch_idx)); - sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG(ch_idx)); - sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG(ch_idx)); + sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG, ch_idx); + sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG, ch_idx); seq_printf(s, " base: %08x; index: %04x_%04x; desc: %04x_%04x %08x\n", - usb_dmas_readl(udc, ENETDMAS_RSTART_REG(ch_idx)), + usb_dmas_readl(udc, ENETDMAS_RSTART_REG, ch_idx), sram2 >> 16, sram2 & 0xffff, sram3 >> 16, sram3 & 0xffff, - usb_dmas_readl(udc, ENETDMAS_SRAM4_REG(ch_idx))); + usb_dmas_readl(udc, ENETDMAS_SRAM4_REG, ch_idx)); seq_printf(s, " desc: %d/%d used", iudma->n_bds_used, iudma->n_bds); @@ -2306,17 +2309,6 @@ static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc) ***********************************************************************/ /** - * bcm63xx_udc_gadget_release - Called from device_release(). - * @dev: Unused. - * - * We get a warning if this function doesn't exist, but it's empty because - * we don't have to free any of the memory allocated with the devm_* APIs. - */ -static void bcm63xx_udc_gadget_release(struct device *dev) -{ -} - -/** * bcm63xx_udc_probe - Initialize a new instance of the UDC. * @pdev: Platform device struct from the bcm63xx BSP code. * @@ -2326,7 +2318,7 @@ static void bcm63xx_udc_gadget_release(struct device *dev) static int bcm63xx_udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct bcm63xx_usbd_platform_data *pd = dev->platform_data; + struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); struct bcm63xx_udc *udc; struct resource *res; int rc = -ENOMEM, i, irq; @@ -2347,33 +2339,20 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "error finding USBD resource\n"); - return -ENXIO; - } - udc->usbd_regs = devm_request_and_ioremap(dev, res); + udc->usbd_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(udc->usbd_regs)) + return PTR_ERR(udc->usbd_regs); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(dev, "error finding IUDMA resource\n"); - return -ENXIO; - } - udc->iudma_regs = devm_request_and_ioremap(dev, res); - - if (!udc->usbd_regs || !udc->iudma_regs) { - dev_err(dev, "error requesting resources\n"); - return -ENXIO; - } + udc->iudma_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(udc->iudma_regs)) + return PTR_ERR(udc->iudma_regs); spin_lock_init(&udc->lock); INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process); - dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.ops = &bcm63xx_udc_ops; udc->gadget.name = dev_name(dev); - udc->gadget.dev.parent = dev; - udc->gadget.dev.release = bcm63xx_udc_gadget_release; - udc->gadget.dev.dma_mask = dev->dma_mask; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; @@ -2413,17 +2392,12 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) } } - rc = device_register(&udc->gadget.dev); - if (rc) - goto out_uninit; - bcm63xx_udc_init_debugfs(udc); rc = usb_add_gadget_udc(dev, &udc->gadget); if (!rc) return 0; bcm63xx_udc_cleanup_debugfs(udc); - device_unregister(&udc->gadget.dev); out_uninit: bcm63xx_uninit_udc_hw(udc); return rc; @@ -2439,10 +2413,8 @@ static int bcm63xx_udc_remove(struct platform_device *pdev) bcm63xx_udc_cleanup_debugfs(udc); usb_del_gadget_udc(&udc->gadget); - device_unregister(&udc->gadget.dev); BUG_ON(udc->driver); - platform_set_drvdata(pdev, NULL); bcm63xx_uninit_udc_hw(udc); return 0; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 1e4bb77f00b..e126b6b248e 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,21 +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 "u_serial.c" -#include "f_acm.c" -#include "f_ecm.c" -#include "u_ether.c" +USB_ETHERNET_MODULE_PARAMETERS(); /*-------------------------------------------------------------------------*/ @@ -105,9 +94,12 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; - /*-------------------------------------------------------------------------*/ +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. @@ -121,15 +113,35 @@ static int __init cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - status = ecm_bind_config(c, hostaddr); - if (status < 0) - return status; + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) { + status = PTR_ERR(f_ecm); + goto err_get_ecm; + } - status = acm_bind_config(c, 0); - if (status < 0) - return status; + status = usb_add_function(c, f_ecm); + if (status) + goto err_add_ecm; + f_acm = usb_get_function(fi_serial); + if (IS_ERR(f_acm)) { + status = PTR_ERR(f_acm); + goto err_get_acm; + } + + status = usb_add_function(c, f_acm); + if (status) + goto err_add_acm; return 0; + +err_add_acm: + usb_put_function(f_acm); +err_get_acm: + usb_remove_function(c, f_ecm); +err_add_ecm: + usb_put_function(f_ecm); +err_get_ecm: + return status; } static struct usb_configuration cdc_config_driver = { @@ -144,6 +156,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)) { @@ -152,15 +165,23 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return -EINVAL; } - /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) + return PTR_ERR(fi_ecm); - /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); - if (status < 0) - goto fail0; + 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. @@ -184,16 +205,20 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return 0; fail1: - gserial_cleanup(); -fail0: - gether_cleanup(); + usb_put_function_instance(fi_serial); +fail: + usb_put_function_instance(fi_ecm); return status; } static int __exit cdc_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); - gether_cleanup(); + usb_put_function(f_acm); + usb_put_function_instance(fi_serial); + 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/composite.c b/drivers/usb/gadget/composite.c index 2a6bfe759c2..f8015193205 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -21,6 +21,24 @@ #include <linux/usb/composite.h> #include <asm/unaligned.h> +#include "u_os_desc.h" + +/** + * struct usb_os_string - represents OS String to be reported by a gadget + * @bLength: total length of the entire descritor, always 0x12 + * @bDescriptorType: USB_DT_STRING + * @qwSignature: the OS String proper + * @bMS_VendorCode: code used by the host for subsequent requests + * @bPad: not used, must be zero + */ +struct usb_os_string { + __u8 bLength; + __u8 bDescriptorType; + __u8 qwSignature[OS_STRING_QW_SIGN_LEN]; + __u8 bMS_VendorCode; + __u8 bPad; +} __packed; + /* * The code in this file is utility code, used to build a gadget driver * from one or more "function" drivers, one or more "configuration" @@ -28,6 +46,12 @@ * with the relevant device-wide data. */ +static struct usb_gadget_strings **get_containers_gs( + struct usb_gadget_string_container *uc) +{ + return (struct usb_gadget_strings **)uc->stash; +} + /** * next_ep_desc() - advance to the next EP descriptor * @t: currect pointer within descriptor array @@ -215,6 +239,18 @@ done: } EXPORT_SYMBOL_GPL(usb_add_function); +void usb_remove_function(struct usb_configuration *c, struct usb_function *f) +{ + if (f->disable) + f->disable(f); + + bitmap_zero(f->endpoints, 32); + list_del(&f->list); + if (f->unbind) + f->unbind(c, f); +} +EXPORT_SYMBOL_GPL(usb_remove_function); + /** * usb_function_deactivate - prevent function and gadget enumeration * @function: the function that isn't yet ready to respond @@ -320,6 +356,25 @@ int usb_interface_id(struct usb_configuration *config, } EXPORT_SYMBOL_GPL(usb_interface_id); +static u8 encode_bMaxPower(enum usb_device_speed speed, + struct usb_configuration *c) +{ + unsigned val; + + if (c->MaxPower) + val = c->MaxPower; + else + val = CONFIG_USB_GADGET_VBUS_DRAW; + if (!val) + return 0; + switch (speed) { + case USB_SPEED_SUPER: + return DIV_ROUND_UP(val, 8); + default: + return DIV_ROUND_UP(val, 2); + } +} + static int config_buf(struct usb_configuration *config, enum usb_device_speed speed, void *buf, u8 type) { @@ -339,7 +394,7 @@ static int config_buf(struct usb_configuration *config, c->bConfigurationValue = config->bConfigurationValue; c->iConfiguration = config->iConfiguration; c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; - c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2); + c->bMaxPower = encode_bMaxPower(speed, config); /* There may be e.g. OTG descriptors */ if (config->descriptors) { @@ -385,6 +440,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) { struct usb_gadget *gadget = cdev->gadget; struct usb_configuration *c; + struct list_head *pos; u8 type = w_value >> 8; enum usb_device_speed speed = USB_SPEED_UNKNOWN; @@ -403,7 +459,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) /* This is a lookup by config *INDEX* */ w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + pos = &cdev->configs; + c = cdev->os_desc_config; + if (c) + goto check_config; + + while ((pos = pos->next) != &cdev->configs) { + c = list_entry(pos, typeof(*c), list); + + /* skip OS Descriptors config which is handled separately */ + if (c == cdev->os_desc_config) + continue; + +check_config: /* ignore configs that won't work at this speed */ switch (speed) { case USB_SPEED_SUPER: @@ -556,6 +625,7 @@ static void reset_config(struct usb_composite_dev *cdev) bitmap_zero(f->endpoints, 32); } cdev->config = NULL; + cdev->delayed_status = 0; } static int set_config(struct usb_composite_dev *cdev, @@ -596,6 +666,7 @@ static int set_config(struct usb_composite_dev *cdev, if (!c) goto done; + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); cdev->config = c; /* Initialize all interfaces by setting them to altsetting zero. */ @@ -656,7 +727,7 @@ static int set_config(struct usb_composite_dev *cdev, } /* when we return, be sure our power usage is valid */ - power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; + power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW; done: usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) @@ -664,6 +735,31 @@ done: return result; } +int usb_add_config_only(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + struct usb_configuration *c; + + if (!config->bConfigurationValue) + return -EINVAL; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) + return -EBUSY; + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + memset(config->interface, 0, sizeof(config->interface)); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_add_config_only); + /** * usb_add_config() - add a configuration to a device. * @cdev: wraps the USB gadget @@ -684,30 +780,18 @@ int usb_add_config(struct usb_composite_dev *cdev, int (*bind)(struct usb_configuration *)) { int status = -EINVAL; - struct usb_configuration *c; + + if (!bind) + goto done; DBG(cdev, "adding config #%u '%s'/%p\n", config->bConfigurationValue, config->label, config); - if (!config->bConfigurationValue || !bind) + status = usb_add_config_only(cdev, config); + if (status) goto done; - /* Prevent duplicate configuration identifiers */ - list_for_each_entry(c, &cdev->configs, list) { - if (c->bConfigurationValue == config->bConfigurationValue) { - status = -EBUSY; - goto done; - } - } - - config->cdev = cdev; - list_add_tail(&config->list, &cdev->configs); - - INIT_LIST_HEAD(&config->functions); - config->next_interface_id = 0; - memset(config->interface, 0, sizeof(config->interface)); - status = bind(config); if (status < 0) { while (!list_empty(&config->functions)) { @@ -860,6 +944,7 @@ static int get_string(struct usb_composite_dev *cdev, void *buf, u16 language, int id) { struct usb_composite_driver *composite = cdev->driver; + struct usb_gadget_string_container *uc; struct usb_configuration *c; struct usb_function *f; int len; @@ -892,6 +977,12 @@ static int get_string(struct usb_composite_dev *cdev, collect_langs(sp, s->wData); } } + list_for_each_entry(uc, &cdev->gstrings, list) { + struct usb_gadget_strings **sp; + + sp = get_containers_gs(uc); + collect_langs(sp, s->wData); + } for (len = 0; len <= 126 && s->wData[len]; len++) continue; @@ -902,6 +993,28 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } + if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) { + struct usb_os_string *b = buf; + b->bLength = sizeof(*b); + b->bDescriptorType = USB_DT_STRING; + compiletime_assert( + sizeof(b->qwSignature) == sizeof(cdev->qw_sign), + "qwSignature size must be equal to qw_sign"); + memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature)); + b->bMS_VendorCode = cdev->b_vendor_code; + b->bPad = 0; + return sizeof(*b); + } + + list_for_each_entry(uc, &cdev->gstrings, list) { + struct usb_gadget_strings **sp; + + sp = get_containers_gs(uc); + len = lookup_string(sp, buf, language, id); + if (len > 0) + return len; + } + /* String IDs are device-scoped, so we look up each string * table we're told about. These lookups are infrequent; * simpler-is-better here. @@ -987,6 +1100,119 @@ int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str) } EXPORT_SYMBOL_GPL(usb_string_ids_tab); +static struct usb_gadget_string_container *copy_gadget_strings( + struct usb_gadget_strings **sp, unsigned n_gstrings, + unsigned n_strings) +{ + struct usb_gadget_string_container *uc; + struct usb_gadget_strings **gs_array; + struct usb_gadget_strings *gs; + struct usb_string *s; + unsigned mem; + unsigned n_gs; + unsigned n_s; + void *stash; + + mem = sizeof(*uc); + mem += sizeof(void *) * (n_gstrings + 1); + mem += sizeof(struct usb_gadget_strings) * n_gstrings; + mem += sizeof(struct usb_string) * (n_strings + 1) * (n_gstrings); + uc = kmalloc(mem, GFP_KERNEL); + if (!uc) + return ERR_PTR(-ENOMEM); + gs_array = get_containers_gs(uc); + stash = uc->stash; + stash += sizeof(void *) * (n_gstrings + 1); + for (n_gs = 0; n_gs < n_gstrings; n_gs++) { + struct usb_string *org_s; + + gs_array[n_gs] = stash; + gs = gs_array[n_gs]; + stash += sizeof(struct usb_gadget_strings); + gs->language = sp[n_gs]->language; + gs->strings = stash; + org_s = sp[n_gs]->strings; + + for (n_s = 0; n_s < n_strings; n_s++) { + s = stash; + stash += sizeof(struct usb_string); + if (org_s->s) + s->s = org_s->s; + else + s->s = ""; + org_s++; + } + s = stash; + s->s = NULL; + stash += sizeof(struct usb_string); + + } + gs_array[n_gs] = NULL; + return uc; +} + +/** + * usb_gstrings_attach() - attach gadget strings to a cdev and assign ids + * @cdev: the device whose string descriptor IDs are being allocated + * and attached. + * @sp: an array of usb_gadget_strings to attach. + * @n_strings: number of entries in each usb_strings array (sp[]->strings) + * + * This function will create a deep copy of usb_gadget_strings and usb_string + * and attach it to the cdev. The actual string (usb_string.s) will not be + * copied but only a referenced will be made. The struct usb_gadget_strings + * array may contain multiple languges and should be NULL terminated. + * The ->language pointer of each struct usb_gadget_strings has to contain the + * same amount of entries. + * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first + * usb_string entry of es-ES containts the translation of the first usb_string + * entry of en-US. Therefore both entries become the same id assign. + */ +struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, + struct usb_gadget_strings **sp, unsigned n_strings) +{ + struct usb_gadget_string_container *uc; + struct usb_gadget_strings **n_gs; + unsigned n_gstrings = 0; + unsigned i; + int ret; + + for (i = 0; sp[i]; i++) + n_gstrings++; + + if (!n_gstrings) + return ERR_PTR(-EINVAL); + + uc = copy_gadget_strings(sp, n_gstrings, n_strings); + if (IS_ERR(uc)) + return ERR_CAST(uc); + + n_gs = get_containers_gs(uc); + ret = usb_string_ids_tab(cdev, n_gs[0]->strings); + if (ret) + goto err; + + for (i = 1; i < n_gstrings; i++) { + struct usb_string *m_s; + struct usb_string *s; + unsigned n; + + m_s = n_gs[0]->strings; + s = n_gs[i]->strings; + for (n = 0; n < n_strings; n++) { + s->id = m_s->id; + s++; + m_s++; + } + } + list_add_tail(&uc->list, &cdev->gstrings); + return n_gs[0]->strings; +err: + kfree(uc); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(usb_gstrings_attach); + /** * usb_string_ids_n() - allocate unused string IDs in batch * @c: the device whose string descriptor IDs are being allocated @@ -1026,6 +1252,156 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) req->status, req->actual, req->length); } +static int count_ext_compat(struct usb_configuration *c) +{ + int i, res; + + res = 0; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + ++res; + } + } + BUG_ON(res > 255); + return res; +} + +static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +{ + int i, count; + + count = 16; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) { + *buf++ = i; + *buf++ = 0x01; + memcpy(buf, d->ext_compat_id, 16); + buf += 22; + } else { + ++buf; + *buf = 0x01; + buf += 23; + } + count += 24; + if (count >= 4096) + return; + } + } +} + +static int count_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + int j; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + return d->ext_prop_count; + } + return 0; +} + +static int len_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + struct usb_os_desc *d; + int j, res; + + res = 10; /* header length */ + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + return min(res + d->ext_prop_len, 4096); + } + return res; +} + +static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) +{ + struct usb_function *f; + struct usb_os_desc *d; + struct usb_os_desc_ext_prop *ext_prop; + int j, count, n, ret; + u8 *start = buf; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + list_for_each_entry(ext_prop, &d->ext_prop, entry) { + /* 4kB minus header length */ + n = buf - start; + if (n >= 4086) + return 0; + + count = ext_prop->data_len + + ext_prop->name_len + 14; + if (count > 4086 - n) + return -EINVAL; + usb_ext_prop_put_size(buf, count); + usb_ext_prop_put_type(buf, ext_prop->type); + ret = usb_ext_prop_put_name(buf, ext_prop->name, + ext_prop->name_len); + if (ret < 0) + return ret; + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + usb_ext_prop_put_unicode(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_BINARY: + usb_ext_prop_put_binary(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_LE32: + /* not implemented */ + case USB_EXT_PROP_BE32: + /* not implemented */ + default: + return -EINVAL; + } + buf += count; + } + } + + return 0; +} + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -1033,7 +1409,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) * housekeeping for the gadget function we're implementing. Most of * the work is in config and function specific setup. */ -static int +int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { struct usb_composite_dev *cdev = get_gadget_data(gadget); @@ -1235,6 +1611,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; default: unknown: + /* + * OS descriptors handling + */ + if (cdev->use_os_string && cdev->os_desc_config && + (ctrl->bRequest & USB_TYPE_VENDOR) && + ctrl->bRequest == cdev->b_vendor_code) { + struct usb_request *req; + struct usb_configuration *os_desc_cfg; + u8 *buf; + int interface; + int count = 0; + + req = cdev->os_desc_req; + req->complete = composite_setup_complete; + buf = req->buf; + os_desc_cfg = cdev->os_desc_config; + memset(buf, 0, w_length); + buf[5] = 0x01; + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_index != 0x4 || (w_value >> 8)) + break; + buf[6] = w_index; + if (w_length == 0x10) { + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + } else { + /* "extended compatibility ID"s */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + buf += 16; + fill_ext_compat(os_desc_cfg, buf); + value = w_length; + } + break; + case USB_RECIP_INTERFACE: + if (w_index != 0x5 || (w_value >> 8)) + break; + interface = w_value & 0xFF; + buf[6] = w_index; + if (w_length == 0x0A) { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + + value = w_length; + } else { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + buf += 10; + value = fill_ext_prop(os_desc_cfg, + interface, buf); + if (value < 0) + return value; + + value = w_length; + } + break; + } + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + return value; + } + VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -1272,8 +1733,22 @@ unknown: struct usb_configuration *c; c = cdev->config; - if (c && c->setup) + if (!c) + goto done; + + /* try current config's setup */ + if (c->setup) { value = c->setup(c, ctrl); + goto done; + } + + /* try the only function in the current config */ + if (!list_is_singular(&c->functions)) + goto done; + f = list_first_entry(&c->functions, struct usb_function, + list); + if (f->setup) + value = f->setup(f, ctrl); } goto done; @@ -1300,7 +1775,7 @@ done: return value; } -static void composite_disconnect(struct usb_gadget *gadget) +void composite_disconnect(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); unsigned long flags; @@ -1318,20 +1793,17 @@ static void composite_disconnect(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ -static ssize_t composite_show_suspended(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t suspended_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct usb_gadget *gadget = dev_to_usb_gadget(dev); struct usb_composite_dev *cdev = get_gadget_data(gadget); return sprintf(buf, "%d\n", cdev->suspended); } +static DEVICE_ATTR_RO(suspended); -static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL); - -static void -composite_unbind(struct usb_gadget *gadget) +static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { struct usb_composite_dev *cdev = get_gadget_data(gadget); @@ -1348,19 +1820,21 @@ composite_unbind(struct usb_gadget *gadget) struct usb_configuration, list); remove_config(cdev, c); } - if (cdev->driver->unbind) + if (cdev->driver->unbind && unbind_driver) cdev->driver->unbind(cdev); - if (cdev->req) { - kfree(cdev->req->buf); - usb_ep_free_request(gadget->ep0, cdev->req); - } - device_remove_file(&gadget->dev, &dev_attr_suspended); + composite_dev_cleanup(cdev); + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); } +static void composite_unbind(struct usb_gadget *gadget) +{ + __composite_unbind(gadget, true); +} + static void update_unchanged_dev_desc(struct usb_device_descriptor *new, const struct usb_device_descriptor *old) { @@ -1399,34 +1873,25 @@ static void update_unchanged_dev_desc(struct usb_device_descriptor *new, new->iProduct = iProduct; } -static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv) -{ - return container_of(gdrv, struct usb_composite_driver, gadget_driver); -} - -static int composite_bind(struct usb_gadget *gadget, - struct usb_gadget_driver *gdriver) +int composite_dev_prepare(struct usb_composite_driver *composite, + struct usb_composite_dev *cdev) { - struct usb_composite_dev *cdev; - struct usb_composite_driver *composite = to_cdriver(gdriver); - int status = -ENOMEM; - - cdev = kzalloc(sizeof *cdev, GFP_KERNEL); - if (!cdev) - return status; - - spin_lock_init(&cdev->lock); - cdev->gadget = gadget; - set_gadget_data(gadget, cdev); - INIT_LIST_HEAD(&cdev->configs); + struct usb_gadget *gadget = cdev->gadget; + int ret = -ENOMEM; /* preallocate control response and buffer */ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); if (!cdev->req) - goto fail; + return -ENOMEM; + cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); if (!cdev->req->buf) goto fail; + + ret = device_create_file(&gadget->dev, &dev_attr_suspended); + if (ret) + goto fail_dev; + cdev->req->complete = composite_setup_complete; gadget->ep0->driver_data = cdev; @@ -1444,7 +1909,79 @@ static int composite_bind(struct usb_gadget *gadget, * we force endpoints to start unassigned; few controller * drivers will zero ep->driver_data. */ - usb_ep_autoconfig_reset(cdev->gadget); + usb_ep_autoconfig_reset(gadget); + return 0; +fail_dev: + kfree(cdev->req->buf); +fail: + usb_ep_free_request(gadget->ep0, cdev->req); + cdev->req = NULL; + return ret; +} + +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0) +{ + int ret = 0; + + cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL); + if (!cdev->os_desc_req) { + ret = PTR_ERR(cdev->os_desc_req); + goto end; + } + + /* OS feature descriptor length <= 4kB */ + cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL); + if (!cdev->os_desc_req->buf) { + ret = PTR_ERR(cdev->os_desc_req->buf); + kfree(cdev->os_desc_req); + goto end; + } + cdev->os_desc_req->complete = composite_setup_complete; +end: + return ret; +} + +void composite_dev_cleanup(struct usb_composite_dev *cdev) +{ + struct usb_gadget_string_container *uc, *tmp; + + list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) { + list_del(&uc->list); + kfree(uc); + } + if (cdev->os_desc_req) { + kfree(cdev->os_desc_req->buf); + usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + } + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(cdev->gadget->ep0, cdev->req); + } + cdev->next_string_id = 0; + device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); +} + +static int composite_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *gdriver) +{ + struct usb_composite_dev *cdev; + struct usb_composite_driver *composite = to_cdriver(gdriver); + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + INIT_LIST_HEAD(&cdev->gstrings); + + status = composite_dev_prepare(composite, cdev); + if (status) + goto fail; /* composite gadget needs to assign strings for whole device (like * serial number), register function drivers, potentially update @@ -1454,22 +1991,23 @@ static int composite_bind(struct usb_gadget *gadget, if (status < 0) goto fail; + if (cdev->use_os_string) { + status = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (status) + goto fail; + } + update_unchanged_dev_desc(&cdev->desc, composite->dev); /* has userspace failed to provide a serial number? */ if (composite->needs_serial && !cdev->desc.iSerialNumber) WARNING(cdev, "userspace failed to provide iSerialNumber\n"); - /* finish up */ - status = device_create_file(&gadget->dev, &dev_attr_suspended); - if (status) - goto fail; - INFO(cdev, "%s ready\n", composite->name); return 0; fail: - composite_unbind(gadget); + __composite_unbind(gadget, false); return status; } @@ -1504,7 +2042,7 @@ composite_resume(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_function *f; - u8 maxpower; + u16 maxpower; /* REVISIT: should we have config level * suspend/resume callbacks? @@ -1518,10 +2056,10 @@ composite_resume(struct usb_gadget *gadget) f->resume(f); } - maxpower = cdev->config->bMaxPower; + maxpower = cdev->config->MaxPower; usb_gadget_vbus_draw(gadget, maxpower ? - (2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW); + maxpower : CONFIG_USB_GADGET_VBUS_DRAW); } cdev->suspended = 0; @@ -1547,10 +2085,7 @@ static const struct usb_gadget_driver composite_driver_template = { /** * usb_composite_probe() - register a composite driver * @driver: the driver to register - * @bind: the callback used to allocate resources that are shared across the - * whole device, such as string IDs, and add its configurations using - * @usb_add_config(). This may fail by returning a negative errno - * value; it should return zero on successful initialization. + * * Context: single threaded during gadget setup * * This function is used to register drivers using the composite driver diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c new file mode 100644 index 00000000000..97142146eea --- /dev/null +++ b/drivers/usb/gadget/configfs.c @@ -0,0 +1,1574 @@ +#include <linux/configfs.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/nls.h> +#include <linux/usb/composite.h> +#include <linux/usb/gadget_configfs.h> +#include "configfs.h" +#include "u_f.h" +#include "u_os_desc.h" + +int check_user_usb_string(const char *name, + struct usb_gadget_strings *stringtab_dev) +{ + unsigned primary_lang; + unsigned sub_lang; + u16 num; + int ret; + + ret = kstrtou16(name, 0, &num); + if (ret) + return ret; + + primary_lang = num & 0x3ff; + sub_lang = num >> 10; + + /* simple sanity check for valid langid */ + switch (primary_lang) { + case 0: + case 0x62 ... 0xfe: + case 0x100 ... 0x3ff: + return -EINVAL; + } + if (!sub_lang) + return -EINVAL; + + stringtab_dev->language = num; + return 0; +} + +#define MAX_NAME_LEN 40 +#define MAX_USB_STRING_LANGS 2 + +struct gadget_info { + struct config_group group; + struct config_group functions_group; + struct config_group configs_group; + struct config_group strings_group; + struct config_group os_desc_group; + struct config_group *default_groups[5]; + + struct mutex lock; + struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; + struct list_head string_list; + struct list_head available_func; + + const char *udc_name; +#ifdef CONFIG_USB_OTG + struct usb_otg_descriptor otg; +#endif + struct usb_composite_driver composite; + struct usb_composite_dev cdev; + bool use_os_desc; + char b_vendor_code; + char qw_sign[OS_STRING_QW_SIGN_LEN]; +}; + +struct config_usb_cfg { + struct config_group group; + struct config_group strings_group; + struct config_group *default_groups[2]; + struct list_head string_list; + struct usb_configuration c; + struct list_head func_list; + struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1]; +}; + +struct gadget_strings { + struct usb_gadget_strings stringtab_dev; + struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; + char *manufacturer; + char *product; + char *serialnumber; + + struct config_group group; + struct list_head list; +}; + +struct os_desc { + struct config_group group; +}; + +struct gadget_config_name { + struct usb_gadget_strings stringtab_dev; + struct usb_string strings; + char *configuration; + + struct config_group group; + struct list_head list; +}; + +static int usb_string_copy(const char *s, char **s_copy) +{ + int ret; + char *str; + char *copy = *s_copy; + ret = strlen(s); + if (ret > 126) + return -EOVERFLOW; + + str = kstrdup(s, GFP_KERNEL); + if (!str) + return -ENOMEM; + if (str[ret - 1] == '\n') + str[ret - 1] = '\0'; + kfree(copy); + *s_copy = str; + return 0; +} + +CONFIGFS_ATTR_STRUCT(gadget_info); +CONFIGFS_ATTR_STRUCT(config_usb_cfg); + +#define GI_DEVICE_DESC_ITEM_ATTR(name) \ + static struct gadget_info_attribute gadget_cdev_desc_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_dev_desc_##name##_show, \ + gadget_dev_desc_##name##_store) + +#define GI_DEVICE_DESC_SIMPLE_R_u8(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ + char *page) \ +{ \ + return sprintf(page, "0x%02x\n", gi->cdev.desc.__name); \ +} + +#define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ + static ssize_t gadget_dev_desc_##__name##_show(struct gadget_info *gi, \ + char *page) \ +{ \ + return sprintf(page, "0x%04x\n", le16_to_cpup(&gi->cdev.desc.__name)); \ +} + + +#define GI_DEVICE_DESC_SIMPLE_W_u8(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u8 val; \ + int ret; \ + ret = kstrtou8(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->cdev.desc._name = val; \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_W_u16(_name) \ + static ssize_t gadget_dev_desc_##_name##_store(struct gadget_info *gi, \ + const char *page, size_t len) \ +{ \ + u16 val; \ + int ret; \ + ret = kstrtou16(page, 0, &val); \ + if (ret) \ + return ret; \ + gi->cdev.desc._name = cpu_to_le16p(&val); \ + return len; \ +} + +#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ + GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ + GI_DEVICE_DESC_SIMPLE_W_##_type(_name) + +GI_DEVICE_DESC_SIMPLE_R_u16(bcdUSB); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceSubClass, u8); +GI_DEVICE_DESC_SIMPLE_RW(bDeviceProtocol, u8); +GI_DEVICE_DESC_SIMPLE_RW(bMaxPacketSize0, u8); +GI_DEVICE_DESC_SIMPLE_RW(idVendor, u16); +GI_DEVICE_DESC_SIMPLE_RW(idProduct, u16); +GI_DEVICE_DESC_SIMPLE_R_u16(bcdDevice); + +static ssize_t is_valid_bcd(u16 bcd_val) +{ + if ((bcd_val & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 4) & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 8) & 0xf) > 9) + return -EINVAL; + if (((bcd_val >> 12) & 0xf) > 9) + return -EINVAL; + return 0; +} + +static ssize_t gadget_dev_desc_bcdDevice_store(struct gadget_info *gi, + const char *page, size_t len) +{ + u16 bcdDevice; + int ret; + + ret = kstrtou16(page, 0, &bcdDevice); + if (ret) + return ret; + ret = is_valid_bcd(bcdDevice); + if (ret) + return ret; + + gi->cdev.desc.bcdDevice = cpu_to_le16(bcdDevice); + return len; +} + +static ssize_t gadget_dev_desc_bcdUSB_store(struct gadget_info *gi, + const char *page, size_t len) +{ + u16 bcdUSB; + int ret; + + ret = kstrtou16(page, 0, &bcdUSB); + if (ret) + return ret; + ret = is_valid_bcd(bcdUSB); + if (ret) + return ret; + + gi->cdev.desc.bcdUSB = cpu_to_le16(bcdUSB); + return len; +} + +static ssize_t gadget_dev_desc_UDC_show(struct gadget_info *gi, char *page) +{ + return sprintf(page, "%s\n", gi->udc_name ?: ""); +} + +static int unregister_gadget(struct gadget_info *gi) +{ + int ret; + + if (!gi->udc_name) + return -ENODEV; + + ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver); + if (ret) + return ret; + kfree(gi->udc_name); + gi->udc_name = NULL; + return 0; +} + +static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, + const char *page, size_t len) +{ + char *name; + int ret; + + name = kstrdup(page, GFP_KERNEL); + if (!name) + return -ENOMEM; + if (name[len - 1] == '\n') + name[len - 1] = '\0'; + + mutex_lock(&gi->lock); + + if (!strlen(name)) { + ret = unregister_gadget(gi); + if (ret) + goto err; + } else { + if (gi->udc_name) { + ret = -EBUSY; + goto err; + } + ret = udc_attach_driver(name, &gi->composite.gadget_driver); + if (ret) + goto err; + gi->udc_name = name; + } + mutex_unlock(&gi->lock); + return len; +err: + kfree(name); + mutex_unlock(&gi->lock); + return ret; +} + +GI_DEVICE_DESC_ITEM_ATTR(bDeviceClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceSubClass); +GI_DEVICE_DESC_ITEM_ATTR(bDeviceProtocol); +GI_DEVICE_DESC_ITEM_ATTR(bMaxPacketSize0); +GI_DEVICE_DESC_ITEM_ATTR(idVendor); +GI_DEVICE_DESC_ITEM_ATTR(idProduct); +GI_DEVICE_DESC_ITEM_ATTR(bcdDevice); +GI_DEVICE_DESC_ITEM_ATTR(bcdUSB); +GI_DEVICE_DESC_ITEM_ATTR(UDC); + +static struct configfs_attribute *gadget_root_attrs[] = { + &gadget_cdev_desc_bDeviceClass.attr, + &gadget_cdev_desc_bDeviceSubClass.attr, + &gadget_cdev_desc_bDeviceProtocol.attr, + &gadget_cdev_desc_bMaxPacketSize0.attr, + &gadget_cdev_desc_idVendor.attr, + &gadget_cdev_desc_idProduct.attr, + &gadget_cdev_desc_bcdDevice.attr, + &gadget_cdev_desc_bcdUSB.attr, + &gadget_cdev_desc_UDC.attr, + NULL, +}; + +static inline struct gadget_info *to_gadget_info(struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_info, group); +} + +static inline struct gadget_strings *to_gadget_strings(struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_strings, + group); +} + +static inline struct gadget_config_name *to_gadget_config_name( + struct config_item *item) +{ + return container_of(to_config_group(item), struct gadget_config_name, + group); +} + +static inline struct config_usb_cfg *to_config_usb_cfg(struct config_item *item) +{ + return container_of(to_config_group(item), struct config_usb_cfg, + group); +} + +static inline struct usb_function_instance *to_usb_function_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), + struct usb_function_instance, group); +} + +static void gadget_info_attr_release(struct config_item *item) +{ + struct gadget_info *gi = to_gadget_info(item); + + WARN_ON(!list_empty(&gi->cdev.configs)); + WARN_ON(!list_empty(&gi->string_list)); + WARN_ON(!list_empty(&gi->available_func)); + kfree(gi->composite.gadget_driver.function); + kfree(gi); +} + +CONFIGFS_ATTR_OPS(gadget_info); + +static struct configfs_item_operations gadget_root_item_ops = { + .release = gadget_info_attr_release, + .show_attribute = gadget_info_attr_show, + .store_attribute = gadget_info_attr_store, +}; + +static void gadget_config_attr_release(struct config_item *item) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(item); + + WARN_ON(!list_empty(&cfg->c.functions)); + list_del(&cfg->c.list); + kfree(cfg->c.label); + kfree(cfg); +} + +static int config_usb_cfg_link( + struct config_item *usb_cfg_ci, + struct config_item *usb_func_ci) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); + struct usb_composite_dev *cdev = cfg->c.cdev; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + struct config_group *group = to_config_group(usb_func_ci); + struct usb_function_instance *fi = container_of(group, + struct usb_function_instance, group); + struct usb_function_instance *a_fi; + struct usb_function *f; + int ret; + + mutex_lock(&gi->lock); + /* + * Make sure this function is from within our _this_ gadget and not + * from another gadget or a random directory. + * Also a function instance can only be linked once. + */ + list_for_each_entry(a_fi, &gi->available_func, cfs_list) { + if (a_fi == fi) + break; + } + if (a_fi != fi) { + ret = -EINVAL; + goto out; + } + + list_for_each_entry(f, &cfg->func_list, list) { + if (f->fi == fi) { + ret = -EEXIST; + goto out; + } + } + + f = usb_get_function(fi); + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto out; + } + + /* stash the function until we bind it to the gadget */ + list_add_tail(&f->list, &cfg->func_list); + ret = 0; +out: + mutex_unlock(&gi->lock); + return ret; +} + +static int config_usb_cfg_unlink( + struct config_item *usb_cfg_ci, + struct config_item *usb_func_ci) +{ + struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci); + struct usb_composite_dev *cdev = cfg->c.cdev; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + struct config_group *group = to_config_group(usb_func_ci); + struct usb_function_instance *fi = container_of(group, + struct usb_function_instance, group); + struct usb_function *f; + + /* + * ideally I would like to forbid to unlink functions while a gadget is + * bound to an UDC. Since this isn't possible at the moment, we simply + * force an unbind, the function is available here and then we can + * remove the function. + */ + mutex_lock(&gi->lock); + if (gi->udc_name) + unregister_gadget(gi); + WARN_ON(gi->udc_name); + + list_for_each_entry(f, &cfg->func_list, list) { + if (f->fi == fi) { + list_del(&f->list); + usb_put_function(f); + mutex_unlock(&gi->lock); + return 0; + } + } + mutex_unlock(&gi->lock); + WARN(1, "Unable to locate function to unbind\n"); + return 0; +} + +CONFIGFS_ATTR_OPS(config_usb_cfg); + +static struct configfs_item_operations gadget_config_item_ops = { + .release = gadget_config_attr_release, + .show_attribute = config_usb_cfg_attr_show, + .store_attribute = config_usb_cfg_attr_store, + .allow_link = config_usb_cfg_link, + .drop_link = config_usb_cfg_unlink, +}; + + +static ssize_t gadget_config_desc_MaxPower_show(struct config_usb_cfg *cfg, + char *page) +{ + return sprintf(page, "%u\n", cfg->c.MaxPower); +} + +static ssize_t gadget_config_desc_MaxPower_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u16 val; + int ret; + ret = kstrtou16(page, 0, &val); + if (ret) + return ret; + if (DIV_ROUND_UP(val, 8) > 0xff) + return -ERANGE; + cfg->c.MaxPower = val; + return len; +} + +static ssize_t gadget_config_desc_bmAttributes_show(struct config_usb_cfg *cfg, + char *page) +{ + return sprintf(page, "0x%02x\n", cfg->c.bmAttributes); +} + +static ssize_t gadget_config_desc_bmAttributes_store(struct config_usb_cfg *cfg, + const char *page, size_t len) +{ + u8 val; + int ret; + ret = kstrtou8(page, 0, &val); + if (ret) + return ret; + if (!(val & USB_CONFIG_ATT_ONE)) + return -EINVAL; + if (val & ~(USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | + USB_CONFIG_ATT_WAKEUP)) + return -EINVAL; + cfg->c.bmAttributes = val; + return len; +} + +#define CFG_CONFIG_DESC_ITEM_ATTR(name) \ + static struct config_usb_cfg_attribute gadget_usb_cfg_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + gadget_config_desc_##name##_show, \ + gadget_config_desc_##name##_store) + +CFG_CONFIG_DESC_ITEM_ATTR(MaxPower); +CFG_CONFIG_DESC_ITEM_ATTR(bmAttributes); + +static struct configfs_attribute *gadget_config_attrs[] = { + &gadget_usb_cfg_MaxPower.attr, + &gadget_usb_cfg_bmAttributes.attr, + NULL, +}; + +static struct config_item_type gadget_config_type = { + .ct_item_ops = &gadget_config_item_ops, + .ct_attrs = gadget_config_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item_type gadget_root_type = { + .ct_item_ops = &gadget_root_item_ops, + .ct_attrs = gadget_root_attrs, + .ct_owner = THIS_MODULE, +}; + +static void composite_init_dev(struct usb_composite_dev *cdev) +{ + spin_lock_init(&cdev->lock); + INIT_LIST_HEAD(&cdev->configs); + INIT_LIST_HEAD(&cdev->gstrings); +} + +static struct config_group *function_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct usb_function_instance *fi; + char buf[MAX_NAME_LEN]; + char *func_name; + char *instance_name; + int ret; + + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + func_name = buf; + instance_name = strchr(func_name, '.'); + if (!instance_name) { + pr_err("Unable to locate . in FUNC.INSTANCE\n"); + return ERR_PTR(-EINVAL); + } + *instance_name = '\0'; + instance_name++; + + fi = usb_get_function_instance(func_name); + if (IS_ERR(fi)) + return ERR_CAST(fi); + + ret = config_item_set_name(&fi->group.cg_item, name); + if (ret) { + usb_put_function_instance(fi); + return ERR_PTR(ret); + } + if (fi->set_inst_name) { + ret = fi->set_inst_name(fi, instance_name); + if (ret) { + usb_put_function_instance(fi); + return ERR_PTR(ret); + } + } + + gi = container_of(group, struct gadget_info, functions_group); + + mutex_lock(&gi->lock); + list_add_tail(&fi->cfs_list, &gi->available_func); + mutex_unlock(&gi->lock); + return &fi->group; +} + +static void function_drop( + struct config_group *group, + struct config_item *item) +{ + struct usb_function_instance *fi = to_usb_function_instance(item); + struct gadget_info *gi; + + gi = container_of(group, struct gadget_info, functions_group); + + mutex_lock(&gi->lock); + list_del(&fi->cfs_list); + mutex_unlock(&gi->lock); + config_item_put(item); +} + +static struct configfs_group_operations functions_ops = { + .make_group = &function_make, + .drop_item = &function_drop, +}; + +static struct config_item_type functions_type = { + .ct_group_ops = &functions_ops, + .ct_owner = THIS_MODULE, +}; + +CONFIGFS_ATTR_STRUCT(gadget_config_name); +GS_STRINGS_RW(gadget_config_name, configuration); + +static struct configfs_attribute *gadget_config_name_langid_attrs[] = { + &gadget_config_name_configuration.attr, + NULL, +}; + +static void gadget_config_name_attr_release(struct config_item *item) +{ + struct gadget_config_name *cn = to_gadget_config_name(item); + + kfree(cn->configuration); + + list_del(&cn->list); + kfree(cn); +} + +USB_CONFIG_STRING_RW_OPS(gadget_config_name); +USB_CONFIG_STRINGS_LANG(gadget_config_name, config_usb_cfg); + +static struct config_group *config_desc_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + struct config_usb_cfg *cfg; + char buf[MAX_NAME_LEN]; + char *num_str; + u8 num; + int ret; + + gi = container_of(group, struct gadget_info, configs_group); + ret = snprintf(buf, MAX_NAME_LEN, "%s", name); + if (ret >= MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + num_str = strchr(buf, '.'); + if (!num_str) { + pr_err("Unable to locate . in name.bConfigurationValue\n"); + return ERR_PTR(-EINVAL); + } + + *num_str = '\0'; + num_str++; + + if (!strlen(buf)) + return ERR_PTR(-EINVAL); + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return ERR_PTR(-ENOMEM); + cfg->c.label = kstrdup(buf, GFP_KERNEL); + if (!cfg->c.label) { + ret = -ENOMEM; + goto err; + } + cfg->c.bConfigurationValue = num; + cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; + cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; + INIT_LIST_HEAD(&cfg->string_list); + INIT_LIST_HEAD(&cfg->func_list); + + cfg->group.default_groups = cfg->default_groups; + cfg->default_groups[0] = &cfg->strings_group; + + config_group_init_type_name(&cfg->group, name, + &gadget_config_type); + config_group_init_type_name(&cfg->strings_group, "strings", + &gadget_config_name_strings_type); + + ret = usb_add_config_only(&gi->cdev, &cfg->c); + if (ret) + goto err; + + return &cfg->group; +err: + kfree(cfg->c.label); + kfree(cfg); + return ERR_PTR(ret); +} + +static void config_desc_drop( + struct config_group *group, + struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations config_desc_ops = { + .make_group = &config_desc_make, + .drop_item = &config_desc_drop, +}; + +static struct config_item_type config_desc_type = { + .ct_group_ops = &config_desc_ops, + .ct_owner = THIS_MODULE, +}; + +CONFIGFS_ATTR_STRUCT(gadget_strings); +GS_STRINGS_RW(gadget_strings, manufacturer); +GS_STRINGS_RW(gadget_strings, product); +GS_STRINGS_RW(gadget_strings, serialnumber); + +static struct configfs_attribute *gadget_strings_langid_attrs[] = { + &gadget_strings_manufacturer.attr, + &gadget_strings_product.attr, + &gadget_strings_serialnumber.attr, + NULL, +}; + +static void gadget_strings_attr_release(struct config_item *item) +{ + struct gadget_strings *gs = to_gadget_strings(item); + + kfree(gs->manufacturer); + kfree(gs->product); + kfree(gs->serialnumber); + + list_del(&gs->list); + kfree(gs); +} + +USB_CONFIG_STRING_RW_OPS(gadget_strings); +USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info); + +static inline struct os_desc *to_os_desc(struct config_item *item) +{ + return container_of(to_config_group(item), struct os_desc, group); +} + +CONFIGFS_ATTR_STRUCT(os_desc); +CONFIGFS_ATTR_OPS(os_desc); + +static ssize_t os_desc_use_show(struct os_desc *os_desc, char *page) +{ + struct gadget_info *gi; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + + return sprintf(page, "%d", gi->use_os_desc); +} + +static ssize_t os_desc_use_store(struct os_desc *os_desc, const char *page, + size_t len) +{ + struct gadget_info *gi; + int ret; + bool use; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + + mutex_lock(&gi->lock); + ret = strtobool(page, &use); + if (!ret) { + gi->use_os_desc = use; + ret = len; + } + mutex_unlock(&gi->lock); + + return ret; +} + +static struct os_desc_attribute os_desc_use = + __CONFIGFS_ATTR(use, S_IRUGO | S_IWUSR, + os_desc_use_show, + os_desc_use_store); + +static ssize_t os_desc_b_vendor_code_show(struct os_desc *os_desc, char *page) +{ + struct gadget_info *gi; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + + return sprintf(page, "%d", gi->b_vendor_code); +} + +static ssize_t os_desc_b_vendor_code_store(struct os_desc *os_desc, + const char *page, size_t len) +{ + struct gadget_info *gi; + int ret; + u8 b_vendor_code; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + + mutex_lock(&gi->lock); + ret = kstrtou8(page, 0, &b_vendor_code); + if (!ret) { + gi->b_vendor_code = b_vendor_code; + ret = len; + } + mutex_unlock(&gi->lock); + + return ret; +} + +static struct os_desc_attribute os_desc_b_vendor_code = + __CONFIGFS_ATTR(b_vendor_code, S_IRUGO | S_IWUSR, + os_desc_b_vendor_code_show, + os_desc_b_vendor_code_store); + +static ssize_t os_desc_qw_sign_show(struct os_desc *os_desc, char *page) +{ + struct gadget_info *gi; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + + memcpy(page, gi->qw_sign, OS_STRING_QW_SIGN_LEN); + + return OS_STRING_QW_SIGN_LEN; +} + +static ssize_t os_desc_qw_sign_store(struct os_desc *os_desc, const char *page, + size_t len) +{ + struct gadget_info *gi; + int res, l; + + gi = to_gadget_info(os_desc->group.cg_item.ci_parent); + l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); + if (page[l - 1] == '\n') + --l; + + mutex_lock(&gi->lock); + res = utf8s_to_utf16s(page, l, + UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign, + OS_STRING_QW_SIGN_LEN); + if (res > 0) + res = len; + mutex_unlock(&gi->lock); + + return res; +} + +static struct os_desc_attribute os_desc_qw_sign = + __CONFIGFS_ATTR(qw_sign, S_IRUGO | S_IWUSR, + os_desc_qw_sign_show, + os_desc_qw_sign_store); + +static struct configfs_attribute *os_desc_attrs[] = { + &os_desc_use.attr, + &os_desc_b_vendor_code.attr, + &os_desc_qw_sign.attr, + NULL, +}; + +static void os_desc_attr_release(struct config_item *item) +{ + struct os_desc *os_desc = to_os_desc(item); + kfree(os_desc); +} + +static int os_desc_link(struct config_item *os_desc_ci, + struct config_item *usb_cfg_ci) +{ + struct gadget_info *gi = container_of(to_config_group(os_desc_ci), + struct gadget_info, os_desc_group); + struct usb_composite_dev *cdev = &gi->cdev; + struct config_usb_cfg *c_target = + container_of(to_config_group(usb_cfg_ci), + struct config_usb_cfg, group); + struct usb_configuration *c; + int ret; + + mutex_lock(&gi->lock); + list_for_each_entry(c, &cdev->configs, list) { + if (c == &c_target->c) + break; + } + if (c != &c_target->c) { + ret = -EINVAL; + goto out; + } + + if (cdev->os_desc_config) { + ret = -EBUSY; + goto out; + } + + cdev->os_desc_config = &c_target->c; + ret = 0; + +out: + mutex_unlock(&gi->lock); + return ret; +} + +static int os_desc_unlink(struct config_item *os_desc_ci, + struct config_item *usb_cfg_ci) +{ + struct gadget_info *gi = container_of(to_config_group(os_desc_ci), + struct gadget_info, os_desc_group); + struct usb_composite_dev *cdev = &gi->cdev; + + mutex_lock(&gi->lock); + if (gi->udc_name) + unregister_gadget(gi); + cdev->os_desc_config = NULL; + WARN_ON(gi->udc_name); + mutex_unlock(&gi->lock); + return 0; +} + +static struct configfs_item_operations os_desc_ops = { + .release = os_desc_attr_release, + .show_attribute = os_desc_attr_show, + .store_attribute = os_desc_attr_store, + .allow_link = os_desc_link, + .drop_link = os_desc_unlink, +}; + +static struct config_item_type os_desc_type = { + .ct_item_ops = &os_desc_ops, + .ct_attrs = os_desc_attrs, + .ct_owner = THIS_MODULE, +}; + +CONFIGFS_ATTR_STRUCT(usb_os_desc); +CONFIGFS_ATTR_OPS(usb_os_desc); + + +static inline struct usb_os_desc_ext_prop +*to_usb_os_desc_ext_prop(struct config_item *item) +{ + return container_of(item, struct usb_os_desc_ext_prop, item); +} + +CONFIGFS_ATTR_STRUCT(usb_os_desc_ext_prop); +CONFIGFS_ATTR_OPS(usb_os_desc_ext_prop); + +static ssize_t ext_prop_type_show(struct usb_os_desc_ext_prop *ext_prop, + char *page) +{ + return sprintf(page, "%d", ext_prop->type); +} + +static ssize_t ext_prop_type_store(struct usb_os_desc_ext_prop *ext_prop, + const char *page, size_t len) +{ + struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); + u8 type; + int ret; + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + ret = kstrtou8(page, 0, &type); + if (ret) + goto end; + if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { + ret = -EINVAL; + goto end; + } + + if ((ext_prop->type == USB_EXT_PROP_BINARY || + ext_prop->type == USB_EXT_PROP_LE32 || + ext_prop->type == USB_EXT_PROP_BE32) && + (type == USB_EXT_PROP_UNICODE || + type == USB_EXT_PROP_UNICODE_ENV || + type == USB_EXT_PROP_UNICODE_LINK)) + ext_prop->data_len <<= 1; + else if ((ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && + (type == USB_EXT_PROP_BINARY || + type == USB_EXT_PROP_LE32 || + type == USB_EXT_PROP_BE32)) + ext_prop->data_len >>= 1; + ext_prop->type = type; + ret = len; + +end: + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + return ret; +} + +static ssize_t ext_prop_data_show(struct usb_os_desc_ext_prop *ext_prop, + char *page) +{ + int len = ext_prop->data_len; + + if (ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) + len >>= 1; + memcpy(page, ext_prop->data, len); + + return len; +} + +static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, + const char *page, size_t len) +{ + struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); + char *new_data; + size_t ret_len = len; + + if (page[len - 1] == '\n' || page[len - 1] == '\0') + --len; + new_data = kzalloc(len, GFP_KERNEL); + if (!new_data) + return -ENOMEM; + + memcpy(new_data, page, len); + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + kfree(ext_prop->data); + ext_prop->data = new_data; + desc->ext_prop_len -= ext_prop->data_len; + ext_prop->data_len = len; + desc->ext_prop_len += ext_prop->data_len; + if (ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { + desc->ext_prop_len -= ext_prop->data_len; + ext_prop->data_len <<= 1; + ext_prop->data_len += 2; + desc->ext_prop_len += ext_prop->data_len; + } + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + return ret_len; +} + +static struct usb_os_desc_ext_prop_attribute ext_prop_type = + __CONFIGFS_ATTR(type, S_IRUGO | S_IWUSR, + ext_prop_type_show, ext_prop_type_store); + +static struct usb_os_desc_ext_prop_attribute ext_prop_data = + __CONFIGFS_ATTR(data, S_IRUGO | S_IWUSR, + ext_prop_data_show, ext_prop_data_store); + +static struct configfs_attribute *ext_prop_attrs[] = { + &ext_prop_type.attr, + &ext_prop_data.attr, + NULL, +}; + +static void usb_os_desc_ext_prop_release(struct config_item *item) +{ + struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); + + kfree(ext_prop); /* frees a whole chunk */ +} + +static struct configfs_item_operations ext_prop_ops = { + .release = usb_os_desc_ext_prop_release, + .show_attribute = usb_os_desc_ext_prop_attr_show, + .store_attribute = usb_os_desc_ext_prop_attr_store, +}; + +static struct config_item *ext_prop_make( + struct config_group *group, + const char *name) +{ + struct usb_os_desc_ext_prop *ext_prop; + struct config_item_type *ext_prop_type; + struct usb_os_desc *desc; + char *vlabuf; + + vla_group(data_chunk); + vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); + vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); + + vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); + if (!vlabuf) + return ERR_PTR(-ENOMEM); + + ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); + ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); + + desc = container_of(group, struct usb_os_desc, group); + ext_prop_type->ct_item_ops = &ext_prop_ops; + ext_prop_type->ct_attrs = ext_prop_attrs; + ext_prop_type->ct_owner = desc->owner; + + config_item_init_type_name(&ext_prop->item, name, ext_prop_type); + + ext_prop->name = kstrdup(name, GFP_KERNEL); + if (!ext_prop->name) { + kfree(vlabuf); + return ERR_PTR(-ENOMEM); + } + desc->ext_prop_len += 14; + ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + desc->ext_prop_len += ext_prop->name_len; + list_add_tail(&ext_prop->entry, &desc->ext_prop); + ++desc->ext_prop_count; + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + + return &ext_prop->item; +} + +static void ext_prop_drop(struct config_group *group, struct config_item *item) +{ + struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); + struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + list_del(&ext_prop->entry); + --desc->ext_prop_count; + kfree(ext_prop->name); + desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + config_item_put(item); +} + +static struct configfs_group_operations interf_grp_ops = { + .make_item = &ext_prop_make, + .drop_item = &ext_prop_drop, +}; + +static struct configfs_item_operations interf_item_ops = { + .show_attribute = usb_os_desc_attr_show, + .store_attribute = usb_os_desc_attr_store, +}; + +static ssize_t interf_grp_compatible_id_show(struct usb_os_desc *desc, + char *page) +{ + memcpy(page, desc->ext_compat_id, 8); + return 8; +} + +static ssize_t interf_grp_compatible_id_store(struct usb_os_desc *desc, + const char *page, size_t len) +{ + int l; + + l = min_t(int, 8, len); + if (page[l - 1] == '\n') + --l; + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + memcpy(desc->ext_compat_id, page, l); + desc->ext_compat_id[l] = '\0'; + + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + + return len; +} + +static struct usb_os_desc_attribute interf_grp_attr_compatible_id = + __CONFIGFS_ATTR(compatible_id, S_IRUGO | S_IWUSR, + interf_grp_compatible_id_show, + interf_grp_compatible_id_store); + +static ssize_t interf_grp_sub_compatible_id_show(struct usb_os_desc *desc, + char *page) +{ + memcpy(page, desc->ext_compat_id + 8, 8); + return 8; +} + +static ssize_t interf_grp_sub_compatible_id_store(struct usb_os_desc *desc, + const char *page, size_t len) +{ + int l; + + l = min_t(int, 8, len); + if (page[l - 1] == '\n') + --l; + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + memcpy(desc->ext_compat_id + 8, page, l); + desc->ext_compat_id[l + 8] = '\0'; + + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + + return len; +} + +static struct usb_os_desc_attribute interf_grp_attr_sub_compatible_id = + __CONFIGFS_ATTR(sub_compatible_id, S_IRUGO | S_IWUSR, + interf_grp_sub_compatible_id_show, + interf_grp_sub_compatible_id_store); + +static struct configfs_attribute *interf_grp_attrs[] = { + &interf_grp_attr_compatible_id.attr, + &interf_grp_attr_sub_compatible_id.attr, + NULL +}; + +int usb_os_desc_prepare_interf_dir(struct config_group *parent, + int n_interf, + struct usb_os_desc **desc, + char **names, + struct module *owner) +{ + struct config_group **f_default_groups, *os_desc_group, + **interface_groups; + struct config_item_type *os_desc_type, *interface_type; + + vla_group(data_chunk); + vla_item(data_chunk, struct config_group *, f_default_groups, 2); + vla_item(data_chunk, struct config_group, os_desc_group, 1); + vla_item(data_chunk, struct config_group *, interface_groups, + n_interf + 1); + vla_item(data_chunk, struct config_item_type, os_desc_type, 1); + vla_item(data_chunk, struct config_item_type, interface_type, 1); + + char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); + if (!vlabuf) + return -ENOMEM; + + f_default_groups = vla_ptr(vlabuf, data_chunk, f_default_groups); + os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); + os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); + interface_groups = vla_ptr(vlabuf, data_chunk, interface_groups); + interface_type = vla_ptr(vlabuf, data_chunk, interface_type); + + parent->default_groups = f_default_groups; + os_desc_type->ct_owner = owner; + config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); + f_default_groups[0] = os_desc_group; + + os_desc_group->default_groups = interface_groups; + interface_type->ct_item_ops = &interf_item_ops; + interface_type->ct_group_ops = &interf_grp_ops; + interface_type->ct_attrs = interf_grp_attrs; + interface_type->ct_owner = owner; + + while (n_interf--) { + struct usb_os_desc *d; + + d = desc[n_interf]; + d->owner = owner; + config_group_init_type_name(&d->group, "", interface_type); + config_item_set_name(&d->group.cg_item, "interface.%s", + names[n_interf]); + interface_groups[n_interf] = &d->group; + } + + return 0; +} +EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); + +static int configfs_do_nothing(struct usb_composite_dev *cdev) +{ + WARN_ON(1); + return -EINVAL; +} + +int composite_dev_prepare(struct usb_composite_driver *composite, + struct usb_composite_dev *dev); + +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0); + +static void purge_configs_funcs(struct gadget_info *gi) +{ + struct usb_configuration *c; + + list_for_each_entry(c, &gi->cdev.configs, list) { + struct usb_function *f, *tmp; + struct config_usb_cfg *cfg; + + cfg = container_of(c, struct config_usb_cfg, c); + + list_for_each_entry_safe(f, tmp, &c->functions, list) { + + list_move_tail(&f->list, &cfg->func_list); + if (f->unbind) { + dev_err(&gi->cdev.gadget->dev, "unbind function" + " '%s'/%p\n", f->name, f); + f->unbind(c, f); + } + } + c->next_interface_id = 0; + c->superspeed = 0; + c->highspeed = 0; + c->fullspeed = 0; + } +} + +static int configfs_composite_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *gdriver) +{ + struct usb_composite_driver *composite = to_cdriver(gdriver); + struct gadget_info *gi = container_of(composite, + struct gadget_info, composite); + struct usb_composite_dev *cdev = &gi->cdev; + struct usb_configuration *c; + struct usb_string *s; + unsigned i; + int ret; + + /* the gi->lock is hold by the caller */ + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + ret = composite_dev_prepare(composite, cdev); + if (ret) + return ret; + /* and now the gadget bind */ + ret = -EINVAL; + + if (list_empty(&gi->cdev.configs)) { + pr_err("Need at least one configuration in %s.\n", + gi->composite.name); + goto err_comp_cleanup; + } + + + list_for_each_entry(c, &gi->cdev.configs, list) { + struct config_usb_cfg *cfg; + + cfg = container_of(c, struct config_usb_cfg, c); + if (list_empty(&cfg->func_list)) { + pr_err("Config %s/%d of %s needs at least one function.\n", + c->label, c->bConfigurationValue, + gi->composite.name); + goto err_comp_cleanup; + } + } + + /* init all strings */ + if (!list_empty(&gi->string_list)) { + struct gadget_strings *gs; + + i = 0; + list_for_each_entry(gs, &gi->string_list, list) { + + gi->gstrings[i] = &gs->stringtab_dev; + gs->stringtab_dev.strings = gs->strings; + gs->strings[USB_GADGET_MANUFACTURER_IDX].s = + gs->manufacturer; + gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product; + gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber; + i++; + } + gi->gstrings[i] = NULL; + s = usb_gstrings_attach(&gi->cdev, gi->gstrings, + USB_GADGET_FIRST_AVAIL_IDX); + if (IS_ERR(s)) { + ret = PTR_ERR(s); + goto err_comp_cleanup; + } + + gi->cdev.desc.iManufacturer = s[USB_GADGET_MANUFACTURER_IDX].id; + gi->cdev.desc.iProduct = s[USB_GADGET_PRODUCT_IDX].id; + gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id; + } + + if (gi->use_os_desc) { + cdev->use_os_string = true; + cdev->b_vendor_code = gi->b_vendor_code; + memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); + } + + /* Go through all configs, attach all functions */ + list_for_each_entry(c, &gi->cdev.configs, list) { + struct config_usb_cfg *cfg; + struct usb_function *f; + struct usb_function *tmp; + struct gadget_config_name *cn; + + cfg = container_of(c, struct config_usb_cfg, c); + if (!list_empty(&cfg->string_list)) { + i = 0; + list_for_each_entry(cn, &cfg->string_list, list) { + cfg->gstrings[i] = &cn->stringtab_dev; + cn->stringtab_dev.strings = &cn->strings; + cn->strings.s = cn->configuration; + i++; + } + cfg->gstrings[i] = NULL; + s = usb_gstrings_attach(&gi->cdev, cfg->gstrings, 1); + if (IS_ERR(s)) { + ret = PTR_ERR(s); + goto err_comp_cleanup; + } + c->iConfiguration = s[0].id; + } + + list_for_each_entry_safe(f, tmp, &cfg->func_list, list) { + list_del(&f->list); + ret = usb_add_function(c, f); + if (ret) { + list_add(&f->list, &cfg->func_list); + goto err_purge_funcs; + } + } + usb_ep_autoconfig_reset(cdev->gadget); + } + if (cdev->use_os_string) { + ret = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (ret) + goto err_purge_funcs; + } + + usb_ep_autoconfig_reset(cdev->gadget); + return 0; + +err_purge_funcs: + purge_configs_funcs(gi); +err_comp_cleanup: + composite_dev_cleanup(cdev); + return ret; +} + +static void configfs_composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + + /* the gi->lock is hold by the caller */ + + cdev = get_gadget_data(gadget); + gi = container_of(cdev, struct gadget_info, cdev); + + purge_configs_funcs(gi); + composite_dev_cleanup(cdev); + usb_ep_autoconfig_reset(cdev->gadget); + cdev->gadget = NULL; + set_gadget_data(gadget, NULL); +} + +static const struct usb_gadget_driver configfs_driver_template = { + .bind = configfs_composite_bind, + .unbind = configfs_composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .max_speed = USB_SPEED_SUPER, + .driver = { + .owner = THIS_MODULE, + .name = "configfs-gadget", + }, +}; + +static struct config_group *gadgets_make( + struct config_group *group, + const char *name) +{ + struct gadget_info *gi; + + gi = kzalloc(sizeof(*gi), GFP_KERNEL); + if (!gi) + return ERR_PTR(-ENOMEM); + + gi->group.default_groups = gi->default_groups; + gi->group.default_groups[0] = &gi->functions_group; + gi->group.default_groups[1] = &gi->configs_group; + gi->group.default_groups[2] = &gi->strings_group; + gi->group.default_groups[3] = &gi->os_desc_group; + + config_group_init_type_name(&gi->functions_group, "functions", + &functions_type); + config_group_init_type_name(&gi->configs_group, "configs", + &config_desc_type); + config_group_init_type_name(&gi->strings_group, "strings", + &gadget_strings_strings_type); + config_group_init_type_name(&gi->os_desc_group, "os_desc", + &os_desc_type); + + gi->composite.bind = configfs_do_nothing; + gi->composite.unbind = configfs_do_nothing; + gi->composite.suspend = NULL; + gi->composite.resume = NULL; + gi->composite.max_speed = USB_SPEED_SUPER; + + mutex_init(&gi->lock); + INIT_LIST_HEAD(&gi->string_list); + INIT_LIST_HEAD(&gi->available_func); + + composite_init_dev(&gi->cdev); + gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; + gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; + gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice()); + + gi->composite.gadget_driver = configfs_driver_template; + + gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); + gi->composite.name = gi->composite.gadget_driver.function; + + if (!gi->composite.gadget_driver.function) + goto err; + +#ifdef CONFIG_USB_OTG + gi->otg.bLength = sizeof(struct usb_otg_descriptor); + gi->otg.bDescriptorType = USB_DT_OTG; + gi->otg.bmAttributes = USB_OTG_SRP | USB_OTG_HNP; +#endif + + config_group_init_type_name(&gi->group, name, + &gadget_root_type); + return &gi->group; +err: + kfree(gi); + return ERR_PTR(-ENOMEM); +} + +static void gadgets_drop(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations gadgets_ops = { + .make_group = &gadgets_make, + .drop_item = &gadgets_drop, +}; + +static struct config_item_type gadgets_type = { + .ct_group_ops = &gadgets_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gadget_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "usb_gadget", + .ci_type = &gadgets_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), +}; + +void unregister_gadget_item(struct config_item *item) +{ + struct gadget_info *gi = to_gadget_info(item); + + unregister_gadget(gi); +} +EXPORT_SYMBOL_GPL(unregister_gadget_item); + +static int __init gadget_cfs_init(void) +{ + int ret; + + config_group_init(&gadget_subsys.su_group); + + ret = configfs_register_subsystem(&gadget_subsys); + return ret; +} +module_init(gadget_cfs_init); + +static void __exit gadget_cfs_exit(void) +{ + configfs_unregister_subsystem(&gadget_subsys); +} +module_exit(gadget_cfs_exit); diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h new file mode 100644 index 00000000000..36c468c4f5e --- /dev/null +++ b/drivers/usb/gadget/configfs.h @@ -0,0 +1,19 @@ +#ifndef USB__GADGET__CONFIGFS__H +#define USB__GADGET__CONFIGFS__H + +#include <linux/configfs.h> + +void unregister_gadget_item(struct config_item *item); + +int usb_os_desc_prepare_interf_dir(struct config_group *parent, + int n_interf, + struct usb_os_desc **desc, + char **names, + struct module *owner); + +static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item) +{ + return container_of(to_config_group(item), struct usb_os_desc, group); +} + +#endif /* USB__GADGET__CONFIGFS__H */ diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 87d16502816..986fc511a2e 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -13,9 +13,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#ifdef CONFIG_USB_G_DBGP_SERIAL -#include "u_serial.c" -#endif +#include "u_serial.h" #define DRIVER_VENDOR_ID 0x0525 /* NetChip */ #define DRIVER_PRODUCT_ID 0xc0de /* undefined */ @@ -233,6 +231,10 @@ static void dbgp_unbind(struct usb_gadget *gadget) gadget->ep0->driver_data = NULL; } +#ifdef CONFIG_USB_G_DBGP_SERIAL +static unsigned char tty_line; +#endif + static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) { int stp; @@ -270,7 +272,7 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) dbgp.serial->in->desc = &i_desc; dbgp.serial->out->desc = &o_desc; - if (gserial_setup(gadget, 1) < 0) { + if (gserial_alloc_line(&tty_line)) { stp = 3; goto fail_3; } @@ -379,7 +381,7 @@ static int dbgp_setup(struct usb_gadget *gadget, #ifdef CONFIG_USB_G_DBGP_PRINTK err = dbgp_enable_ep(); #else - err = gserial_connect(dbgp.serial, 0); + err = gserial_connect(dbgp.serial, tty_line); #endif if (err < 0) goto fail; @@ -422,7 +424,7 @@ static void __exit dbgp_exit(void) { usb_gadget_unregister_driver(&dbgp_driver); #ifdef CONFIG_USB_G_DBGP_SERIAL - gserial_cleanup(); + gserial_free_line(tty_line); #endif } diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 8cf0c0f6fa1..2b54955d316 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep, default: val = "ctrl"; break; - }; val; }), + } val; }), max, ep->stream_en ? "enabled" : "disabled"); /* at this point real hardware should be NAKing transfers @@ -561,7 +561,6 @@ static int dummy_disable(struct usb_ep *_ep) struct dummy_ep *ep; struct dummy *dum; unsigned long flags; - int retval; ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !ep->desc || _ep->name == ep0name) @@ -571,12 +570,11 @@ static int dummy_disable(struct usb_ep *_ep) spin_lock_irqsave(&dum->lock, flags); ep->desc = NULL; ep->stream_en = 0; - retval = 0; nuke(dum, ep); spin_unlock_irqrestore(&dum->lock, flags); dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name); - return retval; + return 0; } static struct usb_request *dummy_alloc_request(struct usb_ep *_ep, @@ -868,7 +866,7 @@ static const struct usb_gadget_ops dummy_ops = { /*-------------------------------------------------------------------------*/ /* "function" sysfs attribute */ -static ssize_t show_function(struct device *dev, struct device_attribute *attr, +static ssize_t function_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dummy *dum = gadget_dev_to_dummy(dev); @@ -877,7 +875,7 @@ static ssize_t show_function(struct device *dev, struct device_attribute *attr, return 0; return scnprintf(buf, PAGE_SIZE, "%s\n", dum->driver->function); } -static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR_RO(function); /*-------------------------------------------------------------------------*/ @@ -912,7 +910,6 @@ static int dummy_udc_start(struct usb_gadget *g, dum->devstatus = 0; dum->driver = driver; - dum->gadget.dev.driver = &driver->driver; dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); return 0; @@ -924,10 +921,10 @@ static int dummy_udc_stop(struct usb_gadget *g, struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); struct dummy *dum = dum_hcd->dum; - dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", - driver->driver.name); + if (driver) + dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", + driver->driver.name); - dum->gadget.dev.driver = NULL; dum->driver = NULL; return 0; @@ -937,11 +934,6 @@ static int dummy_udc_stop(struct usb_gadget *g, /* The gadget structure is stored inside the hcd structure and will be * released along with it. */ -static void dummy_gadget_release(struct device *dev) -{ - return; -} - static void init_dummy_udc_hw(struct dummy *dum) { int i; @@ -957,7 +949,7 @@ static void init_dummy_udc_hw(struct dummy *dum) list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list); ep->halted = ep->wedged = ep->already_seen = ep->setup_stage = 0; - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.max_streams = 16; ep->last_io = jiffies; ep->gadget = &dum->gadget; @@ -984,15 +976,7 @@ static int dummy_udc_probe(struct platform_device *pdev) dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; - dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; - dum->gadget.dev.release = dummy_gadget_release; - rc = device_register(&dum->gadget.dev); - if (rc < 0) { - put_device(&dum->gadget.dev); - return rc; - } - init_dummy_udc_hw(dum); rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget); @@ -1008,7 +992,6 @@ static int dummy_udc_probe(struct platform_device *pdev) err_dev: usb_del_gadget_udc(&dum->gadget); err_udc: - device_unregister(&dum->gadget.dev); return rc; } @@ -1016,10 +999,8 @@ static int dummy_udc_remove(struct platform_device *pdev) { struct dummy *dum = platform_get_drvdata(pdev); - usb_del_gadget_udc(&dum->gadget); - platform_set_drvdata(pdev, NULL); device_remove_file(&dum->gadget.dev, &dev_attr_function); - device_unregister(&dum->gadget.dev); + usb_del_gadget_udc(&dum->gadget); return 0; } @@ -1923,7 +1904,7 @@ done: } /* usb 3.0 root hub device descriptor */ -struct { +static struct { struct usb_bos_descriptor bos; struct usb_ss_cap_descriptor ss_cap; } __packed usb3_bos_desc = { @@ -2288,7 +2269,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) default: s = "?"; break; - }; s; }), + } s; }), ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype(urb->pipe)) { \ @@ -2304,11 +2285,11 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) default: \ s = "-iso"; \ break; \ - }; s; }), + } s; }), urb->actual_length, urb->transfer_buffer_length); } -static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, +static ssize_t urbs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -2329,7 +2310,7 @@ static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR(urbs, S_IRUGO, show_urbs, NULL); +static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { @@ -2678,8 +2659,10 @@ static int __init init(void) } for (i = 0; i < mod_data.num; i++) { dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL); - if (!dum[i]) + if (!dum[i]) { + retval = -ENOMEM; goto err_add_pdata; + } retval = platform_device_add_data(the_hcd_pdev[i], &dum[i], sizeof(void *)); if (retval) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index a777f7bd11b..0567cca1465 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/types.h> #include <linux/device.h> @@ -58,7 +57,7 @@ ep_matches ( return 0; /* only support ep0 for portable CONTROL traffic */ - type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + type = usb_endpoint_type(desc); if (USB_ENDPOINT_XFER_CONTROL == type) return 0; @@ -129,7 +128,7 @@ ep_matches ( * and wants to know the maximum possible, provide the info. */ if (desc->wMaxPacketSize == 0) - desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket); + desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit); /* endpoint maxpacket size is an input parameter, except for bulk * where it's an output parameter representing the full speed limit. @@ -145,7 +144,7 @@ ep_matches ( case USB_ENDPOINT_XFER_ISOC: /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ - if (ep->maxpacket < max) + if (ep->maxpacket_limit < max) return 0; if (!gadget_is_dualspeed(gadget) && max > 1023) return 0; @@ -178,7 +177,7 @@ ep_matches ( /* report (variable) full speed bulk maxpacket */ if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) { - int size = ep->maxpacket; + int size = ep->maxpacket_limit; /* min() doesn't work on bitfields with gcc-3.5 */ if (size > 64) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 18c3f423706..c1c113ef950 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,7 +203,17 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; +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 u8 hostaddr[ETH_ALEN]; */ 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); + 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); - else if (can_support_ecm(c->cdev->gadget)) - return ecm_bind_config(c, hostaddr); - else - return geth_bind_config(c, hostaddr); + 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 */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; - /* 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,35 @@ 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(); + 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(); + if (has_rndis()) { + usb_put_function(f_rndis); + usb_put_function_instance(fi_rndis); + } + if (use_eem) { + usb_put_function(f_eem); + usb_put_function_instance(fi_eem); + } else if (can_support_ecm(cdev->gadget)) { + usb_put_function(f_ecm); + usb_put_function_instance(fi_ecm); + } else { + usb_put_function(f_geth); + usb_put_function_instance(fi_geth); + } return 0; } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 549174466c2..ab1065afbbd 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -16,7 +16,9 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> +#include <linux/err.h> #include "u_serial.h" #include "gadget_chips.h" @@ -283,7 +285,7 @@ static struct usb_string acm_string_defs[] = { [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_DATA_IDX].s = "CDC ACM Data", [ACM_IAD_IDX ].s = "CDC Serial", - { /* ZEROES END LIST */ }, + { } /* end of list */ }; static struct usb_gadget_strings acm_string_table = { @@ -605,9 +607,23 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_acm *acm = func_to_acm(f); + struct usb_string *us; int status; struct usb_ep *ep; + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string IDs, and patch descriptors */ + us = usb_gstrings_attach(cdev, acm_strings, + ARRAY_SIZE(acm_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id; + acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id; + acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id; + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) @@ -700,69 +716,34 @@ fail: return status; } -static void -acm_unbind(struct usb_configuration *c, struct usb_function *f) +static void acm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_acm *acm = func_to_acm(f); acm_string_defs[0].id = 0; usb_free_all_descriptors(f); - gs_free_req(acm->notify, acm->notify_req); - kfree(acm); + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); } -/* Some controllers can't support CDC ACM ... */ -static inline bool can_support_cdc(struct usb_configuration *c) +static void acm_free_func(struct usb_function *f) { - /* everything else is *probably* fine ... */ - return true; + struct f_acm *acm = func_to_acm(f); + + kfree(acm); } -/** - * acm_bind_config - add a CDC ACM function to a configuration - * @c: the configuration to support the CDC ACM instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. - */ -int acm_bind_config(struct usb_configuration *c, u8 port_num) +static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) { - struct f_acm *acm; - int status; + struct f_serial_opts *opts; + struct f_acm *acm; - if (!can_support_cdc(c)) - return -EINVAL; - - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ - - /* maybe allocate device-global string IDs, and patch descriptors */ - if (acm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, acm_string_defs); - if (status < 0) - return status; - acm_control_interface_desc.iInterface = - acm_string_defs[ACM_CTRL_IDX].id; - acm_data_interface_desc.iInterface = - acm_string_defs[ACM_DATA_IDX].id; - acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id; - } - - /* allocate and initialize one new instance */ - acm = kzalloc(sizeof *acm, GFP_KERNEL); + acm = kzalloc(sizeof(*acm), GFP_KERNEL); if (!acm) - return -ENOMEM; + return ERR_PTR(-ENOMEM); spin_lock_init(&acm->lock); - acm->port_num = port_num; - acm->port.connect = acm_connect; acm->port.disconnect = acm_disconnect; acm->port.send_break = acm_send_break; @@ -771,13 +752,97 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) acm->port.func.strings = acm_strings; /* descriptors are per-instance copies */ acm->port.func.bind = acm_bind; - acm->port.func.unbind = acm_unbind; acm->port.func.set_alt = acm_set_alt; acm->port.func.setup = acm_setup; acm->port.func.disable = acm_disable; - status = usb_add_function(c, &acm->port.func); - if (status) - kfree(acm); - return status; + opts = container_of(fi, struct f_serial_opts, func_inst); + acm->port_num = opts->port_num; + acm->port.func.unbind = acm_unbind; + acm->port.func.free_func = acm_free_func; + + return &acm->port.func; +} + +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, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_acm_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; + + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); + return ret; +} + +static void acm_attr_release(struct config_item *item) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations acm_item_ops = { + .release = acm_attr_release, + .show_attribute = f_acm_attr_show, +}; + +static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_acm_port_num = + __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show); + + +static struct configfs_attribute *acm_attrs[] = { + &f_acm_port_num.attr, + NULL, +}; + +static struct config_item_type acm_func_type = { + .ct_item_ops = &acm_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void acm_free_instance(struct usb_function_instance *fi) +{ + struct f_serial_opts *opts; + + opts = container_of(fi, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); + kfree(opts); +} + +static struct usb_function_instance *acm_alloc_instance(void) +{ + struct f_serial_opts *opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->func_inst.free_func_inst = acm_free_instance; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); + } + config_group_init_type_name(&opts->func_inst.group, "", + &acm_func_type); + return &opts->func_inst; } +DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 83420a310fb..798760fa7e7 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,43 @@ 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; + 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; + } + + 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,66 +833,129 @@ fail: return status; } -static void -ecm_unbind(struct usb_configuration *c, struct usb_function *f) +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)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(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"); - ecm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); - kfree(ecm); } -/** - * ecm_bind_config - add CDC Ethernet network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int -ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +static struct usb_function *ecm_alloc(struct usb_function_instance *fi) { struct f_ecm *ecm; - int status; - - 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; - } + struct f_ecm_opts *opts; + int status; /* allocate and initialize one new instance */ - ecm = kzalloc(sizeof *ecm, GFP_KERNEL); + ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); if (!ecm) - return -ENOMEM; + 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 */ - snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr); + 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"; - ecm->port.func.strings = ecm_strings; /* descriptors are per-instance copies */ ecm->port.func.bind = ecm_bind; ecm->port.func.unbind = ecm_unbind; @@ -863,9 +963,11 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) 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; - status = usb_add_function(c, &ecm->port.func); - if (status) - kfree(ecm); - return status; + return &ecm->port.func; } + +DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index cf0ebee8556..d61c11d765d 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,54 +536,127 @@ 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) +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)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(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); +} + +static 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); + 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_fs.c b/drivers/usb/gadget/f_fs.c index 8c2f2512114..8598c27c7d4 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -22,218 +22,21 @@ #include <linux/pagemap.h> #include <linux/export.h> #include <linux/hid.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <linux/usb/composite.h> #include <linux/usb/functionfs.h> +#include <linux/aio.h> +#include <linux/mmu_context.h> +#include <linux/poll.h> -#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ - - -/* Debugging ****************************************************************/ - -#ifdef VERBOSE_DEBUG -#ifndef pr_vdebug -# define pr_vdebug pr_debug -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) \ - print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) -#else -#ifndef pr_vdebug -# define pr_vdebug(...) do { } while (0) -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define ENTER() pr_vdebug("%s()\n", __func__) - - -/* The data structure and setup file ****************************************/ - -enum ffs_state { - /* - * Waiting for descriptors and strings. - * - * In this state no open(2), read(2) or write(2) on epfiles - * may succeed (which should not be the problem as there - * should be no such files opened in the first place). - */ - FFS_READ_DESCRIPTORS, - FFS_READ_STRINGS, - - /* - * We've got descriptors and strings. We are or have called - * functionfs_ready_callback(). functionfs_bind() may have - * been called but we don't know. - * - * This is the only state in which operations on epfiles may - * succeed. - */ - FFS_ACTIVE, - - /* - * All endpoints have been closed. This state is also set if - * we encounter an unrecoverable error. The only - * unrecoverable error is situation when after reading strings - * from user space we fail to initialise epfiles or - * functionfs_ready_callback() returns with error (<0). - * - * In this state no open(2), read(2) or write(2) (both on ep0 - * as well as epfile) may succeed (at this point epfiles are - * unlinked and all closed so this is not a problem; ep0 is - * also closed but ep0 file exists and so open(2) on ep0 must - * fail). - */ - FFS_CLOSING -}; - - -enum ffs_setup_state { - /* There is no setup request pending. */ - FFS_NO_SETUP, - /* - * User has read events and there was a setup request event - * there. The next read/write on ep0 will handle the - * request. - */ - FFS_SETUP_PENDING, - /* - * There was event pending but before user space handled it - * some other event was introduced which canceled existing - * setup. If this state is set read/write on ep0 return - * -EIDRM. This state is only set when adding event. - */ - FFS_SETUP_CANCELED -}; - - - -struct ffs_epfile; -struct ffs_function; - -struct ffs_data { - struct usb_gadget *gadget; - - /* - * Protect access read/write operations, only one read/write - * at a time. As a consequence protects ep0req and company. - * While setup request is being processed (queued) this is - * held. - */ - struct mutex mutex; - - /* - * Protect access to endpoint related structures (basically - * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for - * endpoint zero. - */ - spinlock_t eps_lock; +#include "u_fs.h" +#include "u_f.h" +#include "configfs.h" - /* - * XXX REVISIT do we need our own request? Since we are not - * handling setup requests immediately user space may be so - * slow that another setup will be sent to the gadget but this - * time not to us but another function and then there could be - * a race. Is that the case? Or maybe we can use cdev->req - * after all, maybe we just need some spinlock for that? - */ - struct usb_request *ep0req; /* P: mutex */ - struct completion ep0req_completion; /* P: mutex */ - int ep0req_status; /* P: mutex */ - - /* reference counter */ - atomic_t ref; - /* how many files are opened (EP0 and others) */ - atomic_t opened; - - /* EP0 state */ - enum ffs_state state; - - /* - * Possible transitions: - * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock - * happens only in ep0 read which is P: mutex - * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock - * happens only in ep0 i/o which is P: mutex - * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock - * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg - */ - enum ffs_setup_state setup_state; - -#define FFS_SETUP_STATE(ffs) \ - ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELED, FFS_NO_SETUP)) - - /* Events & such. */ - struct { - u8 types[4]; - unsigned short count; - /* XXX REVISIT need to update it in some places, or do we? */ - unsigned short can_stall; - struct usb_ctrlrequest setup; - - wait_queue_head_t waitq; - } ev; /* the whole structure, P: ev.waitq.lock */ - - /* Flags */ - unsigned long flags; -#define FFS_FL_CALL_CLOSED_CALLBACK 0 -#define FFS_FL_BOUND 1 - - /* Active function */ - struct ffs_function *func; - - /* - * Device name, write once when file system is mounted. - * Intended for user to read if she wants. - */ - const char *dev_name; - /* Private data for our user (ie. gadget). Managed by user. */ - void *private_data; - - /* filled by __ffs_data_got_descs() */ - /* - * Real descriptors are 16 bytes after raw_descs (so you need - * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. - */ - const void *raw_descs; - unsigned raw_descs_length; - unsigned raw_fs_descs_length; - unsigned fs_descs_count; - unsigned hs_descs_count; - - unsigned short strings_count; - unsigned short interfaces_count; - unsigned short eps_count; - unsigned short _pad1; - - /* filled by __ffs_data_got_strings() */ - /* ids in stringtabs are set in functionfs_bind() */ - const void *raw_strings; - struct usb_gadget_strings **stringtabs; - - /* - * File system's super block, write once when file system is - * mounted. - */ - struct super_block *sb; - - /* File permissions, written once when fs is mounted */ - struct ffs_file_perms { - umode_t mode; - kuid_t uid; - kgid_t gid; - } file_perms; - - /* - * The endpoint files, filled by ffs_epfiles_create(), - * destroyed by ffs_epfiles_destroy(). - */ - struct ffs_epfile *epfiles; -}; +#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); @@ -274,15 +77,20 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) return container_of(f, struct ffs_function, function); } -static void ffs_func_free(struct ffs_function *func); + +static inline enum ffs_setup_state +ffs_setup_state_clear_cancelled(struct ffs_data *ffs) +{ + return (enum ffs_setup_state) + cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP); +} + static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); static int ffs_func_bind(struct usb_configuration *, struct usb_function *); -static void ffs_func_unbind(struct usb_configuration *, - struct usb_function *); static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, @@ -301,8 +109,8 @@ struct ffs_ep { struct usb_ep *ep; /* P: ffs->eps_lock */ struct usb_request *req; /* P: epfile->mutex */ - /* [0]: full speed, [1]: high speed */ - struct usb_endpoint_descriptor *descs[2]; + /* [0]: full speed, [1]: high speed, [2]: super speed */ + struct usb_endpoint_descriptor *descs[3]; u8 num; @@ -327,6 +135,25 @@ struct ffs_epfile { unsigned char _pad; }; +/* ffs_io_data structure ***************************************************/ + +struct ffs_io_data { + bool aio; + bool read; + + struct kiocb *kiocb; + const struct iovec *iovec; + unsigned long nr_segs; + char __user *buf; + size_t len; + + struct mm_struct *mm; + struct work_struct work; + + struct usb_ep *ep; + struct usb_request *req; +}; + static int __must_check ffs_epfiles_create(struct ffs_data *ffs); static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); @@ -335,6 +162,19 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, const struct file_operations *fops, struct dentry **dentry_p); +/* Devices management *******************************************************/ + +DEFINE_MUTEX(ffs_lock); +EXPORT_SYMBOL_GPL(ffs_lock); + +static struct ffs_dev *_ffs_find_dev(const char *name); +static struct ffs_dev *_ffs_alloc_dev(void); +static int _ffs_name_dev(struct ffs_dev *dev, const char *name); +static void _ffs_free_dev(struct ffs_dev *dev); +static void *ffs_acquire_dev(const char *dev_name); +static void ffs_release_dev(struct ffs_data *ffs_data); +static int ffs_ready(struct ffs_data *ffs); +static void ffs_closed(struct ffs_data *ffs); /* Misc helper functions ****************************************************/ @@ -373,7 +213,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) if (req->buf == NULL) req->buf = (void *)0xDEADBABE; - INIT_COMPLETION(ffs->ep0req_completion); + reinit_completion(&ffs->ep0req_completion); ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC); if (unlikely(ret < 0)) @@ -386,7 +226,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) } ffs->setup_state = FFS_NO_SETUP; - return ffs->ep0req_status; + return req->status ? req->status : req->actual; } static int __ffs_ep0_stall(struct ffs_data *ffs) @@ -412,7 +252,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -460,7 +300,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ffs->state = FFS_ACTIVE; mutex_unlock(&ffs->mutex); - ret = functionfs_ready_callback(ffs); + ret = ffs_ready(ffs); if (unlikely(ret < 0)) { ffs->state = FFS_CLOSING; return ret; @@ -478,8 +318,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * rather then _irqsave */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: ret = -EIDRM; goto done_spin; @@ -514,7 +354,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, /* * We are guaranteed to be still in FFS_ACTIVE state * but the state of setup could have changed from - * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need + * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need * to check for that. If that happened we copied data * from user space in vain but it's unlikely. * @@ -523,7 +363,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, * transition can be performed and it's protected by * mutex. */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; done_spin: spin_unlock_irq(&ffs->ev.waitq.lock); @@ -589,7 +430,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, ENTER(); /* Fast check if setup was canceled */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; /* Acquire mutex */ @@ -609,8 +450,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, */ spin_lock_irq(&ffs->ev.waitq.lock); - switch (FFS_SETUP_STATE(ffs)) { - case FFS_SETUP_CANCELED: + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: ret = -EIDRM; break; @@ -657,7 +498,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, spin_lock_irq(&ffs->ev.waitq.lock); /* See ffs_ep0_write() */ - if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) { + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { ret = -EIDRM; break; } @@ -726,8 +568,46 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) return ret; } +static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait) +{ + struct ffs_data *ffs = file->private_data; + unsigned int mask = POLLWRNORM; + int ret; + + poll_wait(file, &ffs->ev.waitq, wait); + + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return mask; + + switch (ffs->state) { + case FFS_READ_DESCRIPTORS: + case FFS_READ_STRINGS: + mask |= POLLOUT; + break; + + case FFS_ACTIVE: + switch (ffs->setup_state) { + case FFS_NO_SETUP: + if (ffs->ev.count) + mask |= POLLIN; + break; + + case FFS_SETUP_PENDING: + case FFS_SETUP_CANCELLED: + mask |= (POLLIN | POLLOUT); + break; + } + case FFS_CLOSING: + break; + } + + mutex_unlock(&ffs->mutex); + + return mask; +} + static const struct file_operations ffs_ep0_operations = { - .owner = THIS_MODULE, .llseek = no_llseek, .open = ffs_ep0_open, @@ -735,6 +615,7 @@ static const struct file_operations ffs_ep0_operations = { .read = ffs_ep0_read, .release = ffs_ep0_release, .unlocked_ioctl = ffs_ep0_ioctl, + .poll = ffs_ep0_poll, }; @@ -750,114 +631,226 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) } } -static ssize_t ffs_epfile_io(struct file *file, - char __user *buf, size_t len, int read) +static void ffs_user_copy_worker(struct work_struct *work) +{ + struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, + work); + int ret = io_data->req->status ? io_data->req->status : + io_data->req->actual; + + if (io_data->read && ret > 0) { + int i; + size_t pos = 0; + use_mm(io_data->mm); + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_to_user(io_data->iovec[i].iov_base, + &io_data->buf[pos], + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + break; + } + pos += io_data->iovec[i].iov_len; + } + unuse_mm(io_data->mm); + } + + aio_complete(io_data->kiocb, ret, ret); + + usb_ep_free_request(io_data->ep, io_data->req); + + io_data->kiocb->private = NULL; + if (io_data->read) + kfree(io_data->iovec); + kfree(io_data->buf); + kfree(io_data); +} + +static void ffs_epfile_async_io_complete(struct usb_ep *_ep, + struct usb_request *req) +{ + struct ffs_io_data *io_data = req->context; + + ENTER(); + + INIT_WORK(&io_data->work, ffs_user_copy_worker); + schedule_work(&io_data->work); +} + +static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; char *data = NULL; - ssize_t ret; + ssize_t ret, data_len; int halt; - goto first_try; - do { - spin_unlock_irq(&epfile->ffs->eps_lock); - mutex_unlock(&epfile->mutex); + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { + ret = -ENODEV; + goto error; + } -first_try: - /* Are we still active? */ - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { - ret = -ENODEV; + /* Wait for endpoint to be enabled */ + ep = epfile->ep; + if (!ep) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; goto error; } - /* Wait for endpoint to be enabled */ - ep = epfile->ep; - if (!ep) { - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - goto error; - } - - if (wait_event_interruptible(epfile->wait, - (ep = epfile->ep))) { - ret = -EINTR; - goto error; - } - } - - /* Do we halt? */ - halt = !read == !epfile->in; - if (halt && epfile->isoc) { - ret = -EINVAL; + ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep)); + if (ret) { + ret = -EINTR; goto error; } + } - /* Allocate & copy */ - if (!halt && !data) { - data = kzalloc(len, GFP_KERNEL); - if (unlikely(!data)) - return -ENOMEM; + /* Do we halt? */ + halt = (!io_data->read == !epfile->in); + if (halt && epfile->isoc) { + ret = -EINVAL; + goto error; + } + + /* Allocate & copy */ + if (!halt) { + /* + * if we _do_ wait above, the epfile->ffs->gadget might be NULL + * before the waiting completes, so do not assign to 'gadget' earlier + */ + struct usb_gadget *gadget = epfile->ffs->gadget; - if (!read && - unlikely(__copy_from_user(data, buf, len))) { + spin_lock_irq(&epfile->ffs->eps_lock); + /* In the meantime, endpoint got disabled or changed. */ + if (epfile->ep != ep) { + spin_unlock_irq(&epfile->ffs->eps_lock); + return -ESHUTDOWN; + } + /* + * Controller may require buffer size to be aligned to + * maxpacketsize of an out endpoint. + */ + data_len = io_data->read ? + usb_ep_align_maybe(gadget, ep->ep, io_data->len) : + io_data->len; + spin_unlock_irq(&epfile->ffs->eps_lock); + + data = kmalloc(data_len, GFP_KERNEL); + if (unlikely(!data)) + return -ENOMEM; + if (io_data->aio && !io_data->read) { + int i; + size_t pos = 0; + for (i = 0; i < io_data->nr_segs; i++) { + if (unlikely(copy_from_user(&data[pos], + io_data->iovec[i].iov_base, + io_data->iovec[i].iov_len))) { + ret = -EFAULT; + goto error; + } + pos += io_data->iovec[i].iov_len; + } + } else { + if (!io_data->read && + unlikely(__copy_from_user(data, io_data->buf, + io_data->len))) { ret = -EFAULT; goto error; } } + } - /* We will be using request */ - ret = ffs_mutex_lock(&epfile->mutex, - file->f_flags & O_NONBLOCK); - if (unlikely(ret)) - goto error; - - /* - * We're called from user space, we can use _irq rather then - * _irqsave - */ - spin_lock_irq(&epfile->ffs->eps_lock); + /* We will be using request */ + ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret)) + goto error; - /* - * While we were acquiring mutex endpoint got disabled - * or changed? - */ - } while (unlikely(epfile->ep != ep)); + spin_lock_irq(&epfile->ffs->eps_lock); - /* Halt */ - if (unlikely(halt)) { + if (epfile->ep != ep) { + /* In the meantime, endpoint got disabled or changed. */ + ret = -ESHUTDOWN; + spin_unlock_irq(&epfile->ffs->eps_lock); + } else if (halt) { + /* Halt */ if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep)) usb_ep_set_halt(ep->ep); spin_unlock_irq(&epfile->ffs->eps_lock); ret = -EBADMSG; } else { /* Fire the request */ - DECLARE_COMPLETION_ONSTACK(done); + struct usb_request *req; - struct usb_request *req = ep->req; - req->context = &done; - req->complete = ffs_epfile_io_complete; - req->buf = data; - req->length = len; + if (io_data->aio) { + req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + if (unlikely(!req)) + goto error_lock; - ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + req->buf = data; + req->length = io_data->len; - spin_unlock_irq(&epfile->ffs->eps_lock); + io_data->buf = data; + io_data->ep = ep->ep; + io_data->req = req; - if (unlikely(ret < 0)) { - /* nop */ - } else if (unlikely(wait_for_completion_interruptible(&done))) { - ret = -EINTR; - usb_ep_dequeue(ep->ep, req); + req->context = io_data; + req->complete = ffs_epfile_async_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + if (unlikely(ret)) { + usb_ep_free_request(ep->ep, req); + goto error_lock; + } + ret = -EIOCBQUEUED; + + spin_unlock_irq(&epfile->ffs->eps_lock); } else { - ret = ep->status; - if (read && ret > 0 && - unlikely(copy_to_user(buf, data, ret))) - ret = -EFAULT; + DECLARE_COMPLETION_ONSTACK(done); + + req = ep->req; + req->buf = data; + req->length = io_data->len; + + req->context = &done; + req->complete = ffs_epfile_io_complete; + + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + + spin_unlock_irq(&epfile->ffs->eps_lock); + + if (unlikely(ret < 0)) { + /* nop */ + } else if (unlikely( + wait_for_completion_interruptible(&done))) { + ret = -EINTR; + usb_ep_dequeue(ep->ep, req); + } else { + /* + * XXX We may end up silently droping data + * here. Since data_len (i.e. req->length) may + * be bigger than len (after being rounded up + * to maxpacketsize), we may end up with more + * data then user space has space for. + */ + ret = ep->status; + if (io_data->read && ret > 0) { + ret = min_t(size_t, ret, io_data->len); + + if (unlikely(copy_to_user(io_data->buf, + data, ret))) + ret = -EFAULT; + } + } + kfree(data); } } mutex_unlock(&epfile->mutex); + return ret; + +error_lock: + spin_unlock_irq(&epfile->ffs->eps_lock); + mutex_unlock(&epfile->mutex); error: kfree(data); return ret; @@ -867,17 +860,31 @@ static ssize_t ffs_epfile_write(struct file *file, const char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, (char __user *)buf, len, 0); + io_data.aio = false; + io_data.read = false; + io_data.buf = (char * __user)buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static ssize_t ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr) { + struct ffs_io_data io_data; + ENTER(); - return ffs_epfile_io(file, buf, len, 1); + io_data.aio = false; + io_data.read = true; + io_data.buf = buf; + io_data.len = len; + + return ffs_epfile_io(file, &io_data); } static int @@ -896,6 +903,89 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } +static int ffs_aio_cancel(struct kiocb *kiocb) +{ + struct ffs_io_data *io_data = kiocb->private; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + int value; + + ENTER(); + + spin_lock_irq(&epfile->ffs->eps_lock); + + if (likely(io_data && io_data->ep && io_data->req)) + value = usb_ep_dequeue(io_data->ep, io_data->req); + else + value = -EINVAL; + + spin_unlock_irq(&epfile->ffs->eps_lock); + + return value; +} + +static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + + ENTER(); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) + return -ENOMEM; + + io_data->aio = true; + io_data->read = false; + io_data->kiocb = kiocb; + io_data->iovec = iovec; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + +static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, + const struct iovec *iovec, + unsigned long nr_segs, loff_t loff) +{ + struct ffs_io_data *io_data; + struct iovec *iovec_copy; + + ENTER(); + + iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL); + if (unlikely(!iovec_copy)) + return -ENOMEM; + + memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); + + io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); + if (unlikely(!io_data)) { + kfree(iovec_copy); + return -ENOMEM; + } + + io_data->aio = true; + io_data->read = true; + io_data->kiocb = kiocb; + io_data->iovec = iovec_copy; + io_data->nr_segs = nr_segs; + io_data->len = kiocb->ki_nbytes; + io_data->mm = current->mm; + + kiocb->private = io_data; + + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + + return ffs_epfile_io(kiocb->ki_filp, io_data); +} + static int ffs_epfile_release(struct inode *inode, struct file *file) { @@ -947,12 +1037,13 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, } static const struct file_operations ffs_epfile_operations = { - .owner = THIS_MODULE, .llseek = no_llseek, .open = ffs_epfile_open, .write = ffs_epfile_write, .read = ffs_epfile_read, + .aio_write = ffs_epfile_aio_write, + .aio_read = ffs_epfile_aio_read, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, }; @@ -1036,37 +1127,19 @@ struct ffs_sb_fill_data { struct ffs_file_perms perms; umode_t root_mode; const char *dev_name; - union { - /* set by ffs_fs_mount(), read by ffs_sb_fill() */ - void *private_data; - /* set by ffs_sb_fill(), read by ffs_fs_mount */ - struct ffs_data *ffs_data; - }; + struct ffs_data *ffs_data; }; static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) { struct ffs_sb_fill_data *data = _data; struct inode *inode; - struct ffs_data *ffs; + struct ffs_data *ffs = data->ffs_data; ENTER(); - /* Initialise data */ - ffs = ffs_data_new(); - if (unlikely(!ffs)) - goto Enomem; - ffs->sb = sb; - ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL); - if (unlikely(!ffs->dev_name)) - goto Enomem; - ffs->file_perms = data->perms; - ffs->private_data = data->private_data; - - /* used by the caller of this function */ - data->ffs_data = ffs; - + data->ffs_data = NULL; sb->s_fs_info = ffs; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; @@ -1082,17 +1155,14 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) &data->perms); sb->s_root = d_make_root(inode); if (unlikely(!sb->s_root)) - goto Enomem; + return -ENOMEM; /* EP0 file */ if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations, NULL))) - goto Enomem; + return -ENOMEM; return 0; - -Enomem: - return -ENOMEM; } static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) @@ -1103,8 +1173,8 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) return 0; for (;;) { - char *end, *eq, *comma; unsigned long value; + char *eq, *comma; /* Option limit */ comma = strchr(opts, ','); @@ -1120,8 +1190,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) *eq = 0; /* Parse value */ - value = simple_strtoul(eq + 1, &end, 0); - if (unlikely(*end != ',' && *end != 0)) { + if (kstrtoul(eq + 1, 0, &value)) { pr_err("%s: invalid value: %s\n", opts, eq + 1); return -EINVAL; } @@ -1196,6 +1265,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, struct dentry *rv; int ret; void *ffs_dev; + struct ffs_data *ffs; ENTER(); @@ -1203,18 +1273,30 @@ ffs_fs_mount(struct file_system_type *t, int flags, if (unlikely(ret < 0)) return ERR_PTR(ret); - ffs_dev = functionfs_acquire_dev_callback(dev_name); - if (IS_ERR(ffs_dev)) - return ffs_dev; + ffs = ffs_data_new(); + if (unlikely(!ffs)) + return ERR_PTR(-ENOMEM); + ffs->file_perms = data.perms; - data.dev_name = dev_name; - data.private_data = ffs_dev; - rv = mount_nodev(t, flags, &data, ffs_sb_fill); + ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); + if (unlikely(!ffs->dev_name)) { + ffs_data_put(ffs); + return ERR_PTR(-ENOMEM); + } - /* data.ffs_data is set by ffs_sb_fill */ - if (IS_ERR(rv)) - functionfs_release_dev_callback(data.ffs_data); + ffs_dev = ffs_acquire_dev(dev_name); + if (IS_ERR(ffs_dev)) { + ffs_data_put(ffs); + return ERR_CAST(ffs_dev); + } + ffs->private_data = ffs_dev; + data.ffs_data = ffs; + rv = mount_nodev(t, flags, &data, ffs_sb_fill); + if (IS_ERR(rv) && data.ffs_data) { + ffs_release_dev(data.ffs_data); + ffs_data_put(data.ffs_data); + } return rv; } @@ -1225,7 +1307,7 @@ ffs_fs_kill_sb(struct super_block *sb) kill_litter_super(sb); if (sb->s_fs_info) { - functionfs_release_dev_callback(sb->s_fs_info); + ffs_release_dev(sb->s_fs_info); ffs_data_put(sb->s_fs_info); } } @@ -1236,6 +1318,7 @@ static struct file_system_type ffs_fs_type = { .mount = ffs_fs_mount, .kill_sb = ffs_fs_kill_sb, }; +MODULE_ALIAS_FS("functionfs"); /* Driver's main init/cleanup functions *************************************/ @@ -1314,7 +1397,7 @@ static struct ffs_data *ffs_data_new(void) { struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); if (unlikely(!ffs)) - return 0; + return NULL; ENTER(); @@ -1337,14 +1420,14 @@ static void ffs_data_clear(struct ffs_data *ffs) ENTER(); if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) - functionfs_closed_callback(ffs); + ffs_closed(ffs); BUG_ON(ffs->gadget); if (ffs->epfiles) ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); - kfree(ffs->raw_descs); + kfree(ffs->raw_descs_data); kfree(ffs->raw_strings); kfree(ffs->stringtabs); } @@ -1356,14 +1439,15 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs_data_clear(ffs); ffs->epfiles = NULL; + ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; ffs->raw_strings = NULL; ffs->stringtabs = NULL; ffs->raw_descs_length = 0; - ffs->raw_fs_descs_length = 0; ffs->fs_descs_count = 0; ffs->hs_descs_count = 0; + ffs->ss_descs_count = 0; ffs->strings_count = 0; ffs->interfaces_count = 0; @@ -1399,11 +1483,13 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) ffs->ep0req->context = ffs; lang = ffs->stringtabs; - for (lang = ffs->stringtabs; *lang; ++lang) { - struct usb_string *str = (*lang)->strings; - int id = first_id; - for (; str->s; ++id, ++str) - str->id = id; + if (lang) { + for (; *lang; ++lang) { + struct usb_string *str = (*lang)->strings; + int id = first_id; + for (; str->s; ++id, ++str) + str->id = id; + } } ffs->gadget = cdev->gadget; @@ -1419,8 +1505,8 @@ static void functionfs_unbind(struct ffs_data *ffs) usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req); ffs->ep0req = NULL; ffs->gadget = NULL; - ffs_data_put(ffs); clear_bit(FFS_FL_BOUND, &ffs->flags); + ffs_data_put(ffs); } } @@ -1473,71 +1559,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } -static int functionfs_bind_config(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct ffs_data *ffs) -{ - struct ffs_function *func; - int ret; - - ENTER(); - - func = kzalloc(sizeof *func, GFP_KERNEL); - if (unlikely(!func)) - return -ENOMEM; - - func->function.name = "Function FS Gadget"; - func->function.strings = ffs->stringtabs; - - func->function.bind = ffs_func_bind; - func->function.unbind = ffs_func_unbind; - func->function.set_alt = ffs_func_set_alt; - func->function.disable = ffs_func_disable; - func->function.setup = ffs_func_setup; - func->function.suspend = ffs_func_suspend; - func->function.resume = ffs_func_resume; - - func->conf = c; - func->gadget = cdev->gadget; - func->ffs = ffs; - ffs_data_get(ffs); - - ret = usb_add_function(c, &func->function); - if (unlikely(ret)) - ffs_func_free(func); - - return ret; -} - -static void ffs_func_free(struct ffs_function *func) -{ - struct ffs_ep *ep = func->eps; - unsigned count = func->ffs->eps_count; - unsigned long flags; - - ENTER(); - - /* cleanup after autoconfig */ - spin_lock_irqsave(&func->ffs->eps_lock, flags); - do { - if (ep->ep && ep->req) - usb_ep_free_request(ep->ep, ep->req); - ep->req = NULL; - ++ep; - } while (--count); - spin_unlock_irqrestore(&func->ffs->eps_lock, flags); - - ffs_data_put(func->ffs); - - kfree(func->eps); - /* - * eps and interfaces_nums are allocated in the same chunk so - * only one free is required. Descriptors are also allocated - * in the same chunk. - */ - - kfree(func); -} static void ffs_func_eps_disable(struct ffs_function *func) { @@ -1571,7 +1592,24 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - ds = ep->descs[ep->descs[1] ? 1 : 0]; + int desc_idx; + + if (ffs->gadget->speed == USB_SPEED_SUPER) + desc_idx = 2; + else if (ffs->gadget->speed == USB_SPEED_HIGH) + desc_idx = 1; + else + desc_idx = 0; + + /* fall-back to lower speed if desc missing for current speed */ + do { + ds = ep->descs[desc_idx]; + } while (!ds && --desc_idx >= 0); + + if (!ds) { + ret = -EINVAL; + break; + } ep->ep->driver_data = ep; ep->ep->desc = ds; @@ -1706,6 +1744,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, } break; + case USB_DT_SS_ENDPOINT_COMP: + pr_vdebug("EP SS companion descriptor\n"); + if (length != sizeof(struct usb_ss_ep_comp_descriptor)) + goto inv_length; + break; + case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_INTERFACE_POWER: case USB_DT_DEBUG: @@ -1816,60 +1860,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { - unsigned fs_count, hs_count; - int fs_len, ret = -EINVAL; - char *data = _data; + char *data = _data, *raw_descs; + unsigned counts[3], flags; + int ret = -EINVAL, i; ENTER(); - if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC || - get_unaligned_le32(data + 4) != len)) + if (get_unaligned_le32(data + 4) != len) goto error; - fs_count = get_unaligned_le32(data + 8); - hs_count = get_unaligned_le32(data + 12); - if (!fs_count && !hs_count) - goto einval; - - data += 16; - len -= 16; - - if (likely(fs_count)) { - fs_len = ffs_do_descs(fs_count, data, len, - __ffs_data_do_entity, ffs); - if (unlikely(fs_len < 0)) { - ret = fs_len; + switch (get_unaligned_le32(data)) { + case FUNCTIONFS_DESCRIPTORS_MAGIC: + flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC; + data += 8; + len -= 8; + break; + case FUNCTIONFS_DESCRIPTORS_MAGIC_V2: + flags = get_unaligned_le32(data + 8); + if (flags & ~(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC | + FUNCTIONFS_HAS_SS_DESC)) { + ret = -ENOSYS; goto error; } + data += 12; + len -= 12; + break; + default: + goto error; + } - data += fs_len; - len -= fs_len; - } else { - fs_len = 0; + /* Read fs_count, hs_count and ss_count (if present) */ + for (i = 0; i < 3; ++i) { + if (!(flags & (1 << i))) { + counts[i] = 0; + } else if (len < 4) { + goto error; + } else { + counts[i] = get_unaligned_le32(data); + data += 4; + len -= 4; + } } - if (likely(hs_count)) { - ret = ffs_do_descs(hs_count, data, len, + /* Read descriptors */ + raw_descs = data; + for (i = 0; i < 3; ++i) { + if (!counts[i]) + continue; + ret = ffs_do_descs(counts[i], data, len, __ffs_data_do_entity, ffs); - if (unlikely(ret < 0)) + if (ret < 0) goto error; - } else { - ret = 0; + data += ret; + len -= ret; } - if (unlikely(len != ret)) - goto einval; + if (raw_descs == data || len) { + ret = -EINVAL; + goto error; + } - ffs->raw_fs_descs_length = fs_len; - ffs->raw_descs_length = fs_len + ret; - ffs->raw_descs = _data; - ffs->fs_descs_count = fs_count; - ffs->hs_descs_count = hs_count; + ffs->raw_descs_data = _data; + ffs->raw_descs = raw_descs; + ffs->raw_descs_length = data - raw_descs; + ffs->fs_descs_count = counts[0]; + ffs->hs_descs_count = counts[1]; + ffs->ss_descs_count = counts[2]; return 0; -einval: - ret = -EINVAL; error: kfree(_data); return ret; @@ -1911,30 +1971,34 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, /* Allocate everything in one chunk so there's less maintenance. */ { - struct { - struct usb_gadget_strings *stringtabs[lang_count + 1]; - struct usb_gadget_strings stringtab[lang_count]; - struct usb_string strings[lang_count*(needed_count+1)]; - } *d; unsigned i = 0; + vla_group(d); + vla_item(d, struct usb_gadget_strings *, stringtabs, + lang_count + 1); + vla_item(d, struct usb_gadget_strings, stringtab, lang_count); + vla_item(d, struct usb_string, strings, + lang_count*(needed_count+1)); - d = kmalloc(sizeof *d, GFP_KERNEL); - if (unlikely(!d)) { + char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + + if (unlikely(!vlabuf)) { kfree(_data); return -ENOMEM; } - stringtabs = d->stringtabs; - t = d->stringtab; + /* Initialize the VLA pointers */ + stringtabs = vla_ptr(vlabuf, d, stringtabs); + t = vla_ptr(vlabuf, d, stringtab); i = lang_count; do { *stringtabs++ = t++; } while (--i); *stringtabs = NULL; - stringtabs = d->stringtabs; - t = d->stringtab; - s = d->strings; + /* stringtabs = vlabuf = d_stringtabs for later kfree */ + stringtabs = vla_ptr(vlabuf, d, stringtabs); + t = vla_ptr(vlabuf, d, stringtab); + s = vla_ptr(vlabuf, d, strings); strings = s; } @@ -2022,7 +2086,7 @@ static void __ffs_event_add(struct ffs_data *ffs, * the source does nothing. */ if (ffs->setup_state == FFS_SETUP_PENDING) - ffs->setup_state = FFS_SETUP_CANCELED; + ffs->setup_state = FFS_SETUP_CANCELLED; switch (type) { case FUNCTIONFS_RESUME: @@ -2083,21 +2147,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_endpoint_descriptor *ds = (void *)desc; struct ffs_function *func = priv; struct ffs_ep *ffs_ep; - - /* - * If hs_descriptors is not NULL then we are reading hs - * descriptors now - */ - const int isHS = func->function.hs_descriptors != NULL; - unsigned idx; + unsigned ep_desc_id, idx; + static const char *speed_names[] = { "full", "high", "super" }; if (type != FFS_DESCRIPTOR) return 0; - if (isHS) + /* + * If ss_descriptors is not NULL, we are reading super speed + * descriptors; if hs_descriptors is not NULL, we are reading high + * speed descriptors; otherwise, we are reading full speed + * descriptors. + */ + if (func->function.ss_descriptors) { + ep_desc_id = 2; + func->function.ss_descriptors[(long)valuep] = desc; + } else if (func->function.hs_descriptors) { + ep_desc_id = 1; func->function.hs_descriptors[(long)valuep] = desc; - else + } else { + ep_desc_id = 0; func->function.fs_descriptors[(long)valuep] = desc; + } if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) return 0; @@ -2105,13 +2176,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; ffs_ep = func->eps + idx; - if (unlikely(ffs_ep->descs[isHS])) { - pr_vdebug("two %sspeed descriptors for EP %d\n", - isHS ? "high" : "full", + if (unlikely(ffs_ep->descs[ep_desc_id])) { + pr_err("two %sspeed descriptors for EP %d\n", + speed_names[ep_desc_id], ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); return -EINVAL; } - ffs_ep->descs[isHS] = ds; + ffs_ep->descs[ep_desc_id] = ds; ffs_dump_mem(": Original ep desc", ds, ds->bLength); if (ffs_ep->ep) { @@ -2197,8 +2268,57 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } -static int ffs_func_bind(struct usb_configuration *c, - struct usb_function *f) +static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, + struct usb_configuration *c) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct f_fs_opts *ffs_opts = + container_of(f->fi, struct f_fs_opts, func_inst); + int ret; + + ENTER(); + + /* + * Legacy gadget triggers binding in functionfs_ready_callback, + * which already uses locking; taking the same lock here would + * cause a deadlock. + * + * Configfs-enabled gadgets however do need ffs_dev_lock. + */ + if (!ffs_opts->no_configfs) + ffs_dev_lock(); + ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV; + func->ffs = ffs_opts->dev->ffs_data; + if (!ffs_opts->no_configfs) + ffs_dev_unlock(); + if (ret) + return ERR_PTR(ret); + + func->conf = c; + func->gadget = c->cdev->gadget; + + ffs_data_get(func->ffs); + + /* + * 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 ffs_opts->bound access + */ + if (!ffs_opts->refcnt) { + ret = functionfs_bind(func->ffs, c->cdev); + if (ret) + return ERR_PTR(ret); + } + ffs_opts->refcnt++; + func->function.strings = func->ffs->stringtabs; + + return ffs_opts; +} + +static int _ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; @@ -2206,42 +2326,54 @@ static int ffs_func_bind(struct usb_configuration *c, const int full = !!func->ffs->fs_descs_count; const int high = gadget_is_dualspeed(func->gadget) && func->ffs->hs_descs_count; + const int super = gadget_is_superspeed(func->gadget) && + func->ffs->ss_descs_count; - int ret; + int fs_len, hs_len, ret; /* Make it a single chunk, less management later on */ - struct { - struct ffs_ep eps[ffs->eps_count]; - struct usb_descriptor_header - *fs_descs[full ? ffs->fs_descs_count + 1 : 0]; - struct usb_descriptor_header - *hs_descs[high ? ffs->hs_descs_count + 1 : 0]; - short inums[ffs->interfaces_count]; - char raw_descs[high ? ffs->raw_descs_length - : ffs->raw_fs_descs_length]; - } *data; + vla_group(d); + vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count); + vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs, + full ? ffs->fs_descs_count + 1 : 0); + vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, + high ? ffs->hs_descs_count + 1 : 0); + vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, + super ? ffs->ss_descs_count + 1 : 0); + vla_item_with_sz(d, short, inums, ffs->interfaces_count); + vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); + char *vlabuf; ENTER(); - /* Only high speed but not supported by gadget? */ - if (unlikely(!(full | high))) + /* Has descriptors only for speeds gadget does not support */ + if (unlikely(!(full | high | super))) return -ENOTSUPP; - /* Allocate */ - data = kmalloc(sizeof *data, GFP_KERNEL); - if (unlikely(!data)) + /* Allocate a single chunk, less management later on */ + vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + if (unlikely(!vlabuf)) return -ENOMEM; /* Zero */ - memset(data->eps, 0, sizeof data->eps); - memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs); - memset(data->inums, 0xff, sizeof data->inums); - for (ret = ffs->eps_count; ret; --ret) - data->eps[ret].num = -1; + memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + /* Copy descriptors */ + memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, + ffs->raw_descs_length); - /* Save pointers */ - func->eps = data->eps; - func->interfaces_nums = data->inums; + memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); + for (ret = ffs->eps_count; ret; --ret) { + struct ffs_ep *ptr; + + ptr = vla_ptr(vlabuf, d, eps); + ptr[ret].num = -1; + } + + /* Save pointers + * d_eps == vlabuf, func->eps used to kfree vlabuf later + */ + func->eps = vla_ptr(vlabuf, d, eps); + func->interfaces_nums = vla_ptr(vlabuf, d, inums); /* * Go through all the endpoint descriptors and allocate @@ -2249,23 +2381,41 @@ static int ffs_func_bind(struct usb_configuration *c, * numbers without worrying that it may be described later on. */ if (likely(full)) { - func->function.fs_descriptors = data->fs_descs; - ret = ffs_do_descs(ffs->fs_descs_count, - data->raw_descs, - sizeof data->raw_descs, - __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); + fs_len = ffs_do_descs(ffs->fs_descs_count, + vla_ptr(vlabuf, d, raw_descs), + d_raw_descs__sz, + __ffs_func_bind_do_descs, func); + if (unlikely(fs_len < 0)) { + ret = fs_len; goto error; + } } else { - ret = 0; + fs_len = 0; } if (likely(high)) { - func->function.hs_descriptors = data->hs_descs; - ret = ffs_do_descs(ffs->hs_descs_count, - data->raw_descs + ret, - (sizeof data->raw_descs) - ret, - __ffs_func_bind_do_descs, func); + func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); + hs_len = ffs_do_descs(ffs->hs_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len, + d_raw_descs__sz - fs_len, + __ffs_func_bind_do_descs, func); + if (unlikely(hs_len < 0)) { + ret = hs_len; + goto error; + } + } else { + hs_len = 0; + } + + if (likely(super)) { + func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); + ret = ffs_do_descs(ffs->ss_descs_count, + vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, + d_raw_descs__sz - fs_len - hs_len, + __ffs_func_bind_do_descs, func); + if (unlikely(ret < 0)) + goto error; } /* @@ -2274,8 +2424,9 @@ static int ffs_func_bind(struct usb_configuration *c, * now. */ ret = ffs_do_descs(ffs->fs_descs_count + - (high ? ffs->hs_descs_count : 0), - data->raw_descs, sizeof data->raw_descs, + (high ? ffs->hs_descs_count : 0) + + (super ? ffs->ss_descs_count : 0), + vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_nums, func); if (unlikely(ret < 0)) goto error; @@ -2289,26 +2440,19 @@ error: return ret; } - -/* Other USB function hooks *************************************************/ - -static void ffs_func_unbind(struct usb_configuration *c, - struct usb_function *f) +static int ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) { - struct ffs_function *func = ffs_func_from_usb(f); - struct ffs_data *ffs = func->ffs; + struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); - ENTER(); + if (IS_ERR(ffs_opts)) + return PTR_ERR(ffs_opts); - if (ffs->func == func) { - ffs_func_eps_disable(func); - ffs->func = NULL; - } + return _ffs_func_bind(c, f); +} - ffs_event_add(ffs, FUNCTIONFS_UNBIND); - ffs_func_free(func); -} +/* Other USB function hooks *************************************************/ static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) @@ -2436,6 +2580,413 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) } +/* Devices management *******************************************************/ + +static LIST_HEAD(ffs_devices); + +static struct ffs_dev *_ffs_do_find_dev(const char *name) +{ + struct ffs_dev *dev; + + list_for_each_entry(dev, &ffs_devices, entry) { + if (!dev->name || !name) + continue; + if (strcmp(dev->name, name) == 0) + return dev; + } + + return NULL; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +static struct ffs_dev *_ffs_get_single_dev(void) +{ + struct ffs_dev *dev; + + if (list_is_singular(&ffs_devices)) { + dev = list_first_entry(&ffs_devices, struct ffs_dev, entry); + if (dev->single) + return dev; + } + + return NULL; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +static struct ffs_dev *_ffs_find_dev(const char *name) +{ + struct ffs_dev *dev; + + dev = _ffs_get_single_dev(); + if (dev) + return dev; + + return _ffs_do_find_dev(name); +} + +/* Configfs support *********************************************************/ + +static inline struct f_fs_opts *to_ffs_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_fs_opts, + func_inst.group); +} + +static void ffs_attr_release(struct config_item *item) +{ + struct f_fs_opts *opts = to_ffs_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ffs_item_ops = { + .release = ffs_attr_release, +}; + +static struct config_item_type ffs_func_type = { + .ct_item_ops = &ffs_item_ops, + .ct_owner = THIS_MODULE, +}; + + +/* Function registration interface ******************************************/ + +static void ffs_free_inst(struct usb_function_instance *f) +{ + struct f_fs_opts *opts; + + opts = to_f_fs_opts(f); + ffs_dev_lock(); + _ffs_free_dev(opts->dev); + ffs_dev_unlock(); + kfree(opts); +} + +#define MAX_INST_NAME_LEN 40 + +static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct f_fs_opts *opts; + char *ptr; + const char *tmp; + int name_len, ret; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + opts = to_f_fs_opts(fi); + tmp = NULL; + + ffs_dev_lock(); + + tmp = opts->dev->name_allocated ? opts->dev->name : NULL; + ret = _ffs_name_dev(opts->dev, ptr); + if (ret) { + kfree(ptr); + ffs_dev_unlock(); + return ret; + } + opts->dev->name_allocated = true; + + ffs_dev_unlock(); + + kfree(tmp); + + return 0; +} + +static struct usb_function_instance *ffs_alloc_inst(void) +{ + struct f_fs_opts *opts; + struct ffs_dev *dev; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.set_inst_name = ffs_set_inst_name; + opts->func_inst.free_func_inst = ffs_free_inst; + ffs_dev_lock(); + dev = _ffs_alloc_dev(); + ffs_dev_unlock(); + if (IS_ERR(dev)) { + kfree(opts); + return ERR_CAST(dev); + } + opts->dev = dev; + dev->opts = opts; + + config_group_init_type_name(&opts->func_inst.group, "", + &ffs_func_type); + return &opts->func_inst; +} + +static void ffs_free(struct usb_function *f) +{ + kfree(ffs_func_from_usb(f)); +} + +static void ffs_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + struct f_fs_opts *opts = + container_of(f->fi, struct f_fs_opts, func_inst); + struct ffs_ep *ep = func->eps; + unsigned count = ffs->eps_count; + unsigned long flags; + + ENTER(); + if (ffs->func == func) { + ffs_func_eps_disable(func); + ffs->func = NULL; + } + + if (!--opts->refcnt) + functionfs_unbind(ffs); + + /* cleanup after autoconfig */ + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + if (ep->ep && ep->req) + usb_ep_free_request(ep->ep, ep->req); + ep->req = NULL; + ++ep; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); + kfree(func->eps); + func->eps = NULL; + /* + * eps, descriptors and interfaces_nums are allocated in the + * same chunk so only one free is required. + */ + func->function.fs_descriptors = NULL; + func->function.hs_descriptors = NULL; + func->function.ss_descriptors = NULL; + func->interfaces_nums = NULL; + + ffs_event_add(ffs, FUNCTIONFS_UNBIND); +} + +static struct usb_function *ffs_alloc(struct usb_function_instance *fi) +{ + struct ffs_function *func; + + ENTER(); + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (unlikely(!func)) + return ERR_PTR(-ENOMEM); + + func->function.name = "Function FS Gadget"; + + func->function.bind = ffs_func_bind; + func->function.unbind = ffs_func_unbind; + func->function.set_alt = ffs_func_set_alt; + func->function.disable = ffs_func_disable; + func->function.setup = ffs_func_setup; + func->function.suspend = ffs_func_suspend; + func->function.resume = ffs_func_resume; + func->function.free_func = ffs_free; + + return &func->function; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +static struct ffs_dev *_ffs_alloc_dev(void) +{ + struct ffs_dev *dev; + int ret; + + if (_ffs_get_single_dev()) + return ERR_PTR(-EBUSY); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (list_empty(&ffs_devices)) { + ret = functionfs_init(); + if (ret) { + kfree(dev); + return ERR_PTR(ret); + } + } + + list_add(&dev->entry, &ffs_devices); + + return dev; +} + +/* + * ffs_lock must be taken by the caller of this function + * The caller is responsible for "name" being available whenever f_fs needs it + */ +static int _ffs_name_dev(struct ffs_dev *dev, const char *name) +{ + struct ffs_dev *existing; + + existing = _ffs_do_find_dev(name); + if (existing) + return -EBUSY; + + dev->name = name; + + return 0; +} + +/* + * The caller is responsible for "name" being available whenever f_fs needs it + */ +int ffs_name_dev(struct ffs_dev *dev, const char *name) +{ + int ret; + + ffs_dev_lock(); + ret = _ffs_name_dev(dev, name); + ffs_dev_unlock(); + + return ret; +} +EXPORT_SYMBOL_GPL(ffs_name_dev); + +int ffs_single_dev(struct ffs_dev *dev) +{ + int ret; + + ret = 0; + ffs_dev_lock(); + + if (!list_is_singular(&ffs_devices)) + ret = -EBUSY; + else + dev->single = true; + + ffs_dev_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(ffs_single_dev); + +/* + * ffs_lock must be taken by the caller of this function + */ +static void _ffs_free_dev(struct ffs_dev *dev) +{ + list_del(&dev->entry); + if (dev->name_allocated) + kfree(dev->name); + kfree(dev); + if (list_empty(&ffs_devices)) + functionfs_cleanup(); +} + +static void *ffs_acquire_dev(const char *dev_name) +{ + struct ffs_dev *ffs_dev; + + ENTER(); + ffs_dev_lock(); + + ffs_dev = _ffs_find_dev(dev_name); + if (!ffs_dev) + ffs_dev = ERR_PTR(-ENODEV); + else if (ffs_dev->mounted) + ffs_dev = ERR_PTR(-EBUSY); + else if (ffs_dev->ffs_acquire_dev_callback && + ffs_dev->ffs_acquire_dev_callback(ffs_dev)) + ffs_dev = ERR_PTR(-ENODEV); + else + ffs_dev->mounted = true; + + ffs_dev_unlock(); + return ffs_dev; +} + +static void ffs_release_dev(struct ffs_data *ffs_data) +{ + struct ffs_dev *ffs_dev; + + ENTER(); + ffs_dev_lock(); + + ffs_dev = ffs_data->private_data; + if (ffs_dev) { + ffs_dev->mounted = false; + + if (ffs_dev->ffs_release_dev_callback) + ffs_dev->ffs_release_dev_callback(ffs_dev); + } + + ffs_dev_unlock(); +} + +static int ffs_ready(struct ffs_data *ffs) +{ + struct ffs_dev *ffs_obj; + int ret = 0; + + ENTER(); + ffs_dev_lock(); + + ffs_obj = ffs->private_data; + if (!ffs_obj) { + ret = -EINVAL; + goto done; + } + if (WARN_ON(ffs_obj->desc_ready)) { + ret = -EBUSY; + goto done; + } + + ffs_obj->desc_ready = true; + ffs_obj->ffs_data = ffs; + + if (ffs_obj->ffs_ready_callback) + ret = ffs_obj->ffs_ready_callback(ffs); + +done: + ffs_dev_unlock(); + return ret; +} + +static void ffs_closed(struct ffs_data *ffs) +{ + struct ffs_dev *ffs_obj; + + ENTER(); + ffs_dev_lock(); + + ffs_obj = ffs->private_data; + if (!ffs_obj) + goto done; + + ffs_obj->desc_ready = false; + + if (ffs_obj->ffs_closed_callback) + ffs_obj->ffs_closed_callback(ffs); + + if (!ffs_obj->opts || ffs_obj->opts->no_configfs + || !ffs_obj->opts->func_inst.group.cg_item.ci_parent) + goto done; + + unregister_gadget_item(ffs_obj->opts-> + func_inst.group.cg_item.ci_parent->ci_parent); +done: + ffs_dev_unlock(); +} + /* Misc helper functions ****************************************************/ static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) @@ -2466,3 +3017,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) return data; } + +DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 6e69a8e8d22..a95290a1289 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -20,6 +20,8 @@ #include <linux/sched.h> #include <linux/usb/g_hid.h> +#include "u_f.h" + static int major, minors; static struct class *hidg_class; @@ -334,20 +336,10 @@ static int f_hidg_open(struct inode *inode, struct file *fd) /*-------------------------------------------------------------------------*/ /* usb_function */ -static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length) +static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, + unsigned length) { - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; + return alloc_ep_req(ep, length, length); } static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index bb39cb2bb3a..4557cd03f0b 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -15,10 +15,12 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/usb/composite.h> #include "g_zero.h" -#include "gadget_chips.h" - +#include "u_f.h" /* * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, @@ -44,9 +46,8 @@ static inline struct f_loopback *func_to_loop(struct usb_function *f) return container_of(f, struct f_loopback, function); } -static unsigned qlen = 32; -module_param(qlen, uint, 0); -MODULE_PARM_DESC(qlenn, "depth of loopback queue"); +static unsigned qlen; +static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -119,7 +120,7 @@ static struct usb_endpoint_descriptor ss_loop_source_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 0, @@ -135,7 +136,7 @@ static struct usb_endpoint_descriptor ss_loop_sink_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 0, @@ -171,8 +172,7 @@ static struct usb_gadget_strings *loopback_strings[] = { /*-------------------------------------------------------------------------*/ -static int __init -loopback_bind(struct usb_configuration *c, struct usb_function *f) +static int loopback_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_loopback *loop = func_to_loop(f); @@ -185,6 +185,12 @@ loopback_bind(struct usb_configuration *c, struct usb_function *f) return id; loopback_intf.bInterfaceNumber = id; + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_loopback[0].id = id; + loopback_intf.iInterface = id; + /* allocate endpoints */ loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc); @@ -223,9 +229,16 @@ autoconf_fail: return 0; } -static void -loopback_unbind(struct usb_configuration *c, struct usb_function *f) +static void lb_free_func(struct usb_function *f) { + struct f_lb_opts *opts; + + opts = container_of(f->fi, struct f_lb_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + usb_free_all_descriptors(f); kfree(func_to_loop(f)); } @@ -289,6 +302,11 @@ static void disable_loopback(struct f_loopback *loop) VDBG(cdev, "%s disabled\n", loop->function.name); } +static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) +{ + return alloc_ep_req(ep, len, buflen); +} + static int enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) { @@ -328,7 +346,7 @@ fail0: * than 'buflen' bytes each. */ for (i = 0; i < qlen && result == 0; i++) { - req = alloc_ep_req(ep, 0); + req = lb_alloc_ep_req(ep, 0); if (req) { req->complete = loopback_complete; result = usb_ep_queue(ep, req, GFP_ATOMIC); @@ -366,63 +384,188 @@ static void loopback_disable(struct usb_function *f) disable_loopback(loop); } -/*-------------------------------------------------------------------------*/ - -static int __init loopback_bind_config(struct usb_configuration *c) +static struct usb_function *loopback_alloc(struct usb_function_instance *fi) { struct f_loopback *loop; - int status; + struct f_lb_opts *lb_opts; loop = kzalloc(sizeof *loop, GFP_KERNEL); if (!loop) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + + lb_opts = container_of(fi, struct f_lb_opts, func_inst); + + mutex_lock(&lb_opts->lock); + lb_opts->refcnt++; + mutex_unlock(&lb_opts->lock); + + buflen = lb_opts->bulk_buflen; + qlen = lb_opts->qlen; + if (!qlen) + qlen = 32; loop->function.name = "loopback"; loop->function.bind = loopback_bind; - loop->function.unbind = loopback_unbind; loop->function.set_alt = loopback_set_alt; loop->function.disable = loopback_disable; + loop->function.strings = loopback_strings; + + loop->function.free_func = lb_free_func; - status = usb_add_function(c, &loop->function); - if (status) - kfree(loop); - return status; + return &loop->function; } -static struct usb_configuration loopback_driver = { - .label = "loopback", - .strings = loopback_strings, - .bConfigurationValue = 2, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, - /* .iConfiguration = DYNAMIC */ +static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_lb_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_lb_opts); +CONFIGFS_ATTR_OPS(f_lb_opts); + +static void lb_attr_release(struct config_item *item) +{ + struct f_lb_opts *lb_opts = to_f_lb_opts(item); + + usb_put_function_instance(&lb_opts->func_inst); +} + +static struct configfs_item_operations lb_item_ops = { + .release = lb_attr_release, + .show_attribute = f_lb_opts_attr_show, + .store_attribute = f_lb_opts_attr_store, }; -/** - * loopback_add - add a loopback testing configuration to a device - * @cdev: the device to support the loopback configuration - */ -int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) +static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page) { - int id; + int result; - /* allocate string ID(s) */ - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_loopback[0].id = id; + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->qlen); + mutex_unlock(&opts->lock); - loopback_intf.iInterface = id; - loopback_driver.iConfiguration = id; + return result; +} - /* support autoresume for remote wakeup testing */ - if (autoresume) - loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; - /* support OTG systems */ - if (gadget_is_otg(cdev->gadget)) { - loopback_driver.descriptors = otg_desc; - loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; } - return usb_add_config(cdev, &loopback_driver, loopback_bind_config); + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->qlen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; } + +static struct f_lb_opts_attribute f_lb_opts_qlen = + __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR, + f_lb_opts_qlen_show, + f_lb_opts_qlen_store); + +static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->bulk_buflen); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->bulk_buflen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_lb_opts_attribute f_lb_opts_bulk_buflen = + __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, + f_lb_opts_bulk_buflen_show, + f_lb_opts_bulk_buflen_store); + +static struct configfs_attribute *lb_attrs[] = { + &f_lb_opts_qlen.attr, + &f_lb_opts_bulk_buflen.attr, + NULL, +}; + +static struct config_item_type lb_func_type = { + .ct_item_ops = &lb_item_ops, + .ct_attrs = lb_attrs, + .ct_owner = THIS_MODULE, +}; + +static void lb_free_instance(struct usb_function_instance *fi) +{ + struct f_lb_opts *lb_opts; + + lb_opts = container_of(fi, struct f_lb_opts, func_inst); + kfree(lb_opts); +} + +static struct usb_function_instance *loopback_alloc_instance(void) +{ + struct f_lb_opts *lb_opts; + + lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); + if (!lb_opts) + return ERR_PTR(-ENOMEM); + mutex_init(&lb_opts->lock); + lb_opts->func_inst.free_func_inst = lb_free_instance; + lb_opts->bulk_buflen = GZERO_BULK_BUFLEN; + lb_opts->qlen = GZERO_QLEN; + + config_group_init_type_name(&lb_opts->func_inst.group, "", + &lb_func_type); + + return &lb_opts->func_inst; +} +DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); + +int __init lb_modinit(void) +{ + int ret; + + ret = usb_function_register(&Loopbackusb_func); + if (ret) + return ret; + return ret; +} +void __exit lb_modexit(void) +{ + usb_function_unregister(&Loopbackusb_func); +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5d027b3e1ef..b9639390886 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -213,12 +213,14 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/freezer.h> +#include <linux/module.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/composite.h> #include "gadget_chips.h" +#include "configfs.h" /*------------------------------------------------------------------------*/ @@ -228,40 +230,30 @@ static const char fsg_string_interface[] = "Mass Storage"; -#include "storage_common.c" +#include "storage_common.h" +#include "f_mass_storage.h" +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; /*-------------------------------------------------------------------------*/ struct fsg_dev; struct fsg_common; -/* FSF callback functions */ -struct fsg_operations { - /* - * Callback function to call when thread exits. If no - * callback is set or it returns value lower then zero MSF - * will force eject all LUNs it operates on (including those - * marked as non-removable or with prevent_medium_removal flag - * set). - */ - int (*thread_exits)(struct fsg_common *common); - - /* - * Called prior to ejection. Negative return means error, - * zero means to continue with ejection, positive means not to - * eject. - */ - int (*pre_eject)(struct fsg_common *common, - struct fsg_lun *lun, int num); - /* - * Called after ejection. Negative return means error, zero - * or positive is just a success. - */ - int (*post_eject)(struct fsg_common *common, - struct fsg_lun *lun, int num); -}; - /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; @@ -282,13 +274,14 @@ struct fsg_common { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; struct fsg_buffhd *buffhds; + unsigned int fsg_num_buffers; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; unsigned int nluns; unsigned int lun; - struct fsg_lun *luns; + struct fsg_lun **luns; struct fsg_lun *curlun; unsigned int bulk_out_maxpacket; @@ -308,6 +301,7 @@ struct fsg_common { unsigned int short_packet_received:1; unsigned int bad_lun_okay:1; unsigned int running:1; + unsigned int sysfs:1; int thread_wakeup_needed; struct completion thread_notifier; @@ -327,27 +321,6 @@ struct fsg_common { struct kref ref; }; -struct fsg_config { - unsigned nluns; - struct fsg_lun_config { - const char *filename; - char ro; - char removable; - char cdrom; - char nofua; - } luns[FSG_MAX_LUNS]; - - /* Callback functions. */ - const struct fsg_operations *ops; - /* Gadget's private data. */ - void *private_data; - - const char *vendor_name; /* 8 characters or less */ - const char *product_name; /* 16 characters or less */ - - char can_stall; -}; - struct fsg_dev { struct usb_function function; struct usb_gadget *gadget; /* Copy of cdev->gadget */ @@ -427,6 +400,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) @@ -549,7 +523,7 @@ static int fsg_setup(struct usb_function *f, */ DBG(fsg, "bulk reset request\n"); raise_exception(fsg->common, FSG_STATE_RESET); - return DELAYED_STATUS; + return USB_GADGET_DELAYED_STATUS; case US_BULK_GET_MAX_LUN: if (ctrl->bRequestType != @@ -628,13 +602,14 @@ static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh) return true; } -static int sleep_thread(struct fsg_common *common) +static int sleep_thread(struct fsg_common *common, bool can_freeze) { int rc = 0; /* Wait until a signal arrives or we are woken up */ for (;;) { - try_to_freeze(); + if (can_freeze) + try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { rc = -EINTR; @@ -646,6 +621,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; } @@ -707,7 +683,7 @@ static int do_read(struct fsg_common *common) /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(common); + rc = sleep_thread(common, false); if (rc) return rc; } @@ -962,7 +938,7 @@ static int do_write(struct fsg_common *common) } /* Wait for something to happen */ - rc = sleep_thread(common); + rc = sleep_thread(common, false); if (rc) return rc; } @@ -992,7 +968,7 @@ static int do_synchronize_cache(struct fsg_common *common) static void invalidate_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; - struct inode *inode = filp->f_path.dentry->d_inode; + struct inode *inode = file_inode(filp); unsigned long rc; rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); @@ -1374,26 +1350,13 @@ static int do_start_stop(struct fsg_common *common) if (!loej) return 0; - /* Simulate an unload/eject */ - if (common->ops && common->ops->pre_eject) { - int r = common->ops->pre_eject(common, curlun, - curlun - common->luns); - if (unlikely(r < 0)) - return r; - else if (r) - return 0; - } - up_read(&common->filesem); down_write(&common->filesem); fsg_lun_close(curlun); up_write(&common->filesem); down_read(&common->filesem); - return common->ops && common->ops->post_eject - ? min(0, common->ops->post_eject(common, curlun, - curlun - common->luns)) - : 0; + return 0; } static int do_prevent_allow(struct fsg_common *common) @@ -1542,7 +1505,7 @@ static int throw_away_data(struct fsg_common *common) } /* Otherwise wait for something to happen */ - rc = sleep_thread(common); + rc = sleep_thread(common, true); if (rc) return rc; } @@ -1663,7 +1626,7 @@ static int send_status(struct fsg_common *common) /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(common); + rc = sleep_thread(common, true); if (rc) return rc; } @@ -1718,7 +1681,7 @@ static int check_command(struct fsg_common *common, int cmnd_size, int needs_medium, const char *name) { int i; - int lun = common->cmnd[1] >> 5; + unsigned int lun = common->cmnd[1] >> 5; static const char dirletter[4] = {'u', 'o', 'i', 'n'}; char hdlen[20]; struct fsg_lun *curlun; @@ -1784,7 +1747,7 @@ static int check_command(struct fsg_common *common, int cmnd_size, /* Check that the LUN values are consistent */ if (common->lun != lun) - DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n", common->lun, lun); /* Check the LUN */ @@ -1804,7 +1767,7 @@ static int check_command(struct fsg_common *common, int cmnd_size, */ if (common->cmnd[0] != INQUIRY && common->cmnd[0] != REQUEST_SENSE) { - DBG(common, "unsupported LUN %d\n", common->lun); + DBG(common, "unsupported LUN %u\n", common->lun); return -EINVAL; } } @@ -1866,7 +1829,7 @@ static int do_scsi_command(struct fsg_common *common) bh = common->next_buffhd_to_fill; common->next_buffhd_to_drain = bh; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(common); + rc = sleep_thread(common, true); if (rc) return rc; } @@ -2196,8 +2159,8 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) if (common->data_size == 0) common->data_dir = DATA_DIR_NONE; common->lun = cbw->Lun; - if (common->lun >= 0 && common->lun < common->nluns) - common->curlun = &common->luns[common->lun]; + if (common->lun < common->nluns) + common->curlun = common->luns[common->lun]; else common->curlun = NULL; common->tag = cbw->Tag; @@ -2212,7 +2175,7 @@ static int get_next_command(struct fsg_common *common) /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(common); + rc = sleep_thread(common, true); if (rc) return rc; } @@ -2231,7 +2194,7 @@ static int get_next_command(struct fsg_common *common) /* Wait for the CBW to arrive */ while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(common); + rc = sleep_thread(common, true); if (rc) return rc; } @@ -2269,7 +2232,7 @@ reset: if (common->fsg) { fsg = common->fsg; - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; if (bh->inreq) { @@ -2285,10 +2248,12 @@ reset: /* Disable the endpoints */ if (fsg->bulk_in_enabled) { usb_ep_disable(fsg->bulk_in); + fsg->bulk_in->driver_data = NULL; fsg->bulk_in_enabled = 0; } if (fsg->bulk_out_enabled) { usb_ep_disable(fsg->bulk_out); + fsg->bulk_out->driver_data = NULL; fsg->bulk_out_enabled = 0; } @@ -2326,7 +2291,7 @@ reset: clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; rc = alloc_request(common, fsg->bulk_in, &bh->inreq); @@ -2343,7 +2308,9 @@ reset: common->running = 1; for (i = 0; i < common->nluns; ++i) - common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + if (common->luns[i]) + common->luns[i]->unit_attention_data = + SS_RESET_OCCURRED; return rc; } @@ -2395,7 +2362,7 @@ static void handle_exception(struct fsg_common *common) /* Cancel all the pending transfers */ if (likely(common->fsg)) { - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); @@ -2407,13 +2374,13 @@ static void handle_exception(struct fsg_common *common) /* Wait until everything is idle */ for (;;) { int num_active = 0; - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } if (num_active == 0) break; - if (sleep_thread(common)) + if (sleep_thread(common, true)) return; } @@ -2430,7 +2397,7 @@ static void handle_exception(struct fsg_common *common) */ spin_lock_irq(&common->lock); - for (i = 0; i < fsg_num_buffers; ++i) { + for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -2443,7 +2410,9 @@ static void handle_exception(struct fsg_common *common) common->state = FSG_STATE_STATUS_PHASE; else { for (i = 0; i < common->nluns; ++i) { - curlun = &common->luns[i]; + curlun = common->luns[i]; + if (!curlun) + continue; curlun->prevent_medium_removal = 0; curlun->sense_data = SS_NO_SENSE; curlun->unit_attention_data = SS_NO_SENSE; @@ -2485,8 +2454,9 @@ static void handle_exception(struct fsg_common *common) * CONFIG_CHANGE cases. */ /* for (i = 0; i < common->nluns; ++i) */ - /* common->luns[i].unit_attention_data = */ - /* SS_RESET_OCCURRED; */ + /* if (common->luns[i]) */ + /* common->luns[i]->unit_attention_data = */ + /* SS_RESET_OCCURRED; */ break; case FSG_STATE_CONFIG_CHANGE: @@ -2547,7 +2517,7 @@ static int fsg_main_thread(void *common_) } if (!common->running) { - sleep_thread(common); + sleep_thread(common, true); continue; } @@ -2582,12 +2552,13 @@ static int fsg_main_thread(void *common_) if (!common->ops || !common->ops->thread_exits || common->ops->thread_exits(common) < 0) { - struct fsg_lun *curlun = common->luns; + struct fsg_lun **curlun_it = common->luns; unsigned i = common->nluns; down_write(&common->filesem); - for (; i--; ++curlun) { - if (!fsg_lun_is_open(curlun)) + for (; i--; ++curlun_it) { + struct fsg_lun *curlun = *curlun_it; + if (!curlun || !fsg_lun_is_open(curlun)) continue; fsg_lun_close(curlun); @@ -2603,14 +2574,62 @@ static int fsg_main_thread(void *common_) /*************************** DEVICE ATTRIBUTES ***************************/ -static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); -static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua); -static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); +static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_ro(curlun, buf); +} + +static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_nofua(curlun, buf); +} -static struct device_attribute dev_attr_ro_cdrom = - __ATTR(ro, 0444, fsg_show_ro, NULL); -static struct device_attribute dev_attr_file_nonremovable = - __ATTR(file, 0444, fsg_show_file, NULL); +static ssize_t file_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_show_file(curlun, filesem, buf); +} + +static ssize_t ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_ro(curlun, filesem, buf, count); +} + +static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_store_nofua(curlun, buf, count); +} + +static ssize_t file_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_file(curlun, filesem, buf, count); +} + +static DEVICE_ATTR_RW(ro); +static DEVICE_ATTR_RW(nofua); +static DEVICE_ATTR_RW(file); + +static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro); +static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file); /****************************** FSG COMMON ******************************/ @@ -2622,221 +2641,422 @@ static void fsg_lun_release(struct device *dev) /* Nothing needs to be done */ } -static inline void fsg_common_get(struct fsg_common *common) +void fsg_common_get(struct fsg_common *common) { kref_get(&common->ref); } +EXPORT_SYMBOL_GPL(fsg_common_get); -static inline void fsg_common_put(struct fsg_common *common) +void fsg_common_put(struct fsg_common *common) { kref_put(&common->ref, fsg_common_release); } +EXPORT_SYMBOL_GPL(fsg_common_put); -static struct fsg_common *fsg_common_init(struct fsg_common *common, - struct usb_composite_dev *cdev, - struct fsg_config *cfg) +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers) { - struct usb_gadget *gadget = cdev->gadget; - struct fsg_buffhd *bh; - struct fsg_lun *curlun; - struct fsg_lun_config *lcfg; - int nluns, i, rc; - char *pathbuf; - - rc = fsg_num_buffers_validate(); - if (rc != 0) - return ERR_PTR(rc); - - /* Find out how many LUNs there should be */ - nluns = cfg->nluns; - if (nluns < 1 || nluns > FSG_MAX_LUNS) { - dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); - return ERR_PTR(-EINVAL); - } + if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) + return 0; + pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", + fsg_num_buffers, 2, 4); + return -EINVAL; +} - /* Allocate? */ +static struct fsg_common *fsg_common_setup(struct fsg_common *common) +{ if (!common) { - common = kzalloc(sizeof *common, GFP_KERNEL); + common = kzalloc(sizeof(*common), GFP_KERNEL); if (!common) return ERR_PTR(-ENOMEM); common->free_storage_on_release = 1; } else { - memset(common, 0, sizeof *common); common->free_storage_on_release = 0; } + init_rwsem(&common->filesem); + spin_lock_init(&common->lock); + kref_init(&common->ref); + init_completion(&common->thread_notifier); + init_waitqueue_head(&common->fsg_wait); + common->state = FSG_STATE_TERMINATED; - common->buffhds = kcalloc(fsg_num_buffers, - sizeof *(common->buffhds), GFP_KERNEL); - if (!common->buffhds) { - if (common->free_storage_on_release) - kfree(common); - return ERR_PTR(-ENOMEM); + return common; +} + +void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs) +{ + common->sysfs = sysfs; +} +EXPORT_SYMBOL_GPL(fsg_common_set_sysfs); + +static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n) +{ + if (buffhds) { + struct fsg_buffhd *bh = buffhds; + while (n--) { + kfree(bh->buf); + ++bh; + } + kfree(buffhds); } +} - common->ops = cfg->ops; - common->private_data = cfg->private_data; +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) +{ + struct fsg_buffhd *bh, *buffhds; + int i, rc; - common->gadget = gadget; - common->ep0 = gadget->ep0; - common->ep0req = cdev->req; - common->cdev = cdev; + rc = fsg_num_buffers_validate(n); + if (rc != 0) + return rc; + + buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); + if (!buffhds) + return -ENOMEM; - /* Maybe allocate device-global string IDs, and patch descriptors */ - if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { - rc = usb_string_id(cdev); - if (unlikely(rc < 0)) + /* Data buffers cyclic list */ + bh = buffhds; + i = n; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); + if (unlikely(!bh->buf)) goto error_release; - fsg_strings[FSG_STRING_INTERFACE].id = rc; - fsg_intf_desc.iInterface = rc; - } + } while (--i); + bh->next = buffhds; + + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->fsg_num_buffers = n; + common->buffhds = buffhds; + + return 0; +error_release: /* - * Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. + * "buf"s pointed to by heads after n - i are NULL + * so releasing them won't hurt */ - curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL); - if (unlikely(!curlun)) { - rc = -ENOMEM; - goto error_release; + _fsg_common_free_buffers(buffhds, n); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers); + +static inline void fsg_common_remove_sysfs(struct fsg_lun *lun) +{ + device_remove_file(&lun->dev, &dev_attr_nofua); + /* + * device_remove_file() => + * + * here the attr (e.g. dev_attr_ro) is only used to be passed to: + * + * sysfs_remove_file() => + * + * here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in + * the same namespace and + * from here only attr->name is passed to: + * + * sysfs_hash_and_remove() + * + * attr->name is the same for dev_attr_ro_cdrom and + * dev_attr_ro + * attr->name is the same for dev_attr_file and + * dev_attr_file_nonremovable + * + * so we don't differentiate between removing e.g. dev_attr_ro_cdrom + * and dev_attr_ro + */ + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); +} + +void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs) +{ + if (sysfs) { + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); } - common->luns = curlun; + fsg_lun_close(lun); + kfree(lun); +} +EXPORT_SYMBOL_GPL(fsg_common_remove_lun); - init_rwsem(&common->filesem); +static void _fsg_common_remove_luns(struct fsg_common *common, int n) +{ + int i; - for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { - curlun->cdrom = !!lcfg->cdrom; - curlun->ro = lcfg->cdrom || lcfg->ro; - curlun->initially_ro = curlun->ro; - curlun->removable = lcfg->removable; - curlun->dev.release = fsg_lun_release; - curlun->dev.parent = &gadget->dev; - /* curlun->dev.driver = &fsg_driver.driver; XXX */ - dev_set_drvdata(&curlun->dev, &common->filesem); - dev_set_name(&curlun->dev, "lun%d", i); - - rc = device_register(&curlun->dev); - if (rc) { - INFO(common, "failed to register LUN%d: %d\n", i, rc); - common->nluns = i; - put_device(&curlun->dev); - goto error_release; + for (i = 0; i < n; ++i) + if (common->luns[i]) { + fsg_common_remove_lun(common->luns[i], common->sysfs); + common->luns[i] = NULL; } +} +EXPORT_SYMBOL_GPL(fsg_common_remove_luns); - rc = device_create_file(&curlun->dev, - curlun->cdrom - ? &dev_attr_ro_cdrom - : &dev_attr_ro); - if (rc) - goto error_luns; - rc = device_create_file(&curlun->dev, - curlun->removable - ? &dev_attr_file - : &dev_attr_file_nonremovable); - if (rc) - goto error_luns; - rc = device_create_file(&curlun->dev, &dev_attr_nofua); - if (rc) - goto error_luns; +void fsg_common_remove_luns(struct fsg_common *common) +{ + _fsg_common_remove_luns(common, common->nluns); +} - if (lcfg->filename) { - rc = fsg_lun_open(curlun, lcfg->filename); - if (rc) - goto error_luns; - } else if (!curlun->removable) { - ERROR(common, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto error_luns; - } +void fsg_common_free_luns(struct fsg_common *common) +{ + fsg_common_remove_luns(common); + kfree(common->luns); + common->luns = NULL; +} +EXPORT_SYMBOL_GPL(fsg_common_free_luns); + +int fsg_common_set_nluns(struct fsg_common *common, int nluns) +{ + struct fsg_lun **curlun; + + /* Find out how many LUNs there should be */ + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + pr_err("invalid number of LUNs: %u\n", nluns); + return -EINVAL; } + + curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL); + if (unlikely(!curlun)) + return -ENOMEM; + + if (common->luns) + fsg_common_free_luns(common); + + common->luns = curlun; common->nluns = nluns; - /* Data buffers cyclic list */ - bh = common->buffhds; - i = fsg_num_buffers; - goto buffhds_first_it; - do { - bh->next = bh + 1; - ++bh; -buffhds_first_it: - bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); - if (unlikely(!bh->buf)) { - rc = -ENOMEM; - goto error_release; - } - } while (--i); - bh->next = common->buffhds; + pr_info("Number of LUNs=%d\n", common->nluns); - /* Prepare inquiryString */ - i = get_default_bcdDevice(); - snprintf(common->inquiry_string, sizeof common->inquiry_string, - "%-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"), - i); + return 0; +} +EXPORT_SYMBOL_GPL(fsg_common_set_nluns); + +void fsg_common_set_ops(struct fsg_common *common, + const struct fsg_operations *ops) +{ + common->ops = ops; +} +EXPORT_SYMBOL_GPL(fsg_common_set_ops); + +void fsg_common_free_buffers(struct fsg_common *common) +{ + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->buffhds = NULL; +} +EXPORT_SYMBOL_GPL(fsg_common_free_buffers); + +int fsg_common_set_cdev(struct fsg_common *common, + struct usb_composite_dev *cdev, bool can_stall) +{ + struct usb_string *us; + + common->gadget = cdev->gadget; + common->ep0 = cdev->gadget->ep0; + common->ep0req = cdev->req; + common->cdev = cdev; + + us = usb_gstrings_attach(cdev, fsg_strings_array, + ARRAY_SIZE(fsg_strings)); + if (IS_ERR(us)) + return PTR_ERR(us); + + fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id; /* * Some peripheral controllers are known not to be able to * halt bulk endpoints correctly. If one of them is present, * disable stalls. */ - common->can_stall = cfg->can_stall && - !(gadget_is_at91(common->gadget)); + common->can_stall = can_stall && !(gadget_is_at91(common->gadget)); - spin_lock_init(&common->lock); - kref_init(&common->ref); + return 0; +} +EXPORT_SYMBOL_GPL(fsg_common_set_cdev); - /* Tell the thread to start working */ - common->thread_task = - kthread_create(fsg_main_thread, common, "file-storage"); - if (IS_ERR(common->thread_task)) { - rc = PTR_ERR(common->thread_task); - goto error_release; +static inline int fsg_common_add_sysfs(struct fsg_common *common, + struct fsg_lun *lun) +{ + int rc; + + rc = device_register(&lun->dev); + if (rc) { + put_device(&lun->dev); + return rc; + } + + rc = device_create_file(&lun->dev, + lun->cdrom + ? &dev_attr_ro_cdrom + : &dev_attr_ro); + if (rc) + goto error; + rc = device_create_file(&lun->dev, + lun->removable + ? &dev_attr_file + : &dev_attr_file_nonremovable); + if (rc) + goto error; + rc = device_create_file(&lun->dev, &dev_attr_nofua); + if (rc) + goto error; + + return 0; + +error: + /* removing nonexistent files is a no-op */ + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); + return rc; +} + +int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, + unsigned int id, const char *name, + const char **name_pfx) +{ + struct fsg_lun *lun; + char *pathbuf, *p; + int rc = -ENOMEM; + + if (!common->nluns || !common->luns) + return -ENODEV; + + if (common->luns[id]) + return -EBUSY; + + if (!cfg->filename && !cfg->removable) { + pr_err("no file given for LUN%d\n", id); + return -EINVAL; } - init_completion(&common->thread_notifier); - init_waitqueue_head(&common->fsg_wait); - /* Information */ - INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); - INFO(common, "Number of LUNs=%d\n", common->nluns); + lun = kzalloc(sizeof(*lun), GFP_KERNEL); + if (!lun) + return -ENOMEM; + + lun->name_pfx = name_pfx; + + lun->cdrom = !!cfg->cdrom; + lun->ro = cfg->cdrom || cfg->ro; + lun->initially_ro = lun->ro; + lun->removable = !!cfg->removable; + + if (!common->sysfs) { + /* we DON'T own the name!*/ + lun->name = name; + } else { + lun->dev.release = fsg_lun_release; + lun->dev.parent = &common->gadget->dev; + dev_set_drvdata(&lun->dev, &common->filesem); + dev_set_name(&lun->dev, "%s", name); + lun->name = dev_name(&lun->dev); + + rc = fsg_common_add_sysfs(common, lun); + if (rc) { + pr_info("failed to register LUN%d: %d\n", id, rc); + goto error_sysfs; + } + } + + common->luns[id] = lun; + + if (cfg->filename) { + rc = fsg_lun_open(lun, cfg->filename); + if (rc) + goto error_lun; + } pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0, nluns = common->nluns, curlun = common->luns; - i < nluns; - ++curlun, ++i) { - char *p = "(no medium)"; - if (fsg_lun_is_open(curlun)) { - p = "(error)"; - if (pathbuf) { - p = d_path(&curlun->filp->f_path, - pathbuf, PATH_MAX); - if (IS_ERR(p)) - p = "(error)"; - } + p = "(no medium)"; + if (fsg_lun_is_open(lun)) { + p = "(error)"; + if (pathbuf) { + p = d_path(&lun->filp->f_path, pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = "(error)"; } - LINFO(curlun, "LUN: %s%s%sfile: %s\n", - curlun->removable ? "removable " : "", - curlun->ro ? "read only " : "", - curlun->cdrom ? "CD-ROM " : "", - p); } + pr_info("LUN: %s%s%sfile: %s\n", + lun->removable ? "removable " : "", + lun->ro ? "read only " : "", + lun->cdrom ? "CD-ROM " : "", + p); kfree(pathbuf); + return 0; + +error_lun: + if (common->sysfs) { + fsg_common_remove_sysfs(lun); + device_unregister(&lun->dev); + } + fsg_lun_close(lun); + common->luns[id] = NULL; +error_sysfs: + kfree(lun); + return rc; +} +EXPORT_SYMBOL_GPL(fsg_common_create_lun); + +int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg) +{ + char buf[8]; /* enough for 100000000 different numbers, decimal */ + int i, rc; + + for (i = 0; i < common->nluns; ++i) { + snprintf(buf, sizeof(buf), "lun%d", i); + rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL); + if (rc) + goto fail; + } + + pr_info("Number of LUNs=%d\n", common->nluns); + + return 0; + +fail: + _fsg_common_remove_luns(common, i); + return rc; +} +EXPORT_SYMBOL_GPL(fsg_common_create_luns); + +void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, + const char *pn) +{ + int i; + + /* Prepare inquiryString */ + i = get_default_bcdDevice(); + snprintf(common->inquiry_string, sizeof(common->inquiry_string), + "%-8s%-16s%04x", vn ?: "Linux", + /* Assume product name dependent on the first LUN */ + pn ?: ((*common->luns)->cdrom + ? "File-CD Gadget" + : "File-Stor Gadget"), + i); +} +EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string); + +int fsg_common_run_thread(struct fsg_common *common) +{ + common->state = FSG_STATE_IDLE; + /* Tell the thread to start working */ + common->thread_task = + kthread_create(fsg_main_thread, common, "file-storage"); + if (IS_ERR(common->thread_task)) { + common->state = FSG_STATE_TERMINATED; + return PTR_ERR(common->thread_task); + } + DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); wake_up_process(common->thread_task); - return common; - -error_luns: - common->nluns = i + 1; -error_release: - common->state = FSG_STATE_TERMINATED; /* The thread is dead */ - /* Call fsg_common_release() directly, ref might be not initialised. */ - fsg_common_release(&common->ref); - return ERR_PTR(rc); + return 0; } +EXPORT_SYMBOL_GPL(fsg_common_run_thread); static void fsg_common_release(struct kref *ref) { @@ -2849,36 +3069,26 @@ static void fsg_common_release(struct kref *ref) } if (likely(common->luns)) { - struct fsg_lun *lun = common->luns; + struct fsg_lun **lun_it = common->luns; unsigned i = common->nluns; /* In error recovery common->nluns may be zero. */ - for (; i; --i, ++lun) { - device_remove_file(&lun->dev, &dev_attr_nofua); - device_remove_file(&lun->dev, - lun->cdrom - ? &dev_attr_ro_cdrom - : &dev_attr_ro); - device_remove_file(&lun->dev, - lun->removable - ? &dev_attr_file - : &dev_attr_file_nonremovable); + for (; i; --i, ++lun_it) { + struct fsg_lun *lun = *lun_it; + if (!lun) + continue; + if (common->sysfs) + fsg_common_remove_sysfs(lun); fsg_lun_close(lun); - device_unregister(&lun->dev); + if (common->sysfs) + device_unregister(&lun->dev); + kfree(lun); } kfree(common->luns); } - { - struct fsg_buffhd *bh = common->buffhds; - unsigned i = fsg_num_buffers; - do { - kfree(bh->buf); - } while (++bh, --i); - } - - kfree(common->buffhds); + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); if (common->free_storage_on_release) kfree(common); } @@ -2886,24 +3096,6 @@ static void fsg_common_release(struct kref *ref) /*-------------------------------------------------------------------------*/ -static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct fsg_dev *fsg = fsg_from_func(f); - struct fsg_common *common = fsg->common; - - DBG(fsg, "unbind\n"); - if (fsg->common->fsg == fsg) { - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); - /* FIXME: make interruptible or killable somehow? */ - wait_event(common->fsg_wait, common->fsg != fsg); - } - - fsg_common_put(common); - usb_free_all_descriptors(&fsg->function); - kfree(fsg); -} - static int fsg_bind(struct usb_configuration *c, struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); @@ -2912,6 +3104,19 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; unsigned max_burst; int ret; + struct fsg_opts *opts; + + opts = fsg_opts_from_func_inst(f->fi); + if (!opts->no_configfs) { + ret = fsg_common_set_cdev(fsg->common, c->cdev, + fsg->common->can_stall); + if (ret) + return ret; + fsg_common_set_inquiry_string(fsg->common, NULL, NULL); + ret = fsg_common_run_thread(fsg->common); + if (ret) + return ret; + } fsg->gadget = gadget; @@ -2964,95 +3169,472 @@ autoconf_fail: return -ENOTSUPP; } -/****************************** ADD FUNCTION ******************************/ +/****************************** ALLOCATE FUNCTION *************************/ -static struct usb_gadget_strings *fsg_strings_array[] = { - &fsg_stringtab, +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct fsg_common *common = fsg->common; + + DBG(fsg, "unbind\n"); + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + /* FIXME: make interruptible or killable somehow? */ + wait_event(common->fsg_wait, common->fsg != fsg); + } + + usb_free_all_descriptors(&fsg->function); +} + +static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_lun_opts, group); +} + +static inline struct fsg_opts *to_fsg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(fsg_lun_opts); +CONFIGFS_ATTR_OPS(fsg_lun_opts); + +static void fsg_lun_attr_release(struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + + lun_opts = to_fsg_lun_opts(item); + kfree(lun_opts); +} + +static struct configfs_item_operations fsg_lun_item_ops = { + .release = fsg_lun_attr_release, + .show_attribute = fsg_lun_opts_attr_show, + .store_attribute = fsg_lun_opts_attr_store, +}; + +static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page); +} + +static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_file = + __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show, + fsg_lun_opts_file_store); + +static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_ro(opts->lun, page); +} + +static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_ro = + __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show, + fsg_lun_opts_ro_store); + +static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts, + char *page) +{ + return fsg_show_removable(opts->lun, page); +} + +static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + return fsg_store_removable(opts->lun, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_removable = + __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR, + fsg_lun_opts_removable_show, + fsg_lun_opts_removable_store); + +static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_cdrom(opts->lun, page); +} + +static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + struct fsg_opts *fsg_opts; + + fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page, + len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom = + __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show, + fsg_lun_opts_cdrom_store); + +static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page) +{ + return fsg_show_nofua(opts->lun, page); +} + +static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts, + const char *page, size_t len) +{ + return fsg_store_nofua(opts->lun, page, len); +} + +static struct fsg_lun_opts_attribute fsg_lun_opts_nofua = + __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show, + fsg_lun_opts_nofua_store); + +static struct configfs_attribute *fsg_lun_attrs[] = { + &fsg_lun_opts_file.attr, + &fsg_lun_opts_ro.attr, + &fsg_lun_opts_removable.attr, + &fsg_lun_opts_cdrom.attr, + &fsg_lun_opts_nofua.attr, NULL, }; -static int fsg_bind_config(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct fsg_common *common) +static struct config_item_type fsg_lun_type = { + .ct_item_ops = &fsg_lun_item_ops, + .ct_attrs = fsg_lun_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *fsg_lun_make(struct config_group *group, + const char *name) { - struct fsg_dev *fsg; + struct fsg_lun_opts *opts; + struct fsg_opts *fsg_opts; + struct fsg_lun_config config; + char *num_str; + u8 num; + int ret; + + num_str = strchr(name, '.'); + if (!num_str) { + pr_err("Unable to locate . in LUN.NUMBER\n"); + return ERR_PTR(-EINVAL); + } + num_str++; + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + fsg_opts = to_fsg_opts(&group->cg_item); + if (num >= FSG_MAX_LUNS) + return ERR_PTR(-ERANGE); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt || fsg_opts->common->luns[num]) { + ret = -EBUSY; + goto out; + } + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) { + ret = -ENOMEM; + goto out; + } + + memset(&config, 0, sizeof(config)); + config.removable = true; + + ret = fsg_common_create_lun(fsg_opts->common, &config, num, name, + (const char **)&group->cg_item.ci_name); + if (ret) { + kfree(opts); + goto out; + } + opts->lun = fsg_opts->common->luns[num]; + opts->lun_id = num; + mutex_unlock(&fsg_opts->lock); + + config_group_init_type_name(&opts->group, name, &fsg_lun_type); + + return &opts->group; +out: + mutex_unlock(&fsg_opts->lock); + return ERR_PTR(ret); +} + +static void fsg_lun_drop(struct config_group *group, struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + struct fsg_opts *fsg_opts; + + lun_opts = to_fsg_lun_opts(item); + fsg_opts = to_fsg_opts(&group->cg_item); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt) { + struct config_item *gadget; + + gadget = group->cg_item.ci_parent->ci_parent; + unregister_gadget_item(gadget); + } + + fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs); + fsg_opts->common->luns[lun_opts->lun_id] = NULL; + lun_opts->lun_id = 0; + mutex_unlock(&fsg_opts->lock); + + config_item_put(item); +} + +CONFIGFS_ATTR_STRUCT(fsg_opts); +CONFIGFS_ATTR_OPS(fsg_opts); + +static void fsg_attr_release(struct config_item *item) +{ + struct fsg_opts *opts = to_fsg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations fsg_item_ops = { + .release = fsg_attr_release, + .show_attribute = fsg_opts_attr_show, + .store_attribute = fsg_opts_attr_store, +}; + +static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->can_stall); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page, + size_t len) +{ + int ret; + bool stall; + + mutex_lock(&opts->lock); + + if (opts->refcnt) { + mutex_unlock(&opts->lock); + return -EBUSY; + } + + ret = strtobool(page, &stall); + if (!ret) { + opts->common->can_stall = stall; + ret = len; + } + + mutex_unlock(&opts->lock); + + return ret; +} + +static struct fsg_opts_attribute fsg_opts_stall = + __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show, + fsg_opts_stall_store); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->fsg_num_buffers); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + ret = fsg_num_buffers_validate(num); + if (ret) + goto end; + + fsg_common_set_num_buffers(opts->common, num); + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct fsg_opts_attribute fsg_opts_num_buffers = + __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR, + fsg_opts_num_buffers_show, + fsg_opts_num_buffers_store); + +#endif + +static struct configfs_attribute *fsg_attrs[] = { + &fsg_opts_stall.attr, +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + &fsg_opts_num_buffers.attr, +#endif + NULL, +}; + +static struct configfs_group_operations fsg_group_ops = { + .make_group = fsg_lun_make, + .drop_item = fsg_lun_drop, +}; + +static struct config_item_type fsg_func_type = { + .ct_item_ops = &fsg_item_ops, + .ct_group_ops = &fsg_group_ops, + .ct_attrs = fsg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void fsg_free_inst(struct usb_function_instance *fi) +{ + struct fsg_opts *opts; + + opts = fsg_opts_from_func_inst(fi); + fsg_common_put(opts->common); + kfree(opts); +} + +static struct usb_function_instance *fsg_alloc_inst(void) +{ + struct fsg_opts *opts; + struct fsg_lun_config config; int rc; - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = fsg_free_inst; + opts->common = fsg_common_setup(opts->common); + if (IS_ERR(opts->common)) { + rc = PTR_ERR(opts->common); + goto release_opts; + } + rc = fsg_common_set_nluns(opts->common, FSG_MAX_LUNS); + if (rc) + goto release_opts; + + rc = fsg_common_set_num_buffers(opts->common, + CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS); + if (rc) + goto release_luns; + + pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + + memset(&config, 0, sizeof(config)); + config.removable = true; + rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", + (const char **)&opts->func_inst.group.cg_item.ci_name); + opts->lun0.lun = opts->common->luns[0]; + opts->lun0.lun_id = 0; + config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type); + opts->default_groups[0] = &opts->lun0.group; + opts->func_inst.group.default_groups = opts->default_groups; + + config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type); + + return &opts->func_inst; + +release_luns: + kfree(opts->common->luns); +release_opts: + kfree(opts); + return ERR_PTR(rc); +} + +static void fsg_free(struct usb_function *f) +{ + struct fsg_dev *fsg; + struct fsg_opts *opts; + + fsg = container_of(f, struct fsg_dev, function); + opts = container_of(f->fi, struct fsg_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + + kfree(fsg); +} + +static struct usb_function *fsg_alloc(struct usb_function_instance *fi) +{ + struct fsg_opts *opts = fsg_opts_from_func_inst(fi); + struct fsg_common *common = opts->common; + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); if (unlikely(!fsg)) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - fsg->function.name = FSG_DRIVER_DESC; - fsg->function.strings = fsg_strings_array; - fsg->function.bind = fsg_bind; - fsg->function.unbind = fsg_unbind; - fsg->function.setup = fsg_setup; - fsg->function.set_alt = fsg_set_alt; - fsg->function.disable = fsg_disable; + mutex_lock(&opts->lock); + opts->refcnt++; + mutex_unlock(&opts->lock); + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + fsg->function.free_func = fsg_free; fsg->common = common; - /* - * Our caller holds a reference to common structure so we - * don't have to be worry about it being freed until we return - * from this function. So instead of incrementing counter now - * and decrement in error recovery we increment it only when - * call to usb_add_function() was successful. - */ - rc = usb_add_function(c, &fsg->function); - if (unlikely(rc)) - kfree(fsg); - else - fsg_common_get(fsg->common); - return rc; + return &fsg->function; } +DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); /************************* Module parameters *************************/ -struct fsg_module_parameters { - char *file[FSG_MAX_LUNS]; - bool ro[FSG_MAX_LUNS]; - bool removable[FSG_MAX_LUNS]; - bool cdrom[FSG_MAX_LUNS]; - bool nofua[FSG_MAX_LUNS]; - - unsigned int file_count, ro_count, removable_count, cdrom_count; - unsigned int nofua_count; - unsigned int luns; /* nluns */ - bool stall; /* can_stall */ -}; -#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ - module_param_array_named(prefix ## name, params.name, type, \ - &prefix ## params.name ## _count, \ - S_IRUGO); \ - MODULE_PARM_DESC(prefix ## name, desc) - -#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ - module_param_named(prefix ## name, params.name, type, \ - S_IRUGO); \ - MODULE_PARM_DESC(prefix ## name, desc) - -#define FSG_MODULE_PARAMETERS(prefix, params) \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ - "names of backing files or devices"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ - "true to force read-only"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ - "true to simulate removable media"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ - "true to simulate CD-ROM instead of disk"); \ - _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \ - "true to ignore SCSI WRITE(10,12) FUA bit"); \ - _FSG_MODULE_PARAM(prefix, params, luns, uint, \ - "number of LUNs"); \ - _FSG_MODULE_PARAM(prefix, params, stall, bool, \ - "false to prevent bulk stalls") - -static void -fsg_config_from_params(struct fsg_config *cfg, - const struct fsg_module_parameters *params) +void fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params, + unsigned int fsg_num_buffers) { struct fsg_lun_config *lun; unsigned i; @@ -3068,31 +3650,19 @@ fsg_config_from_params(struct fsg_config *cfg, lun->filename = params->file_count > i && params->file[i][0] ? params->file[i] - : 0; + : NULL; } /* Let MSF use defaults */ - cfg->vendor_name = 0; - cfg->product_name = 0; + cfg->vendor_name = NULL; + cfg->product_name = NULL; cfg->ops = NULL; cfg->private_data = NULL; /* Finalise */ cfg->can_stall = params->stall; + cfg->fsg_num_buffers = fsg_num_buffers; } +EXPORT_SYMBOL_GPL(fsg_config_from_params); -static inline struct fsg_common * -fsg_common_from_params(struct fsg_common *common, - struct usb_composite_dev *cdev, - const struct fsg_module_parameters *params) - __attribute__((unused)); -static inline struct fsg_common * -fsg_common_from_params(struct fsg_common *common, - struct usb_composite_dev *cdev, - const struct fsg_module_parameters *params) -{ - struct fsg_config cfg; - fsg_config_from_params(&cfg, params); - return fsg_common_init(common, cdev, &cfg); -} diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/f_mass_storage.h new file mode 100644 index 00000000000..b4866fcef30 --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.h @@ -0,0 +1,166 @@ +#ifndef USB_F_MASS_STORAGE_H +#define USB_F_MASS_STORAGE_H + +#include <linux/usb/composite.h> +#include "storage_common.h" + +struct fsg_module_parameters { + char *file[FSG_MAX_LUNS]; + bool ro[FSG_MAX_LUNS]; + bool removable[FSG_MAX_LUNS]; + bool cdrom[FSG_MAX_LUNS]; + bool nofua[FSG_MAX_LUNS]; + + unsigned int file_count, ro_count, removable_count, cdrom_count; + unsigned int nofua_count; + unsigned int luns; /* nluns */ + bool stall; /* can_stall */ +}; + +#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ + module_param_array_named(prefix ## name, params.name, type, \ + &prefix ## params.name ## _count, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ + module_param_named(prefix ## name, params.name, type, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define __FSG_MODULE_PARAMETERS(prefix, params) \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ + "names of backing files or devices"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ + "true to force read-only"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ + "true to simulate removable media"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ + "true to simulate CD-ROM instead of disk"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \ + "true to ignore SCSI WRITE(10,12) FUA bit"); \ + _FSG_MODULE_PARAM(prefix, params, luns, uint, \ + "number of LUNs"); \ + _FSG_MODULE_PARAM(prefix, params, stall, bool, \ + "false to prevent bulk stalls") + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + __FSG_MODULE_PARAMETERS(prefix, params); \ + module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\ + MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers") +#else + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + __FSG_MODULE_PARAMETERS(prefix, params) + +#endif + +struct fsg_common; + +/* FSF callback functions */ +struct fsg_operations { + /* + * Callback function to call when thread exits. If no + * callback is set or it returns value lower then zero MSF + * will force eject all LUNs it operates on (including those + * marked as non-removable or with prevent_medium_removal flag + * set). + */ + int (*thread_exits)(struct fsg_common *common); +}; + +struct fsg_lun_opts { + struct config_group group; + struct fsg_lun *lun; + int lun_id; +}; + +struct fsg_opts { + struct fsg_common *common; + struct usb_function_instance func_inst; + struct fsg_lun_opts lun0; + struct config_group *default_groups[2]; + bool no_configfs; /* for legacy gadgets */ + + /* + * 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; +}; + +struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; +}; + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config luns[FSG_MAX_LUNS]; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + + char can_stall; + unsigned int fsg_num_buffers; +}; + +static inline struct fsg_opts * +fsg_opts_from_func_inst(const struct usb_function_instance *fi) +{ + return container_of(fi, struct fsg_opts, func_inst); +} + +void fsg_common_get(struct fsg_common *common); + +void fsg_common_put(struct fsg_common *common); + +void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs); + +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n); + +void fsg_common_free_buffers(struct fsg_common *common); + +int fsg_common_set_cdev(struct fsg_common *common, + struct usb_composite_dev *cdev, bool can_stall); + +void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs); + +void fsg_common_remove_luns(struct fsg_common *common); + +void fsg_common_free_luns(struct fsg_common *common); + +int fsg_common_set_nluns(struct fsg_common *common, int nluns); + +void fsg_common_set_ops(struct fsg_common *common, + const struct fsg_operations *ops); + +int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, + unsigned int id, const char *name, + const char **name_pfx); + +int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg); + +void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, + const char *pn); + +int fsg_common_run_thread(struct fsg_common *common); + +void fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params, + unsigned int fsg_num_buffers); + +#endif /* USB_F_MASS_STORAGE_H */ diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 263e721c269..807b31c0edc 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -32,6 +32,8 @@ #include <linux/usb/audio.h> #include <linux/usb/midi.h> +#include "u_f.h" + MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); @@ -191,20 +193,10 @@ static struct usb_gadget_strings *midi_strings[] = { NULL, }; -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, + unsigned length) { - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; + return alloc_ep_req(ep, length, length); } static void free_ep_req(struct usb_ep *ep, struct usb_request *req) @@ -365,7 +357,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = - alloc_ep_req(midi->out_ep, midi->buflen); + midi_alloc_ep_req(midi->out_ep, midi->buflen); if (req == NULL) return -ENOMEM; @@ -546,7 +538,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) return; if (!req) - req = alloc_ep_req(ep, midi->buflen); + req = midi_alloc_ep_req(ep, midi->buflen); if (!req) { ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); @@ -672,9 +664,10 @@ static int f_midi_register_card(struct f_midi *midi) .dev_free = f_midi_snd_free, }; - err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + err = snd_card_new(&midi->gadget->dev, midi->index, midi->id, + THIS_MODULE, 0, &card); if (err < 0) { - ERROR(midi, "snd_card_create() failed\n"); + ERROR(midi, "snd_card_new() failed\n"); goto fail; } midi->card = card; @@ -711,8 +704,6 @@ static int f_midi_register_card(struct f_midi *midi) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - snd_card_set_dev(card, &midi->gadget->dev); - /* register it - we're ready to go */ err = snd_card_register(card); if (err < 0) { diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 6c8362f937b..a9499fd3079 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. @@ -56,8 +59,9 @@ struct f_ncm { u8 notify_state; bool is_open; - struct ndp_parser_opts *parser_opts; + const struct ndp_parser_opts *parser_opts; bool is_crc; + u32 ndp_sign; /* * for notification, it is accessed from both @@ -124,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, @@ -138,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, @@ -150,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, @@ -158,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, @@ -166,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, @@ -181,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, @@ -193,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, @@ -208,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, @@ -223,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, @@ -233,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, @@ -241,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, @@ -249,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, @@ -268,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, @@ -277,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, @@ -286,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, @@ -295,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, @@ -390,8 +394,8 @@ struct ndp_parser_opts { .next_fp_index = 2, \ } -static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; -static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS; +static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; +static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS; static inline void put_ncm(__le16 **p, unsigned size, unsigned val) { @@ -732,8 +736,7 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) default: goto invalid; } - ncm->parser_opts->ndp_sign &= ~NCM_NDP_HDR_CRC_MASK; - ncm->parser_opts->ndp_sign |= ndp_hdr_crc; + ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; value = 0; break; } @@ -875,7 +878,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port, int ndp_align; int ndp_pad; unsigned max_size = ncm->port.fixed_in_len; - struct ndp_parser_opts *opts = ncm->parser_opts; + const struct ndp_parser_opts *opts = ncm->parser_opts; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; div = le16_to_cpu(ntb_parameters.wNdpInDivisor); @@ -921,7 +924,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port, tmp = (void *)tmp + ndp_pad; /* NDP */ - put_unaligned_le32(opts->ndp_sign, tmp); /* dwSignature */ + put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ tmp += 2; /* wLength */ put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); @@ -965,7 +968,7 @@ static int ncm_unwrap_ntb(struct gether *port, struct sk_buff *skb2; int ret = -EINVAL; unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize); - struct ndp_parser_opts *opts = ncm->parser_opts; + const struct ndp_parser_opts *opts = ncm->parser_opts; unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; int dgram_counter; @@ -1002,7 +1005,7 @@ static int ncm_unwrap_ntb(struct gether *port, /* walk through NDP */ tmp = ((void *)skb->data) + index; - if (get_unaligned_le32(tmp) != opts->ndp_sign) { + if (get_unaligned_le32(tmp) != ncm->ndp_sign) { INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); goto err; } @@ -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,72 +1293,131 @@ 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)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(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]) +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; - } +static 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 = 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; @@ -1332,12 +1425,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 d8dd8782768..aebae1853bc 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -72,7 +72,7 @@ static struct usb_gadget_strings *obex_strings[] = { /*-------------------------------------------------------------------------*/ -static struct usb_interface_descriptor obex_control_intf __initdata = { +static struct usb_interface_descriptor obex_control_intf = { .bLength = sizeof(obex_control_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, @@ -83,7 +83,7 @@ static struct usb_interface_descriptor obex_control_intf __initdata = { .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, }; -static struct usb_interface_descriptor obex_data_nop_intf __initdata = { +static struct usb_interface_descriptor obex_data_nop_intf = { .bLength = sizeof(obex_data_nop_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 1, @@ -93,7 +93,7 @@ static struct usb_interface_descriptor obex_data_nop_intf __initdata = { .bInterfaceClass = USB_CLASS_CDC_DATA, }; -static struct usb_interface_descriptor obex_data_intf __initdata = { +static struct usb_interface_descriptor obex_data_intf = { .bLength = sizeof(obex_data_intf), .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 2, @@ -103,14 +103,14 @@ static struct usb_interface_descriptor obex_data_intf __initdata = { .bInterfaceClass = USB_CLASS_CDC_DATA, }; -static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = { +static struct usb_cdc_header_desc obex_cdc_header_desc = { .bLength = sizeof(obex_cdc_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, .bcdCDC = cpu_to_le16(0x0120), }; -static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { +static struct usb_cdc_union_desc obex_cdc_union_desc = { .bLength = sizeof(obex_cdc_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -118,7 +118,7 @@ static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { .bSlaveInterface0 = 2, }; -static struct usb_cdc_obex_desc obex_desc __initdata = { +static struct usb_cdc_obex_desc obex_desc = { .bLength = sizeof(obex_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_OBEX_TYPE, @@ -127,7 +127,7 @@ static struct usb_cdc_obex_desc obex_desc __initdata = { /* High-Speed Support */ -static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { +static struct usb_endpoint_descriptor obex_hs_ep_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -136,7 +136,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { +static struct usb_endpoint_descriptor obex_hs_ep_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -145,7 +145,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *hs_function[] __initdata = { +static struct usb_descriptor_header *hs_function[] = { (struct usb_descriptor_header *) &obex_control_intf, (struct usb_descriptor_header *) &obex_cdc_header_desc, (struct usb_descriptor_header *) &obex_desc, @@ -160,7 +160,7 @@ static struct usb_descriptor_header *hs_function[] __initdata = { /* Full-Speed Support */ -static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { +static struct usb_endpoint_descriptor obex_fs_ep_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -168,7 +168,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { +static struct usb_endpoint_descriptor obex_fs_ep_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -176,7 +176,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *fs_function[] __initdata = { +static struct usb_descriptor_header *fs_function[] = { (struct usb_descriptor_header *) &obex_control_intf, (struct usb_descriptor_header *) &obex_cdc_header_desc, (struct usb_descriptor_header *) &obex_desc, @@ -290,14 +290,40 @@ static void obex_disconnect(struct gserial *g) /*-------------------------------------------------------------------------*/ -static int __init -obex_bind(struct usb_configuration *c, struct usb_function *f) +/* Some controllers can't support CDC OBEX ... */ +static inline bool can_support_obex(struct usb_configuration *c) +{ + /* Since the first interface is a NOP, we can ignore the + * issue of multi-interface support on most controllers. + * + * Altsettings are mandatory, however... + */ + if (!gadget_supports_altsettings(c->cdev->gadget)) + return false; + + /* everything else is *probably* fine ... */ + return true; +} + +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; + + 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 */ status = usb_interface_id(c, f); @@ -319,6 +345,7 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific endpoints */ + status = -ENODEV; ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); if (!ep) goto fail; @@ -376,86 +403,131 @@ fail: return status; } -static void -obex_unbind(struct usb_configuration *c, struct usb_function *f) +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) { - obex_string_defs[OBEX_CTRL_IDX].id = 0; - usb_free_all_descriptors(f); - kfree(func_to_obex(f)); + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); } -/* Some controllers can't support CDC OBEX ... */ -static inline bool can_support_obex(struct usb_configuration *c) +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_obex_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) { - /* Since the first interface is a NOP, we can ignore the - * issue of multi-interface support on most controllers. - * - * Altsettings are mandatory, however... - */ - if (!gadget_supports_altsettings(c->cdev->gadget)) - return false; + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; - /* everything else is *probably* fine ... */ - return true; + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); + + return ret; } -/** - * 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. - * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. - */ -int __init obex_bind_config(struct usb_configuration *c, u8 port_num) +static void obex_attr_release(struct config_item *item) { - struct f_obex *obex; - int status; + struct f_serial_opts *opts = to_f_serial_opts(item); - if (!can_support_obex(c)) - return -EINVAL; + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations obex_item_ops = { + .release = obex_attr_release, + .show_attribute = f_obex_attr_show, +}; + +static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_obex_port_num = + __CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show); - 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; +static struct configfs_attribute *acm_attrs[] = { + &f_obex_port_num.attr, + NULL, +}; - status = obex_string_defs[OBEX_DATA_IDX].id; - obex_data_nop_intf.iInterface = status; - obex_data_intf.iInterface = status; +static struct config_item_type obex_func_type = { + .ct_item_ops = &obex_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void obex_free_inst(struct usb_function_instance *f) +{ + struct f_serial_opts *opts; + + opts = container_of(f, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); + kfree(opts); +} + +static struct usb_function_instance *obex_alloc_inst(void) +{ + struct f_serial_opts *opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = obex_free_inst; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); } + config_group_init_type_name(&opts->func_inst.group, "", + &obex_func_type); + + return &opts->func_inst; +} + +static void obex_free(struct usb_function *f) +{ + struct f_obex *obex; + + obex = func_to_obex(f); + kfree(obex); +} + +static void obex_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static struct usb_function *obex_alloc(struct usb_function_instance *fi) +{ + struct f_obex *obex; + struct f_serial_opts *opts; /* allocate and initialize one new instance */ - obex = kzalloc(sizeof *obex, GFP_KERNEL); + obex = kzalloc(sizeof(*obex), GFP_KERNEL); if (!obex) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - obex->port_num = port_num; + opts = container_of(fi, struct f_serial_opts, func_inst); + + obex->port_num = opts->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_unbind; obex->port.func.set_alt = obex_set_alt; obex->port.func.get_alt = obex_get_alt; obex->port.func.disable = obex_disable; + obex->port.func.free_func = obex_free; - status = usb_add_function(c, &obex->port.func); - if (status) - kfree(obex); - - return status; + return &obex->port.func; } +DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); MODULE_AUTHOR("Felipe Balbi"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index b21ab558b6c..f2b781773ee 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,25 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; int status, i; + 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; + } + /* Reserve interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -560,8 +580,101 @@ 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)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(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 +687,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) +static 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 71beeb83355..9c41e9515b8 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -17,14 +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" - +#include "configfs.h" /* * This function is an RNDIS Ethernet port -- a Microsoft protocol that's @@ -375,7 +378,7 @@ static struct sk_buff *rndis_add_header(struct gether *port, if (skb2) rndis_add_hdr(skb2); - dev_kfree_skb_any(skb); + dev_kfree_skb(skb); return skb2; } @@ -447,14 +450,13 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) { struct f_rndis *rndis = req->context; - struct usb_composite_dev *cdev = rndis->port.func.config->cdev; int status; /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); status = rndis_msg_parser(rndis->config, (u8 *) req->buf); if (status < 0) - ERROR(cdev, "RNDIS command error %d, %d/%d\n", + pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); // spin_unlock(&dev->lock); } @@ -656,6 +658,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 @@ -663,9 +672,51 @@ 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; + 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); + + if (cdev->use_os_string) { + f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), + GFP_KERNEL); + if (!f->os_desc_table) + return -ENOMEM; + f->os_desc_n = 1; + f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; + } + + /* + * 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) + goto fail; + rndis_opts->bound = true; + } + + us = usb_gstrings_attach(cdev, rndis_strings, + ARRAY_SIZE(rndis_string_defs)); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; + } + 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) @@ -742,11 +793,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) - goto fail; - rndis->config = status; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); @@ -768,6 +814,8 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + kfree(f->os_desc_table); + f->os_desc_n = 0; usb_free_all_descriptors(f); if (rndis->notify_req) { @@ -788,65 +836,149 @@ fail: return status; } -static void -rndis_unbind(struct usb_configuration *c, struct usb_function *f) +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) { - struct f_rndis *rndis = func_to_rndis(f); + struct f_rndis_opts *opts; - rndis_deregister(rndis->config); - rndis_exit(); + 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_GPL(rndis_borrow_net); - rndis_string_defs[0].id = 0; - usb_free_all_descriptors(f); +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); +} - kfree(rndis->notify_req->buf); - usb_ep_free_request(rndis->notify, rndis->notify_req); +/* f_rndis_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(rndis); - kfree(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->rndis_os_desc.group.default_groups); /* single VLA chunk */ + kfree(opts); } -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis(struct usb_configuration *c) +static struct usb_function_instance *rndis_alloc_inst(void) { - /* everything else is *presumably* fine */ - return true; + struct f_rndis_opts *opts; + struct usb_os_desc *descs[1]; + char *names[1]; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id; + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = rndis_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(net); + } + INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop); + + descs[0] = &opts->rndis_os_desc; + names[0] = "rndis"; + usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, + names, THIS_MODULE); + config_group_init_type_name(&opts->func_inst.group, "", + &rndis_func_type); + + return &opts->func_inst; } -int -rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer) +static void rndis_free(struct usb_function *f) { - struct f_rndis *rndis; - int status; + struct f_rndis *rndis; + struct f_rndis_opts *opts; - if (!can_support_rndis(c) || !ethaddr) - return -EINVAL; + 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); +} - if (rndis_string_defs[0].id == 0) { - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; +static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); - status = usb_string_ids_tab(c->cdev, rndis_string_defs); - if (status) - return status; + kfree(f->os_desc_table); + f->os_desc_n = 0; + usb_free_all_descriptors(f); - 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; - } + 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 */ - status = -ENOMEM; - rndis = kzalloc(sizeof *rndis, GFP_KERNEL); + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); if (!rndis) - goto fail; + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_rndis_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; - memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); - rndis->vendorID = vendorID; - rndis->manufacturer = manufacturer; + 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; @@ -856,19 +988,44 @@ 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.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 = usb_add_function(c, &rndis->port.func); - if (status) { + status = rndis_register(rndis_response_available, rndis); + if (status < 0) { kfree(rndis); -fail: - rndis_exit(); + return ERR_PTR(status); } - return status; + rndis->config = status; + + return &rndis->port.func; } + +DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); + +static int __init rndis_mod_init(void) +{ + int ret; + + ret = rndis_init(); + if (ret) + return ret; + + return usb_function_register(&rndisusb_func); +} +module_init(rndis_mod_init); + +static void __exit rndis_mod_exit(void) +{ + usb_function_unregister(&rndisusb_func); + rndis_exit(); +} +module_exit(rndis_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 98fa7795df5..9ecbcbf36a4 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include "u_serial.h" @@ -42,7 +43,7 @@ static inline struct f_gser *func_to_gser(struct usb_function *f) /* interface descriptor: */ -static struct usb_interface_descriptor gser_interface_desc __initdata = { +static struct usb_interface_descriptor gser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -55,21 +56,21 @@ static struct usb_interface_descriptor gser_interface_desc __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *gser_fs_function[] __initdata = { +static struct usb_descriptor_header *gser_fs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_fs_in_desc, (struct usb_descriptor_header *) &gser_fs_out_desc, @@ -78,47 +79,47 @@ static struct usb_descriptor_header *gser_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *gser_hs_function[] __initdata = { +static struct usb_descriptor_header *gser_hs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_hs_in_desc, (struct usb_descriptor_header *) &gser_hs_out_desc, NULL, }; -static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = { +static struct usb_endpoint_descriptor gser_ss_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = { +static struct usb_endpoint_descriptor gser_ss_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; -static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = { +static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = { .bLength = sizeof gser_ss_bulk_comp_desc, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; -static struct usb_descriptor_header *gser_ss_function[] __initdata = { +static struct usb_descriptor_header *gser_ss_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_ss_in_desc, (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, @@ -183,14 +184,25 @@ static void gser_disable(struct usb_function *f) /* serial function driver setup/binding */ -static int __init -gser_bind(struct usb_configuration *c, struct usb_function *f) +static int gser_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_gser *gser = func_to_gser(f); int status; struct usb_ep *ep; + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string ID */ + if (gser_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + gser_string_defs[0].id = status; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -246,48 +258,115 @@ fail: return status; } -static void -gser_unbind(struct usb_configuration *c, struct usb_function *f) +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) { - usb_free_all_descriptors(f); - kfree(func_to_gser(f)); + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); } -/** - * gser_bind_config - add a generic serial function to a configuration - * @c: the configuration to support the serial instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. - */ -int __init gser_bind_config(struct usb_configuration *c, u8 port_num) +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_serial_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) { - struct f_gser *gser; - int status; + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); - /* maybe allocate device-global string ID */ - if (gser_string_defs[0].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - gser_string_defs[0].id = status; + return ret; +} + +static void serial_attr_release(struct config_item *item) +{ + struct f_serial_opts *opts = to_f_serial_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations serial_item_ops = { + .release = serial_attr_release, + .show_attribute = f_serial_attr_show, +}; + +static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_serial_port_num = + __CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show); + +static struct configfs_attribute *acm_attrs[] = { + &f_serial_port_num.attr, + NULL, +}; + +static struct config_item_type serial_func_type = { + .ct_item_ops = &serial_item_ops, + .ct_attrs = acm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void gser_free_inst(struct usb_function_instance *f) +{ + struct f_serial_opts *opts; + + opts = container_of(f, struct f_serial_opts, func_inst); + gserial_free_line(opts->port_num); + kfree(opts); +} + +static struct usb_function_instance *gser_alloc_inst(void) +{ + struct f_serial_opts *opts; + int ret; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = gser_free_inst; + ret = gserial_alloc_line(&opts->port_num); + if (ret) { + kfree(opts); + return ERR_PTR(ret); } + config_group_init_type_name(&opts->func_inst.group, "", + &serial_func_type); + + return &opts->func_inst; +} + +static void gser_free(struct usb_function *f) +{ + struct f_gser *serial; + + serial = func_to_gser(f); + kfree(serial); +} + +static void gser_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static struct usb_function *gser_alloc(struct usb_function_instance *fi) +{ + struct f_gser *gser; + struct f_serial_opts *opts; /* allocate and initialize one new instance */ - gser = kzalloc(sizeof *gser, GFP_KERNEL); + gser = kzalloc(sizeof(*gser), GFP_KERNEL); if (!gser) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - gser->port_num = port_num; + opts = container_of(fi, struct f_serial_opts, func_inst); + + gser->port_num = opts->port_num; gser->port.func.name = "gser"; gser->port.func.strings = gser_strings; @@ -295,9 +374,12 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) gser->port.func.unbind = gser_unbind; gser->port.func.set_alt = gser_set_alt; gser->port.func.disable = gser_disable; + gser->port.func.free_func = gser_free; - status = usb_add_function(c, &gser->port.func); - if (status) - kfree(gser); - return status; + return &gser->port.func; } + +DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 102d49beb9d..d3cd52db78f 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -16,10 +16,12 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/usb/composite.h> +#include <linux/err.h> #include "g_zero.h" #include "gadget_chips.h" - +#include "u_f.h" /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral @@ -62,24 +64,11 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f) } static unsigned pattern; -module_param(pattern, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); - -static unsigned isoc_interval = 4; -module_param(isoc_interval, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_interval, "1 - 16"); - -static unsigned isoc_maxpacket = 1024; -module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); - +static unsigned isoc_interval; +static unsigned isoc_maxpacket; static unsigned isoc_mult; -module_param(isoc_mult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); - static unsigned isoc_maxburst; -module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); +static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -213,7 +202,7 @@ static struct usb_endpoint_descriptor ss_source_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -230,7 +219,7 @@ static struct usb_endpoint_descriptor ss_sink_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -248,7 +237,7 @@ static struct usb_endpoint_descriptor ss_iso_source_desc = { .bInterval = 4, }; -struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -266,7 +255,7 @@ static struct usb_endpoint_descriptor ss_iso_sink_desc = { .bInterval = 4, }; -struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -313,7 +302,43 @@ static struct usb_gadget_strings *sourcesink_strings[] = { /*-------------------------------------------------------------------------*/ -static int __init +static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) +{ + return alloc_ep_req(ep, len, buflen); +} + +void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) +{ + int value; + + if (ep->driver_data) { + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", + ep->name, value); + ep->driver_data = NULL; + } +} + +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out, + struct usb_ep *iso_in, struct usb_ep *iso_out) +{ + disable_ep(cdev, in); + disable_ep(cdev, out); + if (iso_in) + disable_ep(cdev, iso_in); + if (iso_out) + disable_ep(cdev, iso_out); +} + +static int sourcesink_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -450,8 +475,16 @@ no_iso: } static void -sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) +sourcesink_free_func(struct usb_function *f) { + struct f_ss_opts *opts; + + opts = container_of(f->fi, struct f_ss_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + usb_free_all_descriptors(f); kfree(func_to_ss(f)); } @@ -531,8 +564,7 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) check_read_data(ss, req); if (pattern != 2) memset(req->buf, 0x55, req->length); - } else - reinit_write_data(ep, req); + } break; /* this endpoint is normally active while we're configured */ @@ -591,10 +623,10 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, break; } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; - req = alloc_ep_req(ep, size); + req = ss_alloc_ep_req(ep, size); } else { ep = is_in ? ss->in_ep : ss->out_ep; - req = alloc_ep_req(ep, 0); + req = ss_alloc_ep_req(ep, 0); } if (!req) @@ -758,31 +790,10 @@ static void sourcesink_disable(struct usb_function *f) /*-------------------------------------------------------------------------*/ -static int __init sourcesink_bind_config(struct usb_configuration *c) -{ - struct f_sourcesink *ss; - int status; - - ss = kzalloc(sizeof *ss, GFP_KERNEL); - if (!ss) - return -ENOMEM; - - ss->function.name = "source/sink"; - ss->function.bind = sourcesink_bind; - ss->function.unbind = sourcesink_unbind; - ss->function.set_alt = sourcesink_set_alt; - ss->function.get_alt = sourcesink_get_alt; - ss->function.disable = sourcesink_disable; - - status = usb_add_function(c, &ss->function); - if (status) - kfree(ss); - return status; -} - -static int sourcesink_setup(struct usb_configuration *c, +static int sourcesink_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { + struct usb_configuration *c = f->config; struct usb_request *req = c->cdev->req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); @@ -851,42 +862,386 @@ unknown: return value; } -static struct usb_configuration sourcesink_driver = { - .label = "source/sink", - .strings = sourcesink_strings, - .setup = sourcesink_setup, - .bConfigurationValue = 3, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, - /* .iConfiguration = DYNAMIC */ +static struct usb_function *source_sink_alloc_func( + struct usb_function_instance *fi) +{ + struct f_sourcesink *ss; + struct f_ss_opts *ss_opts; + + ss = kzalloc(sizeof(*ss), GFP_KERNEL); + if (!ss) + return NULL; + + ss_opts = container_of(fi, struct f_ss_opts, func_inst); + + mutex_lock(&ss_opts->lock); + ss_opts->refcnt++; + mutex_unlock(&ss_opts->lock); + + pattern = ss_opts->pattern; + isoc_interval = ss_opts->isoc_interval; + isoc_maxpacket = ss_opts->isoc_maxpacket; + isoc_mult = ss_opts->isoc_mult; + isoc_maxburst = ss_opts->isoc_maxburst; + buflen = ss_opts->bulk_buflen; + + ss->function.name = "source/sink"; + ss->function.bind = sourcesink_bind; + ss->function.set_alt = sourcesink_set_alt; + ss->function.get_alt = sourcesink_get_alt; + ss->function.disable = sourcesink_disable; + ss->function.setup = sourcesink_setup; + ss->function.strings = sourcesink_strings; + + ss->function.free_func = sourcesink_free_func; + + return &ss->function; +} + +static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ss_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_ss_opts); +CONFIGFS_ATTR_OPS(f_ss_opts); + +static void ss_attr_release(struct config_item *item) +{ + struct f_ss_opts *ss_opts = to_f_ss_opts(item); + + usb_put_function_instance(&ss_opts->func_inst); +} + +static struct configfs_item_operations ss_item_ops = { + .release = ss_attr_release, + .show_attribute = f_ss_opts_attr_show, + .store_attribute = f_ss_opts_attr_store, }; -/** - * sourcesink_add - add a source/sink testing configuration to a device - * @cdev: the device to support the configuration - */ -int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume) +static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page) { - int id; + int result; - /* allocate string ID(s) */ - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_sourcesink[0].id = id; + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->pattern); + mutex_unlock(&opts->lock); - source_sink_intf_alt0.iInterface = id; - source_sink_intf_alt1.iInterface = id; - sourcesink_driver.iConfiguration = id; + return result; +} - /* support autoresume for remote wakeup testing */ - if (autoresume) - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; +static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; - /* support OTG systems */ - if (gadget_is_otg(cdev->gadget)) { - sourcesink_driver.descriptors = otg_desc; - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; } - return usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config); + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num != 0 && num != 1 && num != 2) { + ret = -EINVAL; + goto end; + } + + opts->pattern = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_pattern = + __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR, + f_ss_opts_pattern_show, + f_ss_opts_pattern_store); + +static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_interval); + mutex_unlock(&opts->lock); + + return result; } + +static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 16) { + ret = -EINVAL; + goto end; + } + + opts->isoc_interval = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_interval = + __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_interval_show, + f_ss_opts_isoc_interval_store); + +static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_maxpacket); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + if (num > 1024) { + ret = -EINVAL; + goto end; + } + + opts->isoc_maxpacket = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket = + __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_maxpacket_show, + f_ss_opts_isoc_maxpacket_store); + +static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_mult); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 2) { + ret = -EINVAL; + goto end; + } + + opts->isoc_mult = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_mult = + __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_mult_show, + f_ss_opts_isoc_mult_store); + +static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->isoc_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst = + __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_maxburst_show, + f_ss_opts_isoc_maxburst_store); + +static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->bulk_buflen); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->bulk_buflen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = + __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, + f_ss_opts_bulk_buflen_show, + f_ss_opts_bulk_buflen_store); + +static struct configfs_attribute *ss_attrs[] = { + &f_ss_opts_pattern.attr, + &f_ss_opts_isoc_interval.attr, + &f_ss_opts_isoc_maxpacket.attr, + &f_ss_opts_isoc_mult.attr, + &f_ss_opts_isoc_maxburst.attr, + &f_ss_opts_bulk_buflen.attr, + NULL, +}; + +static struct config_item_type ss_func_type = { + .ct_item_ops = &ss_item_ops, + .ct_attrs = ss_attrs, + .ct_owner = THIS_MODULE, +}; + +static void source_sink_free_instance(struct usb_function_instance *fi) +{ + struct f_ss_opts *ss_opts; + + ss_opts = container_of(fi, struct f_ss_opts, func_inst); + kfree(ss_opts); +} + +static struct usb_function_instance *source_sink_alloc_inst(void) +{ + struct f_ss_opts *ss_opts; + + ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL); + if (!ss_opts) + return ERR_PTR(-ENOMEM); + mutex_init(&ss_opts->lock); + ss_opts->func_inst.free_func_inst = source_sink_free_instance; + ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; + ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; + ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; + + config_group_init_type_name(&ss_opts->func_inst.group, "", + &ss_func_type); + + return &ss_opts->func_inst; +} +DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst, + source_sink_alloc_func); + +static int __init sslb_modinit(void) +{ + int ret; + + ret = usb_function_register(&SourceSinkusb_func); + if (ret) + return ret; + ret = lb_modinit(); + if (ret) + usb_function_unregister(&SourceSinkusb_func); + return ret; +} +static void __exit sslb_modexit(void) +{ + usb_function_unregister(&SourceSinkusb_func); + lb_modexit(); +} +module_init(sslb_modinit); +module_exit(sslb_modexit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index f172bd152fb..1ea8baf3333 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 @@ -274,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } net = gether_connect(&geth->port); - return IS_ERR(net) ? PTR_ERR(net) : 0; + return PTR_ERR_OR_ZERO(net); } static void geth_disable(struct usb_function *f) @@ -295,9 +297,39 @@ 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; + 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; + } + + 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,63 +392,128 @@ fail: return status; } -static void -geth_unbind(struct usb_configuration *c, struct usb_function *f) +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)) { + struct net_device *net = opts->net; + kfree(opts); + return ERR_CAST(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); - kfree(func_to_geth(f)); } -/** - * geth_bind_config - add CDC Subset network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +static struct usb_function *geth_alloc(struct usb_function_instance *fi) { 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; - } + struct f_gether_opts *opts; + int status; /* allocate and initialize one new instance */ - geth = kzalloc(sizeof *geth, GFP_KERNEL); + geth = kzalloc(sizeof(*geth), GFP_KERNEL); if (!geth) - return -ENOMEM; + 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 */ - snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr); + 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.strings = geth_strings; geth->port.func.bind = geth_bind; geth->port.func.unbind = geth_unbind; geth->port.func.set_alt = geth_set_alt; geth->port.func.disable = geth_disable; + geth->port.func.free_func = geth_free; - status = usb_add_function(c, &geth->port.func); - if (status) - kfree(geth); - return status; + return &geth->port.func; } + +DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c index f570e667a64..2b4c82d84bf 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/f_uac1.c @@ -418,6 +418,7 @@ static int audio_get_intf_req(struct usb_function *f, req->context = audio; req->complete = f_audio_complete; + len = min_t(size_t, sizeof(value), len); memcpy(req->buf, &value, len); return len; @@ -694,7 +695,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) } /* Todo: add more control selecotor dynamically */ -int __init control_selector_init(struct f_audio *audio) +static int __init control_selector_init(struct f_audio *audio) { INIT_LIST_HEAD(&audio->cs); list_add(&feature_unit.list, &audio->cs); @@ -718,7 +719,7 @@ int __init control_selector_init(struct f_audio *audio) * * Returns zero on success, else negative errno. */ -int __init audio_bind_config(struct usb_configuration *c) +static int __init audio_bind_config(struct usb_configuration *c) { struct f_audio *audio; int status; diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index d7da258fa3f..6261db4a991 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,10 +193,10 @@ 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) + if (!prm->ep_enabled || req->status == -ESHUTDOWN) return; /* @@ -260,19 +249,14 @@ static int uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); - struct audio_dev *agdev = uac2_to_agdev(uac2); struct uac2_rtd_params *prm; unsigned long flags; - struct usb_ep *ep; int err = 0; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ep = agdev->in_ep; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) prm = &uac2->p_prm; - } else { - ep = agdev->out_ep; + else prm = &uac2->c_prm; - } spin_lock_irqsave(&prm->lock, flags); @@ -410,7 +394,7 @@ static int snd_uac2_probe(struct platform_device *pdev) int err; /* Choose any slot, with no id */ - err = snd_card_create(-1, NULL, THIS_MODULE, 0, &card); + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); if (err < 0) return err; @@ -437,8 +421,6 @@ static int snd_uac2_probe(struct platform_device *pdev) strcpy(card->shortname, "UAC2_Gadget"); sprintf(card->longname, "UAC2_Gadget %i", pdev->id); - snd_card_set_dev(card, &pdev->dev); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); @@ -461,8 +443,6 @@ static int snd_uac2_remove(struct platform_device *pdev) { struct snd_card *card = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (card) return snd_card_free(card); @@ -901,7 +881,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; @@ -977,6 +957,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; @@ -991,8 +974,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); goto err; } @@ -1001,8 +982,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); goto err; } @@ -1315,10 +1294,8 @@ static int audio_bind_config(struct usb_configuration *cfg) int res; agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL); - if (agdev_g == NULL) { - printk(KERN_ERR "Unable to allocate audio gadget\n"); + if (agdev_g == NULL) return -ENOMEM; - } res = usb_string_ids_tab(cfg->cdev, strings_fn); if (res) diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 5b629876941..e2a1f50bd93 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -16,6 +16,7 @@ #include <linux/fs.h> #include <linux/list.h> #include <linux/mutex.h> +#include <linux/string.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/video.h> @@ -32,19 +33,15 @@ unsigned int uvc_gadget_trace_param; /*-------------------------------------------------------------------------*/ /* module parameters specific to the Video streaming endpoint */ -static unsigned streaming_interval = 1; +static unsigned int streaming_interval = 1; module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(streaming_interval, "1 - 16"); -static unsigned streaming_maxpacket = 1024; +static unsigned int streaming_maxpacket = 1024; module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); -static unsigned streaming_mult; -module_param(streaming_mult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_mult, "0 - 2 (hs/ss only)"); - -static unsigned streaming_maxburst; +static unsigned int streaming_maxburst; module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); @@ -54,13 +51,11 @@ MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); /* string IDs are assigned dynamically */ -#define UVC_STRING_ASSOCIATION_IDX 0 -#define UVC_STRING_CONTROL_IDX 1 -#define UVC_STRING_STREAMING_IDX 2 +#define UVC_STRING_CONTROL_IDX 0 +#define UVC_STRING_STREAMING_IDX 1 static struct usb_string uvc_en_us_strings[] = { - [UVC_STRING_ASSOCIATION_IDX].s = "UVC Camera", - [UVC_STRING_CONTROL_IDX].s = "Video Control", + [UVC_STRING_CONTROL_IDX].s = "UVC Camera", [UVC_STRING_STREAMING_IDX].s = "Video Streaming", { } }; @@ -78,7 +73,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_INTF_VIDEO_CONTROL 0 #define UVC_INTF_VIDEO_STREAMING 1 -#define STATUS_BYTECOUNT 16 /* 16 bytes status */ +#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ static struct usb_interface_assoc_descriptor uvc_iad __initdata = { .bLength = sizeof(uvc_iad), @@ -103,20 +98,29 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_control_ep __initdata = { +static struct usb_endpoint_descriptor uvc_control_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), .bInterval = 8, }; +static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { + .bLength = sizeof(uvc_ss_control_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + /* The following 3 values can be tweaked if necessary. */ + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), +}; + static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubType = UVC_EP_INTERRUPT, - .wMaxTransferSize = cpu_to_le16(STATUS_BYTECOUNT), + .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { @@ -143,63 +147,46 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { +static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 1, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ }; -static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { +static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = 1, -}; - -/* super speed support */ -static struct usb_endpoint_descriptor uvc_ss_control_ep __initdata = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), - .bInterval = 8, -}; - -static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { - .bLength = sizeof uvc_ss_control_comp, - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - - /* the following 3 values can be tweaked if necessary */ - /* .bMaxBurst = 0, */ - /* .bmAttributes = 0, */ - .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ }; static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = cpu_to_le16(1024), - .bInterval = 4, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC + | USB_ENDPOINT_XFER_ISOC, + /* The wMaxPacketSize and bInterval values will be initialized from + * module parameters. + */ }; -static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { - .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), +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 bMaxBurst, bmAttributes and wBytesPerInterval values will be + * initialized from module parameters. + */ }; static const struct usb_descriptor_header * const uvc_fs_streaming[] = { @@ -272,6 +259,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } +void uvc_function_setup_continue(struct uvc_device *uvc) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + + usb_composite_setup_continue(cdev); +} + static int uvc_function_get_alt(struct usb_function *f, unsigned interface) { @@ -334,7 +328,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_CONNECTED; - break; + return 0; case 1: if (uvc->state != UVC_STATE_CONNECTED) @@ -351,15 +345,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(uvc->vdev, &v4l2_event); - - uvc->state = UVC_STATE_STREAMING; - break; + return USB_GADGET_DELAYED_STATUS; default: return -EINVAL; } - - return 0; } static void @@ -416,10 +406,10 @@ uvc_register_video(struct uvc_device *uvc) if (video == NULL) return -ENOMEM; - video->parent = &cdev->gadget->dev; + video->v4l2_dev = &uvc->v4l2_dev; video->fops = &uvc_v4l2_fops; video->release = video_device_release; - strncpy(video->name, cdev->gadget->name, sizeof(video->name)); + strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); uvc->vdev = video; video_set_drvdata(video, uvc); @@ -453,7 +443,6 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) const struct uvc_descriptor_header * const *uvc_streaming_cls; const struct usb_descriptor_header * const *uvc_streaming_std; const struct usb_descriptor_header * const *src; - static struct usb_endpoint_descriptor *uvc_control_ep; struct usb_descriptor_header **dst; struct usb_descriptor_header **hdr; unsigned int control_size; @@ -467,14 +456,12 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_desc = uvc->desc.ss_control; uvc_streaming_cls = uvc->desc.ss_streaming; uvc_streaming_std = uvc_ss_streaming; - uvc_control_ep = &uvc_ss_control_ep; break; case USB_SPEED_HIGH: uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.hs_streaming; uvc_streaming_std = uvc_hs_streaming; - uvc_control_ep = &uvc_fs_control_ep; break; case USB_SPEED_FULL: @@ -482,7 +469,6 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.fs_streaming; uvc_streaming_std = uvc_fs_streaming; - uvc_control_ep = &uvc_fs_control_ep; break; } @@ -493,6 +479,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) * Class-specific UVC control descriptors * uvc_control_ep * uvc_control_cs_ep + * uvc_ss_control_comp (for SS only) * uvc_streaming_intf_alt0 * Class-specific UVC streaming descriptors * uvc_{fs|hs}_streaming @@ -502,7 +489,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) control_size = 0; streaming_size = 0; bytes = uvc_iad.bLength + uvc_control_intf.bLength - + uvc_control_ep->bLength + uvc_control_cs_ep.bLength + + uvc_control_ep.bLength + uvc_control_cs_ep.bLength + uvc_streaming_intf_alt0.bLength; if (speed == USB_SPEED_SUPER) { @@ -513,13 +500,13 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) } for (src = (const struct usb_descriptor_header **)uvc_control_desc; - *src; ++src) { + *src; ++src) { control_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; } for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; - *src; ++src) { + *src; ++src) { streaming_size += (*src)->bLength; bytes += (*src)->bLength; n_desc++; @@ -548,7 +535,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) uvc_control_header->bInCollection = 1; uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; - UVC_COPY_DESCRIPTOR(mem, dst, uvc_control_ep); + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep); if (speed == USB_SPEED_SUPER) UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); @@ -559,8 +546,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) UVC_COPY_DESCRIPTORS(mem, dst, (const struct usb_descriptor_header**)uvc_streaming_cls); uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size); - uvc_streaming_header->bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; + uvc_streaming_header->bEndpointAddress = uvc->video.ep->address; UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std); @@ -577,10 +563,11 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) INFO(cdev, "uvc_function_unbind\n"); video_unregister_device(uvc->vdev); + v4l2_device_unregister(&uvc->v4l2_dev); uvc->control_ep->driver_data = NULL; uvc->video.ep->driver_data = NULL; - uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = 0; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -594,31 +581,52 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); + unsigned int max_packet_mult; + unsigned int max_packet_size; struct usb_ep *ep; int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); - /* sanity check the streaming endpoint module parameters */ - if (streaming_interval < 1) - streaming_interval = 1; - if (streaming_interval > 16) - streaming_interval = 16; - if (streaming_mult > 2) - streaming_mult = 2; - if (streaming_maxburst > 15) - streaming_maxburst = 15; - - /* - * fill in the FS video streaming specific descriptors from the - * module parameters + /* Sanity check the streaming endpoint module parameters. */ - uvc_fs_streaming_ep.wMaxPacketSize = streaming_maxpacket > 1023 ? - 1023 : streaming_maxpacket; + streaming_interval = clamp(streaming_interval, 1U, 16U); + streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U); + streaming_maxburst = min(streaming_maxburst, 15U); + + /* Fill in the FS/HS/SS Video Streaming specific descriptors from the + * module parameters. + * + * NOTE: We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + if (streaming_maxpacket <= 1024) { + max_packet_mult = 1; + max_packet_size = streaming_maxpacket; + } else if (streaming_maxpacket <= 2048) { + max_packet_mult = 2; + max_packet_size = streaming_maxpacket / 2; + } else { + max_packet_mult = 3; + max_packet_size = streaming_maxpacket / 3; + } + + uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U); uvc_fs_streaming_ep.bInterval = streaming_interval; + uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); + uvc_hs_streaming_ep.bInterval = streaming_interval; + + uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_ss_streaming_ep.bInterval = streaming_interval; + uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; + uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + max_packet_size * max_packet_mult * streaming_maxburst; + /* Allocate endpoints. */ - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_control_ep); + ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); if (!ep) { INFO(cdev, "Unable to allocate control EP\n"); goto error; @@ -626,7 +634,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->control_ep = ep; ep->driver_data = uvc; - ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + if (gadget_is_superspeed(c->cdev->gadget)) + ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, + &uvc_ss_streaming_comp); + else if (gadget_is_dualspeed(cdev->gadget)) + ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); + else + ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); + if (!ep) { INFO(cdev, "Unable to allocate streaming EP\n"); goto error; @@ -634,6 +649,10 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc->video.ep = ep; ep->driver_data = uvc; + uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; + uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; + uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) goto error; @@ -647,37 +666,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; - /* sanity check the streaming endpoint module parameters */ - if (streaming_maxpacket > 1024) - streaming_maxpacket = 1024; - /* - * Fill in the HS descriptors from the module parameters for the Video - * Streaming endpoint. - * NOTE: We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. - */ - uvc_hs_streaming_ep.wMaxPacketSize = streaming_maxpacket; - uvc_hs_streaming_ep.wMaxPacketSize |= streaming_mult << 11; - uvc_hs_streaming_ep.bInterval = streaming_interval; - uvc_hs_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; - - /* - * Fill in the SS descriptors from the module parameters for the Video - * Streaming endpoint. - * NOTE: We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. - */ - uvc_ss_streaming_ep.wMaxPacketSize = streaming_maxpacket; - uvc_ss_streaming_ep.bInterval = streaming_interval; - uvc_ss_streaming_comp.bmAttributes = streaming_mult; - uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; - uvc_ss_streaming_comp.wBytesPerInterval = - streaming_maxpacket * (streaming_mult + 1) * - (streaming_maxburst + 1); - uvc_ss_streaming_ep.bEndpointAddress = - uvc_fs_streaming_ep.bEndpointAddress; - /* Copy descriptors */ f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); if (gadget_is_dualspeed(cdev->gadget)) @@ -703,6 +691,11 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if ((ret = usb_function_deactivate(f)) < 0) goto error; + if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) { + printk(KERN_INFO "v4l2_device_register failed\n"); + goto error; + } + /* Initialise video. */ ret = uvc_video_init(&uvc->video); if (ret < 0) @@ -718,6 +711,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; error: + v4l2_device_unregister(&uvc->v4l2_dev); if (uvc->vdev) video_device_release(uvc->vdev); @@ -774,23 +768,23 @@ uvc_bind_config(struct usb_configuration *c, /* Validate the descriptors. */ if (fs_control == NULL || fs_control[0] == NULL || - fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) + fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (ss_control == NULL || ss_control[0] == NULL || - ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) + ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (ss_streaming == NULL || ss_streaming[0] == NULL || - ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) + ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; uvc->desc.fs_control = fs_control; @@ -799,13 +793,16 @@ uvc_bind_config(struct usb_configuration *c, uvc->desc.hs_streaming = hs_streaming; uvc->desc.ss_streaming = ss_streaming; - /* Allocate string descriptor numbers. */ - if (uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id == 0) { + /* String descriptors are global, we only need to allocate string IDs + * for the first UVC function. UVC functions beyond the first (if any) + * will reuse the same IDs. + */ + if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); if (ret) goto error; uvc_iad.iFunction = - uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id; + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; uvc_control_intf.iInterface = uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index c3d258d3018..ec52752f732 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -16,12 +16,12 @@ #include <linux/usb/composite.h> #include <linux/usb/video.h> -extern int uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *hs_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming); +int uvc_bind_config(struct usb_configuration *c, + const struct uvc_descriptor_header * const *fs_control, + const struct uvc_descriptor_header * const *hs_control, + const struct uvc_descriptor_header * const *fs_streaming, + const struct uvc_descriptor_header * const *hs_streaming, + const struct uvc_descriptor_header * const *ss_streaming); #endif /* _F_UVC_H_ */ diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c new file mode 100644 index 00000000000..e143d69f601 --- /dev/null +++ b/drivers/usb/gadget/fotg210-udc.c @@ -0,0 +1,1216 @@ +/* + * 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 fotg210_udc_remove(struct platform_device *pdev) +{ + struct fotg210_udc *fotg210 = platform_get_drvdata(pdev); + + 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 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) + 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) + 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); + + platform_set_drvdata(pdev, 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; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); + } + usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 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, Feng-Hsin Chiang <john453@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 00000000000..bbf991bcbe7 --- /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_mxc_udc.c b/drivers/usb/gadget/fsl_mxc_udc.c index d3bd7b095ba..9b140fc4d3b 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -33,7 +33,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) unsigned long freq; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(mxc_ipg_clk)) { @@ -80,7 +80,7 @@ eclkrate: int fsl_udc_clk_finalize(struct platform_device *pdev) { - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret = 0; /* workaround ENGcm09152 for i.MX35 */ diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index ec50f18c889..ad548333516 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> @@ -33,6 +32,7 @@ #include <linux/io.h> #include <linux/moduleparam.h> #include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/dma-mapping.h> #include <linux/usb/ch9.h> @@ -1894,7 +1894,7 @@ static int fsl_qe_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver); /* defined in usb_gadget.h */ -static struct usb_gadget_ops qe_gadget_ops = { +static const struct usb_gadget_ops qe_gadget_ops = { .get_frame = qe_get_frame, .udc_start = fsl_qe_start, .udc_stop = fsl_qe_stop, @@ -2296,7 +2296,6 @@ static int fsl_qe_start(struct usb_gadget *gadget, driver->driver.bus = NULL; /* hook up the driver */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.speed = driver->max_speed; /* Enable IRQ reg and Set usbcmd reg EN bit */ @@ -2338,7 +2337,6 @@ static int fsl_qe_stop(struct usb_gadget *gadget, nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc->lock, flags); - udc->gadget.dev.driver = NULL; udc->driver = NULL; dev_info(udc->dev, "unregistered gadget driver '%s'\r\n", @@ -2430,7 +2428,7 @@ static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) ep->ep.ops = &qe_ep_ops; ep->stopped = 1; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep.desc = NULL; ep->dir = 0xff; ep->epnum = (u8)pipe_num; @@ -2523,12 +2521,6 @@ static int qe_udc_probe(struct platform_device *ofdev) /* name: Identifies the controller hardware type. */ udc->gadget.name = driver_name; - - device_initialize(&udc->gadget.dev); - - dev_set_name(&udc->gadget.dev, "gadget"); - - udc->gadget.dev.release = qe_udc_release; udc->gadget.dev.parent = &ofdev->dev; /* initialize qe_ep struct */ @@ -2592,22 +2584,17 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err5; } - ret = device_add(&udc->gadget.dev); + ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, + qe_udc_release); if (ret) goto err6; - ret = usb_add_gadget_udc(&ofdev->dev, &udc->gadget); - if (ret) - goto err7; - - 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"); return 0; -err7: - device_unregister(&udc->gadget.dev); err6: free_irq(udc->usb_irq, udc); err5: @@ -2653,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); @@ -2702,7 +2689,6 @@ static int qe_udc_remove(struct platform_device *ofdev) iounmap(udc->usb_regs); - device_unregister(&udc->gadget.dev); /* wait for release() of gadget.dev to free udc */ wait_for_completion(&done); @@ -2730,7 +2716,7 @@ MODULE_DEVICE_TABLE(of, qe_udc_match); static struct platform_driver udc_driver = { .driver = { - .name = (char *)driver_name, + .name = driver_name, .owner = THIS_MODULE, .of_match_table = qe_udc_match, }, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 667275cb7ba..28e4fc95702 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -185,20 +185,7 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); } - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); + usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); if (status && (status != -ESHUTDOWN)) VDBG("complete %s req %p stat %d len %u/%u", @@ -888,6 +875,7 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) struct fsl_req *req = container_of(_req, struct fsl_req, req); struct fsl_udc *udc; unsigned long flags; + int ret; /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf @@ -910,22 +898,9 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->ep = ep; - /* map virtual address to hardware */ - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_is_in(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + return ret; req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -1244,6 +1219,10 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on) struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); + + if (!udc->vbus_active) + return -EOPNOTSUPP; + udc->softconnect = (is_on != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -1255,19 +1234,20 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on) return 0; } -static int fsl_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int fsl_stop(struct usb_gadget_driver *driver); +static int fsl_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int fsl_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); /* defined in gadget.h */ -static struct usb_gadget_ops fsl_gadget_ops = { +static const struct usb_gadget_ops fsl_gadget_ops = { .get_frame = fsl_get_frame, .wakeup = fsl_wakeup, /* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */ .vbus_session = fsl_vbus_session, .vbus_draw = fsl_vbus_draw, .pullup = fsl_pullup, - .start = fsl_start, - .stop = fsl_stop, + .udc_start = fsl_udc_start, + .udc_stop = fsl_udc_stop, }; /* Set protocol stall on ep0, protocol stall will automatically be cleared @@ -1289,6 +1269,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) { struct fsl_req *req = udc->status_req; struct fsl_ep *ep; + int ret; if (direction == EP_DIR_IN) udc->ep0_dir = USB_DIR_IN; @@ -1306,10 +1287,9 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) req->req.complete = NULL; req->dtd_count = 0; - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, - ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + return ret; if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0) fsl_queue_td(ep, req); @@ -1352,6 +1332,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, u16 tmp = 0; /* Status, cpu endian */ struct fsl_req *req; struct fsl_ep *ep; + int ret; ep = &udc->eps[0]; @@ -1389,10 +1370,9 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->req.complete = NULL; req->dtd_count = 0; - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, - ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; + ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); + if (ret) + goto stall; /* prime the data phase */ if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0)) @@ -1951,40 +1931,20 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) * Hook to gadget drivers * Called by initialization code of gadget drivers *----------------------------------------------------------------*/ -static int fsl_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int fsl_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - int retval = -ENODEV; + int retval = 0; unsigned long flags = 0; - if (!udc_controller) - return -ENODEV; - - if (!driver || driver->max_speed < USB_SPEED_FULL - || !bind || !driver->disconnect || !driver->setup) - return -EINVAL; - - if (udc_controller->driver) - return -EBUSY; - /* lock is needed but whether should use this lock or another */ spin_lock_irqsave(&udc_controller->lock, flags); driver->driver.bus = NULL; /* hook up the driver */ udc_controller->driver = driver; - udc_controller->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc_controller->lock, flags); - /* bind udc driver to gadget driver */ - retval = bind(&udc_controller->gadget, driver); - if (retval) { - VDBG("bind to %s --> %d", driver->driver.name, retval); - udc_controller->gadget.dev.driver = NULL; - udc_controller->driver = NULL; - goto out; - } - if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { /* Suspend the controller until OTG enable it */ udc_controller->stopped = 1; @@ -1998,7 +1958,6 @@ static int fsl_start(struct usb_gadget_driver *driver, if (retval < 0) { ERR("can't bind to transceiver\n"); driver->unbind(&udc_controller->gadget); - udc_controller->gadget.dev.driver = 0; udc_controller->driver = 0; return retval; } @@ -2010,28 +1969,17 @@ static int fsl_start(struct usb_gadget_driver *driver, udc_controller->ep0_state = WAIT_FOR_SETUP; udc_controller->ep0_dir = 0; } - printk(KERN_INFO "%s: bind to driver %s\n", - udc_controller->gadget.name, driver->driver.name); -out: - if (retval) - printk(KERN_WARNING "gadget driver register failed %d\n", - retval); return retval; } /* Disconnect from gadget driver */ -static int fsl_stop(struct usb_gadget_driver *driver) +static int fsl_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { struct fsl_ep *loop_ep; unsigned long flags; - if (!udc_controller) - return -ENODEV; - - if (!driver || driver != udc_controller->driver || !driver->unbind) - return -EINVAL; - if (!IS_ERR_OR_NULL(udc_controller->transceiver)) otg_set_peripheral(udc_controller->transceiver->otg, NULL); @@ -2052,16 +2000,8 @@ static int fsl_stop(struct usb_gadget_driver *driver) nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc_controller->lock, flags); - /* report disconnect; the controller is already quiesced */ - driver->disconnect(&udc_controller->gadget); - - /* unbind gadget and unhook driver. */ - driver->unbind(&udc_controller->gadget); - udc_controller->gadget.dev.driver = NULL; udc_controller->driver = NULL; - printk(KERN_WARNING "unregistered gadget driver '%s'\n", - driver->driver.name); return 0; } @@ -2074,47 +2014,37 @@ static int fsl_stop(struct usb_gadget_driver *driver) static const char proc_filename[] = "driver/fsl_usb2_udc"; -static int fsl_proc_read(char *page, char **start, off_t off, int count, - int *eof, void *_dev) +static int fsl_proc_read(struct seq_file *m, void *v) { - char *buf = page; - char *next = buf; - unsigned size = count; unsigned long flags; - int t, i; + int i; u32 tmp_reg; struct fsl_ep *ep = NULL; struct fsl_req *req; struct fsl_udc *udc = udc_controller; - if (off != 0) - return 0; spin_lock_irqsave(&udc->lock, flags); /* ------basic driver information ---- */ - t = scnprintf(next, size, + seq_printf(m, DRIVER_DESC "\n" "%s version: %s\n" "Gadget driver: %s\n\n", driver_name, DRIVER_VERSION, udc->driver ? udc->driver->driver.name : "(none)"); - size -= t; - next += t; /* ------ DR Registers ----- */ tmp_reg = fsl_readl(&dr_regs->usbcmd); - t = scnprintf(next, size, + seq_printf(m, "USBCMD reg:\n" "SetupTW: %d\n" "Run/Stop: %s\n\n", (tmp_reg & USB_CMD_SUTW) ? 1 : 0, (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop"); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->usbsts); - t = scnprintf(next, size, + seq_printf(m, "USB Status Reg:\n" "Dr Suspend: %d Reset Received: %d System Error: %s " "USB Error Interrupt: %s\n\n", @@ -2122,11 +2052,9 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, (tmp_reg & USB_STS_RESET) ? 1 : 0, (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal", (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err"); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->usbintr); - t = scnprintf(next, size, + seq_printf(m, "USB Interrupt Enable Reg:\n" "Sleep Enable: %d SOF Received Enable: %d " "Reset Enable: %d\n" @@ -2140,33 +2068,25 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0, (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0, (tmp_reg & USB_INTR_INT_EN) ? 1 : 0); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->frindex); - t = scnprintf(next, size, + seq_printf(m, "USB Frame Index Reg: Frame Number is 0x%x\n\n", (tmp_reg & USB_FRINDEX_MASKS)); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->deviceaddr); - t = scnprintf(next, size, + seq_printf(m, "USB Device Address Reg: Device Addr is 0x%x\n\n", (tmp_reg & USB_DEVICE_ADDRESS_MASK)); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->endpointlistaddr); - t = scnprintf(next, size, + seq_printf(m, "USB Endpoint List Address Reg: " "Device Addr is 0x%x\n\n", (tmp_reg & USB_EP_LIST_ADDRESS_MASK)); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->portsc1); - t = scnprintf(next, size, + seq_printf(m, "USB Port Status&Control Reg:\n" "Port Transceiver Type : %s Port Speed: %s\n" "PHY Low Power Suspend: %s Port Reset: %s " @@ -2175,7 +2095,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, "Port Enable/Disable Change: %s\n" "Port Enabled/Disabled: %s " "Current Connect Status: %s\n\n", ( { - char *s; + const char *s; switch (tmp_reg & PORTSCX_PTS_FSLS) { case PORTSCX_PTS_UTMI: s = "UTMI"; break; @@ -2201,13 +2121,11 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, "Not correct", (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ? "Attached" : "Not-Att"); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->usbmode); - t = scnprintf(next, size, + seq_printf(m, "USB Mode Reg: Controller Mode is: %s\n\n", ( { - char *s; + const char *s; switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) { case USB_MODE_CTRL_MODE_IDLE: s = "Idle"; break; @@ -2220,103 +2138,87 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, } s; } )); - size -= t; - next += t; tmp_reg = fsl_readl(&dr_regs->endptsetupstat); - t = scnprintf(next, size, + seq_printf(m, "Endpoint Setup Status Reg: SETUP on ep 0x%x\n\n", (tmp_reg & EP_SETUP_STATUS_MASK)); - size -= t; - next += t; for (i = 0; i < udc->max_ep / 2; i++) { tmp_reg = fsl_readl(&dr_regs->endptctrl[i]); - t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n", - i, tmp_reg); - size -= t; - next += t; + seq_printf(m, "EP Ctrl Reg [0x%x]: = [0x%x]\n", i, tmp_reg); } tmp_reg = fsl_readl(&dr_regs->endpointprime); - t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n\n", tmp_reg); - size -= t; - next += t; + seq_printf(m, "EP Prime Reg = [0x%x]\n\n", tmp_reg); #ifndef CONFIG_ARCH_MXC if (udc->pdata->have_sysif_regs) { tmp_reg = usb_sys_regs->snoop1; - t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); - size -= t; - next += t; + seq_printf(m, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); tmp_reg = usb_sys_regs->control; - t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", - tmp_reg); - size -= t; - next += t; + seq_printf(m, "General Control Reg : = [0x%x]\n\n", tmp_reg); } #endif /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ ep = &udc->eps[0]; - t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n", + seq_printf(m, "For %s Maxpkt is 0x%x index is 0x%x\n", ep->ep.name, ep_maxpacket(ep), ep_index(ep)); - size -= t; - next += t; if (list_empty(&ep->queue)) { - t = scnprintf(next, size, "its req queue is empty\n\n"); - size -= t; - next += t; + seq_puts(m, "its req queue is empty\n\n"); } else { list_for_each_entry(req, &ep->queue, queue) { - t = scnprintf(next, size, + seq_printf(m, "req %p actual 0x%x length 0x%x buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); - size -= t; - next += t; } } /* other gadget->eplist ep */ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { if (ep->ep.desc) { - t = scnprintf(next, size, + seq_printf(m, "\nFor %s Maxpkt is 0x%x " "index is 0x%x\n", ep->ep.name, ep_maxpacket(ep), ep_index(ep)); - size -= t; - next += t; if (list_empty(&ep->queue)) { - t = scnprintf(next, size, - "its req queue is empty\n\n"); - size -= t; - next += t; + seq_puts(m, "its req queue is empty\n\n"); } else { list_for_each_entry(req, &ep->queue, queue) { - t = scnprintf(next, size, + seq_printf(m, "req %p actual 0x%x length " "0x%x buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); - size -= t; - next += t; - } /* end for each_entry of ep req */ - } /* end for else */ - } /* end for if(ep->queue) */ - } /* end (ep->desc) */ + } /* end for each_entry of ep req */ + } /* end for else */ + } /* end for if(ep->queue) */ + } /* end (ep->desc) */ spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} - *eof = 1; - return count - size; +/* + * seq_file wrappers for procfile show routines. + */ +static int fsl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, fsl_proc_read, NULL); } -#define create_proc_file() create_proc_read_entry(proc_filename, \ - 0, NULL, fsl_proc_read, NULL) +static const struct file_operations fsl_proc_fops = { + .open = fsl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#define create_proc_file() proc_create(proc_filename, 0, NULL, &fsl_proc_fops) #define remove_proc_file() remove_proc_entry(proc_filename, NULL) #else /* !CONFIG_USB_GADGET_DEBUG_FILES */ @@ -2350,14 +2252,12 @@ static int __init struct_udc_setup(struct fsl_udc *udc, struct fsl_usb2_platform_data *pdata; size_t size; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); udc->phy_mode = pdata->phy_mode; udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); - if (!udc->eps) { - ERR("malloc fsl_ep failed\n"); + if (!udc->eps) return -1; - } /* initialized QHs, take care of alignment */ size = udc->max_ep * sizeof(struct ep_queue_head); @@ -2413,7 +2313,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, /* for ep0: maxP defined in desc * for other eps, maxP is set by epautoconfig() called by gadget layer */ - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); /* the queue lists any req for this ep */ INIT_LIST_HEAD(&ep->queue); @@ -2440,12 +2340,10 @@ static int __init fsl_udc_probe(struct platform_device *pdev) u32 dccparams; udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); - if (udc_controller == NULL) { - ERR("malloc udc failed\n"); + if (udc_controller == NULL) return -ENOMEM; - } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; @@ -2557,12 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) /* Setup gadget.dev and register with kernel */ dev_set_name(&udc_controller->gadget.dev, "gadget"); - udc_controller->gadget.dev.release = fsl_udc_release; - udc_controller->gadget.dev.parent = &pdev->dev; udc_controller->gadget.dev.of_node = pdev->dev.of_node; - ret = device_register(&udc_controller->gadget.dev); - if (ret < 0) - goto err_free_irq; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; @@ -2576,7 +2469,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev) * for other eps, gadget layer called ep_enable with defined desc */ udc_controller->eps[0].ep.desc = &fsl_ep0_desc; - udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + usb_ep_set_maxpacket_limit(&udc_controller->eps[0].ep, + USB_MAX_CTRL_PAYLOAD); /* setup the udc->eps[] for non-control endpoints and link * to gadget.ep_list */ @@ -2595,10 +2489,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev) DTD_ALIGNMENT, UDC_DMA_BOUNDARY); if (udc_controller->td_pool == NULL) { ret = -ENOMEM; - goto err_unregister; + goto err_free_irq; } - ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget); + ret = usb_add_gadget_udc_release(&pdev->dev, &udc_controller->gadget, + fsl_udc_release); if (ret) goto err_del_udc; @@ -2607,8 +2502,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) err_del_udc: dma_pool_destroy(udc_controller->td_pool); -err_unregister: - device_unregister(&udc_controller->gadget.dev); err_free_irq: free_irq(udc_controller->irq, udc_controller); err_iounmap: @@ -2632,15 +2525,15 @@ err_kfree: static int __exit fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); DECLARE_COMPLETION(done); if (!udc_controller) return -ENODEV; - usb_del_gadget_udc(&udc_controller->gadget); udc_controller->done = &done; + usb_del_gadget_udc(&udc_controller->gadget); fsl_udc_clk_release(); @@ -2658,7 +2551,6 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_DEVICE) release_mem_region(res->start, resource_size(res)); - device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ wait_for_completion(&done); @@ -2775,7 +2667,7 @@ static struct platform_driver udc_driver = { .suspend = fsl_udc_suspend, .resume = fsl_udc_resume, .driver = { - .name = (char *)driver_name, + .name = driver_name, .owner = THIS_MODULE, /* udc suspend/resume called from OTG driver */ .suspend = fsl_udc_otg_suspend, @@ -2783,21 +2675,7 @@ static struct platform_driver udc_driver = { }, }; -static int __init udc_init(void) -{ - printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); - return platform_driver_probe(&udc_driver, fsl_udc_probe); -} - -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); - printk(KERN_WARNING "%s unregistered\n", driver_desc); -} - -module_exit(udc_exit); +module_platform_driver_probe(udc_driver, fsl_udc_probe); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c new file mode 100644 index 00000000000..b13f839e736 --- /dev/null +++ b/drivers/usb/gadget/functions.c @@ -0,0 +1,116 @@ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/err.h> + +#include <linux/usb/composite.h> + +static LIST_HEAD(func_list); +static DEFINE_MUTEX(func_lock); + +static struct usb_function_instance *try_get_usb_function_instance(const char *name) +{ + struct usb_function_driver *fd; + struct usb_function_instance *fi; + + fi = ERR_PTR(-ENOENT); + mutex_lock(&func_lock); + list_for_each_entry(fd, &func_list, list) { + + if (strcmp(name, fd->name)) + continue; + + if (!try_module_get(fd->mod)) { + fi = ERR_PTR(-EBUSY); + break; + } + fi = fd->alloc_inst(); + if (IS_ERR(fi)) + module_put(fd->mod); + else + fi->fd = fd; + break; + } + mutex_unlock(&func_lock); + return fi; +} + +struct usb_function_instance *usb_get_function_instance(const char *name) +{ + struct usb_function_instance *fi; + int ret; + + fi = try_get_usb_function_instance(name); + if (!IS_ERR(fi)) + return fi; + ret = PTR_ERR(fi); + if (ret != -ENOENT) + return fi; + ret = request_module("usbfunc:%s", name); + if (ret < 0) + return ERR_PTR(ret); + return try_get_usb_function_instance(name); +} +EXPORT_SYMBOL_GPL(usb_get_function_instance); + +struct usb_function *usb_get_function(struct usb_function_instance *fi) +{ + struct usb_function *f; + + f = fi->fd->alloc_func(fi); + if (IS_ERR(f)) + return f; + f->fi = fi; + return f; +} +EXPORT_SYMBOL_GPL(usb_get_function); + +void usb_put_function_instance(struct usb_function_instance *fi) +{ + struct module *mod; + + if (!fi) + return; + + mod = fi->fd->mod; + fi->free_func_inst(fi); + module_put(mod); +} +EXPORT_SYMBOL_GPL(usb_put_function_instance); + +void usb_put_function(struct usb_function *f) +{ + if (!f) + return; + + f->free_func(f); +} +EXPORT_SYMBOL_GPL(usb_put_function); + +int usb_function_register(struct usb_function_driver *newf) +{ + struct usb_function_driver *fd; + int ret; + + ret = -EEXIST; + + mutex_lock(&func_lock); + list_for_each_entry(fd, &func_list, list) { + if (!strcmp(fd->name, newf->name)) + goto out; + } + ret = 0; + list_add_tail(&newf->list, &func_list); +out: + mutex_unlock(&func_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_function_register); + +void usb_function_unregister(struct usb_function_driver *fd) +{ + mutex_lock(&func_lock); + list_del(&fd->list); + mutex_unlock(&func_lock); +} +EXPORT_SYMBOL_GPL(usb_function_unregister); diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 72cd5e6719d..3deb4e93807 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -22,7 +22,7 @@ MODULE_DESCRIPTION("FUSB300 USB gadget driver"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yuan Hsin Chen <yhchen@faraday-tech.com>"); +MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>"); MODULE_ALIAS("platform:fusb300_udc"); #define DRIVER_VERSION "20 October 2010" @@ -394,7 +394,7 @@ static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep) if (reg & FUSB300_EPSET0_STL) { printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep); - reg &= ~FUSB300_EPSET0_STL; + reg |= FUSB300_EPSET0_STL_CLR; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); } } @@ -557,7 +557,7 @@ static void fusb300_set_cxdone(struct fusb300 *fusb300) } /* read data from cx fifo */ -void fusb300_rdcxf(struct fusb300 *fusb300, +static void fusb300_rdcxf(struct fusb300 *fusb300, u8 *buffer, u32 length) { int i = 0; @@ -930,33 +930,33 @@ static void fusb300_wait_idma_finished(struct fusb300_ep *ep) fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0, FUSB300_IGR0_EPn_PRD_INT(ep->epnum)); + return; + IDMA_RESET: - fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGER0, - FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0); + reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum); + iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0); } -static void fusb300_set_idma(struct fusb300_ep *ep, +static void fusb300_set_idma(struct fusb300_ep *ep, struct fusb300_request *req) { - dma_addr_t d; - - d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE); + int ret; - if (dma_mapping_error(NULL, d)) { - printk(KERN_DEBUG "dma_mapping_error\n"); + ret = usb_gadget_map_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); + if (ret) return; - } - - dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE); fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); - fusb300_fill_idma_prdtbl(ep, d, req->req.length); + fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length); /* check idma is done */ fusb300_wait_idma_finished(ep); - dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE); + usb_gadget_unmap_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); } static void in_ep_fifo_handler(struct fusb300_ep *ep) @@ -1308,65 +1308,26 @@ static void init_controller(struct fusb300 *fusb300) iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1); } /*------------------------------------------------------------------------*/ -static struct fusb300 *the_controller; - -static int fusb300_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int fusb300_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct fusb300 *fusb300 = the_controller; - int retval; - - if (!driver - || driver->max_speed < USB_SPEED_FULL - || !bind - || !driver->setup) - return -EINVAL; - - if (!fusb300) - return -ENODEV; - - if (fusb300->driver) - return -EBUSY; + struct fusb300 *fusb300 = to_fusb300(g); /* hook up the driver */ driver->driver.bus = NULL; fusb300->driver = driver; - fusb300->gadget.dev.driver = &driver->driver; - - retval = device_add(&fusb300->gadget.dev); - if (retval) { - pr_err("device_add error (%d)\n", retval); - goto error; - } - - retval = bind(&fusb300->gadget, driver); - if (retval) { - pr_err("bind to driver error (%d)\n", retval); - device_del(&fusb300->gadget.dev); - goto error; - } return 0; - -error: - fusb300->driver = NULL; - fusb300->gadget.dev.driver = NULL; - - return retval; } -static int fusb300_udc_stop(struct usb_gadget_driver *driver) +static int fusb300_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct fusb300 *fusb300 = the_controller; - - if (driver != fusb300->driver || !driver->unbind) - return -EINVAL; + struct fusb300 *fusb300 = to_fusb300(g); driver->unbind(&fusb300->gadget); - fusb300->gadget.dev.driver = NULL; init_controller(fusb300); - device_del(&fusb300->gadget.dev); fusb300->driver = NULL; return 0; @@ -1378,15 +1339,15 @@ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active) return 0; } -static struct usb_gadget_ops fusb300_gadget_ops = { +static const struct usb_gadget_ops fusb300_gadget_ops = { .pullup = fusb300_udc_pullup, - .start = fusb300_udc_start, - .stop = fusb300_udc_stop, + .udc_start = fusb300_udc_start, + .udc_stop = fusb300_udc_stop, }; 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); @@ -1439,34 +1400,23 @@ static int __init fusb300_probe(struct platform_device *pdev) /* initialize udc */ fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); - if (fusb300 == NULL) { - pr_err("kzalloc error\n"); + if (fusb300 == NULL) goto clean_up; - } for (i = 0; i < FUSB300_MAX_NUM_EP; i++) { _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL); - if (_ep[i] == NULL) { - pr_err("_ep kzalloc error\n"); + if (_ep[i] == NULL) goto clean_up; - } fusb300->ep[i] = _ep[i]; } spin_lock_init(&fusb300->lock); - dev_set_drvdata(&pdev->dev, fusb300); + platform_set_drvdata(pdev, fusb300); fusb300->gadget.ops = &fusb300_gadget_ops; - device_initialize(&fusb300->gadget.dev); - - dev_set_name(&fusb300->gadget.dev, "gadget"); - fusb300->gadget.max_speed = USB_SPEED_HIGH; - fusb300->gadget.dev.parent = &pdev->dev; - fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask; - fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; fusb300->reg = reg; @@ -1498,19 +1448,19 @@ static int __init fusb300_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = fusb300_ep_name[i]; ep->ep.ops = &fusb300_ep_ops; - ep->ep.maxpacket = HS_BULK_MAX_PACKET_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE); } - fusb300->ep[0]->ep.maxpacket = HS_CTL_MAX_PACKET_SIZE; + usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE); fusb300->ep[0]->epnum = 0; fusb300->gadget.ep0 = &fusb300->ep[0]->ep; INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list); - the_controller = fusb300; - fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep, GFP_KERNEL); - if (fusb300->ep0_req == NULL) + if (fusb300->ep0_req == NULL) { + ret = -ENOMEM; goto clean_up3; + } init_controller(fusb300); ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget); @@ -1520,6 +1470,7 @@ static int __init fusb300_probe(struct platform_device *pdev) dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; + err_add_udc: fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); @@ -1547,15 +1498,4 @@ static struct platform_driver fusb300_driver = { }, }; -static int __init fusb300_udc_init(void) -{ - return platform_driver_probe(&fusb300_driver, fusb300_probe); -} - -module_init(fusb300_udc_init); - -static void __exit fusb300_udc_cleanup(void) -{ - platform_driver_unregister(&fusb300_driver); -} -module_exit(fusb300_udc_cleanup); +module_platform_driver_probe(fusb300_driver, fusb300_probe); diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h index 542cd83cc80..ae811d8d38b 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/fusb300_udc.h @@ -111,8 +111,8 @@ /* * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 ) * */ +#define FUSB300_EPSET0_STL_CLR (1 << 3) #define FUSB300_EPSET0_CLRSEQNUM (1 << 2) -#define FUSB300_EPSET0_EPn_TX0BYTE (1 << 1) #define FUSB300_EPSET0_STL (1 << 0) /* @@ -673,4 +673,6 @@ struct fusb300 { u8 reenum; /* if re-enumeration */ }; +#define to_fusb300(g) (container_of((g), struct fusb300, gadget)) + #endif diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 3953dd4d718..fe12e6a2744 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -14,14 +14,9 @@ #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. - */ #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS +#include <linux/netdevice.h> + # if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS # endif @@ -29,25 +24,31 @@ # define USB_ETH_RNDIS y # endif -# 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" # endif -# include "u_ether.c" +# include "u_ether.h" + +USB_ETHERNET_MODULE_PARAMETERS(); -static u8 gfs_hostaddr[ETH_ALEN]; # ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +static int eth_bind_config(struct usb_configuration *c); +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_ecm; +static struct usb_function_instance *fi_geth; +static struct usb_function *f_geth; +# endif +# ifdef CONFIG_USB_FUNCTIONFS_RNDIS +static int bind_rndis_config(struct usb_configuration *c); +static struct usb_function_instance *fi_rndis; +static struct usb_function *f_rndis; # endif -#else -# define gether_cleanup() do { } while (0) -# define gether_setup(gadget, hostaddr) ((int)0) -# define gfs_hostaddr NULL #endif -#include "f_fs.c" +#include "u_fs.h" #define DRIVER_NAME "g_ffs" #define DRIVER_DESC "USB Function Filesystem" @@ -62,13 +63,6 @@ MODULE_LICENSE("GPL"); #define GFS_MAX_DEVS 10 -struct gfs_ffs_obj { - const char *name; - bool mounted; - bool desc_ready; - struct ffs_data *ffs_data; -}; - USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor gfs_dev_desc = { @@ -137,11 +131,12 @@ static struct usb_gadget_strings *gfs_dev_strings[] = { struct gfs_configuration { struct usb_configuration c; - int (*eth)(struct usb_configuration *c, u8 *ethaddr); + int (*eth)(struct usb_configuration *c); + int num; } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { - .eth = rndis_bind_config, + .eth = bind_rndis_config, }, #endif @@ -157,10 +152,15 @@ struct gfs_configuration { #endif }; +static void *functionfs_acquire_dev(struct ffs_dev *dev); +static void functionfs_release_dev(struct ffs_dev *dev); +static int functionfs_ready_callback(struct ffs_data *ffs); +static void functionfs_closed_callback(struct ffs_data *ffs); static int gfs_bind(struct usb_composite_dev *cdev); static int gfs_unbind(struct usb_composite_dev *cdev); static int gfs_do_config(struct usb_configuration *c); + static __refdata struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, @@ -170,202 +170,244 @@ static __refdata struct usb_composite_driver gfs_driver = { .unbind = gfs_unbind, }; -static DEFINE_MUTEX(gfs_lock); static unsigned int missing_funcs; -static bool gfs_ether_setup; static bool gfs_registered; static bool gfs_single_func; -static struct gfs_ffs_obj *ffs_tab; +static struct usb_function_instance **fi_ffs; +static struct usb_function **f_ffs[] = { +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + NULL, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_ETH + NULL, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_GENERIC + NULL, +#endif +}; + +#define N_CONF ARRAY_SIZE(f_ffs) static int __init gfs_init(void) { + struct f_fs_opts *opts; int i; + int ret = 0; ENTER(); - if (!func_num) { + if (func_num < 2) { gfs_single_func = true; func_num = 1; } - ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); - if (!ffs_tab) - return -ENOMEM; + /* + * Allocate in one chunk for easier maintenance + */ + f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL); + if (!f_ffs[0]) { + ret = -ENOMEM; + goto no_func; + } + for (i = 1; i < N_CONF; ++i) + f_ffs[i] = f_ffs[0] + i * func_num; - if (!gfs_single_func) - for (i = 0; i < func_num; i++) - ffs_tab[i].name = func_names[i]; + fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL); + if (!fi_ffs) { + ret = -ENOMEM; + goto no_func; + } + + for (i = 0; i < func_num; i++) { + fi_ffs[i] = usb_get_function_instance("ffs"); + if (IS_ERR(fi_ffs[i])) { + ret = PTR_ERR(fi_ffs[i]); + --i; + goto no_dev; + } + opts = to_f_fs_opts(fi_ffs[i]); + if (gfs_single_func) + ret = ffs_single_dev(opts->dev); + else + ret = ffs_name_dev(opts->dev, func_names[i]); + if (ret) + goto no_dev; + opts->dev->ffs_ready_callback = functionfs_ready_callback; + opts->dev->ffs_closed_callback = functionfs_closed_callback; + opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev; + opts->dev->ffs_release_dev_callback = functionfs_release_dev; + opts->no_configfs = true; + } missing_funcs = func_num; - return functionfs_init(); + return 0; +no_dev: + while (i >= 0) + usb_put_function_instance(fi_ffs[i--]); + kfree(fi_ffs); +no_func: + kfree(f_ffs[0]); + return ret; } module_init(gfs_init); static void __exit gfs_exit(void) { + int i; + ENTER(); - mutex_lock(&gfs_lock); if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; - functionfs_cleanup(); + kfree(f_ffs[0]); + + for (i = 0; i < func_num; i++) + usb_put_function_instance(fi_ffs[i]); - mutex_unlock(&gfs_lock); - kfree(ffs_tab); + kfree(fi_ffs); } module_exit(gfs_exit); -static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) +static void *functionfs_acquire_dev(struct ffs_dev *dev) { - int i; - - ENTER(); - - if (gfs_single_func) - return &ffs_tab[0]; - - for (i = 0; i < func_num; i++) - if (strcmp(ffs_tab[i].name, dev_name) == 0) - return &ffs_tab[i]; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(-ENODEV); + + return 0; +} - return NULL; +static void functionfs_release_dev(struct ffs_dev *dev) +{ + module_put(THIS_MODULE); } +/* + * The caller of this function takes ffs_lock + */ static int functionfs_ready_callback(struct ffs_data *ffs) { - struct gfs_ffs_obj *ffs_obj; - int ret; + int ret = 0; - ENTER(); - mutex_lock(&gfs_lock); - - ffs_obj = ffs->private_data; - if (!ffs_obj) { - ret = -EINVAL; - goto done; - } - - if (WARN_ON(ffs_obj->desc_ready)) { - ret = -EBUSY; - goto done; - } - ffs_obj->desc_ready = true; - ffs_obj->ffs_data = ffs; + if (--missing_funcs) + return 0; - if (--missing_funcs) { - ret = 0; - goto done; - } + if (gfs_registered) + return -EBUSY; - if (gfs_registered) { - ret = -EBUSY; - goto done; - } gfs_registered = true; ret = usb_composite_probe(&gfs_driver); if (unlikely(ret < 0)) gfs_registered = false; - -done: - mutex_unlock(&gfs_lock); + return ret; } +/* + * The caller of this function takes ffs_lock + */ static void functionfs_closed_callback(struct ffs_data *ffs) { - struct gfs_ffs_obj *ffs_obj; - - ENTER(); - mutex_lock(&gfs_lock); - - ffs_obj = ffs->private_data; - if (!ffs_obj) - goto done; - - ffs_obj->desc_ready = false; missing_funcs++; if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; - -done: - mutex_unlock(&gfs_lock); } -static void *functionfs_acquire_dev_callback(const char *dev_name) +/* + * It is assumed that gfs_bind is called from a context where ffs_lock is held + */ +static int gfs_bind(struct usb_composite_dev *cdev) { - struct gfs_ffs_obj *ffs_dev; +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + struct net_device *net; +#endif + int ret, i; ENTER(); - mutex_lock(&gfs_lock); - ffs_dev = gfs_find_dev(dev_name); - if (!ffs_dev) { - ffs_dev = ERR_PTR(-ENODEV); - goto done; + if (missing_funcs) + return -ENODEV; +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; + + 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; + } else { + struct f_gether_opts *geth_opts; + + 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; } +#endif - if (ffs_dev->mounted) { - ffs_dev = ERR_PTR(-EBUSY); - goto done; - } - ffs_dev->mounted = true; +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { + struct f_rndis_opts *rndis_opts; -done: - mutex_unlock(&gfs_lock); - return ffs_dev; -} + fi_rndis = usb_get_function_instance("rndis"); + if (IS_ERR(fi_rndis)) { + ret = PTR_ERR(fi_rndis); + goto error; + } + rndis_opts = container_of(fi_rndis, struct f_rndis_opts, + func_inst); +#ifndef CONFIG_USB_FUNCTIONFS_ETH + net = rndis_opts->net; +#endif + } +#endif -static void functionfs_release_dev_callback(struct ffs_data *ffs_data) -{ - struct gfs_ffs_obj *ffs_dev; +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + 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); +#endif - ENTER(); - mutex_lock(&gfs_lock); +#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH + gether_set_gadget(net, cdev->gadget); + ret = gether_register_netdev(net); + if (ret) + goto error_rndis; - ffs_dev = ffs_data->private_data; - if (ffs_dev) - ffs_dev->mounted = false; + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; - mutex_unlock(&gfs_lock); -} + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + ecm_opts->bound = true; + } else { + struct f_gether_opts *geth_opts; -/* - * It is assumed that gfs_bind is called from a context where gfs_lock is held - */ -static int gfs_bind(struct usb_composite_dev *cdev) -{ - int ret, i; - - ENTER(); - - if (missing_funcs) - return -ENODEV; + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + geth_opts->bound = true; + } - ret = gether_setup(cdev->gadget, gfs_hostaddr); - if (unlikely(ret < 0)) - goto error_quick; - gfs_ether_setup = true; + rndis_borrow_net(fi_rndis, net); +#endif + /* TODO: gstrings_attach? */ ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) - goto error; + goto error_rndis; gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; - for (i = func_num; --i; ) { - ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); - if (unlikely(ret < 0)) { - while (++i < func_num) - functionfs_unbind(ffs_tab[i].ffs_data); - goto error; - } - } - for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { struct gfs_configuration *c = gfs_configurations + i; int sid = USB_GADGET_FIRST_AVAIL_IDX + i; @@ -375,6 +417,8 @@ static int gfs_bind(struct usb_composite_dev *cdev) c->c.bConfigurationValue = 1 + i; c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; + c->num = i; + ret = usb_add_config(cdev, &c->c, gfs_do_config); if (unlikely(ret < 0)) goto error_unbind; @@ -382,18 +426,24 @@ static int gfs_bind(struct usb_composite_dev *cdev) usb_composite_overwrite_options(cdev, &coverwrite); return 0; +/* TODO */ error_unbind: - for (i = 0; i < func_num; i++) - functionfs_unbind(ffs_tab[i].ffs_data); +error_rndis: +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + usb_put_function_instance(fi_rndis); error: - gether_cleanup(); -error_quick: - gfs_ether_setup = false; +#endif +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) + usb_put_function_instance(fi_ecm); + else + usb_put_function_instance(fi_geth); +#endif return ret; } /* - * It is assumed that gfs_unbind is called from a context where gfs_lock is held + * It is assumed that gfs_unbind is called from a context where ffs_lock is held */ static int gfs_unbind(struct usb_composite_dev *cdev) { @@ -401,28 +451,30 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ENTER(); - /* - * We may have been called in an error recovery from - * composite_bind() after gfs_unbind() failure so we need to - * check if gfs_ffs_data is not NULL since gfs_bind() handles - * all error recovery itself. I'd rather we werent called - * from composite on orror recovery, but what you're gonna - * do...? - */ - if (gfs_ether_setup) - gether_cleanup(); - gfs_ether_setup = false; - for (i = func_num; --i; ) - if (ffs_tab[i].ffs_data) - functionfs_unbind(ffs_tab[i].ffs_data); +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + usb_put_function(f_rndis); + usb_put_function_instance(fi_rndis); +#endif + +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) { + usb_put_function(f_ecm); + usb_put_function_instance(fi_ecm); + } else { + usb_put_function(f_geth); + usb_put_function_instance(fi_geth); + } +#endif + for (i = 0; i < N_CONF * func_num; ++i) + usb_put_function(*(f_ffs[0] + i)); return 0; } /* * It is assumed that gfs_do_config is called from a context where - * gfs_lock is held + * ffs_lock is held */ static int gfs_do_config(struct usb_configuration *c) { @@ -440,15 +492,22 @@ static int gfs_do_config(struct usb_configuration *c) } if (gc->eth) { - ret = gc->eth(c, gfs_hostaddr); + ret = gc->eth(c); if (unlikely(ret < 0)) return ret; } for (i = 0; i < func_num; i++) { - ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); - if (unlikely(ret < 0)) - return ret; + f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]); + if (IS_ERR(f_ffs[gc->num][i])) { + ret = PTR_ERR(f_ffs[gc->num][i]); + goto error; + } + ret = usb_add_function(c, f_ffs[gc->num][i]); + if (ret < 0) { + usb_put_function(f_ffs[gc->num][i]); + goto error; + } } /* @@ -465,15 +524,59 @@ static int gfs_do_config(struct usb_configuration *c) c->interface[c->next_interface_id] = NULL; return 0; +error: + while (--i >= 0) { + if (!IS_ERR(f_ffs[gc->num][i])) + usb_remove_function(c, f_ffs[gc->num][i]); + usb_put_function(f_ffs[gc->num][i]); + } + return ret; } #ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +static int eth_bind_config(struct usb_configuration *c) { - return can_support_ecm(c->cdev->gadget) - ? ecm_bind_config(c, ethaddr) - : geth_bind_config(c, ethaddr); + int status = 0; + + 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); + + } 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; +} + +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + +static int bind_rndis_config(struct usb_configuration *c) +{ + int status = 0; + + 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; } #endif diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h index 71ca193358b..15f180904f8 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -6,21 +6,62 @@ #ifndef __G_ZERO_H #define __G_ZERO_H -#include <linux/usb/composite.h> +#define GZERO_BULK_BUFLEN 4096 +#define GZERO_QLEN 32 +#define GZERO_ISOC_INTERVAL 4 +#define GZERO_ISOC_MAXPACKET 1024 -/* global state */ -extern unsigned buflen; -extern const struct usb_descriptor_header *otg_desc[]; +struct usb_zero_options { + unsigned pattern; + unsigned isoc_interval; + unsigned isoc_maxpacket; + unsigned isoc_mult; + unsigned isoc_maxburst; + unsigned bulk_buflen; + unsigned qlen; +}; + +struct f_ss_opts { + struct usb_function_instance func_inst; + unsigned pattern; + unsigned isoc_interval; + unsigned isoc_maxpacket; + unsigned isoc_mult; + unsigned isoc_maxburst; + unsigned bulk_buflen; + + /* + * 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; +}; + +struct f_lb_opts { + struct usb_function_instance func_inst; + unsigned bulk_buflen; + unsigned qlen; + + /* + * 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 lb_modexit(void); +int lb_modinit(void); /* common utilities */ -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, struct usb_ep *iso_in, struct usb_ep *iso_out); -/* configuration-specific linkup */ -int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume); -int loopback_add(struct usb_composite_dev *cdev, bool autoresume); - #endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 881aab86ae9..e879e2c9f46 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -125,7 +125,7 @@ static struct usb_configuration midi_config = { .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, + .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, }; static int __init midi_bind_config(struct usb_configuration *c) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 51037cb7860..6c85839e15a 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -30,11 +30,11 @@ #include <linux/ioport.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/device.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -51,8 +51,6 @@ #define DRIVER_DESC "TC86C001 USB Device Controller" #define DRIVER_VERSION "30-Oct 2003" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - static const char driver_name [] = "goku_udc"; static const char driver_desc [] = DRIVER_DESC; @@ -232,7 +230,7 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep) } } - ep->ep.maxpacket = MAX_FIFO_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, MAX_FIFO_SIZE); ep->ep.desc = NULL; ep->stopped = 1; ep->irqs = 0; @@ -275,7 +273,6 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; } @@ -774,7 +771,7 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } /* else pio or dma irq handler advances the queue. */ - if (likely(req != 0)) + if (likely(req != NULL)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue)) @@ -993,21 +990,22 @@ static int goku_get_frame(struct usb_gadget *_gadget) return -EOPNOTSUPP; } -static int goku_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int goku_stop(struct usb_gadget_driver *driver); +static int goku_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int goku_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops goku_ops = { .get_frame = goku_get_frame, - .start = goku_start, - .stop = goku_stop, + .udc_start = goku_udc_start, + .udc_stop = goku_udc_stop, // no remote wakeup // not selfpowered }; /*-------------------------------------------------------------------------*/ -static inline char *dmastr(void) +static inline const char *dmastr(void) { if (use_dma == 0) return "(dma disabled)"; @@ -1024,13 +1022,10 @@ static const char proc_node_name [] = "driver/udc"; #define FOURBITS "%s%s%s%s" #define EIGHTBITS FOURBITS FOURBITS -static void -dump_intmask(const char *label, u32 mask, char **next, unsigned *size) +static void dump_intmask(struct seq_file *m, const char *label, u32 mask) { - int t; - /* int_status is the same format ... */ - t = scnprintf(*next, *size, + seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", label, mask, (mask & INT_PWRDETECT) ? " power" : "", @@ -1057,33 +1052,23 @@ dump_intmask(const char *label, u32 mask, char **next, unsigned *size) (mask & INT_ENDPOINT0) ? " ep0" : "", (mask & INT_USBRESET) ? " reset" : "", (mask & INT_SUSPEND) ? " suspend" : ""); - *size -= t; - *next += t; } -static int -udc_proc_read(char *buffer, char **start, off_t off, int count, - int *eof, void *_dev) +static int udc_proc_read(struct seq_file *m, void *v) { - char *buf = buffer; - struct goku_udc *dev = _dev; + struct goku_udc *dev = m->private; struct goku_udc_regs __iomem *regs = dev->regs; - char *next = buf; - unsigned size = count; unsigned long flags; - int i, t, is_usb_connected; + int i, is_usb_connected; u32 tmp; - if (off != 0) - return 0; - local_irq_save(flags); /* basic device status */ tmp = readl(®s->power_detect); is_usb_connected = tmp & PW_DETECT; - t = scnprintf(next, size, + seq_printf(m, "%s - %s\n" "%s version: %s %s\n" "Gadget driver: %s\n" @@ -1095,7 +1080,7 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, is_usb_connected ? ((tmp & PW_PULLUP) ? "full speed" : "powered") : "disconnected", - ({char *state; + ({const char *state; switch(dev->ep0state){ case EP0_DISCONNECT: state = "ep0_disconnect"; break; case EP0_IDLE: state = "ep0_idle"; break; @@ -1107,27 +1092,24 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, default: state = "ep0_?"; break; } state; }) ); - size -= t; - next += t; - dump_intmask("int_status", readl(®s->int_status), &next, &size); - dump_intmask("int_enable", readl(®s->int_enable), &next, &size); + dump_intmask(m, "int_status", readl(®s->int_status)); + dump_intmask(m, "int_enable", readl(®s->int_enable)); if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0) goto done; /* registers for (active) device and ep0 */ - t = scnprintf(next, size, "\nirqs %lu\ndataset %02x " + if (seq_printf(m, "\nirqs %lu\ndataset %02x " "single.bcs %02x.%02x state %x addr %u\n", dev->irqs, readl(®s->DataSet), readl(®s->EPxSingle), readl(®s->EPxBCS), readl(®s->UsbState), - readl(®s->address)); - size -= t; - next += t; + readl(®s->address)) < 0) + goto done; tmp = readl(®s->dma_master); - t = scnprintf(next, size, + if (seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n", tmp, (tmp & MST_EOPB_DIS) ? " eopb-" : "", (tmp & MST_EOPB_ENA) ? " eopb+" : "", @@ -1142,9 +1124,8 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, (tmp & MST_WR_ENA) ? " OUT" : "", (tmp & MST_CONNECTION) ? "ep1in/ep2out" - : "ep1out/ep2in"); - size -= t; - next += t; + : "ep1out/ep2in") < 0) + goto done; /* dump endpoint queues */ for (i = 0; i < 4; i++) { @@ -1155,7 +1136,7 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, continue; tmp = readl(ep->reg_status); - t = scnprintf(next, size, + if (seq_printf(m, "%s %s max %u %s, irqs %lu, " "status %02x (%s) " FOURBITS "\n", ep->ep.name, @@ -1183,23 +1164,17 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, s = "invalid"; break; default: s = "?"; break; - }; s; }), + } s; }), (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" - ); - if (t <= 0 || t > size) + ) < 0) goto done; - size -= t; - next += t; if (list_empty(&ep->queue)) { - t = scnprintf(next, size, "\t(nothing queued)\n"); - if (t <= 0 || t > size) + if (seq_puts(m, "\t(nothing queued)\n") < 0) goto done; - size -= t; - next += t; continue; } list_for_each_entry(req, &ep->queue, queue) { @@ -1213,23 +1188,34 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, } else tmp = req->req.actual; - t = scnprintf(next, size, + if (seq_printf(m, "\treq %p len %u/%u buf %p\n", &req->req, tmp, req->req.length, - req->req.buf); - if (t <= 0 || t > size) + req->req.buf) < 0) goto done; - size -= t; - next += t; } } done: local_irq_restore(flags); - *eof = 1; - return count - size; + return 0; } +/* + * seq_file wrappers for procfile show routines. + */ +static int udc_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, udc_proc_read, PDE_DATA(file_inode(file))); +} + +static const struct file_operations udc_proc_fops = { + .open = udc_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /*-------------------------------------------------------------------------*/ @@ -1264,7 +1250,7 @@ static void udc_reinit (struct goku_udc *dev) } dev->ep[0].reg_mode = NULL; - dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, MAX_EP0_SIZE); list_del_init (&dev->ep[0].ep.ep_list); } @@ -1339,96 +1325,56 @@ static void udc_enable(struct goku_udc *dev) * - one function driver, initted second */ -static struct goku_udc *the_controller; - /* when a driver is successfully registered, it will receive * control requests including set_configuration(), which enables * non-control requests. then usb traffic follows until a * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int goku_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int goku_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct goku_udc *dev = the_controller; - int retval; - - if (!driver - || driver->max_speed < USB_SPEED_FULL - || !bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + struct goku_udc *dev = to_goku_udc(g); /* hook up the driver */ driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget, driver); - if (retval) { - DBG(dev, "bind to driver %s --> error %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - /* then enable host detection and ep0; and we're ready + /* + * then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ udc_enable(dev); - DBG(dev, "registered gadget driver '%s'\n", driver->driver.name); return 0; } -static void -stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) +static void stop_activity(struct goku_udc *dev) { unsigned i; DBG (dev, "%s\n", __func__); - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - /* disconnect gadget driver after quiesceing hw and the driver */ udc_reset (dev); for (i = 0; i < 4; i++) nuke(&dev->ep [i], -ESHUTDOWN); - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } if (dev->driver) udc_enable(dev); } -static int goku_stop(struct usb_gadget_driver *driver) +static int goku_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct goku_udc *dev = the_controller; + struct goku_udc *dev = to_goku_udc(g); unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; - spin_lock_irqsave(&dev->lock, flags); dev->driver = NULL; - stop_activity(dev, driver); + stop_activity(dev); spin_unlock_irqrestore(&dev->lock, flags); - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; - - DBG(dev, "unregistered driver '%s'\n", driver->driver.name); return 0; } @@ -1570,7 +1516,7 @@ rescan: if (unlikely(stat & INT_DEVWIDE)) { if (stat & INT_SYSERROR) { ERROR(dev, "system error\n"); - stop_activity(dev, dev->driver); + stop_activity(dev); stat = 0; handled = 1; // FIXME have a neater way to prevent re-enumeration @@ -1585,7 +1531,7 @@ rescan: } else { DBG(dev, "disconnect\n"); if (dev->gadget.speed == USB_SPEED_FULL) - stop_activity(dev, dev->driver); + stop_activity(dev); dev->ep0state = EP0_DISCONNECT; dev->int_enable = INT_DEVWIDE; writel(dev->int_enable, &dev->regs->int_enable); @@ -1749,12 +1695,8 @@ static void goku_remove(struct pci_dev *pdev) pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device(pdev); - if (dev->registered) - device_unregister(&dev->gadget.dev); - pci_set_drvdata(pdev, NULL); dev->regs = NULL; - the_controller = NULL; INFO(dev, "unbind\n"); } @@ -1770,13 +1712,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval; - /* if you want to support more than one controller in a system, - * usb_gadget_driver_{register,unregister}() must change. - */ - if (the_controller) { - pr_warning("ignoring %s\n", pci_name(pdev)); - return -EBUSY; - } if (!pdev->irq) { printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev)); retval = -ENODEV; @@ -1797,10 +1732,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_FULL; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; /* now all the pci goodies ... */ @@ -1848,17 +1779,11 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) #ifdef CONFIG_USB_GADGET_DEBUG_FILES - create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); + proc_create_data(proc_node_name, 0, NULL, &udc_proc_fops, dev); #endif - the_controller = dev; - retval = device_register(&dev->gadget.dev); - if (retval) { - put_device(&dev->gadget.dev); - goto err; - } - dev->registered = 1; - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto err; diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index 85cdce0d190..86d2adafe14 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -250,8 +250,7 @@ struct goku_udc { got_region:1, req_config:1, configured:1, - enabled:1, - registered:1; + enabled:1; /* pci state used to access those endpoints */ struct pci_dev *pdev; @@ -261,6 +260,7 @@ struct goku_udc { /* statistics... */ unsigned long irqs; }; +#define to_goku_udc(g) (container_of((g), struct goku_udc, gadget)) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c new file mode 100644 index 00000000000..c7004ee89c9 --- /dev/null +++ b/drivers/usb/gadget/gr_udc.c @@ -0,0 +1,2236 @@ +/* + * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. + * + * 2013 (c) Aeroflex Gaisler AB + * + * This driver supports GRUSBDC USB Device Controller cores available in the + * GRLIB VHDL IP core library. + * + * Full documentation of the GRUSBDC core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * 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. + * + * Contributors: + * - Andreas Larsson <andreas@gaisler.com> + * - Marko Isomaki + */ + +/* + * A GRUSBDC core can have up to 16 IN endpoints and 16 OUT endpoints each + * individually configurable to any of the four USB transfer types. This driver + * only supports cores in DMA mode. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> + +#include <asm/byteorder.h> + +#include "gr_udc.h" + +#define DRIVER_NAME "gr_udc" +#define DRIVER_DESC "Aeroflex Gaisler GRUSBDC USB Peripheral Controller" + +static const char driver_name[] = DRIVER_NAME; +static const char driver_desc[] = DRIVER_DESC; + +#define gr_read32(x) (ioread32be((x))) +#define gr_write32(x, v) (iowrite32be((v), (x))) + +/* USB speed and corresponding string calculated from status register value */ +#define GR_SPEED(status) \ + ((status & GR_STATUS_SP) ? USB_SPEED_FULL : USB_SPEED_HIGH) +#define GR_SPEED_STR(status) usb_speed_string(GR_SPEED(status)) + +/* Size of hardware buffer calculated from epctrl register value */ +#define GR_BUFFER_SIZE(epctrl) \ + ((((epctrl) & GR_EPCTRL_BUFSZ_MASK) >> GR_EPCTRL_BUFSZ_POS) * \ + GR_EPCTRL_BUFSZ_SCALER) + +/* ---------------------------------------------------------------------- */ +/* Debug printout functionality */ + +static const char * const gr_modestring[] = {"control", "iso", "bulk", "int"}; + +static const char *gr_ep0state_string(enum gr_ep0state state) +{ + static const char *const names[] = { + [GR_EP0_DISCONNECT] = "disconnect", + [GR_EP0_SETUP] = "setup", + [GR_EP0_IDATA] = "idata", + [GR_EP0_ODATA] = "odata", + [GR_EP0_ISTATUS] = "istatus", + [GR_EP0_OSTATUS] = "ostatus", + [GR_EP0_STALL] = "stall", + [GR_EP0_SUSPEND] = "suspend", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[state]; +} + +#ifdef VERBOSE_DEBUG + +static void gr_dbgprint_request(const char *str, struct gr_ep *ep, + struct gr_request *req) +{ + int buflen = ep->is_in ? req->req.length : req->req.actual; + int rowlen = 32; + int plen = min(rowlen, buflen); + + dev_dbg(ep->dev->dev, "%s: 0x%p, %d bytes data%s:\n", str, req, buflen, + (buflen > plen ? " (truncated)" : "")); + print_hex_dump_debug(" ", DUMP_PREFIX_NONE, + rowlen, 4, req->req.buf, plen, false); +} + +static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index, u16 length) +{ + dev_vdbg(dev->dev, "REQ: %02x.%02x v%04x i%04x l%04x\n", + type, request, value, index, length); +} +#else /* !VERBOSE_DEBUG */ + +static void gr_dbgprint_request(const char *str, struct gr_ep *ep, + struct gr_request *req) {} + +static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index, u16 length) {} + +#endif /* VERBOSE_DEBUG */ + +/* ---------------------------------------------------------------------- */ +/* Debugfs functionality */ + +#ifdef CONFIG_USB_GADGET_DEBUG_FS + +static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep) +{ + u32 epctrl = gr_read32(&ep->regs->epctrl); + u32 epstat = gr_read32(&ep->regs->epstat); + int mode = (epctrl & GR_EPCTRL_TT_MASK) >> GR_EPCTRL_TT_POS; + struct gr_request *req; + + seq_printf(seq, "%s:\n", ep->ep.name); + seq_printf(seq, " mode = %s\n", gr_modestring[mode]); + seq_printf(seq, " halted: %d\n", !!(epctrl & GR_EPCTRL_EH)); + seq_printf(seq, " disabled: %d\n", !!(epctrl & GR_EPCTRL_ED)); + seq_printf(seq, " valid: %d\n", !!(epctrl & GR_EPCTRL_EV)); + seq_printf(seq, " dma_start = %d\n", ep->dma_start); + seq_printf(seq, " stopped = %d\n", ep->stopped); + seq_printf(seq, " wedged = %d\n", ep->wedged); + seq_printf(seq, " callback = %d\n", ep->callback); + seq_printf(seq, " maxpacket = %d\n", ep->ep.maxpacket); + seq_printf(seq, " maxpacket_limit = %d\n", ep->ep.maxpacket_limit); + seq_printf(seq, " bytes_per_buffer = %d\n", ep->bytes_per_buffer); + if (mode == 1 || mode == 3) + seq_printf(seq, " nt = %d\n", + (epctrl & GR_EPCTRL_NT_MASK) >> GR_EPCTRL_NT_POS); + + seq_printf(seq, " Buffer 0: %s %s%d\n", + epstat & GR_EPSTAT_B0 ? "valid" : "invalid", + epstat & GR_EPSTAT_BS ? " " : "selected ", + (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS); + seq_printf(seq, " Buffer 1: %s %s%d\n", + epstat & GR_EPSTAT_B1 ? "valid" : "invalid", + epstat & GR_EPSTAT_BS ? "selected " : " ", + (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS); + + if (list_empty(&ep->queue)) { + seq_puts(seq, " Queue: empty\n\n"); + return; + } + + seq_puts(seq, " Queue:\n"); + list_for_each_entry(req, &ep->queue, queue) { + struct gr_dma_desc *desc; + struct gr_dma_desc *next; + + seq_printf(seq, " 0x%p: 0x%p %d %d\n", req, + &req->req.buf, req->req.actual, req->req.length); + + next = req->first_desc; + do { + desc = next; + next = desc->next_desc; + seq_printf(seq, " %c 0x%p (0x%08x): 0x%05x 0x%08x\n", + desc == req->curr_desc ? 'c' : ' ', + desc, desc->paddr, desc->ctrl, desc->data); + } while (desc != req->last_desc); + } + seq_puts(seq, "\n"); +} + + +static int gr_seq_show(struct seq_file *seq, void *v) +{ + struct gr_udc *dev = seq->private; + u32 control = gr_read32(&dev->regs->control); + u32 status = gr_read32(&dev->regs->status); + struct gr_ep *ep; + + seq_printf(seq, "usb state = %s\n", + usb_state_string(dev->gadget.state)); + seq_printf(seq, "address = %d\n", + (control & GR_CONTROL_UA_MASK) >> GR_CONTROL_UA_POS); + seq_printf(seq, "speed = %s\n", GR_SPEED_STR(status)); + seq_printf(seq, "ep0state = %s\n", gr_ep0state_string(dev->ep0state)); + seq_printf(seq, "irq_enabled = %d\n", dev->irq_enabled); + seq_printf(seq, "remote_wakeup = %d\n", dev->remote_wakeup); + seq_printf(seq, "test_mode = %d\n", dev->test_mode); + seq_puts(seq, "\n"); + + list_for_each_entry(ep, &dev->ep_list, ep_list) + gr_seq_ep_show(seq, ep); + + return 0; +} + +static int gr_dfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, gr_seq_show, inode->i_private); +} + +static const struct file_operations gr_dfs_fops = { + .owner = THIS_MODULE, + .open = gr_dfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void gr_dfs_create(struct gr_udc *dev) +{ + const char *name = "gr_udc_state"; + + dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); + dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev, + &gr_dfs_fops); +} + +static void gr_dfs_delete(struct gr_udc *dev) +{ + /* Handles NULL and ERR pointers internally */ + debugfs_remove(dev->dfs_state); + debugfs_remove(dev->dfs_root); +} + +#else /* !CONFIG_USB_GADGET_DEBUG_FS */ + +static void gr_dfs_create(struct gr_udc *dev) {} +static void gr_dfs_delete(struct gr_udc *dev) {} + +#endif /* CONFIG_USB_GADGET_DEBUG_FS */ + +/* ---------------------------------------------------------------------- */ +/* DMA and request handling */ + +/* Allocates a new struct gr_dma_desc, sets paddr and zeroes the rest */ +static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags) +{ + dma_addr_t paddr; + struct gr_dma_desc *dma_desc; + + dma_desc = dma_pool_alloc(ep->dev->desc_pool, gfp_flags, &paddr); + if (!dma_desc) { + dev_err(ep->dev->dev, "Could not allocate from DMA pool\n"); + return NULL; + } + + memset(dma_desc, 0, sizeof(*dma_desc)); + dma_desc->paddr = paddr; + + return dma_desc; +} + +static inline void gr_free_dma_desc(struct gr_udc *dev, + struct gr_dma_desc *desc) +{ + dma_pool_free(dev->desc_pool, desc, (dma_addr_t)desc->paddr); +} + +/* Frees the chain of struct gr_dma_desc for the given request */ +static void gr_free_dma_desc_chain(struct gr_udc *dev, struct gr_request *req) +{ + struct gr_dma_desc *desc; + struct gr_dma_desc *next; + + next = req->first_desc; + if (!next) + return; + + do { + desc = next; + next = desc->next_desc; + gr_free_dma_desc(dev, desc); + } while (desc != req->last_desc); + + req->first_desc = NULL; + req->curr_desc = NULL; + req->last_desc = NULL; +} + +static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req); + +/* + * Frees allocated resources and calls the appropriate completion function/setup + * package handler for a finished request. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, + int status) + __releases(&dev->lock) + __acquires(&dev->lock) +{ + struct gr_udc *dev; + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); + gr_free_dma_desc_chain(dev, req); + + if (ep->is_in) /* For OUT, actual gets updated bit by bit */ + req->req.actual = req->req.length; + + if (!status) { + if (ep->is_in) + gr_dbgprint_request("SENT", ep, req); + else + gr_dbgprint_request("RECV", ep, req); + } + + /* Prevent changes to ep->queue during callback */ + ep->callback = 1; + if (req == dev->ep0reqo && !status) { + if (req->setup) + gr_ep0_setup(dev, req); + else + dev_err(dev->dev, + "Unexpected non setup packet on ep0in\n"); + } else if (req->req.complete) { + spin_unlock(&dev->lock); + + req->req.complete(&ep->ep, &req->req); + + spin_lock(&dev->lock); + } + ep->callback = 0; +} + +static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct gr_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +/* + * Starts DMA for endpoint ep if there are requests in the queue. + * + * Must be called with dev->lock held and with !ep->stopped. + */ +static void gr_start_dma(struct gr_ep *ep) +{ + struct gr_request *req; + u32 dmactrl; + + if (list_empty(&ep->queue)) { + ep->dma_start = 0; + return; + } + + req = list_first_entry(&ep->queue, struct gr_request, queue); + + /* A descriptor should already have been allocated */ + BUG_ON(!req->curr_desc); + + wmb(); /* Make sure all is settled before handing it over to DMA */ + + /* Set the descriptor pointer in the hardware */ + gr_write32(&ep->regs->dmaaddr, req->curr_desc->paddr); + + /* Announce available descriptors */ + dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_DA); + + ep->dma_start = 1; +} + +/* + * Finishes the first request in the ep's queue and, if available, starts the + * next request in queue. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static void gr_dma_advance(struct gr_ep *ep, int status) +{ + struct gr_request *req; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + gr_finish_request(ep, req, status); + gr_start_dma(ep); /* Regardless of ep->dma_start */ +} + +/* + * Abort DMA for an endpoint. Sets the abort DMA bit which causes an ongoing DMA + * transfer to be canceled and clears GR_DMACTRL_DA. + * + * Must be called with dev->lock held. + */ +static void gr_abort_dma(struct gr_ep *ep) +{ + u32 dmactrl; + + dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_AD); +} + +/* + * Allocates and sets up a struct gr_dma_desc and putting it on the descriptor + * chain. + * + * Size is not used for OUT endpoints. Hardware can not be instructed to handle + * smaller buffer than MAXPL in the OUT direction. + */ +static int gr_add_dma_desc(struct gr_ep *ep, struct gr_request *req, + dma_addr_t data, unsigned size, gfp_t gfp_flags) +{ + struct gr_dma_desc *desc; + + desc = gr_alloc_dma_desc(ep, gfp_flags); + if (!desc) + return -ENOMEM; + + desc->data = data; + if (ep->is_in) + desc->ctrl = + (GR_DESC_IN_CTRL_LEN_MASK & size) | GR_DESC_IN_CTRL_EN; + else + desc->ctrl = GR_DESC_OUT_CTRL_IE; + + if (!req->first_desc) { + req->first_desc = desc; + req->curr_desc = desc; + } else { + req->last_desc->next_desc = desc; + req->last_desc->next = desc->paddr; + req->last_desc->ctrl |= GR_DESC_OUT_CTRL_NX; + } + req->last_desc = desc; + + return 0; +} + +/* + * Sets up a chain of struct gr_dma_descriptors pointing to buffers that + * together covers req->req.length bytes of the buffer at DMA address + * req->req.dma for the OUT direction. + * + * The first descriptor in the chain is enabled, the rest disabled. The + * interrupt handler will later enable them one by one when needed so we can + * find out when the transfer is finished. For OUT endpoints, all descriptors + * therefore generate interrutps. + */ +static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + u16 bytes_left; /* Bytes left to provide descriptors for */ + u16 bytes_used; /* Bytes accommodated for */ + int ret = 0; + + req->first_desc = NULL; /* Signals that no allocation is done yet */ + bytes_left = req->req.length; + bytes_used = 0; + while (bytes_left > 0) { + dma_addr_t start = req->req.dma + bytes_used; + u16 size = min(bytes_left, ep->bytes_per_buffer); + + /* Should not happen however - gr_queue stops such lengths */ + if (size < ep->bytes_per_buffer) + dev_warn(ep->dev->dev, + "Buffer overrun risk: %u < %u bytes/buffer\n", + size, ep->bytes_per_buffer); + + ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); + if (ret) + goto alloc_err; + + bytes_left -= size; + bytes_used += size; + } + + req->first_desc->ctrl |= GR_DESC_OUT_CTRL_EN; + + return 0; + +alloc_err: + gr_free_dma_desc_chain(ep->dev, req); + + return ret; +} + +/* + * Sets up a chain of struct gr_dma_descriptors pointing to buffers that + * together covers req->req.length bytes of the buffer at DMA address + * req->req.dma for the IN direction. + * + * When more data is provided than the maximum payload size, the hardware splits + * this up into several payloads automatically. Moreover, ep->bytes_per_buffer + * is always set to a multiple of the maximum payload (restricted to the valid + * number of maximum payloads during high bandwidth isochronous or interrupt + * transfers) + * + * All descriptors are enabled from the beginning and we only generate an + * interrupt for the last one indicating that the entire request has been pushed + * to hardware. + */ +static int gr_setup_in_desc_list(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + u16 bytes_left; /* Bytes left in req to provide descriptors for */ + u16 bytes_used; /* Bytes in req accommodated for */ + int ret = 0; + + req->first_desc = NULL; /* Signals that no allocation is done yet */ + bytes_left = req->req.length; + bytes_used = 0; + do { /* Allow for zero length packets */ + dma_addr_t start = req->req.dma + bytes_used; + u16 size = min(bytes_left, ep->bytes_per_buffer); + + ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); + if (ret) + goto alloc_err; + + bytes_left -= size; + bytes_used += size; + } while (bytes_left > 0); + + /* + * Send an extra zero length packet to indicate that no more data is + * available when req->req.zero is set and the data length is even + * multiples of ep->ep.maxpacket. + */ + if (req->req.zero && (req->req.length % ep->ep.maxpacket == 0)) { + ret = gr_add_dma_desc(ep, req, 0, 0, gfp_flags); + if (ret) + goto alloc_err; + } + + /* + * For IN packets we only want to know when the last packet has been + * transmitted (not just put into internal buffers). + */ + req->last_desc->ctrl |= GR_DESC_IN_CTRL_PI; + + return 0; + +alloc_err: + gr_free_dma_desc_chain(ep->dev, req); + + return ret; +} + +/* Must be called with dev->lock held */ +static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) +{ + struct gr_udc *dev = ep->dev; + int ret; + + if (unlikely(!ep->ep.desc && ep->num != 0)) { + dev_err(dev->dev, "No ep descriptor for %s\n", ep->ep.name); + return -EINVAL; + } + + if (unlikely(!req->req.buf || !list_empty(&req->queue))) { + dev_err(dev->dev, + "Invalid request for %s: buf=%p list_empty=%d\n", + ep->ep.name, req->req.buf, list_empty(&req->queue)); + return -EINVAL; + } + + /* + * The DMA controller can not handle smaller OUT buffers than + * maxpacket. It could lead to buffer overruns if unexpectedly long + * packet are received. + */ + if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { + dev_err(dev->dev, + "OUT request length %d is not multiple of maxpacket\n", + req->req.length); + return -EMSGSIZE; + } + + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + dev_err(dev->dev, "-ESHUTDOWN"); + return -ESHUTDOWN; + } + + /* Can't touch registers when suspended */ + if (dev->ep0state == GR_EP0_SUSPEND) { + dev_err(dev->dev, "-EBUSY"); + return -EBUSY; + } + + /* Set up DMA mapping in case the caller didn't */ + ret = usb_gadget_map_request(&dev->gadget, &req->req, ep->is_in); + if (ret) { + dev_err(dev->dev, "usb_gadget_map_request"); + return ret; + } + + if (ep->is_in) + ret = gr_setup_in_desc_list(ep, req, gfp_flags); + else + ret = gr_setup_out_desc_list(ep, req, gfp_flags); + if (ret) + return ret; + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + list_add_tail(&req->queue, &ep->queue); + + /* Start DMA if not started, otherwise interrupt handler handles it */ + if (!ep->dma_start && likely(!ep->stopped)) + gr_start_dma(ep); + + return 0; +} + +/* + * Queue a request from within the driver. + * + * Must be called with dev->lock held. + */ +static inline int gr_queue_int(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + if (ep->is_in) + gr_dbgprint_request("RESP", ep, req); + + return gr_queue(ep, req, gfp_flags); +} + +/* ---------------------------------------------------------------------- */ +/* General helper functions */ + +/* + * Dequeue ALL requests. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_ep_nuke(struct gr_ep *ep) +{ + struct gr_request *req; + + ep->stopped = 1; + ep->dma_start = 0; + gr_abort_dma(ep); + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct gr_request, queue); + gr_finish_request(ep, req, -ESHUTDOWN); + } +} + +/* + * Reset the hardware state of this endpoint. + * + * Must be called with dev->lock held. + */ +static void gr_ep_reset(struct gr_ep *ep) +{ + gr_write32(&ep->regs->epctrl, 0); + gr_write32(&ep->regs->dmactrl, 0); + + ep->ep.maxpacket = MAX_CTRL_PL_SIZE; + ep->ep.desc = NULL; + ep->stopped = 1; + ep->dma_start = 0; +} + +/* + * Generate STALL on ep0in/out. + * + * Must be called with dev->lock held. + */ +static void gr_control_stall(struct gr_udc *dev) +{ + u32 epctrl; + + epctrl = gr_read32(&dev->epo[0].regs->epctrl); + gr_write32(&dev->epo[0].regs->epctrl, epctrl | GR_EPCTRL_CS); + epctrl = gr_read32(&dev->epi[0].regs->epctrl); + gr_write32(&dev->epi[0].regs->epctrl, epctrl | GR_EPCTRL_CS); + + dev->ep0state = GR_EP0_STALL; +} + +/* + * Halts, halts and wedges, or clears halt for an endpoint. + * + * Must be called with dev->lock held. + */ +static int gr_ep_halt_wedge(struct gr_ep *ep, int halt, int wedge, int fromhost) +{ + u32 epctrl; + int retval = 0; + + if (ep->num && !ep->ep.desc) + return -EINVAL; + + if (ep->num && ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) + return -EOPNOTSUPP; + + /* Never actually halt ep0, and therefore never clear halt for ep0 */ + if (!ep->num) { + if (halt && !fromhost) { + /* ep0 halt from gadget - generate protocol stall */ + gr_control_stall(ep->dev); + dev_dbg(ep->dev->dev, "EP: stall ep0\n"); + return 0; + } + return -EINVAL; + } + + dev_dbg(ep->dev->dev, "EP: %s halt %s\n", + (halt ? (wedge ? "wedge" : "set") : "clear"), ep->ep.name); + + epctrl = gr_read32(&ep->regs->epctrl); + if (halt) { + /* Set HALT */ + gr_write32(&ep->regs->epctrl, epctrl | GR_EPCTRL_EH); + ep->stopped = 1; + if (wedge) + ep->wedged = 1; + } else { + gr_write32(&ep->regs->epctrl, epctrl & ~GR_EPCTRL_EH); + ep->stopped = 0; + ep->wedged = 0; + + /* Things might have been queued up in the meantime */ + if (!ep->dma_start) + gr_start_dma(ep); + } + + return retval; +} + +/* Must be called with dev->lock held */ +static inline void gr_set_ep0state(struct gr_udc *dev, enum gr_ep0state value) +{ + if (dev->ep0state != value) + dev_vdbg(dev->dev, "STATE: ep0state=%s\n", + gr_ep0state_string(value)); + dev->ep0state = value; +} + +/* + * Should only be called when endpoints can not generate interrupts. + * + * Must be called with dev->lock held. + */ +static void gr_disable_interrupts_and_pullup(struct gr_udc *dev) +{ + gr_write32(&dev->regs->control, 0); + wmb(); /* Make sure that we do not deny one of our interrupts */ + dev->irq_enabled = 0; +} + +/* + * Stop all device activity and disable data line pullup. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_stop_activity(struct gr_udc *dev) +{ + struct gr_ep *ep; + + list_for_each_entry(ep, &dev->ep_list, ep_list) + gr_ep_nuke(ep); + + gr_disable_interrupts_and_pullup(dev); + + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + usb_gadget_set_state(&dev->gadget, USB_STATE_NOTATTACHED); +} + +/* ---------------------------------------------------------------------- */ +/* ep0 setup packet handling */ + +static void gr_ep0_testmode_complete(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct gr_ep *ep; + struct gr_udc *dev; + u32 control; + + ep = container_of(_ep, struct gr_ep, ep); + dev = ep->dev; + + spin_lock(&dev->lock); + + control = gr_read32(&dev->regs->control); + control |= GR_CONTROL_TM | (dev->test_mode << GR_CONTROL_TS_POS); + gr_write32(&dev->regs->control, control); + + spin_unlock(&dev->lock); +} + +static void gr_ep0_dummy_complete(struct usb_ep *_ep, struct usb_request *_req) +{ + /* Nothing needs to be done here */ +} + +/* + * Queue a response on ep0in. + * + * Must be called with dev->lock held. + */ +static int gr_ep0_respond(struct gr_udc *dev, u8 *buf, int length, + void (*complete)(struct usb_ep *ep, + struct usb_request *req)) +{ + u8 *reqbuf = dev->ep0reqi->req.buf; + int status; + int i; + + for (i = 0; i < length; i++) + reqbuf[i] = buf[i]; + dev->ep0reqi->req.length = length; + dev->ep0reqi->req.complete = complete; + + status = gr_queue_int(&dev->epi[0], dev->ep0reqi, GFP_ATOMIC); + if (status < 0) + dev_err(dev->dev, + "Could not queue ep0in setup response: %d\n", status); + + return status; +} + +/* + * Queue a 2 byte response on ep0in. + * + * Must be called with dev->lock held. + */ +static inline int gr_ep0_respond_u16(struct gr_udc *dev, u16 response) +{ + __le16 le_response = cpu_to_le16(response); + + return gr_ep0_respond(dev, (u8 *)&le_response, 2, + gr_ep0_dummy_complete); +} + +/* + * Queue a ZLP response on ep0in. + * + * Must be called with dev->lock held. + */ +static inline int gr_ep0_respond_empty(struct gr_udc *dev) +{ + return gr_ep0_respond(dev, NULL, 0, gr_ep0_dummy_complete); +} + +/* + * This is run when a SET_ADDRESS request is received. First writes + * the new address to the control register which is updated internally + * when the next IN packet is ACKED. + * + * Must be called with dev->lock held. + */ +static void gr_set_address(struct gr_udc *dev, u8 address) +{ + u32 control; + + control = gr_read32(&dev->regs->control) & ~GR_CONTROL_UA_MASK; + control |= (address << GR_CONTROL_UA_POS) & GR_CONTROL_UA_MASK; + control |= GR_CONTROL_SU; + gr_write32(&dev->regs->control, control); +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_device_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + u16 response; + u8 test; + + switch (request) { + case USB_REQ_SET_ADDRESS: + dev_dbg(dev->dev, "STATUS: address %d\n", value & 0xff); + gr_set_address(dev, value & 0xff); + if (value) + usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); + else + usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); + return gr_ep0_respond_empty(dev); + + case USB_REQ_GET_STATUS: + /* Self powered | remote wakeup */ + response = 0x0001 | (dev->remote_wakeup ? 0x0002 : 0); + return gr_ep0_respond_u16(dev, response); + + case USB_REQ_SET_FEATURE: + switch (value) { + case USB_DEVICE_REMOTE_WAKEUP: + /* Allow remote wakeup */ + dev->remote_wakeup = 1; + return gr_ep0_respond_empty(dev); + + case USB_DEVICE_TEST_MODE: + /* The hardware does not support TEST_FORCE_EN */ + test = index >> 8; + if (test >= TEST_J && test <= TEST_PACKET) { + dev->test_mode = test; + return gr_ep0_respond(dev, NULL, 0, + gr_ep0_testmode_complete); + } + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (value) { + case USB_DEVICE_REMOTE_WAKEUP: + /* Disallow remote wakeup */ + dev->remote_wakeup = 0; + return gr_ep0_respond_empty(dev); + } + break; + } + + return 1; /* Delegate the rest */ +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_interface_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + if (dev->gadget.state != USB_STATE_CONFIGURED) + return -1; + + /* + * Should return STALL for invalid interfaces, but udc driver does not + * know anything about that. However, many gadget drivers do not handle + * GET_STATUS so we need to take care of that. + */ + + switch (request) { + case USB_REQ_GET_STATUS: + return gr_ep0_respond_u16(dev, 0x0000); + + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + /* + * No possible valid standard requests. Still let gadget drivers + * have a go at it. + */ + break; + } + + return 1; /* Delegate the rest */ +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_endpoint_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + struct gr_ep *ep; + int status; + int halted; + u8 epnum = index & USB_ENDPOINT_NUMBER_MASK; + u8 is_in = index & USB_ENDPOINT_DIR_MASK; + + if ((is_in && epnum >= dev->nepi) || (!is_in && epnum >= dev->nepo)) + return -1; + + if (dev->gadget.state != USB_STATE_CONFIGURED && epnum != 0) + return -1; + + ep = (is_in ? &dev->epi[epnum] : &dev->epo[epnum]); + + switch (request) { + case USB_REQ_GET_STATUS: + halted = gr_read32(&ep->regs->epctrl) & GR_EPCTRL_EH; + return gr_ep0_respond_u16(dev, halted ? 0x0001 : 0); + + case USB_REQ_SET_FEATURE: + switch (value) { + case USB_ENDPOINT_HALT: + status = gr_ep_halt_wedge(ep, 1, 0, 1); + if (status >= 0) + status = gr_ep0_respond_empty(dev); + return status; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (value) { + case USB_ENDPOINT_HALT: + if (ep->wedged) + return -1; + status = gr_ep_halt_wedge(ep, 0, 0, 1); + if (status >= 0) + status = gr_ep0_respond_empty(dev); + return status; + } + break; + } + + return 1; /* Delegate the rest */ +} + +/* Must be called with dev->lock held */ +static void gr_ep0out_requeue(struct gr_udc *dev) +{ + int ret = gr_queue_int(&dev->epo[0], dev->ep0reqo, GFP_ATOMIC); + + if (ret) + dev_err(dev->dev, "Could not queue ep0out setup request: %d\n", + ret); +} + +/* + * The main function dealing with setup requests on ep0. + * + * Must be called with dev->lock held and irqs disabled + */ +static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req) + __releases(&dev->lock) + __acquires(&dev->lock) +{ + union { + struct usb_ctrlrequest ctrl; + u8 raw[8]; + u32 word[2]; + } u; + u8 type; + u8 request; + u16 value; + u16 index; + u16 length; + int i; + int status; + + /* Restore from ep0 halt */ + if (dev->ep0state == GR_EP0_STALL) { + gr_set_ep0state(dev, GR_EP0_SETUP); + if (!req->req.actual) + goto out; + } + + if (dev->ep0state == GR_EP0_ISTATUS) { + gr_set_ep0state(dev, GR_EP0_SETUP); + if (req->req.actual > 0) + dev_dbg(dev->dev, + "Unexpected setup packet at state %s\n", + gr_ep0state_string(GR_EP0_ISTATUS)); + else + goto out; /* Got expected ZLP */ + } else if (dev->ep0state != GR_EP0_SETUP) { + dev_info(dev->dev, + "Unexpected ep0out request at state %s - stalling\n", + gr_ep0state_string(dev->ep0state)); + gr_control_stall(dev); + gr_set_ep0state(dev, GR_EP0_SETUP); + goto out; + } else if (!req->req.actual) { + dev_dbg(dev->dev, "Unexpected ZLP at state %s\n", + gr_ep0state_string(dev->ep0state)); + goto out; + } + + /* Handle SETUP packet */ + for (i = 0; i < req->req.actual; i++) + u.raw[i] = ((u8 *)req->req.buf)[i]; + + type = u.ctrl.bRequestType; + request = u.ctrl.bRequest; + value = le16_to_cpu(u.ctrl.wValue); + index = le16_to_cpu(u.ctrl.wIndex); + length = le16_to_cpu(u.ctrl.wLength); + + gr_dbgprint_devreq(dev, type, request, value, index, length); + + /* Check for data stage */ + if (length) { + if (type & USB_DIR_IN) + gr_set_ep0state(dev, GR_EP0_IDATA); + else + gr_set_ep0state(dev, GR_EP0_ODATA); + } + + status = 1; /* Positive status flags delegation */ + if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (type & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = gr_device_request(dev, type, request, + value, index); + break; + case USB_RECIP_ENDPOINT: + status = gr_endpoint_request(dev, type, request, + value, index); + break; + case USB_RECIP_INTERFACE: + status = gr_interface_request(dev, type, request, + value, index); + break; + } + } + + if (status > 0) { + spin_unlock(&dev->lock); + + dev_vdbg(dev->dev, "DELEGATE\n"); + status = dev->driver->setup(&dev->gadget, &u.ctrl); + + spin_lock(&dev->lock); + } + + /* Generate STALL on both ep0out and ep0in if requested */ + if (unlikely(status < 0)) { + dev_vdbg(dev->dev, "STALL\n"); + gr_control_stall(dev); + } + + if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD && + request == USB_REQ_SET_CONFIGURATION) { + if (!value) { + dev_dbg(dev->dev, "STATUS: deconfigured\n"); + usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); + } else if (status >= 0) { + /* Not configured unless gadget OK:s it */ + dev_dbg(dev->dev, "STATUS: configured: %d\n", value); + usb_gadget_set_state(&dev->gadget, + USB_STATE_CONFIGURED); + } + } + + /* Get ready for next stage */ + if (dev->ep0state == GR_EP0_ODATA) + gr_set_ep0state(dev, GR_EP0_OSTATUS); + else if (dev->ep0state == GR_EP0_IDATA) + gr_set_ep0state(dev, GR_EP0_ISTATUS); + else + gr_set_ep0state(dev, GR_EP0_SETUP); + +out: + gr_ep0out_requeue(dev); +} + +/* ---------------------------------------------------------------------- */ +/* VBUS and USB reset handling */ + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_vbus_connected(struct gr_udc *dev, u32 status) +{ + u32 control; + + dev->gadget.speed = GR_SPEED(status); + usb_gadget_set_state(&dev->gadget, USB_STATE_POWERED); + + /* Turn on full interrupts and pullup */ + control = (GR_CONTROL_SI | GR_CONTROL_UI | GR_CONTROL_VI | + GR_CONTROL_SP | GR_CONTROL_EP); + gr_write32(&dev->regs->control, control); +} + +/* Must be called with dev->lock held */ +static void gr_enable_vbus_detect(struct gr_udc *dev) +{ + u32 status; + + dev->irq_enabled = 1; + wmb(); /* Make sure we do not ignore an interrupt */ + gr_write32(&dev->regs->control, GR_CONTROL_VI); + + /* Take care of the case we are already plugged in at this point */ + status = gr_read32(&dev->regs->status); + if (status & GR_STATUS_VB) + gr_vbus_connected(dev, status); +} + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_vbus_disconnected(struct gr_udc *dev) +{ + gr_stop_activity(dev); + + /* Report disconnect */ + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + + dev->driver->disconnect(&dev->gadget); + + spin_lock(&dev->lock); + } + + gr_enable_vbus_detect(dev); +} + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_udc_usbreset(struct gr_udc *dev, u32 status) +{ + gr_set_address(dev, 0); + gr_set_ep0state(dev, GR_EP0_SETUP); + usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); + dev->gadget.speed = GR_SPEED(status); + + gr_ep_nuke(&dev->epo[0]); + gr_ep_nuke(&dev->epi[0]); + dev->epo[0].stopped = 0; + dev->epi[0].stopped = 0; + gr_ep0out_requeue(dev); +} + +/* ---------------------------------------------------------------------- */ +/* Irq handling */ + +/* + * Handles interrupts from in endpoints. Returns whether something was handled. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static int gr_handle_in_ep(struct gr_ep *ep) +{ + struct gr_request *req; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + if (!req->last_desc) + return 0; + + if (ACCESS_ONCE(req->last_desc->ctrl) & GR_DESC_IN_CTRL_EN) + return 0; /* Not put in hardware buffers yet */ + + if (gr_read32(&ep->regs->epstat) & (GR_EPSTAT_B1 | GR_EPSTAT_B0)) + return 0; /* Not transmitted yet, still in hardware buffers */ + + /* Write complete */ + gr_dma_advance(ep, 0); + + return 1; +} + +/* + * Handles interrupts from out endpoints. Returns whether something was handled. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static int gr_handle_out_ep(struct gr_ep *ep) +{ + u32 ep_dmactrl; + u32 ctrl; + u16 len; + struct gr_request *req; + struct gr_udc *dev = ep->dev; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + if (!req->curr_desc) + return 0; + + ctrl = ACCESS_ONCE(req->curr_desc->ctrl); + if (ctrl & GR_DESC_OUT_CTRL_EN) + return 0; /* Not received yet */ + + /* Read complete */ + len = ctrl & GR_DESC_OUT_CTRL_LEN_MASK; + req->req.actual += len; + if (ctrl & GR_DESC_OUT_CTRL_SE) + req->setup = 1; + + if (len < ep->ep.maxpacket || req->req.actual == req->req.length) { + /* Short packet or the expected size - we are done */ + + if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) { + /* + * Send a status stage ZLP to ack the DATA stage in the + * OUT direction. This needs to be done before + * gr_dma_advance as that can lead to a call to + * ep0_setup that can change dev->ep0state. + */ + gr_ep0_respond_empty(dev); + gr_set_ep0state(dev, GR_EP0_SETUP); + } + + gr_dma_advance(ep, 0); + } else { + /* Not done yet. Enable the next descriptor to receive more. */ + req->curr_desc = req->curr_desc->next_desc; + req->curr_desc->ctrl |= GR_DESC_OUT_CTRL_EN; + + ep_dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, ep_dmactrl | GR_DMACTRL_DA); + } + + return 1; +} + +/* + * Handle state changes. Returns whether something was handled. + * + * Must be called with dev->lock held and irqs disabled. + */ +static int gr_handle_state_changes(struct gr_udc *dev) +{ + u32 status = gr_read32(&dev->regs->status); + int handled = 0; + int powstate = !(dev->gadget.state == USB_STATE_NOTATTACHED || + dev->gadget.state == USB_STATE_ATTACHED); + + /* VBUS valid detected */ + if (!powstate && (status & GR_STATUS_VB)) { + dev_dbg(dev->dev, "STATUS: vbus valid detected\n"); + gr_vbus_connected(dev, status); + handled = 1; + } + + /* Disconnect */ + if (powstate && !(status & GR_STATUS_VB)) { + dev_dbg(dev->dev, "STATUS: vbus invalid detected\n"); + gr_vbus_disconnected(dev); + handled = 1; + } + + /* USB reset detected */ + if (status & GR_STATUS_UR) { + dev_dbg(dev->dev, "STATUS: USB reset - speed is %s\n", + GR_SPEED_STR(status)); + gr_write32(&dev->regs->status, GR_STATUS_UR); + gr_udc_usbreset(dev, status); + handled = 1; + } + + /* Speed change */ + if (dev->gadget.speed != GR_SPEED(status)) { + dev_dbg(dev->dev, "STATUS: USB Speed change to %s\n", + GR_SPEED_STR(status)); + dev->gadget.speed = GR_SPEED(status); + handled = 1; + } + + /* Going into suspend */ + if ((dev->ep0state != GR_EP0_SUSPEND) && !(status & GR_STATUS_SU)) { + dev_dbg(dev->dev, "STATUS: USB suspend\n"); + gr_set_ep0state(dev, GR_EP0_SUSPEND); + dev->suspended_from = dev->gadget.state; + usb_gadget_set_state(&dev->gadget, USB_STATE_SUSPENDED); + + if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && + dev->driver && dev->driver->suspend) { + spin_unlock(&dev->lock); + + dev->driver->suspend(&dev->gadget); + + spin_lock(&dev->lock); + } + handled = 1; + } + + /* Coming out of suspend */ + if ((dev->ep0state == GR_EP0_SUSPEND) && (status & GR_STATUS_SU)) { + dev_dbg(dev->dev, "STATUS: USB resume\n"); + if (dev->suspended_from == USB_STATE_POWERED) + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + else + gr_set_ep0state(dev, GR_EP0_SETUP); + usb_gadget_set_state(&dev->gadget, dev->suspended_from); + + if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && + dev->driver && dev->driver->resume) { + spin_unlock(&dev->lock); + + dev->driver->resume(&dev->gadget); + + spin_lock(&dev->lock); + } + handled = 1; + } + + return handled; +} + +/* Non-interrupt context irq handler */ +static irqreturn_t gr_irq_handler(int irq, void *_dev) +{ + struct gr_udc *dev = _dev; + struct gr_ep *ep; + int handled = 0; + int i; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (!dev->irq_enabled) + goto out; + + /* + * Check IN ep interrupts. We check these before the OUT eps because + * some gadgets reuse the request that might already be currently + * outstanding and needs to be completed (mainly setup requests). + */ + for (i = 0; i < dev->nepi; i++) { + ep = &dev->epi[i]; + if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) + handled = gr_handle_in_ep(ep) || handled; + } + + /* Check OUT ep interrupts */ + for (i = 0; i < dev->nepo; i++) { + ep = &dev->epo[i]; + if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) + handled = gr_handle_out_ep(ep) || handled; + } + + /* Check status interrupts */ + handled = gr_handle_state_changes(dev) || handled; + + /* + * Check AMBA DMA errors. Only check if we didn't find anything else to + * handle because this shouldn't happen if we did everything right. + */ + if (!handled) { + list_for_each_entry(ep, &dev->ep_list, ep_list) { + if (gr_read32(&ep->regs->dmactrl) & GR_DMACTRL_AE) { + dev_err(dev->dev, + "AMBA Error occurred for %s\n", + ep->ep.name); + handled = 1; + } + } + } + +out: + spin_unlock_irqrestore(&dev->lock, flags); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +/* Interrupt context irq handler */ +static irqreturn_t gr_irq(int irq, void *_dev) +{ + struct gr_udc *dev = _dev; + + if (!dev->irq_enabled) + return IRQ_NONE; + + return IRQ_WAKE_THREAD; +} + +/* ---------------------------------------------------------------------- */ +/* USB ep ops */ + +/* Enable endpoint. Not for ep0in and ep0out that are handled separately. */ +static int gr_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct gr_udc *dev; + struct gr_ep *ep; + u8 mode; + u8 nt; + u16 max; + u16 buffer_size = 0; + u32 epctrl; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + dev = ep->dev; + + /* 'ep0' IN and OUT are reserved */ + if (ep == &dev->epo[0] || ep == &dev->epi[0]) + return -EINVAL; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* Make sure we are clear for enabling */ + epctrl = gr_read32(&ep->regs->epctrl); + if (epctrl & GR_EPCTRL_EV) + return -EBUSY; + + /* Check that directions match */ + if (!ep->is_in != !usb_endpoint_dir_in(desc)) + return -EINVAL; + + /* Check ep num */ + if ((!ep->is_in && ep->num >= dev->nepo) || + (ep->is_in && ep->num >= dev->nepi)) + return -EINVAL; + + if (usb_endpoint_xfer_control(desc)) { + mode = 0; + } else if (usb_endpoint_xfer_isoc(desc)) { + mode = 1; + } else if (usb_endpoint_xfer_bulk(desc)) { + mode = 2; + } else if (usb_endpoint_xfer_int(desc)) { + mode = 3; + } else { + dev_err(dev->dev, "Unknown transfer type for %s\n", + ep->ep.name); + return -EINVAL; + } + + /* + * Bits 10-0 set the max payload. 12-11 set the number of + * additional transactions. + */ + max = 0x7ff & usb_endpoint_maxp(desc); + nt = 0x3 & (usb_endpoint_maxp(desc) >> 11); + buffer_size = GR_BUFFER_SIZE(epctrl); + if (nt && (mode == 0 || mode == 2)) { + dev_err(dev->dev, + "%s mode: multiple trans./microframe not valid\n", + (mode == 2 ? "Bulk" : "Control")); + return -EINVAL; + } else if (nt == 0x3) { + dev_err(dev->dev, + "Invalid value 0x3 for additional trans./microframe\n"); + return -EINVAL; + } else if ((nt + 1) * max > buffer_size) { + dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n", + buffer_size, (nt + 1), max); + return -EINVAL; + } else if (max == 0) { + dev_err(dev->dev, "Max payload cannot be set to 0\n"); + return -EINVAL; + } else if (max > ep->ep.maxpacket_limit) { + dev_err(dev->dev, "Requested max payload %d > limit %d\n", + max, ep->ep.maxpacket_limit); + return -EINVAL; + } + + spin_lock(&ep->dev->lock); + + if (!ep->stopped) { + spin_unlock(&ep->dev->lock); + return -EBUSY; + } + + ep->stopped = 0; + ep->wedged = 0; + ep->ep.desc = desc; + ep->ep.maxpacket = max; + ep->dma_start = 0; + + + if (nt) { + /* + * Maximum possible size of all payloads in one microframe + * regardless of direction when using high-bandwidth mode. + */ + ep->bytes_per_buffer = (nt + 1) * max; + } else if (ep->is_in) { + /* + * The biggest multiple of maximum packet size that fits into + * the buffer. The hardware will split up into many packets in + * the IN direction. + */ + ep->bytes_per_buffer = (buffer_size / max) * max; + } else { + /* + * Only single packets will be placed the buffers in the OUT + * direction. + */ + ep->bytes_per_buffer = max; + } + + epctrl = (max << GR_EPCTRL_MAXPL_POS) + | (nt << GR_EPCTRL_NT_POS) + | (mode << GR_EPCTRL_TT_POS) + | GR_EPCTRL_EV; + if (ep->is_in) + epctrl |= GR_EPCTRL_PI; + gr_write32(&ep->regs->epctrl, epctrl); + + gr_write32(&ep->regs->dmactrl, GR_DMACTRL_IE | GR_DMACTRL_AI); + + spin_unlock(&ep->dev->lock); + + dev_dbg(ep->dev->dev, "EP: %s enabled - %s with %d bytes/buffer\n", + ep->ep.name, gr_modestring[mode], ep->bytes_per_buffer); + return 0; +} + +/* Disable endpoint. Not for ep0in and ep0out that are handled separately. */ +static int gr_ep_disable(struct usb_ep *_ep) +{ + struct gr_ep *ep; + struct gr_udc *dev; + unsigned long flags; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !ep->ep.desc) + return -ENODEV; + + dev = ep->dev; + + /* 'ep0' IN and OUT are reserved */ + if (ep == &dev->epo[0] || ep == &dev->epi[0]) + return -EINVAL; + + if (dev->ep0state == GR_EP0_SUSPEND) + return -EBUSY; + + dev_dbg(ep->dev->dev, "EP: disable %s\n", ep->ep.name); + + spin_lock_irqsave(&dev->lock, flags); + + gr_ep_nuke(ep); + gr_ep_reset(ep); + ep->ep.desc = NULL; + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* + * Frees a request, but not any DMA buffers associated with it + * (gr_finish_request should already have taken care of that). + */ +static void gr_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct gr_request *req; + + if (!_ep || !_req) + return; + req = container_of(_req, struct gr_request, req); + + /* Leads to memory leak */ + WARN(!list_empty(&req->queue), + "request not dequeued properly before freeing\n"); + + kfree(req); +} + +/* Queue a request from the gadget */ +static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct gr_ep *ep; + struct gr_request *req; + struct gr_udc *dev; + int ret; + + if (unlikely(!_ep || !_req)) + return -EINVAL; + + ep = container_of(_ep, struct gr_ep, ep); + req = container_of(_req, struct gr_request, req); + dev = ep->dev; + + spin_lock(&ep->dev->lock); + + /* + * The ep0 pointer in the gadget struct is used both for ep0in and + * ep0out. In a data stage in the out direction ep0out needs to be used + * instead of the default ep0in. Completion functions might use + * driver_data, so that needs to be copied as well. + */ + if ((ep == &dev->epi[0]) && (dev->ep0state == GR_EP0_ODATA)) { + ep = &dev->epo[0]; + ep->ep.driver_data = dev->epi[0].ep.driver_data; + } + + if (ep->is_in) + gr_dbgprint_request("EXTERN", ep, req); + + ret = gr_queue(ep, req, GFP_ATOMIC); + + spin_unlock(&ep->dev->lock); + + return ret; +} + +/* Dequeue JUST ONE request */ +static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct gr_request *req; + struct gr_ep *ep; + struct gr_udc *dev; + int ret = 0; + unsigned long flags; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !_req || (!ep->ep.desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver) + return -ESHUTDOWN; + + /* We can't touch (DMA) registers when suspended */ + if (dev->ep0state == GR_EP0_SUSPEND) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + + /* Make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + if (list_first_entry(&ep->queue, struct gr_request, queue) == req) { + /* This request is currently being processed */ + gr_abort_dma(ep); + if (ep->stopped) + gr_finish_request(ep, req, -ECONNRESET); + else + gr_dma_advance(ep, -ECONNRESET); + } else if (!list_empty(&req->queue)) { + /* Not being processed - gr_finish_request dequeues it */ + gr_finish_request(ep, req, -ECONNRESET); + } else { + ret = -EOPNOTSUPP; + } + +out: + spin_unlock_irqrestore(&dev->lock, flags); + + return ret; +} + +/* Helper for gr_set_halt and gr_set_wedge */ +static int gr_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) +{ + int ret; + struct gr_ep *ep; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct gr_ep, ep); + + spin_lock(&ep->dev->lock); + + /* Halting an IN endpoint should fail if queue is not empty */ + if (halt && ep->is_in && !list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + + ret = gr_ep_halt_wedge(ep, halt, wedge, 0); + +out: + spin_unlock(&ep->dev->lock); + + return ret; +} + +/* Halt endpoint */ +static int gr_set_halt(struct usb_ep *_ep, int halt) +{ + return gr_set_halt_wedge(_ep, halt, 0); +} + +/* Halt and wedge endpoint */ +static int gr_set_wedge(struct usb_ep *_ep) +{ + return gr_set_halt_wedge(_ep, 1, 1); +} + +/* + * Return the total number of bytes currently stored in the internal buffers of + * the endpoint. + */ +static int gr_fifo_status(struct usb_ep *_ep) +{ + struct gr_ep *ep; + u32 epstat; + u32 bytes = 0; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct gr_ep, ep); + + epstat = gr_read32(&ep->regs->epstat); + + if (epstat & GR_EPSTAT_B0) + bytes += (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS; + if (epstat & GR_EPSTAT_B1) + bytes += (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS; + + return bytes; +} + + +/* Empty data from internal buffers of an endpoint. */ +static void gr_fifo_flush(struct usb_ep *_ep) +{ + struct gr_ep *ep; + u32 epctrl; + + if (!_ep) + return; + ep = container_of(_ep, struct gr_ep, ep); + dev_vdbg(ep->dev->dev, "EP: flush fifo %s\n", ep->ep.name); + + spin_lock(&ep->dev->lock); + + epctrl = gr_read32(&ep->regs->epctrl); + epctrl |= GR_EPCTRL_CB; + gr_write32(&ep->regs->epctrl, epctrl); + + spin_unlock(&ep->dev->lock); +} + +static struct usb_ep_ops gr_ep_ops = { + .enable = gr_ep_enable, + .disable = gr_ep_disable, + + .alloc_request = gr_alloc_request, + .free_request = gr_free_request, + + .queue = gr_queue_ext, + .dequeue = gr_dequeue, + + .set_halt = gr_set_halt, + .set_wedge = gr_set_wedge, + .fifo_status = gr_fifo_status, + .fifo_flush = gr_fifo_flush, +}; + +/* ---------------------------------------------------------------------- */ +/* USB Gadget ops */ + +static int gr_get_frame(struct usb_gadget *_gadget) +{ + struct gr_udc *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + return gr_read32(&dev->regs->status) & GR_STATUS_FN_MASK; +} + +static int gr_wakeup(struct usb_gadget *_gadget) +{ + struct gr_udc *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + + /* Remote wakeup feature not enabled by host*/ + if (!dev->remote_wakeup) + return -EINVAL; + + spin_lock(&dev->lock); + + gr_write32(&dev->regs->control, + gr_read32(&dev->regs->control) | GR_CONTROL_RW); + + spin_unlock(&dev->lock); + + return 0; +} + +static int gr_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct gr_udc *dev; + u32 control; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + + spin_lock(&dev->lock); + + control = gr_read32(&dev->regs->control); + if (is_on) + control |= GR_CONTROL_EP; + else + control &= ~GR_CONTROL_EP; + gr_write32(&dev->regs->control, control); + + spin_unlock(&dev->lock); + + return 0; +} + +static int gr_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct gr_udc *dev = to_gr_udc(gadget); + + spin_lock(&dev->lock); + + /* Hook up the driver */ + driver->driver.bus = NULL; + dev->driver = driver; + + /* Get ready for host detection */ + gr_enable_vbus_detect(dev); + + spin_unlock(&dev->lock); + + dev_info(dev->dev, "Started with gadget driver '%s'\n", + driver->driver.name); + + return 0; +} + +static int gr_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct gr_udc *dev = to_gr_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + dev->driver = NULL; + gr_stop_activity(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + dev_info(dev->dev, "Stopped\n"); + + return 0; +} + +static const struct usb_gadget_ops gr_ops = { + .get_frame = gr_get_frame, + .wakeup = gr_wakeup, + .pullup = gr_pullup, + .udc_start = gr_udc_start, + .udc_stop = gr_udc_stop, + /* Other operations not supported */ +}; + +/* ---------------------------------------------------------------------- */ +/* Module probe, removal and of-matching */ + +static const char * const onames[] = { + "ep0out", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out", + "ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out", + "ep12out", "ep13out", "ep14out", "ep15out" +}; + +static const char * const inames[] = { + "ep0in", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in", + "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in", + "ep12in", "ep13in", "ep14in", "ep15in" +}; + +/* Must be called with dev->lock held */ +static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit) +{ + struct gr_ep *ep; + struct gr_request *req; + struct usb_request *_req; + void *buf; + + if (is_in) { + ep = &dev->epi[num]; + ep->ep.name = inames[num]; + ep->regs = &dev->regs->epi[num]; + } else { + ep = &dev->epo[num]; + ep->ep.name = onames[num]; + ep->regs = &dev->regs->epo[num]; + } + + gr_ep_reset(ep); + ep->num = num; + ep->is_in = is_in; + ep->dev = dev; + ep->ep.ops = &gr_ep_ops; + INIT_LIST_HEAD(&ep->queue); + + if (num == 0) { + _req = gr_alloc_request(&ep->ep, GFP_ATOMIC); + buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_ATOMIC); + if (!_req || !buf) { + /* possible _req freed by gr_probe via gr_remove */ + return -ENOMEM; + } + + req = container_of(_req, struct gr_request, req); + req->req.buf = buf; + req->req.length = MAX_CTRL_PL_SIZE; + + if (is_in) + dev->ep0reqi = req; /* Complete gets set as used */ + else + dev->ep0reqo = req; /* Completion treated separately */ + + usb_ep_set_maxpacket_limit(&ep->ep, MAX_CTRL_PL_SIZE); + ep->bytes_per_buffer = MAX_CTRL_PL_SIZE; + } else { + usb_ep_set_maxpacket_limit(&ep->ep, (u16)maxplimit); + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + } + list_add_tail(&ep->ep_list, &dev->ep_list); + + return 0; +} + +/* Must be called with dev->lock held */ +static int gr_udc_init(struct gr_udc *dev) +{ + struct device_node *np = dev->dev->of_node; + u32 epctrl_val; + u32 dmactrl_val; + int i; + int ret = 0; + u32 bufsize; + + gr_set_address(dev, 0); + + INIT_LIST_HEAD(&dev->gadget.ep_list); + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->gadget.ep0 = &dev->epi[0].ep; + + INIT_LIST_HEAD(&dev->ep_list); + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + + for (i = 0; i < dev->nepo; i++) { + if (of_property_read_u32_index(np, "epobufsizes", i, &bufsize)) + bufsize = 1024; + ret = gr_ep_init(dev, i, 0, bufsize); + if (ret) + return ret; + } + + for (i = 0; i < dev->nepi; i++) { + if (of_property_read_u32_index(np, "epibufsizes", i, &bufsize)) + bufsize = 1024; + ret = gr_ep_init(dev, i, 1, bufsize); + if (ret) + return ret; + } + + /* Must be disabled by default */ + dev->remote_wakeup = 0; + + /* Enable ep0out and ep0in */ + epctrl_val = (MAX_CTRL_PL_SIZE << GR_EPCTRL_MAXPL_POS) | GR_EPCTRL_EV; + dmactrl_val = GR_DMACTRL_IE | GR_DMACTRL_AI; + gr_write32(&dev->epo[0].regs->epctrl, epctrl_val); + gr_write32(&dev->epi[0].regs->epctrl, epctrl_val | GR_EPCTRL_PI); + gr_write32(&dev->epo[0].regs->dmactrl, dmactrl_val); + gr_write32(&dev->epi[0].regs->dmactrl, dmactrl_val); + + return 0; +} + +static int gr_remove(struct platform_device *pdev) +{ + struct gr_udc *dev = platform_get_drvdata(pdev); + + if (dev->added) + usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ + if (dev->driver) + return -EBUSY; + + gr_dfs_delete(dev); + if (dev->desc_pool) + dma_pool_destroy(dev->desc_pool); + platform_set_drvdata(pdev, NULL); + + gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); + gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req); + + return 0; +} +static int gr_request_irq(struct gr_udc *dev, int irq) +{ + return devm_request_threaded_irq(dev->dev, irq, gr_irq, gr_irq_handler, + IRQF_SHARED, driver_name, dev); +} + +static int gr_probe(struct platform_device *pdev) +{ + struct gr_udc *dev; + struct resource *res; + struct gr_regs __iomem *regs; + int retval; + u32 status; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq <= 0) { + dev_err(dev->dev, "No irq found\n"); + return -ENODEV; + } + + /* Some core configurations has separate irqs for IN and OUT events */ + dev->irqi = platform_get_irq(pdev, 1); + if (dev->irqi > 0) { + dev->irqo = platform_get_irq(pdev, 2); + if (dev->irqo <= 0) { + dev_err(dev->dev, "Found irqi but not irqo\n"); + return -ENODEV; + } + } else { + dev->irqi = 0; + } + + dev->gadget.name = driver_name; + dev->gadget.max_speed = USB_SPEED_HIGH; + dev->gadget.ops = &gr_ops; + dev->gadget.quirk_ep_out_aligned_size = true; + + spin_lock_init(&dev->lock); + dev->regs = regs; + + platform_set_drvdata(pdev, dev); + + /* Determine number of endpoints and data interface mode */ + status = gr_read32(&dev->regs->status); + dev->nepi = ((status & GR_STATUS_NEPI_MASK) >> GR_STATUS_NEPI_POS) + 1; + dev->nepo = ((status & GR_STATUS_NEPO_MASK) >> GR_STATUS_NEPO_POS) + 1; + + if (!(status & GR_STATUS_DM)) { + dev_err(dev->dev, "Slave mode cores are not supported\n"); + return -ENODEV; + } + + /* --- Effects of the following calls might need explicit cleanup --- */ + + /* Create DMA pool for descriptors */ + dev->desc_pool = dma_pool_create("desc_pool", dev->dev, + sizeof(struct gr_dma_desc), 4, 0); + if (!dev->desc_pool) { + dev_err(dev->dev, "Could not allocate DMA pool"); + return -ENOMEM; + } + + spin_lock(&dev->lock); + + /* Inside lock so that no gadget can use this udc until probe is done */ + retval = usb_add_gadget_udc(dev->dev, &dev->gadget); + if (retval) { + dev_err(dev->dev, "Could not add gadget udc"); + goto out; + } + dev->added = 1; + + retval = gr_udc_init(dev); + if (retval) + goto out; + + gr_dfs_create(dev); + + /* Clear all interrupt enables that might be left on since last boot */ + gr_disable_interrupts_and_pullup(dev); + + retval = gr_request_irq(dev, dev->irq); + if (retval) { + dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); + goto out; + } + + if (dev->irqi) { + retval = gr_request_irq(dev, dev->irqi); + if (retval) { + dev_err(dev->dev, "Failed to request irqi %d\n", + dev->irqi); + goto out; + } + retval = gr_request_irq(dev, dev->irqo); + if (retval) { + dev_err(dev->dev, "Failed to request irqo %d\n", + dev->irqo); + goto out; + } + } + + if (dev->irqi) + dev_info(dev->dev, "regs: %p, irqs %d, %d, %d\n", dev->regs, + dev->irq, dev->irqi, dev->irqo); + else + dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); + +out: + spin_unlock(&dev->lock); + + if (retval) + gr_remove(pdev); + + return retval; +} + +static struct of_device_id gr_match[] = { + {.name = "GAISLER_USBDC"}, + {.name = "01_021"}, + {}, +}; +MODULE_DEVICE_TABLE(of, gr_match); + +static struct platform_driver gr_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = gr_match, + }, + .probe = gr_probe, + .remove = gr_remove, +}; +module_platform_driver(gr_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/gr_udc.h new file mode 100644 index 00000000000..8388897d9ec --- /dev/null +++ b/drivers/usb/gadget/gr_udc.h @@ -0,0 +1,220 @@ +/* + * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. + * + * 2013 (c) Aeroflex Gaisler AB + * + * This driver supports GRUSBDC USB Device Controller cores available in the + * GRLIB VHDL IP core library. + * + * Full documentation of the GRUSBDC core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * 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. + * + * Contributors: + * - Andreas Larsson <andreas@gaisler.com> + * - Marko Isomaki + */ + +/* Control registers on the AMBA bus */ + +#define GR_MAXEP 16 /* Max # endpoints for *each* direction */ + +struct gr_epregs { + u32 epctrl; + union { + struct { /* Slave mode*/ + u32 slvctrl; + u32 slvdata; + }; + struct { /* DMA mode*/ + u32 dmactrl; + u32 dmaaddr; + }; + }; + u32 epstat; +}; + +struct gr_regs { + struct gr_epregs epo[GR_MAXEP]; /* 0x000 - 0x0fc */ + struct gr_epregs epi[GR_MAXEP]; /* 0x100 - 0x1fc */ + u32 control; /* 0x200 */ + u32 status; /* 0x204 */ +}; + +#define GR_EPCTRL_BUFSZ_SCALER 8 +#define GR_EPCTRL_BUFSZ_MASK 0xffe00000 +#define GR_EPCTRL_BUFSZ_POS 21 +#define GR_EPCTRL_PI BIT(20) +#define GR_EPCTRL_CB BIT(19) +#define GR_EPCTRL_CS BIT(18) +#define GR_EPCTRL_MAXPL_MASK 0x0003ff80 +#define GR_EPCTRL_MAXPL_POS 7 +#define GR_EPCTRL_NT_MASK 0x00000060 +#define GR_EPCTRL_NT_POS 5 +#define GR_EPCTRL_TT_MASK 0x00000018 +#define GR_EPCTRL_TT_POS 3 +#define GR_EPCTRL_EH BIT(2) +#define GR_EPCTRL_ED BIT(1) +#define GR_EPCTRL_EV BIT(0) + +#define GR_DMACTRL_AE BIT(10) +#define GR_DMACTRL_AD BIT(3) +#define GR_DMACTRL_AI BIT(2) +#define GR_DMACTRL_IE BIT(1) +#define GR_DMACTRL_DA BIT(0) + +#define GR_EPSTAT_PT BIT(29) +#define GR_EPSTAT_PR BIT(29) +#define GR_EPSTAT_B1CNT_MASK 0x1fff0000 +#define GR_EPSTAT_B1CNT_POS 16 +#define GR_EPSTAT_B0CNT_MASK 0x0000fff8 +#define GR_EPSTAT_B0CNT_POS 3 +#define GR_EPSTAT_B1 BIT(2) +#define GR_EPSTAT_B0 BIT(1) +#define GR_EPSTAT_BS BIT(0) + +#define GR_CONTROL_SI BIT(31) +#define GR_CONTROL_UI BIT(30) +#define GR_CONTROL_VI BIT(29) +#define GR_CONTROL_SP BIT(28) +#define GR_CONTROL_FI BIT(27) +#define GR_CONTROL_EP BIT(14) +#define GR_CONTROL_DH BIT(13) +#define GR_CONTROL_RW BIT(12) +#define GR_CONTROL_TS_MASK 0x00000e00 +#define GR_CONTROL_TS_POS 9 +#define GR_CONTROL_TM BIT(8) +#define GR_CONTROL_UA_MASK 0x000000fe +#define GR_CONTROL_UA_POS 1 +#define GR_CONTROL_SU BIT(0) + +#define GR_STATUS_NEPI_MASK 0xf0000000 +#define GR_STATUS_NEPI_POS 28 +#define GR_STATUS_NEPO_MASK 0x0f000000 +#define GR_STATUS_NEPO_POS 24 +#define GR_STATUS_DM BIT(23) +#define GR_STATUS_SU BIT(17) +#define GR_STATUS_UR BIT(16) +#define GR_STATUS_VB BIT(15) +#define GR_STATUS_SP BIT(14) +#define GR_STATUS_AF_MASK 0x00003800 +#define GR_STATUS_AF_POS 11 +#define GR_STATUS_FN_MASK 0x000007ff +#define GR_STATUS_FN_POS 0 + + +#define MAX_CTRL_PL_SIZE 64 /* As per USB standard for full and high speed */ + +/*-------------------------------------------------------------------------*/ + +/* Driver data structures and utilities */ + +struct gr_dma_desc { + u32 ctrl; + u32 data; + u32 next; + + /* These must be last because hw uses the previous three */ + u32 paddr; + struct gr_dma_desc *next_desc; +}; + +#define GR_DESC_OUT_CTRL_SE BIT(17) +#define GR_DESC_OUT_CTRL_IE BIT(15) +#define GR_DESC_OUT_CTRL_NX BIT(14) +#define GR_DESC_OUT_CTRL_EN BIT(13) +#define GR_DESC_OUT_CTRL_LEN_MASK 0x00001fff + +#define GR_DESC_IN_CTRL_MO BIT(18) +#define GR_DESC_IN_CTRL_PI BIT(17) +#define GR_DESC_IN_CTRL_ML BIT(16) +#define GR_DESC_IN_CTRL_IE BIT(15) +#define GR_DESC_IN_CTRL_NX BIT(14) +#define GR_DESC_IN_CTRL_EN BIT(13) +#define GR_DESC_IN_CTRL_LEN_MASK 0x00001fff + +#define GR_DESC_DMAADDR_MASK 0xfffffffc + +struct gr_ep { + struct usb_ep ep; + struct gr_udc *dev; + u16 bytes_per_buffer; + unsigned int dma_start; + struct gr_epregs __iomem *regs; + + unsigned num:8; + unsigned is_in:1; + unsigned stopped:1; + unsigned wedged:1; + unsigned callback:1; + + /* analogous to a host-side qh */ + struct list_head queue; + + struct list_head ep_list; +}; + +struct gr_request { + struct usb_request req; + struct list_head queue; + + /* Chain of dma descriptors */ + struct gr_dma_desc *first_desc; /* First in the chain */ + struct gr_dma_desc *curr_desc; /* Current descriptor */ + struct gr_dma_desc *last_desc; /* Last in the chain */ + + u8 setup; /* Setup packet */ +}; + +enum gr_ep0state { + GR_EP0_DISCONNECT = 0, /* No host */ + GR_EP0_SETUP, /* Between STATUS ack and SETUP report */ + GR_EP0_IDATA, /* IN data stage */ + GR_EP0_ODATA, /* OUT data stage */ + GR_EP0_ISTATUS, /* Status stage after IN data stage */ + GR_EP0_OSTATUS, /* Status stage after OUT data stage */ + GR_EP0_STALL, /* Data or status stages */ + GR_EP0_SUSPEND, /* USB suspend */ +}; + +struct gr_udc { + struct usb_gadget gadget; + struct gr_ep epi[GR_MAXEP]; + struct gr_ep epo[GR_MAXEP]; + struct usb_gadget_driver *driver; + struct dma_pool *desc_pool; + struct device *dev; + + enum gr_ep0state ep0state; + struct gr_request *ep0reqo; + struct gr_request *ep0reqi; + + struct gr_regs __iomem *regs; + int irq; + int irqi; + int irqo; + + unsigned added:1; + unsigned irq_enabled:1; + unsigned remote_wakeup:1; + + u8 test_mode; + + enum usb_device_state suspended_from; + + unsigned int nepi; + unsigned int nepo; + + struct list_head ep_list; + + spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */ + + struct dentry *dfs_root; + struct dentry *dfs_state; +}; + +#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget)) diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index c36260ea8bf..778613eb37a 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -185,7 +185,7 @@ static int __exit hid_unbind(struct usb_composite_dev *cdev) static int __init hidg_plat_driver_probe(struct platform_device *pdev) { - struct hidg_func_descriptor *func = pdev->dev.platform_data; + struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev); struct hidg_func_node *entry; if (!func) { diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c deleted file mode 100644 index a0eb85794fd..00000000000 --- a/drivers/usb/gadget/imx_udc.c +++ /dev/null @@ -1,1574 +0,0 @@ -/* - * driver/usb/gadget/imx_udc.c - * - * Copyright (C) 2005 Mike Lee <eemike@gmail.com> - * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/timer.h> -#include <linux/slab.h> -#include <linux/prefetch.h> - -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -#include <linux/platform_data/usb-imx_udc.h> -#include <mach/hardware.h> - -#include "imx_udc.h" - -static const char driver_name[] = "imx_udc"; -static const char ep0name[] = "ep0"; - -void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, - enum ep0_state stat); - -/******************************************************************************* - * IMX UDC hardware related functions - ******************************************************************************* - */ - -void imx_udc_enable(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, - imx_usb->base + USB_CTRL); - imx_usb->gadget.speed = USB_SPEED_FULL; -} - -void imx_udc_disable(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_CTRL); - - __raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA), - imx_usb->base + USB_CTRL); - - ep0_chg_stat(__func__, imx_usb, EP0_IDLE); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; -} - -void imx_udc_reset(struct imx_udc_struct *imx_usb) -{ - int temp = __raw_readl(imx_usb->base + USB_ENAB); - - /* set RST bit */ - __raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB); - - /* wait RST bit to clear */ - do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST); - - /* wait CFG bit to assert */ - do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); - - /* udc module is now ready */ -} - -void imx_udc_config(struct imx_udc_struct *imx_usb) -{ - u8 ep_conf[5]; - u8 i, j, cfg; - struct imx_ep_struct *imx_ep; - - /* wait CFG bit to assert */ - do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); - - /* Download the endpoint buffer for endpoint 0. */ - for (j = 0; j < 5; j++) { - i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00); - __raw_writeb(i, imx_usb->base + USB_DDAT); - do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY); - } - - /* Download the endpoint buffers for endpoints 1-5. - * We specify two configurations, one interface - */ - for (cfg = 1; cfg < 3; cfg++) { - for (i = 1; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - /* EP no | Config no */ - ep_conf[0] = (i << 4) | (cfg << 2); - /* Type | Direction */ - ep_conf[1] = (imx_ep->bmAttributes << 3) | - (EP_DIR(imx_ep) << 2); - /* Max packet size */ - ep_conf[2] = imx_ep->fifosize; - /* TRXTYP */ - ep_conf[3] = 0xC0; - /* FIFO no */ - ep_conf[4] = i; - - D_INI(imx_usb->dev, - "<%s> ep%d_conf[%d]:" - "[%02x-%02x-%02x-%02x-%02x]\n", - __func__, i, cfg, - ep_conf[0], ep_conf[1], ep_conf[2], - ep_conf[3], ep_conf[4]); - - for (j = 0; j < 5; j++) { - __raw_writeb(ep_conf[j], - imx_usb->base + USB_DDAT); - do {} while (__raw_readl(imx_usb->base - + USB_DADR) - & DADR_BSY); - } - } - } - - /* wait CFG bit to clear */ - do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG); -} - -void imx_udc_init_irq(struct imx_udc_struct *imx_usb) -{ - int i; - - /* Mask and clear all irqs */ - __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); - __raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR); - for (i = 0; i < IMX_USB_NB_EP; i++) { - __raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i)); - } - - /* Enable USB irqs */ - __raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK); - - /* Enable EP0 irqs */ - __raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_MDEVREQ | EPINTR_EOT - | EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL), - imx_usb->base + USB_EP_MASK(0)); -} - -void imx_udc_init_ep(struct imx_udc_struct *imx_usb) -{ - int i, max, temp; - struct imx_ep_struct *imx_ep; - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - switch (imx_ep->fifosize) { - case 8: - max = 0; - break; - case 16: - max = 1; - break; - case 32: - max = 2; - break; - case 64: - max = 3; - break; - default: - max = 1; - break; - } - temp = (EP_DIR(imx_ep) << 7) | (max << 5) - | (imx_ep->bmAttributes << 3); - __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); - __raw_writel(temp | EPSTAT_FLUSH, - imx_usb->base + USB_EP_STAT(i)); - D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_STAT(i))); - } -} - -void imx_udc_init_fifo(struct imx_udc_struct *imx_usb) -{ - int i, temp; - struct imx_ep_struct *imx_ep; - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - - /* Fifo control */ - temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; - __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); - D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); - - /* Fifo alarm */ - temp = (i ? imx_ep->fifosize / 2 : 0); - __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); - D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i, - __raw_readl(imx_usb->base + USB_EP_FALRM(i))); - } -} - -static void imx_udc_init(struct imx_udc_struct *imx_usb) -{ - /* Reset UDC */ - imx_udc_reset(imx_usb); - - /* Download config to enpoint buffer */ - imx_udc_config(imx_usb); - - /* Setup interrups */ - imx_udc_init_irq(imx_usb); - - /* Setup endpoints */ - imx_udc_init_ep(imx_usb); - - /* Setup fifos */ - imx_udc_init_fifo(imx_usb); -} - -void imx_ep_irq_enable(struct imx_ep_struct *imx_ep) -{ - - int i = EP_NO(imx_ep); - - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); - __raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF), - imx_ep->imx_usb->base + USB_EP_MASK(i)); -} - -void imx_ep_irq_disable(struct imx_ep_struct *imx_ep) -{ - - int i = EP_NO(imx_ep); - - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); - __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); -} - -int imx_ep_empty(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) - & FSTAT_EMPTY; -} - -unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) - & EPSTAT_BCOUNT) >> 16; -} - -void imx_flush(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - - int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_FLUSH, - imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); -} - -void imx_ep_stall(struct imx_ep_struct *imx_ep) -{ - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - int temp, i; - - D_ERR(imx_usb->dev, - "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name); - - imx_flush(imx_ep); - - /* Special care for ep0 */ - if (!EP_NO(imx_ep)) { - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, - imx_usb->base + USB_CTRL); - do { } while (__raw_readl(imx_usb->base + USB_CTRL) - & CTRL_CMDOVER); - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL); - } - else { - temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_STALL, - imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - - for (i = 0; i < 100; i ++) { - temp = __raw_readl(imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - if (!(temp & EPSTAT_STALL)) - break; - udelay(20); - } - if (i == 100) - D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n", - __func__, imx_ep->ep.name); - } -} - -static int imx_udc_get_frame(struct usb_gadget *_gadget) -{ - struct imx_udc_struct *imx_usb = container_of(_gadget, - struct imx_udc_struct, gadget); - - return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF; -} - -static int imx_udc_wakeup(struct usb_gadget *_gadget) -{ - return 0; -} - -/******************************************************************************* - * USB request control functions - ******************************************************************************* - */ - -static void ep_add_request(struct imx_ep_struct *imx_ep, - struct imx_request *req) -{ - if (unlikely(!req)) - return; - - req->in_use = 1; - list_add_tail(&req->queue, &imx_ep->queue); -} - -static void ep_del_request(struct imx_ep_struct *imx_ep, - struct imx_request *req) -{ - if (unlikely(!req)) - return; - - list_del_init(&req->queue); - req->in_use = 0; -} - -static void done(struct imx_ep_struct *imx_ep, - struct imx_request *req, int status) -{ - ep_del_request(imx_ep, req); - - if (likely(req->req.status == -EINPROGRESS)) - req->req.status = status; - else - status = req->req.status; - - if (status && status != -ESHUTDOWN) - D_ERR(imx_ep->imx_usb->dev, - "<%s> complete %s req %p stat %d len %u/%u\n", __func__, - imx_ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - req->req.complete(&imx_ep->ep, &req->req); -} - -static void nuke(struct imx_ep_struct *imx_ep, int status) -{ - struct imx_request *req; - - while (!list_empty(&imx_ep->queue)) { - req = list_entry(imx_ep->queue.next, struct imx_request, queue); - done(imx_ep, req, status); - } -} - -/******************************************************************************* - * Data tansfer over USB functions - ******************************************************************************* - */ -static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - u8 *buf; - int bytes_ep, bufferspace, count, i; - - bytes_ep = imx_fifo_bcount(imx_ep); - bufferspace = req->req.length - req->req.actual; - - buf = req->req.buf + req->req.actual; - prefetchw(buf); - - if (unlikely(imx_ep_empty(imx_ep))) - count = 0; /* zlp */ - else - count = min(bytes_ep, bufferspace); - - for (i = count; i > 0; i--) - *buf++ = __raw_readb(imx_ep->imx_usb->base - + USB_EP_FDAT0(EP_NO(imx_ep))); - req->req.actual += count; - - return count; -} - -static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - u8 *buf; - int length, count, temp; - - if (unlikely(__raw_readl(imx_ep->imx_usb->base + - USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) { - D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n", - __func__, imx_ep->ep.name); - return -1; - } - - buf = req->req.buf + req->req.actual; - prefetch(buf); - - length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize); - - if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { - D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n", - __func__, imx_ep->ep.name); - return -1; - } - - req->req.actual += length; - count = length; - - if (!count && req->req.zero) { /* zlp */ - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__); - return 0; - } - - while (count--) { - if (count == 0) { /* last byte */ - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_FCTRL(EP_NO(imx_ep))); - __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base - + USB_EP_FCTRL(EP_NO(imx_ep))); - } - __raw_writeb(*buf++, - imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); - } - - return length; -} - -static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - int bytes = 0, - count, - completed = 0; - - while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) - & FSTAT_FR) { - count = read_packet(imx_ep, req); - bytes += count; - - completed = (count != imx_ep->fifosize); - if (completed || req->req.actual == req->req.length) { - completed = 1; - break; - } - } - - if (completed || !req->req.length) { - done(imx_ep, req, 0); - D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", - __func__, imx_ep->ep.name, req, - completed ? "completed" : "not completed"); - if (!EP_NO(imx_ep)) - ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); - } - - D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes); - - return completed; -} - -static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) -{ - int bytes = 0, - count, - completed = 0; - - while (!completed) { - count = write_packet(imx_ep, req); - if (count < 0) - break; /* busy */ - bytes += count; - - /* last packet "must be" short (or a zlp) */ - completed = (count != imx_ep->fifosize); - - if (unlikely(completed)) { - done(imx_ep, req, 0); - D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", - __func__, imx_ep->ep.name, req, - completed ? "completed" : "not completed"); - if (!EP_NO(imx_ep)) - ep0_chg_stat(__func__, - imx_ep->imx_usb, EP0_IDLE); - } - } - - D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes); - - return completed; -} - -/******************************************************************************* - * Endpoint handlers - ******************************************************************************* - */ -static int handle_ep(struct imx_ep_struct *imx_ep) -{ - struct imx_request *req; - int completed = 0; - - do { - if (!list_empty(&imx_ep->queue)) - req = list_entry(imx_ep->queue.next, - struct imx_request, queue); - else { - D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n", - __func__, imx_ep->ep.name); - return 0; - } - - if (EP_DIR(imx_ep)) /* to host */ - completed = write_fifo(imx_ep, req); - else /* to device */ - completed = read_fifo(imx_ep, req); - - dump_ep_stat(__func__, imx_ep); - - } while (completed); - - return 0; -} - -static int handle_ep0(struct imx_ep_struct *imx_ep) -{ - struct imx_request *req = NULL; - int ret = 0; - - if (!list_empty(&imx_ep->queue)) { - req = list_entry(imx_ep->queue.next, struct imx_request, queue); - - switch (imx_ep->imx_usb->ep0state) { - - case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ - write_fifo(imx_ep, req); - break; - case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ - read_fifo(imx_ep, req); - break; - default: - D_EP0(imx_ep->imx_usb->dev, - "<%s> ep0 i/o, odd state %d\n", - __func__, imx_ep->imx_usb->ep0state); - ep_del_request(imx_ep, req); - ret = -EL2HLT; - break; - } - } - - else - D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n", - __func__, imx_ep->ep.name); - - return ret; -} - -static void handle_ep0_devreq(struct imx_udc_struct *imx_usb) -{ - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; - union { - struct usb_ctrlrequest r; - u8 raw[8]; - u32 word[2]; - } u; - int temp, i; - - nuke(imx_ep, -EPROTO); - - /* read SETUP packet */ - for (i = 0; i < 2; i++) { - if (imx_ep_empty(imx_ep)) { - D_ERR(imx_usb->dev, - "<%s> no setup packet received\n", __func__); - goto stall; - } - u.word[i] = __raw_readl(imx_usb->base - + USB_EP_FDAT(EP_NO(imx_ep))); - } - - temp = imx_ep_empty(imx_ep); - while (!imx_ep_empty(imx_ep)) { - i = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); - D_ERR(imx_usb->dev, - "<%s> wrong to have extra bytes for setup : 0x%08x\n", - __func__, i); - } - if (!temp) - goto stall; - - le16_to_cpus(&u.r.wValue); - le16_to_cpus(&u.r.wIndex); - le16_to_cpus(&u.r.wLength); - - D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n", - __func__, u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, u.r.wLength); - - if (imx_usb->set_config) { - /* NACK the host by using CMDOVER */ - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); - - D_ERR(imx_usb->dev, - "<%s> set config req is pending, NACK the host\n", - __func__); - return; - } - - if (u.r.bRequestType & USB_DIR_IN) - ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); - else - ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); - - i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); - if (i < 0) { - D_ERR(imx_usb->dev, "<%s> device setup error %d\n", - __func__, i); - goto stall; - } - - return; -stall: - D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__); - imx_ep_stall(imx_ep); - ep0_chg_stat(__func__, imx_usb, EP0_STALL); - return; -} - -/******************************************************************************* - * USB gadget callback functions - ******************************************************************************* - */ - -static int imx_ep_enable(struct usb_ep *usb_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct imx_ep_struct *imx_ep = container_of(usb_ep, - struct imx_ep_struct, ep); - struct imx_udc_struct *imx_usb = imx_ep->imx_usb; - unsigned long flags; - - if (!usb_ep - || !desc - || !EP_NO(imx_ep) - || desc->bDescriptorType != USB_DT_ENDPOINT - || imx_ep->bEndpointAddress != desc->bEndpointAddress) { - D_ERR(imx_usb->dev, - "<%s> bad ep or descriptor\n", __func__); - return -EINVAL; - } - - if (imx_ep->bmAttributes != desc->bmAttributes) { - D_ERR(imx_usb->dev, - "<%s> %s type mismatch\n", __func__, usb_ep->name); - return -EINVAL; - } - - if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { - D_ERR(imx_usb->dev, - "<%s> bad %s maxpacket\n", __func__, usb_ep->name); - return -ERANGE; - } - - if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { - D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); - return -ESHUTDOWN; - } - - local_irq_save(flags); - - imx_ep->stopped = 0; - imx_flush(imx_ep); - imx_ep_irq_enable(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name); - return 0; -} - -static int imx_ep_disable(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of(usb_ep, - struct imx_ep_struct, ep); - unsigned long flags; - - if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { - D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n", - __func__, usb_ep ? imx_ep->ep.name : NULL); - return -EINVAL; - } - - local_irq_save(flags); - - imx_ep->stopped = 1; - nuke(imx_ep, -ESHUTDOWN); - imx_flush(imx_ep); - imx_ep_irq_disable(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_ep->imx_usb->dev, - "<%s> DISABLED %s\n", __func__, usb_ep->name); - return 0; -} - -static struct usb_request *imx_ep_alloc_request - (struct usb_ep *usb_ep, gfp_t gfp_flags) -{ - struct imx_request *req; - - if (!usb_ep) - return NULL; - - req = kzalloc(sizeof *req, gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - req->in_use = 0; - - return &req->req; -} - -static void imx_ep_free_request - (struct usb_ep *usb_ep, struct usb_request *usb_req) -{ - struct imx_request *req; - - req = container_of(usb_req, struct imx_request, req); - WARN_ON(!list_empty(&req->queue)); - kfree(req); -} - -static int imx_ep_queue - (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) -{ - struct imx_ep_struct *imx_ep; - struct imx_udc_struct *imx_usb; - struct imx_request *req; - unsigned long flags; - int ret = 0; - - imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); - imx_usb = imx_ep->imx_usb; - req = container_of(usb_req, struct imx_request, req); - - /* - Special care on IMX udc. - Ignore enqueue when after set configuration from the - host. This assume all gadget drivers reply set - configuration with the next ep0 req enqueue. - */ - if (imx_usb->set_config && !EP_NO(imx_ep)) { - imx_usb->set_config = 0; - D_ERR(imx_usb->dev, - "<%s> gadget reply set config\n", __func__); - return 0; - } - - if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) { - D_ERR(imx_usb->dev, "<%s> bad params\n", __func__); - return -EINVAL; - } - - if (unlikely(!usb_ep || !imx_ep)) { - D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { - D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); - return -ESHUTDOWN; - } - - /* Debug */ - D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n", - __func__, EP_NO(imx_ep), - ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state - == EP0_IN_DATA_PHASE) - || (EP_NO(imx_ep) && EP_DIR(imx_ep))) - ? "IN" : "OUT", usb_req->length); - dump_req(__func__, imx_ep, usb_req); - - if (imx_ep->stopped) { - usb_req->status = -ESHUTDOWN; - return -ESHUTDOWN; - } - - if (req->in_use) { - D_ERR(imx_usb->dev, - "<%s> refusing to queue req %p (already queued)\n", - __func__, req); - return 0; - } - - local_irq_save(flags); - - usb_req->status = -EINPROGRESS; - usb_req->actual = 0; - - ep_add_request(imx_ep, req); - - if (!EP_NO(imx_ep)) - ret = handle_ep0(imx_ep); - else - ret = handle_ep(imx_ep); - - local_irq_restore(flags); - return ret; -} - -static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) -{ - - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - struct imx_request *req; - unsigned long flags; - - if (unlikely(!usb_ep || !EP_NO(imx_ep))) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - local_irq_save(flags); - - /* make sure it's actually queued on this endpoint */ - list_for_each_entry(req, &imx_ep->queue, queue) { - if (&req->req == usb_req) - break; - } - if (&req->req != usb_req) { - local_irq_restore(flags); - return -EINVAL; - } - - done(imx_ep, req, -ECONNRESET); - - local_irq_restore(flags); - return 0; -} - -static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - unsigned long flags; - - if (unlikely(!usb_ep || !EP_NO(imx_ep))) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -EINVAL; - } - - local_irq_save(flags); - - if ((imx_ep->bEndpointAddress & USB_DIR_IN) - && !list_empty(&imx_ep->queue)) { - local_irq_restore(flags); - return -EAGAIN; - } - - imx_ep_stall(imx_ep); - - local_irq_restore(flags); - - D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); - return 0; -} - -static int imx_ep_fifo_status(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - - if (!usb_ep) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - return -ENODEV; - } - - if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) - return 0; - else - return imx_fifo_bcount(imx_ep); -} - -static void imx_ep_fifo_flush(struct usb_ep *usb_ep) -{ - struct imx_ep_struct *imx_ep = container_of - (usb_ep, struct imx_ep_struct, ep); - unsigned long flags; - - local_irq_save(flags); - - if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { - D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); - local_irq_restore(flags); - return; - } - - /* toggle and halt bits stay unchanged */ - imx_flush(imx_ep); - - local_irq_restore(flags); -} - -static struct usb_ep_ops imx_ep_ops = { - .enable = imx_ep_enable, - .disable = imx_ep_disable, - - .alloc_request = imx_ep_alloc_request, - .free_request = imx_ep_free_request, - - .queue = imx_ep_queue, - .dequeue = imx_ep_dequeue, - - .set_halt = imx_ep_set_halt, - .fifo_status = imx_ep_fifo_status, - .fifo_flush = imx_ep_fifo_flush, -}; - -/******************************************************************************* - * USB endpoint control functions - ******************************************************************************* - */ - -void ep0_chg_stat(const char *label, - struct imx_udc_struct *imx_usb, enum ep0_state stat) -{ - D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n", - label, state_name[imx_usb->ep0state], state_name[stat]); - - if (imx_usb->ep0state == stat) - return; - - imx_usb->ep0state = stat; -} - -static void usb_init_data(struct imx_udc_struct *imx_usb) -{ - struct imx_ep_struct *imx_ep; - u8 i; - - /* device/ep0 records init */ - INIT_LIST_HEAD(&imx_usb->gadget.ep_list); - INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); - ep0_chg_stat(__func__, imx_usb, EP0_IDLE); - - /* basic endpoint records init */ - for (i = 0; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - - if (i) { - list_add_tail(&imx_ep->ep.ep_list, - &imx_usb->gadget.ep_list); - imx_ep->stopped = 1; - } else - imx_ep->stopped = 0; - - INIT_LIST_HEAD(&imx_ep->queue); - } -} - -static void udc_stop_activity(struct imx_udc_struct *imx_usb, - struct usb_gadget_driver *driver) -{ - struct imx_ep_struct *imx_ep; - int i; - - if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - - /* prevent new request submissions, kill any outstanding requests */ - for (i = 1; i < IMX_USB_NB_EP; i++) { - imx_ep = &imx_usb->imx_ep[i]; - imx_flush(imx_ep); - imx_ep->stopped = 1; - imx_ep_irq_disable(imx_ep); - nuke(imx_ep, -ESHUTDOWN); - } - - imx_usb->cfg = 0; - imx_usb->intf = 0; - imx_usb->alt = 0; - - if (driver) - driver->disconnect(&imx_usb->gadget); -} - -/******************************************************************************* - * Interrupt handlers - ******************************************************************************* - */ - -/* - * Called when timer expires. - * Timer is started when CFG_CHG is received. - */ -static void handle_config(unsigned long data) -{ - struct imx_udc_struct *imx_usb = (void *)data; - struct usb_ctrlrequest u; - int temp, cfg, intf, alt; - - local_irq_disable(); - - temp = __raw_readl(imx_usb->base + USB_STAT); - cfg = (temp & STAT_CFG) >> 5; - intf = (temp & STAT_INTF) >> 3; - alt = temp & STAT_ALTSET; - - D_REQ(imx_usb->dev, - "<%s> orig config C=%d, I=%d, A=%d / " - "req config C=%d, I=%d, A=%d\n", - __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, - cfg, intf, alt); - - if (cfg == 1 || cfg == 2) { - - if (imx_usb->cfg != cfg) { - u.bRequest = USB_REQ_SET_CONFIGURATION; - u.bRequestType = USB_DIR_OUT | - USB_TYPE_STANDARD | - USB_RECIP_DEVICE; - u.wValue = cfg; - u.wIndex = 0; - u.wLength = 0; - imx_usb->cfg = cfg; - imx_usb->driver->setup(&imx_usb->gadget, &u); - - } - if (imx_usb->intf != intf || imx_usb->alt != alt) { - u.bRequest = USB_REQ_SET_INTERFACE; - u.bRequestType = USB_DIR_OUT | - USB_TYPE_STANDARD | - USB_RECIP_INTERFACE; - u.wValue = alt; - u.wIndex = intf; - u.wLength = 0; - imx_usb->intf = intf; - imx_usb->alt = alt; - imx_usb->driver->setup(&imx_usb->gadget, &u); - } - } - - imx_usb->set_config = 0; - - local_irq_enable(); -} - -static irqreturn_t imx_udc_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - int intr = __raw_readl(imx_usb->base + USB_INTR); - int temp; - - if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START - | INTR_RESET_STOP | INTR_CFG_CHG)) { - dump_intr(__func__, intr, imx_usb->dev); - dump_usb_stat(__func__, imx_usb); - } - - if (!imx_usb->driver) - goto end_irq; - - if (intr & INTR_SOF) { - /* Copy from Freescale BSP. - We must enable SOF intr and set CMDOVER. - Datasheet don't specifiy this action, but it - is done in Freescale BSP, so just copy it. - */ - if (imx_usb->ep0state == EP0_IDLE) { - temp = __raw_readl(imx_usb->base + USB_CTRL); - __raw_writel(temp | CTRL_CMDOVER, - imx_usb->base + USB_CTRL); - } - } - - if (intr & INTR_CFG_CHG) { - /* A workaround of serious IMX UDC bug. - Handling of CFG_CHG should be delayed for some time, because - IMX does not NACK the host when CFG_CHG interrupt is pending. - There is no time to handle current CFG_CHG - if next CFG_CHG or SETUP packed is send immediately. - We have to clear CFG_CHG, start the timer and - NACK the host by setting CTRL_CMDOVER - if it sends any SETUP packet. - When timer expires, handler is called to handle configuration - changes. While CFG_CHG is not handled (set_config=1), - we must NACK the host to every SETUP packed. - This delay prevents from going out of sync with host. - */ - __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR); - imx_usb->set_config = 1; - mod_timer(&imx_usb->timer, jiffies + 5); - goto end_irq; - } - - if (intr & INTR_WAKEUP) { - if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN - && imx_usb->driver && imx_usb->driver->resume) - imx_usb->driver->resume(&imx_usb->gadget); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_FULL; - } - - if (intr & INTR_SUSPEND) { - if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN - && imx_usb->driver && imx_usb->driver->suspend) - imx_usb->driver->suspend(&imx_usb->gadget); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; - } - - if (intr & INTR_RESET_START) { - __raw_writel(intr, imx_usb->base + USB_INTR); - udc_stop_activity(imx_usb, imx_usb->driver); - imx_usb->set_config = 0; - del_timer(&imx_usb->timer); - imx_usb->gadget.speed = USB_SPEED_UNKNOWN; - } - - if (intr & INTR_RESET_STOP) - imx_usb->gadget.speed = USB_SPEED_FULL; - -end_irq: - __raw_writel(intr, imx_usb->base + USB_INTR); - return IRQ_HANDLED; -} - -static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; - int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0)); - - dump_ep_intr(__func__, 0, intr, imx_usb->dev); - - if (!imx_usb->driver) { - __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); - return IRQ_HANDLED; - } - - /* DEVREQ has highest priority */ - if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ)) - handle_ep0_devreq(imx_usb); - /* Seem i.MX is missing EOF interrupt sometimes. - * Therefore we don't monitor EOF. - * We call handle_ep0() only if a request is queued for ep0. - */ - else if (!list_empty(&imx_ep->queue)) - handle_ep0(imx_ep); - - __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); - - return IRQ_HANDLED; -} - -#ifndef MX1_INT_USBD0 -#define MX1_INT_USBD0 MX1_USBD_INT0 -#endif - -static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) -{ - struct imx_udc_struct *imx_usb = dev; - struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0]; - int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - - dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev); - - if (!imx_usb->driver) { - __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - return IRQ_HANDLED; - } - - handle_ep(imx_ep); - - __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - - return IRQ_HANDLED; -} - -irq_handler_t intr_handler(int i) -{ - switch (i) { - case 0: - return imx_udc_ctrl_irq; - case 1: - case 2: - case 3: - case 4: - case 5: - return imx_udc_bulk_irq; - default: - return imx_udc_irq; - } -} - -/******************************************************************************* - * Static defined IMX UDC structure - ******************************************************************************* - */ - -static int imx_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver); -static int imx_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver); -static const struct usb_gadget_ops imx_udc_ops = { - .get_frame = imx_udc_get_frame, - .wakeup = imx_udc_wakeup, - .udc_start = imx_udc_start, - .udc_stop = imx_udc_stop, -}; - -static struct imx_udc_struct controller = { - .gadget = { - .ops = &imx_udc_ops, - .ep0 = &controller.imx_ep[0].ep, - .name = driver_name, - .dev = { - .init_name = "gadget", - }, - }, - - .imx_ep[0] = { - .ep = { - .name = ep0name, - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = 0, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - }, - .imx_ep[1] = { - .ep = { - .name = "ep1in-bulk", - .ops = &imx_ep_ops, - .maxpacket = 64, - }, - .imx_usb = &controller, - .fifosize = 64, - .bEndpointAddress = USB_DIR_IN | 1, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[2] = { - .ep = { - .name = "ep2out-bulk", - .ops = &imx_ep_ops, - .maxpacket = 64, - }, - .imx_usb = &controller, - .fifosize = 64, - .bEndpointAddress = USB_DIR_OUT | 2, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[3] = { - .ep = { - .name = "ep3out-bulk", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_OUT | 3, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - }, - .imx_ep[4] = { - .ep = { - .name = "ep4in-int", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_IN | 4, - .bmAttributes = USB_ENDPOINT_XFER_INT, - }, - .imx_ep[5] = { - .ep = { - .name = "ep5out-int", - .ops = &imx_ep_ops, - .maxpacket = 32, - }, - .imx_usb = &controller, - .fifosize = 32, - .bEndpointAddress = USB_DIR_OUT | 5, - .bmAttributes = USB_ENDPOINT_XFER_INT, - }, -}; - -/******************************************************************************* - * USB gadget driver functions - ******************************************************************************* - */ -static int imx_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb; - int retval; - - imx_usb = container_of(gadget, struct imx_udc_struct, gadget); - /* first hook up the driver ... */ - imx_usb->driver = driver; - imx_usb->gadget.dev.driver = &driver->driver; - - retval = device_add(&imx_usb->gadget.dev); - if (retval) - goto fail; - - D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", - __func__, driver->driver.name); - - imx_udc_enable(imx_usb); - - return 0; -fail: - imx_usb->driver = NULL; - imx_usb->gadget.dev.driver = NULL; - return retval; -} - -static int imx_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb = container_of(gadget, - struct imx_udc_struct, gadget); - - udc_stop_activity(imx_usb, driver); - imx_udc_disable(imx_usb); - del_timer(&imx_usb->timer); - - imx_usb->gadget.dev.driver = NULL; - imx_usb->driver = NULL; - - device_del(&imx_usb->gadget.dev); - - D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", - __func__, driver->driver.name); - - return 0; -} - -/******************************************************************************* - * Module functions - ******************************************************************************* - */ - -static int __init imx_udc_probe(struct platform_device *pdev) -{ - struct imx_udc_struct *imx_usb = &controller; - struct resource *res; - struct imxusb_platform_data *pdata; - struct clk *clk; - void __iomem *base; - int ret = 0; - int i; - resource_size_t res_size; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENODEV; - } - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "driver needs platform data\n"); - return -ENODEV; - } - - res_size = resource_size(res); - if (!request_mem_region(res->start, res_size, res->name)) { - dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", - res_size, res->start); - return -ENOMEM; - } - - if (pdata->init) { - ret = pdata->init(&pdev->dev); - if (ret) - goto fail0; - } - - base = ioremap(res->start, res_size); - if (!base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EIO; - goto fail1; - } - - clk = clk_get(NULL, "usbd_clk"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err(&pdev->dev, "can't get USB clock\n"); - goto fail2; - } - clk_prepare_enable(clk); - - if (clk_get_rate(clk) != 48000000) { - D_INI(&pdev->dev, - "Bad USB clock (%d Hz), changing to 48000000 Hz\n", - (int)clk_get_rate(clk)); - if (clk_set_rate(clk, 48000000)) { - dev_err(&pdev->dev, - "Unable to set correct USB clock (48MHz)\n"); - ret = -EIO; - goto fail3; - } - } - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) { - imx_usb->usbd_int[i] = platform_get_irq(pdev, i); - if (imx_usb->usbd_int[i] < 0) { - dev_err(&pdev->dev, "can't get irq number\n"); - ret = -ENODEV; - goto fail3; - } - } - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) { - ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), - 0, driver_name, imx_usb); - if (ret) { - dev_err(&pdev->dev, "can't get irq %i, err %d\n", - imx_usb->usbd_int[i], ret); - for (--i; i >= 0; i--) - free_irq(imx_usb->usbd_int[i], imx_usb); - goto fail3; - } - } - - imx_usb->res = res; - imx_usb->base = base; - imx_usb->clk = clk; - imx_usb->dev = &pdev->dev; - - device_initialize(&imx_usb->gadget.dev); - - imx_usb->gadget.dev.parent = &pdev->dev; - imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; - - platform_set_drvdata(pdev, imx_usb); - - usb_init_data(imx_usb); - imx_udc_init(imx_usb); - - init_timer(&imx_usb->timer); - imx_usb->timer.function = handle_config; - imx_usb->timer.data = (unsigned long)imx_usb; - - ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget); - if (ret) - goto fail4; - - return 0; -fail4: - for (i = 0; i < IMX_USB_NB_EP + 1; i++) - free_irq(imx_usb->usbd_int[i], imx_usb); -fail3: - clk_put(clk); - clk_disable_unprepare(clk); -fail2: - iounmap(base); -fail1: - if (pdata->exit) - pdata->exit(&pdev->dev); -fail0: - release_mem_region(res->start, res_size); - return ret; -} - -static int __exit imx_udc_remove(struct platform_device *pdev) -{ - struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); - struct imxusb_platform_data *pdata = pdev->dev.platform_data; - int i; - - usb_del_gadget_udc(&imx_usb->gadget); - imx_udc_disable(imx_usb); - del_timer(&imx_usb->timer); - - for (i = 0; i < IMX_USB_NB_EP + 1; i++) - free_irq(imx_usb->usbd_int[i], imx_usb); - - clk_put(imx_usb->clk); - clk_disable_unprepare(imx_usb->clk); - iounmap(imx_usb->base); - - release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); - - if (pdata->exit) - pdata->exit(&pdev->dev); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -/*----------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM -#define imx_udc_suspend NULL -#define imx_udc_resume NULL -#else -#define imx_udc_suspend NULL -#define imx_udc_resume NULL -#endif - -/*----------------------------------------------------------------------------*/ - -static struct platform_driver udc_driver = { - .driver = { - .name = driver_name, - .owner = THIS_MODULE, - }, - .remove = __exit_p(imx_udc_remove), - .suspend = imx_udc_suspend, - .resume = imx_udc_resume, -}; - -static int __init udc_init(void) -{ - return platform_driver_probe(&udc_driver, imx_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); - -MODULE_DESCRIPTION("IMX USB Device Controller driver"); -MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx_udc"); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h deleted file mode 100644 index d118fb77784..00000000000 --- a/drivers/usb/gadget/imx_udc.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2005 Mike Lee(eemike@gmail.com) - * - * This udc driver is now under testing and code is based on pxa2xx_udc.h - * Please use it with your own risk! - * - * 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. - */ - -#ifndef __LINUX_USB_GADGET_IMX_H -#define __LINUX_USB_GADGET_IMX_H - -#include <linux/types.h> - -/* Helper macros */ -#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */ -#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) -#define IMX_USB_NB_EP 6 - -/* Driver structures */ -struct imx_request { - struct usb_request req; - struct list_head queue; - unsigned int in_use; -}; - -enum ep0_state { - EP0_IDLE, - EP0_IN_DATA_PHASE, - EP0_OUT_DATA_PHASE, - EP0_CONFIG, - EP0_STALL, -}; - -struct imx_ep_struct { - struct usb_ep ep; - struct imx_udc_struct *imx_usb; - struct list_head queue; - unsigned char stopped; - unsigned char fifosize; - unsigned char bEndpointAddress; - unsigned char bmAttributes; -}; - -struct imx_udc_struct { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - struct device *dev; - struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; - struct clk *clk; - struct timer_list timer; - enum ep0_state ep0state; - struct resource *res; - void __iomem *base; - unsigned char set_config; - int cfg, - intf, - alt, - usbd_int[7]; -}; - -/* USB registers */ -#define USB_FRAME (0x00) /* USB frame */ -#define USB_SPEC (0x04) /* USB Spec */ -#define USB_STAT (0x08) /* USB Status */ -#define USB_CTRL (0x0C) /* USB Control */ -#define USB_DADR (0x10) /* USB Desc RAM addr */ -#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */ -#define USB_INTR (0x18) /* USB interrupt */ -#define USB_MASK (0x1C) /* USB Mask */ -#define USB_ENAB (0x24) /* USB Enable */ -#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */ -#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */ -#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */ -#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */ -#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */ -#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */ -#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last rd f. pointer */ -#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last wr f. pointer */ -#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */ -#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */ -#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */ -/* USB Control Register Bit Fields.*/ -#define CTRL_CMDOVER (1<<6) /* UDC status */ -#define CTRL_CMDERROR (1<<5) /* UDC status */ -#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */ -#define CTRL_UDC_RST (1<<2) /* UDC reset */ -#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */ -#define CTRL_RESUME (1<<0) /* UDC resume */ -/* USB Status Register Bit Fields.*/ -#define STAT_RST (1<<8) -#define STAT_SUSP (1<<7) -#define STAT_CFG (3<<5) -#define STAT_INTF (3<<3) -#define STAT_ALTSET (7<<0) -/* USB Interrupt Status/Mask Registers Bit fields */ -#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */ -#define INTR_MSOF (1<<7) /* Missed Start of Frame */ -#define INTR_SOF (1<<6) /* Start of Frame */ -#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */ -#define INTR_RESET_START (1<<4) /* Reset Signaling start */ -#define INTR_RESUME (1<<3) /* Suspend to resume */ -#define INTR_SUSPEND (1<<2) /* Active to suspend */ -#define INTR_FRAME_MATCH (1<<1) /* Frame matched */ -#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */ -/* USB Enable Register Bit Fields.*/ -#define ENAB_RST (1<<31) /* Reset USB modules */ -#define ENAB_ENAB (1<<30) /* Enable USB modules*/ -#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */ -#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */ -#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */ -/* USB Descriptor Ram Address Register bit fields */ -#define DADR_CFG (1<<31) /* Configuration */ -#define DADR_BSY (1<<30) /* Busy status */ -#define DADR_DADR (0x1FF) /* Descriptor Ram Address */ -/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */ -#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */ -/* USB Endpoint Status Register bit fields */ -#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */ -#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */ -#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */ -#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */ -#define EPSTAT_TYP (3<<3) /* Endpoint type */ -#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */ -#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */ -#define EPSTAT_STALL (1<<0) /* Force stall */ -/* USB Endpoint FIFO Status Register bit fields */ -#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */ -#define FSTAT_ERR (1<<22) /* FIFO error */ -#define FSTAT_UF (1<<21) /* FIFO underflow */ -#define FSTAT_OF (1<<20) /* FIFO overflow */ -#define FSTAT_FR (1<<19) /* FIFO frame ready */ -#define FSTAT_FULL (1<<18) /* FIFO full */ -#define FSTAT_ALRM (1<<17) /* FIFO alarm */ -#define FSTAT_EMPTY (1<<16) /* FIFO empty */ -/* USB Endpoint FIFO Control Register bit fields */ -#define FCTRL_WFR (1<<29) /* Write frame end */ -/* USB Endpoint Interrupt Status Regsiter bit fields */ -#define EPINTR_FIFO_FULL (1<<8) /* fifo full */ -#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */ -#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */ -#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */ -#define EPINTR_FIFO_LOW (1<<4) /* fifo low */ -#define EPINTR_MDEVREQ (1<<3) /* multi Device request */ -#define EPINTR_EOT (1<<2) /* fifo end of transfer */ -#define EPINTR_DEVREQ (1<<1) /* Device request */ -#define EPINTR_EOF (1<<0) /* fifo end of frame */ - -/* Debug macros */ -#ifdef DEBUG - -/* #define DEBUG_REQ */ -/* #define DEBUG_TRX */ -/* #define DEBUG_INIT */ -/* #define DEBUG_EP0 */ -/* #define DEBUG_EPX */ -/* #define DEBUG_IRQ */ -/* #define DEBUG_EPIRQ */ -/* #define DEBUG_DUMP */ -/* #define DEBUG_ERR */ - -#ifdef DEBUG_REQ - #define D_REQ(dev, args...) dev_dbg(dev, ## args) -#else - #define D_REQ(dev, args...) do {} while (0) -#endif /* DEBUG_REQ */ - -#ifdef DEBUG_TRX - #define D_TRX(dev, args...) dev_dbg(dev, ## args) -#else - #define D_TRX(dev, args...) do {} while (0) -#endif /* DEBUG_TRX */ - -#ifdef DEBUG_INIT - #define D_INI(dev, args...) dev_dbg(dev, ## args) -#else - #define D_INI(dev, args...) do {} while (0) -#endif /* DEBUG_INIT */ - -#ifdef DEBUG_EP0 - static const char *state_name[] = { - "EP0_IDLE", - "EP0_IN_DATA_PHASE", - "EP0_OUT_DATA_PHASE", - "EP0_CONFIG", - "EP0_STALL" - }; - #define D_EP0(dev, args...) dev_dbg(dev, ## args) -#else - #define D_EP0(dev, args...) do {} while (0) -#endif /* DEBUG_EP0 */ - -#ifdef DEBUG_EPX - #define D_EPX(dev, args...) dev_dbg(dev, ## args) -#else - #define D_EPX(dev, args...) do {} while (0) -#endif /* DEBUG_EP0 */ - -#ifdef DEBUG_IRQ - static void dump_intr(const char *label, int irqreg, struct device *dev) - { - dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, - (irqreg & INTR_WAKEUP) ? " wake" : "", - (irqreg & INTR_MSOF) ? " msof" : "", - (irqreg & INTR_SOF) ? " sof" : "", - (irqreg & INTR_RESUME) ? " resume" : "", - (irqreg & INTR_SUSPEND) ? " suspend" : "", - (irqreg & INTR_RESET_STOP) ? " noreset" : "", - (irqreg & INTR_RESET_START) ? " reset" : "", - (irqreg & INTR_FRAME_MATCH) ? " fmatch" : "", - (irqreg & INTR_CFG_CHG) ? " config" : ""); - } -#else - #define dump_intr(x, y, z) do {} while (0) -#endif /* DEBUG_IRQ */ - -#ifdef DEBUG_EPIRQ - static void dump_ep_intr(const char *label, int nr, int irqreg, - struct device *dev) - { - dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr, - (irqreg & EPINTR_FIFO_FULL) ? " full" : "", - (irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "", - (irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "", - (irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "", - (irqreg & EPINTR_FIFO_LOW) ? " flow" : "", - (irqreg & EPINTR_MDEVREQ) ? " mreq" : "", - (irqreg & EPINTR_EOF) ? " eof" : "", - (irqreg & EPINTR_DEVREQ) ? " devreq" : "", - (irqreg & EPINTR_EOT) ? " eot" : ""); - } -#else - #define dump_ep_intr(x, y, z, i) do {} while (0) -#endif /* DEBUG_IRQ */ - -#ifdef DEBUG_DUMP - static void dump_usb_stat(const char *label, - struct imx_udc_struct *imx_usb) - { - int temp = __raw_readl(imx_usb->base + USB_STAT); - - dev_dbg(imx_usb->dev, - "<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label, - (temp & STAT_RST) ? " reset" : "", - (temp & STAT_SUSP) ? " suspend" : "", - (temp & STAT_CFG) >> 5, - (temp & STAT_INTF) >> 3, - (temp & STAT_ALTSET)); - } - - static void dump_ep_stat(const char *label, - struct imx_ep_struct *imx_ep) - { - int temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_INTR(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", - label, EP_NO(imx_ep), - (temp & EPINTR_FIFO_FULL) ? " full" : "", - (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "", - (temp & EPINTR_FIFO_ERROR) ? " ferr" : "", - (temp & EPINTR_FIFO_HIGH) ? " fhigh" : "", - (temp & EPINTR_FIFO_LOW) ? " flow" : "", - (temp & EPINTR_MDEVREQ) ? " mreq" : "", - (temp & EPINTR_EOF) ? " eof" : "", - (temp & EPINTR_DEVREQ) ? " devreq" : "", - (temp & EPINTR_EOT) ? " eot" : ""); - - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_STAT(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_STAT=[%s%s bcount=%d]\n", - label, EP_NO(imx_ep), - (temp & EPSTAT_SIP) ? " sip" : "", - (temp & EPSTAT_STALL) ? " stall" : "", - (temp & EPSTAT_BCOUNT) >> 16); - - temp = __raw_readl(imx_ep->imx_usb->base - + USB_EP_FSTAT(EP_NO(imx_ep))); - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", - label, EP_NO(imx_ep), - (temp & FSTAT_ERR) ? " ferr" : "", - (temp & FSTAT_UF) ? " funder" : "", - (temp & FSTAT_OF) ? " fover" : "", - (temp & FSTAT_FR) ? " fready" : "", - (temp & FSTAT_FULL) ? " ffull" : "", - (temp & FSTAT_ALRM) ? " falarm" : "", - (temp & FSTAT_EMPTY) ? " fempty" : ""); - } - - static void dump_req(const char *label, struct imx_ep_struct *imx_ep, - struct usb_request *req) - { - int i; - - if (!req || !req->buf) { - dev_dbg(imx_ep->imx_usb->dev, - "<%s> req or req buf is free\n", label); - return; - } - - if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state - == EP0_IN_DATA_PHASE) - || (EP_NO(imx_ep) && EP_DIR(imx_ep))) { - - dev_dbg(imx_ep->imx_usb->dev, - "<%s> request dump <", label); - for (i = 0; i < req->length; i++) - printk("%02x-", *((u8 *)req->buf + i)); - printk(">\n"); - } - } - -#else - #define dump_ep_stat(x, y) do {} while (0) - #define dump_usb_stat(x, y) do {} while (0) - #define dump_req(x, y, z) do {} while (0) -#endif /* DEBUG_DUMP */ - -#ifdef DEBUG_ERR - #define D_ERR(dev, args...) dev_dbg(dev, ## args) -#else - #define D_ERR(dev, args...) do {} while (0) -#endif - -#else - #define D_REQ(dev, args...) do {} while (0) - #define D_TRX(dev, args...) do {} while (0) - #define D_INI(dev, args...) do {} while (0) - #define D_EP0(dev, args...) do {} while (0) - #define D_EPX(dev, args...) do {} while (0) - #define dump_ep_intr(x, y, z, i) do {} while (0) - #define dump_intr(x, y, z) do {} while (0) - #define dump_ep_stat(x, y) do {} while (0) - #define dump_usb_stat(x, y) do {} while (0) - #define dump_req(x, y, z) do {} while (0) - #define D_ERR(dev, args...) do {} while (0) -#endif /* DEBUG */ - -#endif /* __LINUX_USB_GADGET_IMX_H */ diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 8ac840f25ba..2e4ce770490 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -24,6 +24,8 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> +#include <linux/mmu_context.h> +#include <linux/aio.h> #include <linux/device.h> #include <linux/moduleparam.h> @@ -437,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ value = -ENOMEM; - kbuf = kmalloc (len, GFP_KERNEL); - if (!kbuf) - goto free1; - if (copy_from_user (kbuf, buf, len)) { - value = -EFAULT; + kbuf = memdup_user(buf, len); + if (!kbuf) { + value = PTR_ERR(kbuf); goto free1; } @@ -450,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) data->name, len, (int) value); free1: mutex_unlock(&data->lock); - kfree (kbuf); return value; } @@ -513,13 +512,16 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) struct kiocb_priv { struct usb_request *req; struct ep_data *epdata; + struct kiocb *iocb; + struct mm_struct *mm; + struct work_struct work; void *buf; const struct iovec *iv; unsigned long nr_segs; unsigned actual; }; -static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) +static int ep_aio_cancel(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +530,6 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) local_irq_disable(); epdata = priv->epdata; // spin_lock(&epdata->dev->lock); - kiocbSetCancelled(iocb); if (likely(epdata && epdata->ep && priv->req)) value = usb_ep_dequeue (epdata->ep, priv->req); else @@ -536,19 +537,15 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) // spin_unlock(&epdata->dev->lock); local_irq_enable(); - aio_put_req(iocb); return value; } -static ssize_t ep_aio_read_retry(struct kiocb *iocb) +static ssize_t ep_copy_to_user(struct kiocb_priv *priv) { - struct kiocb_priv *priv = iocb->private; ssize_t len, total; void *to_copy; int i; - /* we "retry" to get the right mm context for this: */ - /* copy stuff into user buffers */ total = priv->actual; len = 0; @@ -568,9 +565,26 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) if (total == 0) break; } + + return len; +} + +static void ep_user_copy_worker(struct work_struct *work) +{ + struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work); + struct mm_struct *mm = priv->mm; + struct kiocb *iocb = priv->iocb; + size_t ret; + + use_mm(mm); + ret = ep_copy_to_user(priv); + unuse_mm(mm); + + /* completing the iocb can drop the ctx and mm, don't touch mm after */ + aio_complete(iocb, ret, ret); + kfree(priv->buf); kfree(priv); - return len; } static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) @@ -596,14 +610,14 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) aio_complete(iocb, req->actual ? req->actual : req->status, req->status); } else { - /* retry() won't report both; so we hide some faults */ + /* ep_copy_to_user() won't report both; we hide some faults */ if (unlikely(0 != req->status)) DBG(epdata->dev, "%s fault %d len %d\n", ep->name, req->status, req->actual); priv->buf = req->buf; priv->actual = req->actual; - kick_iocb(iocb); + schedule_work(&priv->work); } spin_unlock(&epdata->dev->lock); @@ -633,8 +647,10 @@ fail: return value; } iocb->private = priv; + priv->iocb = iocb; priv->iv = iv; priv->nr_segs = nr_segs; + INIT_WORK(&priv->work, ep_user_copy_worker); value = get_ready_ep(iocb->ki_filp->f_flags, epdata); if (unlikely(value < 0)) { @@ -642,10 +658,11 @@ fail: goto fail; } - iocb->ki_cancel = ep_aio_cancel; + kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; + priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ /* each kiocb is coupled to one usb_request, but we can't * allocate or submit those if the host disconnected. @@ -674,7 +691,7 @@ fail: kfree(priv); put_ep(epdata); } else - value = (iv ? -EIOCBRETRY : -EIOCBQUEUED); + value = -EIOCBQUEUED; return value; } @@ -688,12 +705,11 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, if (unlikely(usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; - buf = kmalloc(iocb->ki_left, GFP_KERNEL); + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); if (unlikely(!buf)) return -ENOMEM; - iocb->ki_retry = ep_aio_read_retry; - return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); + return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); } static ssize_t @@ -708,7 +724,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; - buf = kmalloc(iocb->ki_left, GFP_KERNEL); + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); if (unlikely(!buf)) return -ENOMEM; @@ -887,7 +903,6 @@ ep_open (struct inode *inode, struct file *fd) /* used before endpoint configuration */ static const struct file_operations ep_config_operations = { - .owner = THIS_MODULE, .llseek = no_llseek, .open = ep_open, @@ -1249,12 +1264,13 @@ dev_release (struct inode *inode, struct file *fd) kfree (dev->buf); dev->buf = NULL; - put_dev (dev); /* other endpoints were all decoupled from this device */ spin_lock_irq(&dev->lock); dev->state = STATE_DEV_DISABLED; spin_unlock_irq(&dev->lock); + + put_dev (dev); return 0; } @@ -1483,6 +1499,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) */ if (value == 0) { INFO (dev, "configuration #%d\n", dev->current_config); + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); if (dev->usermode_setup) { dev->setup_can_stall = 0; goto delegate; @@ -1490,7 +1507,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } break; -#ifndef CONFIG_USB_GADGET_PXA25X +#ifndef CONFIG_USB_PXA25X /* PXA automagically handles this request too */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != 0x80) @@ -1940,7 +1957,6 @@ dev_open (struct inode *inode, struct file *fd) } static const struct file_operations dev_init_operations = { - .owner = THIS_MODULE, .llseek = no_llseek, .open = dev_open, @@ -2033,6 +2049,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) return -ESRCH; /* fake probe to determine $CHIP */ + CHIP = NULL; usb_gadget_probe_driver(&probe_driver); if (!CHIP) return -ENODEV; @@ -2105,6 +2122,7 @@ static struct file_system_type gadgetfs_type = { .mount = gadgetfs_mount, .kill_sb = gadgetfs_kill_sb, }; +MODULE_ALIAS_FS("gadgetfs"); /*----------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index dd1c9b1fe52..e471580a2a3 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -55,7 +55,6 @@ #include <mach/hardware.h> #include <linux/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <mach/platform.h> #include <mach/irqs.h> @@ -565,7 +564,7 @@ static int proc_udc_show(struct seq_file *s, void *unused) static int proc_udc_open(struct inode *inode, struct file *file) { - return single_open(file, proc_udc_show, PDE(inode)->data); + return single_open(file, proc_udc_show, PDE_DATA(inode)); } static const struct file_operations proc_ops = { @@ -1449,7 +1448,7 @@ static void udc_reinit(struct lpc32xx_udc *udc) if (i != 0) list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - ep->ep.maxpacket = ep->maxpacket; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); INIT_LIST_HEAD(&ep->queue); ep->req_pending = 0; } @@ -1469,23 +1468,7 @@ static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) status = req->req.status; if (ep->lep) { - enum dma_data_direction direction; - - if (ep->is_in) - direction = DMA_TO_DEVICE; - else - direction = DMA_FROM_DEVICE; - - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - direction); - req->req.dma = 0; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - direction); + usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); /* Free DDs */ udc_dd_free(udc, req->dd_desc_ptr); @@ -1841,26 +1824,11 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, } if (ep->lep) { - enum dma_data_direction direction; struct lpc32xx_usbd_dd_gad *dd; - /* Map DMA pointer */ - if (ep->is_in) - direction = DMA_TO_DEVICE; - else - direction = DMA_FROM_DEVICE; - - if (req->req.dma == 0) { - req->req.dma = dma_map_single( - ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, direction); - req->mapped = 1; - } else { - dma_sync_single_for_device( - ep->udc->gadget.dev.parent, req->req.dma, - req->req.length, direction); - req->mapped = 0; - } + status = usb_gadget_map_request(&udc->gadget, _req, ep->is_in); + if (status) + return status; /* For the request, build a list of DDs */ dd = udc_dd_alloc(udc); @@ -2977,7 +2945,6 @@ static int lpc32xx_start(struct usb_gadget *gadget, } udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.of_node = udc->dev->of_node; udc->enabled = 1; udc->selfpowered = 1; @@ -3026,7 +2993,6 @@ static int lpc32xx_stop(struct usb_gadget *gadget, } udc->enabled = 0; - udc->gadget.dev.driver = NULL; udc->driver = NULL; return 0; @@ -3111,7 +3077,9 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) udc->isp1301_i2c_client->addr); pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto resource_fail; udc->board = &lpc32xx_usbddata; @@ -3248,12 +3216,6 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) udc_disable(udc); udc_reinit(udc); - retval = device_register(&udc->gadget.dev); - if (retval < 0) { - dev_err(udc->dev, "Device registration failure\n"); - goto dev_register_fail; - } - /* Request IRQs - low and high priority USB device IRQs are routed to * the same handler, while the DMA interrupt is routed elsewhere */ retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq, @@ -3320,8 +3282,6 @@ irq_dev_fail: irq_hp_fail: free_irq(udc->udp_irq[IRQ_USB_LP], udc); irq_lp_fail: - device_unregister(&udc->gadget.dev); -dev_register_fail: dma_pool_destroy(udc->dd_cache); dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, @@ -3334,9 +3294,9 @@ usb_clk_enable_fail: pll_set_fail: clk_disable(udc->usb_pll_clk); pll_enable_fail: - clk_put(udc->usb_slv_clk); -usb_otg_clk_get_fail: clk_put(udc->usb_otg_clk); +usb_otg_clk_get_fail: + clk_put(udc->usb_slv_clk); usb_clk_get_fail: clk_put(udc->usb_pll_clk); pll_get_fail: @@ -3376,8 +3336,6 @@ static int lpc32xx_udc_remove(struct platform_device *pdev) free_irq(udc->udp_irq[IRQ_USB_HP], udc); free_irq(udc->udp_irq[IRQ_USB_LP], udc); - device_unregister(&udc->gadget.dev); - clk_disable(udc->usb_otg_clk); clk_put(udc->usb_otg_clk); clk_disable(udc->usb_slv_clk); @@ -3458,17 +3416,7 @@ static struct platform_driver lpc32xx_udc_driver = { }, }; -static int __init udc_init_module(void) -{ - return platform_driver_probe(&lpc32xx_udc_driver, lpc32xx_udc_probe); -} -module_init(udc_init_module); - -static void __exit udc_exit_module(void) -{ - platform_driver_unregister(&lpc32xx_udc_driver); -} -module_exit(udc_exit_module); +module_platform_driver_probe(lpc32xx_udc_driver, lpc32xx_udc_probe); MODULE_DESCRIPTION("LPC32XX udc driver"); MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index b6401f1b56c..0d17174b86f 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1463,41 +1463,14 @@ static struct usb_ep_ops m66592_ep_ops = { }; /*-------------------------------------------------------------------------*/ -static struct m66592 *the_controller; - -static int m66592_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int m66592_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct m66592 *m66592 = the_controller; - int retval; - - if (!driver - || driver->max_speed < USB_SPEED_HIGH - || !bind - || !driver->setup) - return -EINVAL; - if (!m66592) - return -ENODEV; - if (m66592->driver) - return -EBUSY; + struct m66592 *m66592 = to_m66592(g); /* hook up the driver */ driver->driver.bus = NULL; m66592->driver = driver; - m66592->gadget.dev.driver = &driver->driver; - - retval = device_add(&m66592->gadget.dev); - if (retval) { - pr_err("device_add error (%d)\n", retval); - goto error; - } - - retval = bind(&m66592->gadget, driver); - if (retval) { - pr_err("bind to driver error (%d)\n", retval); - device_del(&m66592->gadget.dev); - goto error; - } m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) { @@ -1510,37 +1483,22 @@ static int m66592_start(struct usb_gadget_driver *driver, } return 0; - -error: - m66592->driver = NULL; - m66592->gadget.dev.driver = NULL; - - return retval; } -static int m66592_stop(struct usb_gadget_driver *driver) +static int m66592_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct m66592 *m66592 = the_controller; - unsigned long flags; - - if (driver != m66592->driver || !driver->unbind) - return -EINVAL; - - spin_lock_irqsave(&m66592->lock, flags); - if (m66592->gadget.speed != USB_SPEED_UNKNOWN) - m66592_usb_disconnect(m66592); - spin_unlock_irqrestore(&m66592->lock, flags); + struct m66592 *m66592 = to_m66592(g); m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); driver->unbind(&m66592->gadget); - m66592->gadget.dev.driver = NULL; init_controller(m66592); disable_controller(m66592); - device_del(&m66592->gadget.dev); m66592->driver = NULL; + return 0; } @@ -1566,16 +1524,16 @@ static int m66592_pullup(struct usb_gadget *gadget, int is_on) return 0; } -static struct usb_gadget_ops m66592_gadget_ops = { +static const struct usb_gadget_ops m66592_gadget_ops = { .get_frame = m66592_get_frame, - .start = m66592_start, - .stop = m66592_stop, + .udc_start = m66592_udc_start, + .udc_stop = m66592_udc_stop, .pullup = m66592_pullup, }; 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); @@ -1626,7 +1584,7 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - if (pdev->dev.platform_data == NULL) { + if (dev_get_platdata(&pdev->dev) == NULL) { dev_err(&pdev->dev, "no platform data\n"); ret = -ENODEV; goto clean_up; @@ -1636,23 +1594,17 @@ static int __init m66592_probe(struct platform_device *pdev) m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); if (m66592 == NULL) { ret = -ENOMEM; - pr_err("kzalloc error\n"); goto clean_up; } - m66592->pdata = pdev->dev.platform_data; + m66592->pdata = dev_get_platdata(&pdev->dev); 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; - device_initialize(&m66592->gadget.dev); - dev_set_name(&m66592->gadget.dev, "gadget"); m66592->gadget.max_speed = USB_SPEED_HIGH; - m66592->gadget.dev.parent = &pdev->dev; - m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; - m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; init_timer(&m66592->timer); @@ -1694,9 +1646,9 @@ static int __init m66592_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = m66592_ep_name[i]; ep->ep.ops = &m66592_ep_ops; - ep->ep.maxpacket = 512; + usb_ep_set_maxpacket_limit(&ep->ep, 512); } - m66592->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64); m66592->ep[0].pipenum = 0; m66592->ep[0].fifoaddr = M66592_CFIFO; m66592->ep[0].fifosel = M66592_CFIFOSEL; @@ -1706,11 +1658,11 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->pipenum2ep[0] = &m66592->ep[0]; m66592->epaddr2ep[0] = &m66592->ep[0]; - the_controller = m66592; - m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL); - if (m66592->ep0_req == NULL) + if (m66592->ep0_req == NULL) { + ret = -ENOMEM; goto clean_up3; + } m66592->ep0_req->complete = nop_completion; init_controller(m66592); @@ -1753,14 +1705,4 @@ static struct platform_driver m66592_driver = { }, }; -static int __init m66592_udc_init(void) -{ - return platform_driver_probe(&m66592_driver, m66592_probe); -} -module_init(m66592_udc_init); - -static void __exit m66592_udc_cleanup(void) -{ - platform_driver_unregister(&m66592_driver); -} -module_exit(m66592_udc_cleanup); +module_platform_driver_probe(m66592_driver, m66592_probe); diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 16c7e14678b..96d49d7bfb6 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -492,6 +492,7 @@ struct m66592 { int isochronous; int num_dma; }; +#define to_m66592(g) (container_of((g), struct m66592, gadget)) #define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget) #define m66592_to_gadget(m66592) (&m66592->gadget) diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 080e577773d..8e27a8c9644 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -37,16 +37,16 @@ #define DRIVER_DESC "Mass Storage Gadget" #define DRIVER_VERSION "2009/09/11" -/*-------------------------------------------------------------------------*/ - /* - * 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. + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ -#include "f_mass_storage.c" +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +#include "f_mass_storage.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function_instance *fi_msg; +static struct usb_function *f_msg; + /****************************** Configurations ******************************/ static struct fsg_module_parameters mod_data = { .stall = 1 }; +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); static unsigned long msg_registered; @@ -115,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common) static int __init msg_do_config(struct usb_configuration *c) { - static const struct fsg_operations ops = { - .thread_exits = msg_thread_exits, - }; - static struct fsg_common common; - - struct fsg_common *retp; - struct fsg_config config; + struct fsg_opts *opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -129,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - fsg_config_from_params(&config, &mod_data); - config.ops = &ops; + opts = fsg_opts_from_func_inst(fi_msg); + + f_msg = usb_get_function(fi_msg); + if (IS_ERR(f_msg)) + return PTR_ERR(f_msg); + + ret = fsg_common_run_thread(opts->common); + if (ret) + goto put_func; + + ret = usb_add_function(c, f_msg); + if (ret) + goto put_func; - retp = fsg_common_init(&common, c->cdev, &config); - if (IS_ERR(retp)) - return PTR_ERR(retp); + return 0; - ret = fsg_bind_config(c->cdev, c, &common); - fsg_common_put(&common); +put_func: + usb_put_function(f_msg); return ret; } @@ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = { static int __init msg_bind(struct usb_composite_dev *cdev) { + static const struct fsg_operations ops = { + .thread_exits = msg_thread_exits, + }; + struct fsg_opts *opts; + struct fsg_config config; int status; + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) + return PTR_ERR(fi_msg); + + fsg_config_from_params(&config, &mod_data, fsg_num_buffers); + opts = fsg_opts_from_func_inst(fi_msg); + + opts->no_configfs = true; + status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); + if (status) + goto fail; + + status = fsg_common_set_nluns(opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + fsg_common_set_ops(opts->common, &ops); + + status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(opts->common, true); + status = fsg_common_create_luns(opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(opts->common, config.vendor_name, + config.product_name); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - return status; + goto fail_string_ids; msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; status = usb_add_config(cdev, &msg_config_driver, msg_do_config); if (status < 0) - return status; + goto fail_string_ids; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&cdev->gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); set_bit(0, &msg_registered); return 0; + +fail_string_ids: + fsg_common_remove_luns(opts->common); +fail_set_cdev: + fsg_common_free_luns(opts->common); +fail_set_nluns: + fsg_common_free_buffers(opts->common); +fail: + usb_put_function_instance(fi_msg); + return status; } +static int msg_unbind(struct usb_composite_dev *cdev) +{ + if (!IS_ERR(f_msg)) + usb_put_function(f_msg); + + if (!IS_ERR(fi_msg)) + usb_put_function_instance(fi_msg); + + return 0; +} /****************************** Some noise ******************************/ @@ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = { .needs_serial = 1, .strings = dev_strings, .bind = msg_bind, + .unbind = msg_unbind, }; MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 88472bf7dbb..940f6cde8e8 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -15,7 +15,9 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> +#include "u_serial.h" #if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS #endif @@ -31,30 +33,19 @@ MODULE_AUTHOR("Michal Nazarewicz"); MODULE_LICENSE("GPL"); -/***************************** All the files... *****************************/ +#include "f_mass_storage.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_mass_storage.c" - -#include "u_serial.c" -#include "f_acm.c" - -#include "f_ecm.c" -#include "f_subset.c" +#include "u_ecm.h" #ifdef USB_ETH_RNDIS -# include "f_rndis.c" -# include "rndis.c" +# include "u_rndis.h" +# 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 */ @@ -131,19 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; -FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; -static struct fsg_common fsg_common; +#else -static u8 hostaddr[ETH_ALEN]; +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +static struct usb_function_instance *fi_acm; +static struct usb_function_instance *fi_msg; /********** RNDIS **********/ #ifdef USB_ETH_RNDIS +static struct usb_function_instance *fi_rndis; +static struct usb_function *f_acm_rndis; +static struct usb_function *f_rndis; +static struct usb_function *f_msg_rndis; static __init int rndis_do_config(struct usb_configuration *c) { + struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -151,22 +159,54 @@ static __init int rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = rndis_bind_config(c, hostaddr); - if (ret < 0) - return ret; + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); - ret = acm_bind_config(c, 0); + ret = usb_add_function(c, f_rndis); if (ret < 0) - return ret; + goto err_func_rndis; - ret = fsg_bind_config(c->cdev, c, &fsg_common); - if (ret < 0) - return ret; + f_acm_rndis = usb_get_function(fi_acm); + if (IS_ERR(f_acm_rndis)) { + ret = PTR_ERR(f_acm_rndis); + goto err_func_acm; + } + + ret = usb_add_function(c, f_acm_rndis); + if (ret) + goto err_conf; + + f_msg_rndis = usb_get_function(fi_msg); + if (IS_ERR(f_msg_rndis)) { + ret = PTR_ERR(f_msg_rndis); + goto err_fsg; + } + + fsg_opts = fsg_opts_from_func_inst(fi_msg); + ret = fsg_common_run_thread(fsg_opts->common); + if (ret) + goto err_run; + + ret = usb_add_function(c, f_msg_rndis); + if (ret) + goto err_run; return 0; +err_run: + usb_put_function(f_msg_rndis); +err_fsg: + usb_remove_function(c, f_acm_rndis); +err_conf: + usb_put_function(f_acm_rndis); +err_func_acm: + usb_remove_function(c, f_rndis); +err_func_rndis: + usb_put_function(f_rndis); + return ret; } -static int rndis_config_register(struct usb_composite_dev *cdev) +static __ref int rndis_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, @@ -181,7 +221,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev) #else -static int rndis_config_register(struct usb_composite_dev *cdev) +static __ref int rndis_config_register(struct usb_composite_dev *cdev) { return 0; } @@ -192,9 +232,14 @@ static int rndis_config_register(struct usb_composite_dev *cdev) /********** CDC ECM **********/ #ifdef CONFIG_USB_G_MULTI_CDC +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_acm_multi; +static struct usb_function *f_ecm; +static struct usb_function *f_msg_multi; static __init int cdc_do_config(struct usb_configuration *c) { + struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -202,22 +247,55 @@ static __init int cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - ret = ecm_bind_config(c, hostaddr); - if (ret < 0) - return ret; + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) + return PTR_ERR(f_ecm); - ret = acm_bind_config(c, 0); + ret = usb_add_function(c, f_ecm); if (ret < 0) - return ret; + goto err_func_ecm; - ret = fsg_bind_config(c->cdev, c, &fsg_common); - if (ret < 0) - return ret; + /* implicit port_num is zero */ + f_acm_multi = usb_get_function(fi_acm); + if (IS_ERR(f_acm_multi)) { + ret = PTR_ERR(f_acm_multi); + goto err_func_acm; + } + + ret = usb_add_function(c, f_acm_multi); + if (ret) + goto err_conf; + + f_msg_multi = usb_get_function(fi_msg); + if (IS_ERR(f_msg_multi)) { + ret = PTR_ERR(f_msg_multi); + goto err_fsg; + } + + fsg_opts = fsg_opts_from_func_inst(fi_msg); + ret = fsg_common_run_thread(fsg_opts->common); + if (ret) + goto err_run; + + ret = usb_add_function(c, f_msg_multi); + if (ret) + goto err_run; return 0; +err_run: + usb_put_function(f_msg_multi); +err_fsg: + usb_remove_function(c, f_acm_multi); +err_conf: + usb_put_function(f_acm_multi); +err_func_acm: + usb_remove_function(c, f_ecm); +err_func_ecm: + usb_put_function(f_ecm); + return ret; } -static int cdc_config_register(struct usb_composite_dev *cdev) +static __ref int cdc_config_register(struct usb_composite_dev *cdev) { static struct usb_configuration config = { .bConfigurationValue = MULTI_CDC_CONFIG_NUM, @@ -232,7 +310,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev) #else -static int cdc_config_register(struct usb_composite_dev *cdev) +static __ref int cdc_config_register(struct usb_composite_dev *cdev) { return 0; } @@ -243,74 +321,175 @@ static int cdc_config_register(struct usb_composite_dev *cdev) /****************************** Gadget Bind ******************************/ - static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; +#ifdef CONFIG_USB_G_MULTI_CDC + struct f_ecm_opts *ecm_opts; +#endif +#ifdef USB_ETH_RNDIS + struct f_rndis_opts *rndis_opts; +#endif + struct fsg_opts *fsg_opts; + struct fsg_config config; int status; if (!can_support_ecm(cdev->gadget)) { dev_err(&gadget->dev, "controller '%s' not usable\n", - gadget->name); + gadget->name); return -EINVAL; } - /* set up network link layer */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; +#ifdef CONFIG_USB_G_MULTI_CDC + 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); +#endif + +#ifdef USB_ETH_RNDIS + fi_rndis = usb_get_function_instance("rndis"); + if (IS_ERR(fi_rndis)) { + status = PTR_ERR(fi_rndis); + goto fail; + } + + rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst); + + gether_set_qmult(rndis_opts->net, qmult); + if (!gether_set_host_addr(rndis_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(rndis_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); +#endif + +#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS) + /* + * If both ecm and rndis are selected then: + * 1) rndis borrows the net interface from ecm + * 2) since the interface is shared it must not be bound + * twice - in ecm's _and_ rndis' binds, so do it here. + */ + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + if (status) + goto fail0; + + rndis_borrow_net(fi_rndis, ecm_opts->net); + ecm_opts->bound = true; +#endif /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); - if (status < 0) + fi_acm = usb_get_function_instance("acm"); + if (IS_ERR(fi_acm)) { + status = PTR_ERR(fi_acm); goto fail0; + } /* set up mass storage function */ - { - void *retp; - retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); - if (IS_ERR(retp)) { - status = PTR_ERR(retp); - goto fail1; - } + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) { + status = PTR_ERR(fi_msg); + goto fail1; } + fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); + fsg_opts = fsg_opts_from_func_inst(fi_msg); + + fsg_opts->no_configfs = true; + status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers); + if (status) + goto fail2; + + status = fsg_common_set_nluns(fsg_opts->common, config.nluns); + if (status) + goto fail_set_nluns; + + status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall); + if (status) + goto fail_set_cdev; + + fsg_common_set_sysfs(fsg_opts->common, true); + status = fsg_common_create_luns(fsg_opts->common, &config); + if (status) + goto fail_set_cdev; + + fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name, + config.product_name); /* allocate string IDs */ status = usb_string_ids_tab(cdev, strings_dev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register configurations */ status = rndis_config_register(cdev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; status = cdc_config_register(cdev); if (unlikely(status < 0)) - goto fail2; + goto fail_string_ids; usb_composite_overwrite_options(cdev, &coverwrite); /* we're done */ dev_info(&gadget->dev, DRIVER_DESC "\n"); - fsg_common_put(&fsg_common); return 0; /* error recovery */ +fail_string_ids: + fsg_common_remove_luns(fsg_opts->common); +fail_set_cdev: + fsg_common_free_luns(fsg_opts->common); +fail_set_nluns: + fsg_common_free_buffers(fsg_opts->common); fail2: - fsg_common_put(&fsg_common); + usb_put_function_instance(fi_msg); fail1: - gserial_cleanup(); + usb_put_function_instance(fi_acm); fail0: - gether_cleanup(); +#ifdef USB_ETH_RNDIS + usb_put_function_instance(fi_rndis); +fail: +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function_instance(fi_ecm); +#endif return status; } static int __exit multi_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); - gether_cleanup(); +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function(f_msg_multi); +#endif +#ifdef USB_ETH_RNDIS + usb_put_function(f_msg_rndis); +#endif + usb_put_function_instance(fi_msg); +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function(f_acm_multi); +#endif +#ifdef USB_ETH_RNDIS + usb_put_function(f_acm_rndis); +#endif + usb_put_function_instance(fi_acm); +#ifdef USB_ETH_RNDIS + usb_put_function(f_rndis); + usb_put_function_instance(fi_rndis); +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + usb_put_function(f_ecm); + usb_put_function_instance(fi_ecm); +#endif return 0; } diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index b5cea273c95..16248711c15 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -15,7 +15,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/notifier.h> @@ -30,9 +29,6 @@ #include <linux/platform_device.h> #include <linux/platform_data/mv_usb.h> #include <linux/clk.h> -#include <asm/system.h> -#include <asm/unaligned.h> -#include <asm/byteorder.h> #include "mv_u3d.h" @@ -125,7 +121,7 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, struct mv_u3d_trb *curr_trb; dma_addr_t cur_deq_lo; struct mv_u3d_ep_context *curr_ep_context; - int trb_complete, actual, remaining_length; + int trb_complete, actual, remaining_length = 0; int direction, ep_num; int retval = 0; u32 tmp, status, length; @@ -189,6 +185,8 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, */ static void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d; @@ -299,10 +297,8 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, u3d = req->ep->u3d; trb = kzalloc(sizeof(*trb), GFP_ATOMIC); - if (!trb) { - dev_err(u3d->dev, "%s, trb alloc fail\n", __func__); + if (!trb) return NULL; - } /* * Be careful that no _GFP_HIGHMEM is set, @@ -311,6 +307,7 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, */ trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); if (!trb_hw) { + kfree(trb); dev_err(u3d->dev, "%s, dma_pool_alloc fail\n", __func__); return NULL; @@ -447,16 +444,12 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req) trb_num++; trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC); - if (!trb) { - dev_err(u3d->dev, - "%s, trb alloc fail\n", __func__); + if (!trb) return -ENOMEM; - } trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); if (!trb_hw) { - dev_err(u3d->dev, - "%s, trb_hw alloc fail\n", __func__); + kfree(trb); return -ENOMEM; } @@ -646,6 +639,7 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep) struct mv_u3d_ep *ep; struct mv_u3d_ep_context *ep_context; u32 epxcr, direction; + unsigned long flags; if (!_ep) return -EINVAL; @@ -662,7 +656,9 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep) direction = mv_u3d_ep_dir(ep); /* nuke all pending requests (does flush) */ + spin_lock_irqsave(&u3d->lock, flags); mv_u3d_nuke(ep, -ESHUTDOWN); + spin_unlock_irqrestore(&u3d->lock, flags); /* Disable the endpoint for Rx or Tx and reset the endpoint type */ if (direction == MV_U3D_EP_DIR_OUT) { @@ -812,19 +808,19 @@ mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return 0; } - dev_dbg(u3d->dev, "%s: %s, req: 0x%x\n", - __func__, _ep->name, (u32)req); + dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n", + __func__, _ep->name, req); /* catch various bogus parameters */ if (!req->req.complete || !req->req.buf || !list_empty(&req->queue)) { dev_err(u3d->dev, - "%s, bad params, _req: 0x%x," - "req->req.complete: 0x%x, req->req.buf: 0x%x," + "%s, bad params, _req: 0x%p," + "req->req.complete: 0x%p, req->req.buf: 0x%p," "list_empty: 0x%x\n", - __func__, (u32)_req, - (u32)req->req.complete, (u32)req->req.buf, - (u32)list_empty(&req->queue)); + __func__, _req, + req->req.complete, req->req.buf, + list_empty(&req->queue)); return -EINVAL; } if (unlikely(!ep->ep.desc)) { @@ -905,7 +901,7 @@ static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct mv_u3d_req, queue); /* Point first TRB of next request to the EP context. */ - iowrite32((u32) next_req->trb_head, + iowrite32((unsigned long) next_req->trb_head, &ep_context->trb_addr_lo); } else { struct mv_u3d_ep_context *ep_context; @@ -1110,7 +1106,7 @@ static int mv_u3d_controller_reset(struct mv_u3d *u3d) static int mv_u3d_enable(struct mv_u3d *u3d) { - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); int retval; if (u3d->active) @@ -1139,7 +1135,7 @@ static int mv_u3d_enable(struct mv_u3d *u3d) static void mv_u3d_disable(struct mv_u3d *u3d) { - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); if (u3d->clock_gating && u3d->active) { dev_dbg(u3d->dev, "disable u3d\n"); if (pdata->phy_deinit) @@ -1247,7 +1243,7 @@ static int mv_u3d_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); unsigned long flags; if (u3d->driver) @@ -1264,7 +1260,6 @@ static int mv_u3d_start(struct usb_gadget *g, /* hook up the driver ... */ driver->driver.bus = NULL; u3d->driver = driver; - u3d->gadget.dev.driver = &driver->driver; u3d->ep0_dir = USB_DIR_OUT; @@ -1279,7 +1274,7 @@ static int mv_u3d_stop(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = u3d->dev->platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); unsigned long flags; u3d->vbus_valid_detect = 0; @@ -1302,7 +1297,6 @@ static int mv_u3d_stop(struct usb_gadget *g, spin_unlock_irqrestore(&u3d->lock, flags); - u3d->gadget.dev.driver = NULL; u3d->driver = NULL; return 0; @@ -1334,7 +1328,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) ep->ep.name = ep->name; ep->ep.ops = &mv_u3d_ep_ops; ep->wedge = 0; - ep->ep.maxpacket = MV_U3D_EP0_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE); ep->ep_num = 0; ep->ep.desc = &mv_u3d_ep0_desc; INIT_LIST_HEAD(&ep->queue); @@ -1359,7 +1353,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) ep->ep.name = ep->name; ep->ep.ops = &mv_u3d_ep_ops; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep_num = i / 2; INIT_LIST_HEAD(&ep->queue); @@ -1525,6 +1519,8 @@ static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num, struct usb_ctrlrequest *setup) + __releases(&u3c->lock) + __acquires(&u3c->lock) { bool delegate = false; @@ -1758,11 +1754,6 @@ static irqreturn_t mv_u3d_irq(int irq, void *dev) return IRQ_HANDLED; } -static void mv_u3d_gadget_release(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); -} - static int mv_u3d_remove(struct platform_device *dev) { struct mv_u3d *u3d = platform_get_drvdata(dev); @@ -1782,7 +1773,7 @@ static int mv_u3d_remove(struct platform_device *dev) kfree(u3d->eps); if (u3d->irq) - free_irq(u3d->irq, &dev->dev); + free_irq(u3d->irq, u3d); if (u3d->cap_regs) iounmap(u3d->cap_regs); @@ -1792,10 +1783,6 @@ static int mv_u3d_remove(struct platform_device *dev) clk_put(u3d->clk); - device_unregister(&u3d->gadget.dev); - - platform_set_drvdata(dev, NULL); - kfree(u3d); return 0; @@ -1804,12 +1791,12 @@ static int mv_u3d_remove(struct platform_device *dev) static int mv_u3d_probe(struct platform_device *dev) { struct mv_u3d *u3d = NULL; - struct mv_usb_platform_data *pdata = dev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); int retval = 0; struct resource *r; size_t size; - if (!dev->dev.platform_data) { + if (!dev_get_platdata(&dev->dev)) { dev_err(&dev->dev, "missing platform_data\n"); retval = -ENODEV; goto err_pdata; @@ -1817,7 +1804,6 @@ static int mv_u3d_probe(struct platform_device *dev) u3d = kzalloc(sizeof(*u3d), GFP_KERNEL); if (!u3d) { - dev_err(&dev->dev, "failed to allocate memory for u3d\n"); retval = -ENOMEM; goto err_alloc_private; } @@ -1829,7 +1815,7 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->dev = &dev->dev; u3d->vbus = pdata->vbus; - u3d->clk = clk_get(&dev->dev, pdata->clkname[0]); + u3d->clk = clk_get(&dev->dev, NULL); if (IS_ERR(u3d->clk)) { retval = PTR_ERR(u3d->clk); goto err_get_clk; @@ -1849,8 +1835,9 @@ static int mv_u3d_probe(struct platform_device *dev) retval = -EBUSY; goto err_map_cap_regs; } else { - dev_dbg(&dev->dev, "cap_regs address: 0x%x/0x%x\n", - (unsigned int)r->start, (unsigned int)u3d->cap_regs); + dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n", + (unsigned long) r->start, + (unsigned long) u3d->cap_regs); } /* we will access controller register, so enable the u3d controller */ @@ -1864,10 +1851,10 @@ static int mv_u3d_probe(struct platform_device *dev) } } - u3d->op_regs = (struct mv_u3d_op_regs __iomem *)((u32)u3d->cap_regs + u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs + MV_U3D_USB3_OP_REGS_OFFSET); - u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)((u32)u3d->cap_regs + u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs + ioread32(&u3d->cap_regs->vuoff)); u3d->max_eps = 16; @@ -1910,7 +1897,6 @@ static int mv_u3d_probe(struct platform_device *dev) size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2; u3d->eps = kzalloc(size, GFP_KERNEL); if (!u3d->eps) { - dev_err(&dev->dev, "allocate ep memory failed\n"); retval = -ENOMEM; goto err_alloc_eps; } @@ -1918,7 +1904,6 @@ static int mv_u3d_probe(struct platform_device *dev) /* initialize ep0 status request structure */ u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL); if (!u3d->status_req) { - dev_err(&dev->dev, "allocate status_req memory failed\n"); retval = -ENOMEM; goto err_alloc_status_req; } @@ -1942,7 +1927,7 @@ static int mv_u3d_probe(struct platform_device *dev) } u3d->irq = r->start; if (request_irq(u3d->irq, mv_u3d_irq, - IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) { + IRQF_SHARED, driver_name, u3d)) { u3d->irq = 0; dev_err(&dev->dev, "Request irq %d for u3d failed\n", u3d->irq); @@ -1957,16 +1942,8 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&u3d->gadget.dev, "gadget"); - u3d->gadget.dev.parent = &dev->dev; - u3d->gadget.dev.dma_mask = dev->dev.dma_mask; - u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ - retval = device_register(&u3d->gadget.dev); - if (retval) - goto err_register_gadget_device; - mv_u3d_eps_init(u3d); /* external vbus detection */ @@ -1991,9 +1968,7 @@ static int mv_u3d_probe(struct platform_device *dev) return 0; err_unregister: - device_unregister(&u3d->gadget.dev); -err_register_gadget_device: - free_irq(u3d->irq, &dev->dev); + free_irq(u3d->irq, u3d); err_request_irq: err_get_irq: kfree(u3d->status_req); @@ -2014,14 +1989,13 @@ 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: return retval; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mv_u3d_suspend(struct device *dev) { struct mv_u3d *u3d = dev_get_drvdata(dev); @@ -2064,13 +2038,13 @@ static int mv_u3d_resume(struct device *dev) return 0; } - -SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); #endif +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); @@ -2080,14 +2054,12 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove = __exit_p(mv_u3d_remove), + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .owner = THIS_MODULE, .name = "mv-u3d", -#ifdef CONFIG_PM .pm = &mv_u3d_pm_ops, -#endif }, }; diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 9073436d8b2..be77f207dba 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -222,8 +222,7 @@ struct mv_udc { struct mv_usb_platform_data *pdata; /* some SOC has mutiple clock sources for USB*/ - unsigned int clknum; - struct clk *clk[0]; + struct clk *clk; }; /* endpoint data structure */ diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 6e8b1272ebc..fcff3a571b4 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -61,9 +60,6 @@ static DECLARE_COMPLETION(release_done); static const char driver_name[] = "mv_udc"; static const char driver_desc[] = DRIVER_DESC; -/* controller device global variable */ -static struct mv_udc *the_controller; - static void nuke(struct mv_ep *ep, int status); static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); @@ -215,6 +211,8 @@ static int process_ep_req(struct mv_udc *udc, int index, * request is still in progress. */ static void done(struct mv_ep *ep, struct mv_req *req, int status) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { struct mv_udc *udc = NULL; unsigned char stopped = ep->stopped; @@ -240,18 +238,7 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status) dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma); } - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); + usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); if (status && (status != -ESHUTDOWN)) dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u", @@ -735,21 +722,9 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->ep = ep; /* map virtual address to hardware */ - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, - req->req.length, ep_dir(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 1; - } else { - dma_sync_single_for_device(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ep_dir(ep) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } + retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep)); + if (retval) + return retval; req->req.status = -EINPROGRESS; req->req.actual = 0; @@ -783,18 +758,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return 0; err_unmap_dma: - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); + usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep)); return retval; } @@ -1009,18 +973,12 @@ static struct usb_ep_ops mv_ep_ops = { static void udc_clock_enable(struct mv_udc *udc) { - unsigned int i; - - for (i = 0; i < udc->clknum; i++) - clk_prepare_enable(udc->clk[i]); + clk_prepare_enable(udc->clk); } static void udc_clock_disable(struct mv_udc *udc) { - unsigned int i; - - for (i = 0; i < udc->clknum; i++) - clk_disable_unprepare(udc->clk[i]); + clk_disable_unprepare(udc->clk); } static void udc_stop(struct mv_udc *udc) @@ -1268,9 +1226,8 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) return retval; } -static int mv_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int mv_udc_stop(struct usb_gadget_driver *driver); +static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *); +static int mv_udc_stop(struct usb_gadget *, struct usb_gadget_driver *); /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops mv_ops = { @@ -1285,8 +1242,8 @@ static const struct usb_gadget_ops mv_ops = { /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = mv_udc_pullup, - .start = mv_udc_start, - .stop = mv_udc_stop, + .udc_start = mv_udc_start, + .udc_stop = mv_udc_stop, }; static int eps_init(struct mv_udc *udc) @@ -1303,7 +1260,7 @@ static int eps_init(struct mv_udc *udc) ep->ep.ops = &mv_ep_ops; ep->wedge = 0; ep->stopped = 0; - ep->ep.maxpacket = EP0_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE); ep->ep_num = 0; ep->ep.desc = &mv_ep0_desc; INIT_LIST_HEAD(&ep->queue); @@ -1326,7 +1283,7 @@ static int eps_init(struct mv_udc *udc) ep->ep.ops = &mv_ep_ops; ep->stopped = 0; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep_num = i / 2; INIT_LIST_HEAD(&ep->queue); @@ -1373,15 +1330,14 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver) } } -static int mv_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int mv_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; int retval = 0; unsigned long flags; - if (!udc) - return -ENODEV; + udc = container_of(gadget, struct mv_udc, gadget); if (udc->driver) return -EBUSY; @@ -1391,7 +1347,6 @@ static int mv_udc_start(struct usb_gadget_driver *driver, /* hook up the driver ... */ driver->driver.bus = NULL; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; @@ -1399,26 +1354,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&udc->lock, flags); - retval = bind(&udc->gadget, driver); - if (retval) { - dev_err(&udc->dev->dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - udc->driver = NULL; - udc->gadget.dev.driver = NULL; - return retval; - } - - if (!IS_ERR_OR_NULL(udc->transceiver)) { + if (udc->transceiver) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { dev_err(&udc->dev->dev, "unable to register peripheral to otg\n"); - if (driver->unbind) { - driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; - udc->driver = NULL; - } + udc->driver = NULL; return retval; } } @@ -1433,13 +1375,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver, return 0; } -static int mv_udc_stop(struct usb_gadget_driver *driver) +static int mv_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; unsigned long flags; - if (!udc) - return -ENODEV; + udc = container_of(gadget, struct mv_udc, gadget); spin_lock_irqsave(&udc->lock, flags); @@ -1454,8 +1396,6 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&udc->lock, flags); /* unbind gadget driver */ - driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; udc->driver = NULL; return 0; @@ -1472,10 +1412,13 @@ static void mv_set_ptc(struct mv_udc *udc, u32 mode) static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) { - struct mv_udc *udc = the_controller; + struct mv_ep *mvep = container_of(ep, struct mv_ep, ep); struct mv_req *req = container_of(_req, struct mv_req, req); + struct mv_udc *udc; unsigned long flags; + udc = mvep->udc; + dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode); spin_lock_irqsave(&udc->lock, flags); @@ -1543,14 +1486,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) return 0; out: - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - ((ep_dir(ep) == EP_DIR_IN) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE)); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); return retval; } @@ -1710,6 +1646,8 @@ out: static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, struct usb_ctrlrequest *setup) + __releases(&ep->udc->lock) + __acquires(&ep->udc->lock) { bool delegate = false; @@ -1906,7 +1844,7 @@ static void irq_process_tr_complete(struct mv_udc *udc) } } -void irq_process_reset(struct mv_udc *udc) +static void irq_process_reset(struct mv_udc *udc) { u32 tmp; unsigned int loops; @@ -2123,15 +2061,18 @@ static void mv_udc_vbus_work(struct work_struct *work) /* release device structure */ static void gadget_release(struct device *_dev) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; + + udc = dev_get_drvdata(_dev); complete(udc->done); } -static int mv_udc_remove(struct platform_device *dev) +static int mv_udc_remove(struct platform_device *pdev) { - struct mv_udc *udc = the_controller; - int clk_i; + struct mv_udc *udc; + + udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); @@ -2140,129 +2081,95 @@ static int mv_udc_remove(struct platform_device *dev) destroy_workqueue(udc->qwork); } - /* - * If we have transceiver inited, - * then vbus irq will not be requested in udc driver. - */ - if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) - free_irq(udc->pdata->vbus->irq, &dev->dev); - /* free memory allocated in probe */ if (udc->dtd_pool) dma_pool_destroy(udc->dtd_pool); if (udc->ep_dqh) - dma_free_coherent(&dev->dev, udc->ep_dqh_size, + dma_free_coherent(&pdev->dev, udc->ep_dqh_size, udc->ep_dqh, udc->ep_dqh_dma); - kfree(udc->eps); - - if (udc->irq) - free_irq(udc->irq, &dev->dev); - mv_udc_disable(udc); - if (udc->cap_regs) - iounmap(udc->cap_regs); - - if (udc->phy_regs) - iounmap(udc->phy_regs); - - if (udc->status_req) { - kfree(udc->status_req->req.buf); - kfree(udc->status_req); - } - - for (clk_i = 0; clk_i <= udc->clknum; clk_i++) - clk_put(udc->clk[clk_i]); - - device_unregister(&udc->gadget.dev); - /* free dev, wait for the release() finished */ wait_for_completion(udc->done); - kfree(udc); - - the_controller = NULL; return 0; } -static int mv_udc_probe(struct platform_device *dev) +static int mv_udc_probe(struct platform_device *pdev) { - struct mv_usb_platform_data *pdata = dev->dev.platform_data; + struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct mv_udc *udc; int retval = 0; - int clk_i = 0; struct resource *r; size_t size; if (pdata == NULL) { - dev_err(&dev->dev, "missing platform_data\n"); + dev_err(&pdev->dev, "missing platform_data\n"); return -ENODEV; } - size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum; - udc = kzalloc(size, GFP_KERNEL); + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); if (udc == NULL) { - dev_err(&dev->dev, "failed to allocate memory for udc\n"); + dev_err(&pdev->dev, "failed to allocate memory for udc\n"); return -ENOMEM; } - the_controller = udc; udc->done = &release_done; - udc->pdata = dev->dev.platform_data; + udc->pdata = dev_get_platdata(&pdev->dev); spin_lock_init(&udc->lock); - udc->dev = dev; + udc->dev = pdev; -#ifdef CONFIG_USB_OTG_UTILS - if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); -#endif + if (pdata->mode == MV_USB_MODE_OTG) { + udc->transceiver = devm_usb_get_phy(&pdev->dev, + USB_PHY_TYPE_USB2); + if (IS_ERR(udc->transceiver)) { + retval = PTR_ERR(udc->transceiver); + + if (retval == -ENXIO) + return retval; - udc->clknum = pdata->clknum; - for (clk_i = 0; clk_i < udc->clknum; clk_i++) { - udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]); - if (IS_ERR(udc->clk[clk_i])) { - retval = PTR_ERR(udc->clk[clk_i]); - goto err_put_clk; + udc->transceiver = NULL; + return -EPROBE_DEFER; } } + /* udc only have one sysclk. */ + udc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(udc->clk)) + return PTR_ERR(udc->clk); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); if (r == NULL) { - dev_err(&dev->dev, "no I/O memory resource defined\n"); - retval = -ENODEV; - goto err_put_clk; + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + return -ENODEV; } udc->cap_regs = (struct mv_cap_regs __iomem *) - ioremap(r->start, resource_size(r)); + devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (udc->cap_regs == NULL) { - dev_err(&dev->dev, "failed to map I/O memory\n"); - retval = -EBUSY; - goto err_put_clk; + dev_err(&pdev->dev, "failed to map I/O memory\n"); + return -EBUSY; } r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); if (r == NULL) { - dev_err(&dev->dev, "no phy I/O memory resource defined\n"); - retval = -ENODEV; - goto err_iounmap_capreg; + dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); + return -ENODEV; } udc->phy_regs = ioremap(r->start, resource_size(r)); if (udc->phy_regs == NULL) { - dev_err(&dev->dev, "failed to map phy I/O memory\n"); - retval = -EBUSY; - goto err_iounmap_capreg; + dev_err(&pdev->dev, "failed to map phy I/O memory\n"); + return -EBUSY; } /* we will acces controller register, so enable the clk */ retval = mv_udc_enable_internal(udc); if (retval) - goto err_iounmap_phyreg; + return retval; udc->op_regs = (struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs @@ -2279,11 +2186,11 @@ static int mv_udc_probe(struct platform_device *dev) size = udc->max_eps * sizeof(struct mv_dqh) *2; size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); - udc->ep_dqh = dma_alloc_coherent(&dev->dev, size, + udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size, &udc->ep_dqh_dma, GFP_KERNEL); if (udc->ep_dqh == NULL) { - dev_err(&dev->dev, "allocate dQH memory failed\n"); + dev_err(&pdev->dev, "allocate dQH memory failed\n"); retval = -ENOMEM; goto err_disable_clock; } @@ -2291,7 +2198,7 @@ static int mv_udc_probe(struct platform_device *dev) /* create dTD dma_pool resource */ udc->dtd_pool = dma_pool_create("mv_dtd", - &dev->dev, + &pdev->dev, sizeof(struct mv_dtd), DTD_ALIGNMENT, DMA_BOUNDARY); @@ -2302,19 +2209,20 @@ static int mv_udc_probe(struct platform_device *dev) } size = udc->max_eps * sizeof(struct mv_ep) *2; - udc->eps = kzalloc(size, GFP_KERNEL); + udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); if (udc->eps == NULL) { - dev_err(&dev->dev, "allocate ep memory failed\n"); + dev_err(&pdev->dev, "allocate ep memory failed\n"); retval = -ENOMEM; goto err_destroy_dma; } /* initialize ep0 status request structure */ - udc->status_req = kzalloc(sizeof(struct mv_req), GFP_KERNEL); + udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req), + GFP_KERNEL); if (!udc->status_req) { - dev_err(&dev->dev, "allocate status_req memory failed\n"); + dev_err(&pdev->dev, "allocate status_req memory failed\n"); retval = -ENOMEM; - goto err_free_eps; + goto err_destroy_dma; } INIT_LIST_HEAD(&udc->status_req->queue); @@ -2329,17 +2237,17 @@ static int mv_udc_probe(struct platform_device *dev) r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0); if (r == NULL) { - dev_err(&dev->dev, "no IRQ resource defined\n"); + dev_err(&pdev->dev, "no IRQ resource defined\n"); retval = -ENODEV; - goto err_free_status_req; + goto err_destroy_dma; } udc->irq = r->start; - if (request_irq(udc->irq, mv_udc_irq, + if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq, IRQF_SHARED, driver_name, udc)) { - dev_err(&dev->dev, "Request irq %d for UDC failed\n", + dev_err(&pdev->dev, "Request irq %d for UDC failed\n", udc->irq); retval = -ENODEV; - goto err_free_status_req; + goto err_destroy_dma; } /* initialize gadget structure */ @@ -2350,27 +2258,20 @@ static int mv_udc_probe(struct platform_device *dev) udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.parent = &dev->dev; - udc->gadget.dev.dma_mask = dev->dev.dma_mask; - udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ - retval = device_register(&udc->gadget.dev); - if (retval) - goto err_free_irq; - eps_init(udc); /* VBUS detect: we can disable/enable clock on demand.*/ - if (!IS_ERR_OR_NULL(udc->transceiver)) + if (udc->transceiver) udc->clock_gating = 1; else if (pdata->vbus) { udc->clock_gating = 1; - retval = request_threaded_irq(pdata->vbus->irq, NULL, + retval = devm_request_threaded_irq(&pdev->dev, + pdata->vbus->irq, NULL, mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); if (retval) { - dev_info(&dev->dev, + dev_info(&pdev->dev, "Can not request irq for VBUS, " "disable clock gating\n"); udc->clock_gating = 0; @@ -2378,9 +2279,9 @@ static int mv_udc_probe(struct platform_device *dev) udc->qwork = create_singlethread_workqueue("mv_udc_queue"); if (!udc->qwork) { - dev_err(&dev->dev, "cannot create workqueue\n"); + dev_err(&pdev->dev, "cannot create workqueue\n"); retval = -ENOMEM; - goto err_unregister; + goto err_destroy_dma; } INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); @@ -2396,53 +2297,39 @@ static int mv_udc_probe(struct platform_device *dev) else udc->vbus_active = 1; - retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + gadget_release); if (retval) - goto err_unregister; + goto err_create_workqueue; - dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n", + platform_set_drvdata(pdev, udc); + dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n", udc->clock_gating ? "with" : "without"); return 0; -err_unregister: - if (udc->pdata && udc->pdata->vbus - && udc->clock_gating && IS_ERR_OR_NULL(udc->transceiver)) - free_irq(pdata->vbus->irq, &dev->dev); - device_unregister(&udc->gadget.dev); -err_free_irq: - free_irq(udc->irq, &dev->dev); -err_free_status_req: - kfree(udc->status_req->req.buf); - kfree(udc->status_req); -err_free_eps: - kfree(udc->eps); +err_create_workqueue: + destroy_workqueue(udc->qwork); err_destroy_dma: dma_pool_destroy(udc->dtd_pool); err_free_dma: - dma_free_coherent(&dev->dev, udc->ep_dqh_size, + dma_free_coherent(&pdev->dev, udc->ep_dqh_size, udc->ep_dqh, udc->ep_dqh_dma); err_disable_clock: mv_udc_disable_internal(udc); -err_iounmap_phyreg: - iounmap(udc->phy_regs); -err_iounmap_capreg: - iounmap(udc->cap_regs); -err_put_clk: - for (clk_i--; clk_i >= 0; clk_i--) - clk_put(udc->clk[clk_i]); - the_controller = NULL; - kfree(udc); + return retval; } #ifdef CONFIG_PM -static int mv_udc_suspend(struct device *_dev) +static int mv_udc_suspend(struct device *dev) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; + + udc = dev_get_drvdata(dev); /* if OTG is enabled, the following will be done in OTG driver*/ - if (!IS_ERR_OR_NULL(udc->transceiver)) + if (udc->transceiver) return 0; if (udc->pdata->vbus && udc->pdata->vbus->poll) @@ -2469,13 +2356,15 @@ static int mv_udc_suspend(struct device *_dev) return 0; } -static int mv_udc_resume(struct device *_dev) +static int mv_udc_resume(struct device *dev) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; int retval; + udc = dev_get_drvdata(dev); + /* if OTG is enabled, the following will be done in OTG driver*/ - if (!IS_ERR_OR_NULL(udc->transceiver)) + if (udc->transceiver) return 0; if (!udc->clock_gating) { @@ -2499,11 +2388,12 @@ static const struct dev_pm_ops mv_udc_pm_ops = { }; #endif -static void mv_udc_shutdown(struct platform_device *dev) +static void mv_udc_shutdown(struct platform_device *pdev) { - struct mv_udc *udc = the_controller; + struct mv_udc *udc; u32 mode; + udc = platform_get_drvdata(pdev); /* reset controller mode to IDLE */ mv_udc_enable(udc); mode = readl(&udc->op_regs->usbmode); @@ -2514,7 +2404,7 @@ static void mv_udc_shutdown(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove = __exit_p(mv_udc_remove), + .remove = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .owner = THIS_MODULE, diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index a22ad9af056..81956feca1b 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,12 +102,15 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -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)) { @@ -124,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); + 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 = { @@ -140,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 */ - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - return status; + 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. @@ -168,13 +182,16 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) return 0; fail: - gether_cleanup(); + usb_put_function_instance(f_ncm_inst); return status; } static int __exit gncm_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(); + 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/net2272.c b/drivers/usb/gadget/net2272.c index d226058e3b8..ca15405583e 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -58,8 +58,7 @@ static const char * const ep_name[] = { "ep-a", "ep-b", "ep-c", }; -#define DMA_ADDR_INVALID (~(dma_addr_t)0) -#ifdef CONFIG_USB_GADGET_NET2272_DMA +#ifdef CONFIG_USB_NET2272_DMA /* * use_dma: the NET2272 can use an external DMA controller. * Note that since there is no generic DMA api, some functions, @@ -267,7 +266,7 @@ static void net2272_ep_reset(struct net2272_ep *ep) ep->desc = NULL; INIT_LIST_HEAD(&ep->queue); - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2272_ep_ops; /* disable irqs, endpoint */ @@ -341,7 +340,6 @@ net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; @@ -913,7 +911,7 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } } } - if (likely(req != 0)) + if (likely(req)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue))) @@ -1186,7 +1184,7 @@ static const struct usb_gadget_ops net2272_ops = { /*---------------------------------------------------------------------------*/ static ssize_t -net2272_show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +registers_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct net2272 *dev; char *next; @@ -1310,7 +1308,7 @@ net2272_show_registers(struct device *_dev, struct device_attribute *attr, char return PAGE_SIZE - size; } -static DEVICE_ATTR(registers, S_IRUGO, net2272_show_registers, NULL); +static DEVICE_ATTR_RO(registers); /*---------------------------------------------------------------------------*/ @@ -1411,7 +1409,7 @@ net2272_usb_reinit(struct net2272 *dev) ep->fifo_size = 64; net2272_ep_reset(ep); } - dev->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); dev->gadget.ep0 = &dev->ep[0].ep; dev->ep[0].stopped = 0; @@ -1467,7 +1465,6 @@ static int net2272_start(struct usb_gadget *_gadget, dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1495,6 +1492,13 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 4; ++i) net2272_dequeue_all(&dev->ep[i]); + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + net2272_usb_reinit(dev); } @@ -1510,7 +1514,6 @@ static int net2272_stop(struct usb_gadget *_gadget, stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name); @@ -1542,7 +1545,7 @@ net2272_handle_dma(struct net2272_ep *ep) | (ep->dev->dma_eot_polarity << EOT_POLARITY) | (ep->dev->dma_dack_polarity << DACK_POLARITY) | (ep->dev->dma_dreq_polarity << DREQ_POLARITY) - | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT)); + | (ep->dma << DMA_ENDPOINT_SELECT)); ep->dev->dma_busy = 0; @@ -1615,7 +1618,7 @@ net2272_handle_ep(struct net2272_ep *ep) ep->irqs++; dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n", - ep->ep.name, stat0, stat1, req ? &req->req : 0); + ep->ep.name, stat0, stat1, req ? &req->req : NULL); net2272_ep_write(ep, EP_STAT0, stat0 & ~((1 << NAK_OUT_PACKETS) @@ -2209,7 +2212,6 @@ net2272_remove(struct net2272 *dev) free_irq(dev->irq, dev); iounmap(dev->base_addr); - device_unregister(&dev->gadget.dev); device_remove_file(dev->dev, &dev_attr_registers); dev_info(dev->dev, "unbind\n"); @@ -2236,10 +2238,6 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&ret->gadget.dev, "gadget"); - ret->gadget.dev.parent = dev; - ret->gadget.dev.dma_mask = dev->dma_mask; - ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; return ret; @@ -2275,14 +2273,12 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) dma_mode_string()); dev_info(dev->dev, "version: %s\n", driver_vers); - ret = device_register(&dev->gadget.dev); - if (ret) - goto err_irq; ret = device_create_file(dev->dev, &dev_attr_registers); if (ret) - goto err_dev_reg; + goto err_irq; - ret = usb_add_gadget_udc(dev->dev, &dev->gadget); + ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget, + net2272_gadget_release); if (ret) goto err_add_udc; @@ -2290,8 +2286,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) err_add_udc: device_remove_file(dev->dev, &dev_attr_registers); - err_dev_reg: - device_unregister(&dev->gadget.dev); err_irq: free_irq(dev->irq, dev); err: diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 708c0b55dcc..300b3a71383 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -65,7 +65,6 @@ #define DRIVER_DESC "PLX NET228x USB Peripheral Controller" #define DRIVER_VERSION "2005 Sept 27" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) #define EP_DONTUSE 13 /* nonzero */ #define USE_RDK_LEDS /* GPIO pins control three LEDs */ @@ -116,6 +115,10 @@ static bool enable_suspend = 0; /* "modprobe net2280 enable_suspend=1" etc */ module_param (enable_suspend, bool, S_IRUGO); +/* force full-speed operation */ +static bool full_speed; +module_param(full_speed, bool, 0444); +MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") @@ -126,7 +129,7 @@ static char *type_string (u8 bmAttributes) case USB_ENDPOINT_XFER_BULK: return "bulk"; case USB_ENDPOINT_XFER_ISOC: return "iso"; case USB_ENDPOINT_XFER_INT: return "intr"; - }; + } return "control"; } #endif @@ -290,7 +293,7 @@ static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) ep->desc = NULL; INIT_LIST_HEAD (&ep->queue); - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2280_ep_ops; /* disable the dma, irqs, endpoint... */ @@ -402,7 +405,6 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) if (!req) return NULL; - req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); /* this dma descriptor may be swapped with the previous dummy */ @@ -416,7 +418,6 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) return NULL; } td->dmacount = 0; /* not VALID */ - td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID); td->dmadesc = td->dmaaddr; req->td = td; } @@ -1423,8 +1424,8 @@ static const struct usb_gadget_ops net2280_ops = { */ /* "function" sysfs attribute */ -static ssize_t -show_function (struct device *_dev, struct device_attribute *attr, char *buf) +static ssize_t function_show(struct device *_dev, struct device_attribute *attr, + char *buf) { struct net2280 *dev = dev_get_drvdata (_dev); @@ -1434,10 +1435,10 @@ show_function (struct device *_dev, struct device_attribute *attr, char *buf) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); } -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR_RO(function); -static ssize_t net2280_show_registers(struct device *_dev, - struct device_attribute *attr, char *buf) +static ssize_t registers_show(struct device *_dev, + struct device_attribute *attr, char *buf) { struct net2280 *dev; char *next; @@ -1589,10 +1590,10 @@ static ssize_t net2280_show_registers(struct device *_dev, return PAGE_SIZE - size; } -static DEVICE_ATTR(registers, S_IRUGO, net2280_show_registers, NULL); +static DEVICE_ATTR_RO(registers); -static ssize_t -show_queues (struct device *_dev, struct device_attribute *attr, char *buf) +static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, + char *buf) { struct net2280 *dev; char *next; @@ -1629,7 +1630,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) val = "intr"; break; default: val = "iso"; break; - }; val; }), + } val; }), usb_endpoint_maxp (d) & 0x1fff, ep->dma ? "dma" : "pio", ep->fifo_size ); @@ -1689,7 +1690,7 @@ done: spin_unlock_irqrestore (&dev->lock, flags); return PAGE_SIZE - size; } -static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL); +static DEVICE_ATTR_RO(queues); #else @@ -1804,9 +1805,9 @@ static void usb_reinit (struct net2280 *dev) ep->regs = &dev->epregs [tmp]; ep_reset (dev->regs, ep); } - dev->ep [0].ep.maxpacket = 64; - dev->ep [5].ep.maxpacket = 64; - dev->ep [6].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64); dev->gadget.ep0 = &dev->ep [0].ep; dev->ep [0].stopped = 0; @@ -1892,13 +1893,16 @@ static int net2280_start(struct usb_gadget *_gadget, dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; retval = device_create_file (&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; retval = device_create_file (&dev->pdev->dev, &dev_attr_queues); if (retval) goto err_func; + /* Enable force-full-speed testing mode, if desired */ + if (full_speed) + writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag); + /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ @@ -1916,8 +1920,6 @@ static int net2280_start(struct usb_gadget *_gadget, err_func: device_remove_file (&dev->pdev->dev, &dev_attr_function); err_unbind: - driver->unbind (&dev->gadget); - dev->gadget.dev.driver = NULL; dev->driver = NULL; return retval; } @@ -1938,6 +1940,13 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 7; i++) nuke (&dev->ep [i]); + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + usb_reinit (dev); } @@ -1953,14 +1962,19 @@ static int net2280_stop(struct usb_gadget *_gadget, stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - dev->gadget.dev.driver = NULL; dev->driver = NULL; net2280_led_active (dev, 0); + + /* Disable full-speed test mode */ + writel(0, &dev->usb->xcvrdiag); + device_remove_file (&dev->pdev->dev, &dev_attr_function); device_remove_file (&dev->pdev->dev, &dev_attr_queues); - DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); + DEBUG(dev, "unregistered driver '%s'\n", + driver ? driver->driver.name : ""); + return 0; } @@ -2054,7 +2068,7 @@ static void handle_ep_small (struct net2280_ep *ep) return; /* manual DMA queue advance after short OUT */ - if (likely (ep->dma != 0)) { + if (likely (ep->dma)) { if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { u32 count; int stopped = ep->stopped; @@ -2312,7 +2326,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device and interface status */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) goto delegate; - if ((e = get_ep_by_addr (dev, w_index)) == 0 + if ((e = get_ep_by_addr (dev, w_index)) == NULL || w_length > 2) goto do_stall; @@ -2340,7 +2354,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == NULL) goto do_stall; if (e->wedged) { VDEBUG(dev, "%s wedged, halt not cleared\n", @@ -2362,7 +2376,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == 0) + if ((e = get_ep_by_addr (dev, w_index)) == NULL) goto do_stall; if (e->ep.name == ep0name) goto do_stall; @@ -2667,9 +2681,7 @@ static void net2280_remove (struct pci_dev *pdev) pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device (pdev); - device_unregister (&dev->gadget.dev); device_remove_file (&pdev->dev, &dev_attr_registers); - pci_set_drvdata (pdev, NULL); INFO (dev, "unbind\n"); } @@ -2699,10 +2711,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ - dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; /* now all the pci goodies ... */ @@ -2784,7 +2792,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) goto done; } td->dmacount = 0; /* not VALID */ - td->dmaaddr = cpu_to_le32 (DMA_ADDR_INVALID); td->dmadesc = td->dmaaddr; dev->ep [i].dummy = td; } @@ -2811,12 +2818,11 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - retval = device_register (&dev->gadget.dev); - if (retval) goto done; retval = device_create_file (&pdev->dev, &dev_attr_registers); if (retval) goto done; - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto done; return 0; @@ -2841,6 +2847,9 @@ static void net2280_shutdown (struct pci_dev *pdev) /* disable the pullup so the host will think we're gone */ writel (0, &dev->usb->usbctl); + + /* Disable full-speed test mode */ + writel(0, &dev->usb->xcvrdiag); } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 661600abace..3ab38616751 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,26 +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. - */ -#include "u_serial.c" -#include "f_acm.c" -#include "f_ecm.c" -#include "f_obex.c" -#include "f_serial.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,42 +84,24 @@ MODULE_AUTHOR("Felipe Balbi"); MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ +static struct usb_function *f_acm_cfg1; +static struct usb_function *f_acm_cfg2; +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 u8 hostaddr[ETH_ALEN]; - -static int __init nokia_bind_config(struct usb_configuration *c) -{ - int status = 0; - - status = phonet_bind_config(c); - if (status) - printk(KERN_DEBUG "could not bind phonet config\n"); - - status = obex_bind_config(c, 0); - if (status) - printk(KERN_DEBUG "could not bind obex config %d\n", 0); - - status = obex_bind_config(c, 1); - if (status) - printk(KERN_DEBUG "could not bind obex config %d\n", 0); - - status = acm_bind_config(c, 2); - if (status) - printk(KERN_DEBUG "could not bind acm config\n"); - - status = ecm_bind_config(c, hostaddr); - if (status) - printk(KERN_DEBUG "could not bind ecm config\n"); - - return status; -} static struct usb_configuration nokia_config_500ma_driver = { .label = "Bus Powered", .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = 250, /* 500mA */ + .MaxPower = 500, }; static struct usb_configuration nokia_config_100ma_driver = { @@ -141,26 +109,126 @@ static struct usb_configuration nokia_config_100ma_driver = { .bConfigurationValue = 2, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, /* 100 mA */ + .MaxPower = 100, }; +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 = -1; + int obex2_stat = -1; + int phonet_stat = -1; + + 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"); + } + + 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"); + } + + 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)) { + 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 = 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) { + f_acm_cfg1 = f_acm; + 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; +} + static int __init nokia_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; int status; - status = gphonet_setup(cdev->gadget); - if (status < 0) - goto err_phonet; - - status = gserial_setup(cdev->gadget, 3); - if (status < 0) - goto err_serial; - - status = gether_setup(cdev->gadget, hostaddr); - if (status < 0) - goto err_ether; - status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto err_usb; @@ -170,40 +238,102 @@ 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)) { + 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_usb; + goto err_ecm_inst; status = usb_add_config(cdev, &nokia_config_100ma_driver, nokia_bind_config); if (status < 0) - goto err_usb; + goto err_put_cfg1; usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME); return 0; +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(); -err_ether: - gserial_cleanup(); -err_serial: - gphonet_cleanup(); -err_phonet: return status; } static int __exit nokia_unbind(struct usb_composite_dev *cdev) { - gphonet_cleanup(); - gserial_cleanup(); - gether_cleanup(); + 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); return 0; } @@ -228,4 +358,3 @@ static void __exit nokia_cleanup(void) usb_composite_unregister(&nokia_driver); } module_exit(nokia_cleanup); - diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 8bfe990caf1..2ae4f6d69f7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -22,7 +22,6 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -62,6 +61,7 @@ #define DRIVER_VERSION "4 October 2004" #define OMAP_DMA_USB_W2FC_TX0 29 +#define OMAP_DMA_USB_W2FC_RX0 26 /* * The OMAP UDC needs _very_ early endpoint setup: before enabling the @@ -1309,19 +1309,20 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on) return 0; } -static int omap_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int omap_udc_stop(struct usb_gadget_driver *driver); +static int omap_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int omap_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); -static struct usb_gadget_ops omap_gadget_ops = { +static const struct usb_gadget_ops omap_gadget_ops = { .get_frame = omap_get_frame, .wakeup = omap_wakeup, .set_selfpowered = omap_set_selfpowered, .vbus_session = omap_vbus_session, .vbus_draw = omap_vbus_draw, .pullup = omap_pullup, - .start = omap_udc_start, - .stop = omap_udc_stop, + .udc_start = omap_udc_start, + .udc_stop = omap_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -2041,28 +2042,15 @@ static inline int machine_without_vbus_sense(void) || cpu_is_omap7xx(); } -static int omap_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int omap_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { int status = -ENODEV; struct omap_ep *ep; unsigned long flags; - /* basic sanity tests */ - if (!udc) - return -ENODEV; - if (!driver - /* FIXME if otg, check: driver->is_otg */ - || driver->max_speed < USB_SPEED_FULL - || !bind || !driver->setup) - return -EINVAL; spin_lock_irqsave(&udc->lock, flags); - if (udc->driver) { - spin_unlock_irqrestore(&udc->lock, flags); - return -EBUSY; - } - /* reset state */ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { ep->irqs = 0; @@ -2078,21 +2066,11 @@ static int omap_udc_start(struct usb_gadget_driver *driver, /* hook up the driver */ driver->driver.bus = NULL; udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); if (udc->dc_clk != NULL) omap_udc_enable_clock(1); - status = bind(&udc->gadget, driver); - if (status) { - DBG("bind to %s --> %d\n", driver->driver.name, status); - udc->gadget.dev.driver = NULL; - udc->driver = NULL; - goto done; - } - DBG("bound to driver %s\n", driver->driver.name); - omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC); /* connect to bus through transceiver */ @@ -2103,7 +2081,6 @@ static int omap_udc_start(struct usb_gadget_driver *driver, ERR("can't bind to transceiver\n"); if (driver->unbind) { driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; udc->driver = NULL; } goto done; @@ -2124,19 +2101,16 @@ static int omap_udc_start(struct usb_gadget_driver *driver, done: if (udc->dc_clk != NULL) omap_udc_enable_clock(0); + return status; } -static int omap_udc_stop(struct usb_gadget_driver *driver) +static int omap_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { unsigned long flags; int status = -ENODEV; - if (!udc) - return -ENODEV; - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; - if (udc->dc_clk != NULL) omap_udc_enable_clock(1); @@ -2152,13 +2126,11 @@ static int omap_udc_stop(struct usb_gadget_driver *driver) udc_quiesce(udc); spin_unlock_irqrestore(&udc->lock, flags); - driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; udc->driver = NULL; if (udc->dc_clk != NULL) omap_udc_enable_clock(0); - DBG("unregistered driver '%s'\n", driver->driver.name); + return status; } @@ -2613,7 +2585,8 @@ omap_ep_setup(char *name, u8 addr, u8 type, ep->ep.name = ep->name; ep->ep.ops = &omap_ep_ops; - ep->ep.maxpacket = ep->maxpacket = maxp; + ep->maxpacket = maxp; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); return buf; @@ -2655,14 +2628,6 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_FULL; udc->gadget.name = driver_name; - - device_initialize(&udc->gadget.dev); - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.release = omap_udc_release; - udc->gadget.dev.parent = &odev->dev; - if (use_dma) - udc->gadget.dev.dma_mask = odev->dev.dma_mask; - udc->transceiver = xceiv; /* ep0 is special; put it right after the SETUP buffer */ @@ -2769,7 +2734,7 @@ static int omap_udc_probe(struct platform_device *pdev) int hmc; struct usb_phy *xceiv = NULL; const char *type = NULL; - struct omap_usb_config *config = pdev->dev.platform_data; + struct omap_usb_config *config = dev_get_platdata(&pdev->dev); struct clk *dc_clk = NULL; struct clk *hhc_clk = NULL; @@ -2936,14 +2901,13 @@ bad_on_1710: } create_proc_file(); - status = device_add(&udc->gadget.dev); + status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + omap_udc_release); if (status) goto cleanup4; - status = usb_add_gadget_udc(&pdev->dev, &udc->gadget); - if (!status) - return status; - /* If fail, fall through */ + return 0; + cleanup4: remove_proc_file(); @@ -3014,7 +2978,6 @@ static int omap_udc_remove(struct platform_device *pdev) release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); - device_unregister(&udc->gadget.dev); wait_for_completion(&done); return 0; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 6490c0040e3..eb8c3bedb57 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -358,7 +358,6 @@ struct pch_udc_dev { prot_stall:1, irq_registered:1, mem_region:1, - registered:1, suspended:1, connected:1, vbus_session:1, @@ -375,6 +374,7 @@ struct pch_udc_dev { struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio; }; +#define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget)) #define PCH_UDC_PCI_BAR 1 #define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808 @@ -384,7 +384,6 @@ struct pch_udc_dev { static const char ep0_string[] = "ep0in"; static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */ -struct pch_udc_dev *pch_udc; /* pointer to device object */ static bool speed_fs; module_param_named(speed_fs, speed_fs, bool, S_IRUGO); MODULE_PARM_DESC(speed_fs, "true for Full speed operation"); @@ -1235,9 +1234,10 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA) return -EOPNOTSUPP; } -static int pch_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int pch_udc_stop(struct usb_gadget_driver *driver); +static int pch_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int pch_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops pch_udc_ops = { .get_frame = pch_udc_pcd_get_frame, .wakeup = pch_udc_pcd_wakeup, @@ -1245,8 +1245,8 @@ static const struct usb_gadget_ops pch_udc_ops = { .pullup = pch_udc_pcd_pullup, .vbus_session = pch_udc_pcd_vbus_session, .vbus_draw = pch_udc_pcd_vbus_draw, - .start = pch_udc_start, - .stop = pch_udc_stop, + .udc_start = pch_udc_start, + .udc_stop = pch_udc_stop, }; /** @@ -1440,6 +1440,8 @@ static void pch_vbus_gpio_free(struct pch_udc_dev *dev) */ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, int status) + __releases(&dev->lock) + __acquires(&dev->lock) { struct pch_udc_dev *dev; unsigned halted = ep->halted; @@ -2381,6 +2383,8 @@ static void pch_udc_svc_control_in(struct pch_udc_dev *dev) * @dev: Reference to the device structure */ static void pch_udc_svc_control_out(struct pch_udc_dev *dev) + __releases(&dev->lock) + __acquires(&dev->lock) { u32 stat; int setup_supported; @@ -2892,12 +2896,12 @@ static void pch_udc_pcd_reinit(struct pch_udc_dev *dev) ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) * UDC_EP_REG_SHIFT; /* need to set ep->ep.maxpacket and set Default Configuration?*/ - ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, UDC_BULK_MAX_PKT_SIZE); list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); INIT_LIST_HEAD(&ep->queue); } - dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IDX].ep, UDC_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IDX].ep, UDC_EP0OUT_MAX_PKT_SIZE); /* remove ep0 in and out from the list. They have own pointer */ list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list); @@ -2981,40 +2985,14 @@ static int init_dma_pools(struct pch_udc_dev *dev) return 0; } -static int pch_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int pch_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct pch_udc_dev *dev = pch_udc; - int retval; - - if (!driver || (driver->max_speed == USB_SPEED_UNKNOWN) || !bind || - !driver->setup || !driver->unbind || !driver->disconnect) { - dev_err(&dev->pdev->dev, - "%s: invalid driver parameter\n", __func__); - return -EINVAL; - } - - if (!dev) - return -ENODEV; + struct pch_udc_dev *dev = to_pch_udc(g); - if (dev->driver) { - dev_err(&dev->pdev->dev, "%s: already bound\n", __func__); - return -EBUSY; - } driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - - /* Invoke the bind routine of the gadget driver */ - retval = bind(&dev->gadget, driver); - if (retval) { - dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n", - __func__, driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } /* get ready for ep0 traffic */ pch_udc_setup_ep0(dev); @@ -3026,30 +3004,20 @@ static int pch_udc_start(struct usb_gadget_driver *driver, return 0; } -static int pch_udc_stop(struct usb_gadget_driver *driver) +static int pch_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct pch_udc_dev *dev = pch_udc; - - if (!dev) - return -ENODEV; - - if (!driver || (driver != dev->driver)) { - dev_err(&dev->pdev->dev, - "%s: invalid driver parameter\n", __func__); - return -EINVAL; - } + struct pch_udc_dev *dev = to_pch_udc(g); pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); /* Assures that there are no pending requests with this driver */ - driver->disconnect(&dev->gadget); - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; dev->driver = NULL; dev->connected = 0; /* set SD */ pch_udc_set_disconnect(dev); + return 0; } @@ -3111,10 +3079,7 @@ static void pch_udc_remove(struct pci_dev *pdev) pci_resource_len(pdev, PCH_UDC_PCI_BAR)); if (dev->active) pci_disable_device(pdev); - if (dev->registered) - device_unregister(&dev->gadget.dev); kfree(dev); - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM @@ -3164,11 +3129,6 @@ static int pch_udc_probe(struct pci_dev *pdev, int retval; struct pch_udc_dev *dev; - /* one udc only */ - if (pch_udc) { - pr_err("%s: already probed\n", __func__); - return -EBUSY; - } /* init */ dev = kzalloc(sizeof *dev, GFP_KERNEL); if (!dev) { @@ -3207,7 +3167,6 @@ static int pch_udc_probe(struct pci_dev *pdev, retval = -ENODEV; goto finished; } - pch_udc = dev; /* initialize the hardware */ if (pch_udc_pcd_init(dev)) { retval = -ENODEV; @@ -3235,21 +3194,13 @@ static int pch_udc_probe(struct pci_dev *pdev, if (retval) goto finished; - dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; - retval = device_register(&dev->gadget.dev); - if (retval) - goto finished; - dev->registered = 1; - /* Put the device in disconnected state till a driver is bound */ pch_udc_set_disconnect(dev); - retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); if (retval) goto finished; return 0; @@ -3259,7 +3210,7 @@ finished: return retval; } -static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = { +static const struct pci_device_id pch_udc_pcidev_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 35bcc83d1e0..6474081dcba 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev) req->length = USB_BUFSIZE; req->complete = rx_complete; + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); if (error) { DBG(dev, "rx submit --> %d\n", error); list_add(&req->list, &dev->rx_reqs); break; - } else { + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) { list_add(&req->list, &dev->rx_reqs_active); } } @@ -688,7 +693,7 @@ static int printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) { struct printer_dev *dev = fd->private_data; - struct inode *inode = fd->f_path.dentry->d_inode; + struct inode *inode = file_inode(fd); unsigned long flags; int tx_list_empty; @@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c) NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); + status = PTR_ERR(dev->pdev); goto fail; } @@ -1157,7 +1163,7 @@ static int __init printer_bind_config(struct usb_configuration *c) usb_gadget_set_selfpowered(gadget); - if (gadget->is_otg) { + if (gadget_is_otg(gadget)) { otg_descriptor.bmAttributes |= USB_OTG_HNP; printer_cfg_driver.descriptors = otg_desc; printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d4ca9f1f7f2..9984437d295 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -24,7 +24,6 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -54,6 +53,7 @@ */ #ifdef CONFIG_ARCH_PXA #include <mach/pxa25x-udc.h> +#include <mach/hardware.h> #endif #ifdef CONFIG_ARCH_LUBBOCK @@ -996,9 +996,10 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } -static int pxa25x_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int pxa25x_stop(struct usb_gadget_driver *driver); +static int pxa25x_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int pxa25x_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops pxa25x_udc_ops = { .get_frame = pxa25x_udc_get_frame, @@ -1006,8 +1007,8 @@ static const struct usb_gadget_ops pxa25x_udc_ops = { .vbus_session = pxa25x_udc_vbus_session, .pullup = pxa25x_udc_pullup, .vbus_draw = pxa25x_udc_vbus_draw, - .start = pxa25x_start, - .stop = pxa25x_stop, + .udc_start = pxa25x_udc_start, + .udc_stop = pxa25x_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -1192,6 +1193,7 @@ static void udc_reinit(struct pxa25x_udc *dev) ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = 0; + usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); } /* the rest was statically initialized, and is read-only */ @@ -1254,58 +1256,25 @@ static void udc_enable (struct pxa25x_udc *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int pxa25x_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int pxa25x_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct pxa25x_udc *dev = the_controller; + struct pxa25x_udc *dev = to_pxa25x(g); int retval; - if (!driver - || driver->max_speed < USB_SPEED_FULL - || !bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; - /* first hook up the driver ... */ dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; dev->pullup = 1; - retval = device_add (&dev->gadget.dev); - if (retval) { -fail: - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = bind(&dev->gadget, driver); - if (retval) { - DMSG("bind to driver %s --> error %d\n", - driver->driver.name, retval); - device_del (&dev->gadget.dev); - goto fail; - } - /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ - DMSG("registered gadget driver '%s'\n", driver->driver.name); - /* connect to bus through transceiver */ if (!IS_ERR_OR_NULL(dev->transceiver)) { retval = otg_set_peripheral(dev->transceiver->otg, &dev->gadget); - if (retval) { - DMSG("can't bind to transceiver\n"); - if (driver->unbind) - driver->unbind(&dev->gadget); + if (retval) goto bind_fail; - } } pullup(dev); @@ -1342,14 +1311,10 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) udc_reinit(dev); } -static int pxa25x_stop(struct usb_gadget_driver *driver) +static int pxa25x_udc_stop(struct usb_gadget*g, + struct usb_gadget_driver *driver) { - struct pxa25x_udc *dev = the_controller; - - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; + struct pxa25x_udc *dev = to_pxa25x(g); local_irq_disable(); dev->pullup = 0; @@ -1360,14 +1325,10 @@ static int pxa25x_stop(struct usb_gadget_driver *driver) if (!IS_ERR_OR_NULL(dev->transceiver)) (void) otg_set_peripheral(dev->transceiver->otg, NULL); - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; dev->driver = NULL; - device_del (&dev->gadget.dev); - - DMSG("unregistered gadget driver '%s'\n", driver->driver.name); dump_state(dev); + return 0; } @@ -2094,12 +2055,14 @@ static struct pxa25x_udc memory = { /* * probe - binds to the platform device */ -static int __init pxa25x_udc_probe(struct platform_device *pdev) +static int pxa25x_udc_probe(struct platform_device *pdev) { struct pxa25x_udc *dev = &memory; int retval, irq; u32 chiprev; + pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); + /* insist on Intel/ARM/XScale */ asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { @@ -2155,7 +2118,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) /* other non-static parts of init */ dev->dev = &pdev->dev; - dev->mach = pdev->dev.platform_data; + dev->mach = dev_get_platdata(&pdev->dev); dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); @@ -2174,10 +2137,6 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev; - device_initialize(&dev->gadget.dev); - dev->gadget.dev.parent = &pdev->dev; - dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - the_controller = dev; platform_set_drvdata(pdev, dev); @@ -2245,14 +2204,14 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev) pullup_off(); } -static int __exit pxa25x_udc_remove(struct platform_device *pdev) +static int pxa25x_udc_remove(struct platform_device *pdev) { struct pxa25x_udc *dev = platform_get_drvdata(pdev); - usb_del_gadget_udc(&dev->gadget); if (dev->driver) return -EBUSY; + usb_del_gadget_udc(&dev->gadget); dev->pullup = 0; pullup(dev); @@ -2278,7 +2237,6 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) dev->transceiver = NULL; } - platform_set_drvdata(pdev, NULL); the_controller = NULL; return 0; } @@ -2337,7 +2295,8 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, - .remove = __exit_p(pxa25x_udc_remove), + .probe = pxa25x_udc_probe, + .remove = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { @@ -2346,18 +2305,7 @@ static struct platform_driver udc_driver = { }, }; -static int __init udc_init(void) -{ - pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); - return platform_driver_probe(&udc_driver, pxa25x_udc_probe); -} -module_init(udc_init); - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); +module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 2eca1e71fec..3fe5931dc21 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -126,6 +126,7 @@ struct pxa25x_udc { struct dentry *debugfs_udc; #endif }; +#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget)) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 2b3b01d5f40..cdf4d678be9 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -24,14 +24,12 @@ #include <linux/gpio.h> #include <linux/slab.h> #include <linux/prefetch.h> - -#include <asm/byteorder.h> -#include <mach/hardware.h> +#include <linux/byteorder/generic.h> +#include <linux/platform_data/pxa2xx_udc.h> #include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -#include <mach/udc.h> #include "pxa27x_udc.h" @@ -611,7 +609,7 @@ static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) * * Find the physical pxa27x ep, and setup its UDCCR */ -static __init void pxa_ep_setup(struct pxa_ep *ep) +static void pxa_ep_setup(struct pxa_ep *ep) { u32 new_udccr; @@ -633,7 +631,7 @@ static __init void pxa_ep_setup(struct pxa_ep *ep) * * Setup all pxa physical endpoints, except ep0 */ -static __init void pxa_eps_setup(struct pxa_udc *dev) +static void pxa_eps_setup(struct pxa_udc *dev) { unsigned int i; @@ -1671,9 +1669,10 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } -static int pxa27x_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int pxa27x_udc_stop(struct usb_gadget_driver *driver); +static int pxa27x_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int pxa27x_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops pxa_udc_ops = { .get_frame = pxa_udc_get_frame, @@ -1681,8 +1680,8 @@ static const struct usb_gadget_ops pxa_udc_ops = { .pullup = pxa_udc_pullup, .vbus_session = pxa_udc_vbus_session, .vbus_draw = pxa_udc_vbus_draw, - .start = pxa27x_udc_start, - .stop = pxa27x_udc_stop, + .udc_start = pxa27x_udc_start, + .udc_stop = pxa27x_udc_stop, }; /** @@ -1717,7 +1716,7 @@ static void udc_disable(struct pxa_udc *udc) * Initializes gadget endpoint list, endpoints locks. No action is taken * on the hardware. */ -static __init void udc_init_data(struct pxa_udc *dev) +static void udc_init_data(struct pxa_udc *dev) { int i; struct pxa_ep *ep; @@ -1738,9 +1737,12 @@ static __init void udc_init_data(struct pxa_udc *dev) } /* USB endpoints init */ - for (i = 1; i < NR_USB_ENDPOINTS; i++) + for (i = 1; i < NR_USB_ENDPOINTS; i++) { list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list, &dev->gadget.ep_list); + usb_ep_set_maxpacket_limit(&dev->udc_usb_ep[i].usb_ep, + dev->udc_usb_ep[i].usb_ep.maxpacket); + } } /** @@ -1802,45 +1804,22 @@ static void udc_enable(struct pxa_udc *udc) * * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise */ -static int pxa27x_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int pxa27x_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct pxa_udc *udc = the_controller; + struct pxa_udc *udc = to_pxa(g); int retval; - if (!driver || driver->max_speed < USB_SPEED_FULL || !bind - || !driver->disconnect || !driver->setup) - return -EINVAL; - if (!udc) - return -ENODEV; - if (udc->driver) - return -EBUSY; - /* first hook up the driver ... */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; dplus_pullup(udc, 1); - retval = device_add(&udc->gadget.dev); - if (retval) { - dev_err(udc->dev, "device_add error %d\n", retval); - goto add_fail; - } - retval = bind(&udc->gadget, driver); - if (retval) { - dev_err(udc->dev, "bind to driver %s --> error %d\n", - driver->driver.name, retval); - goto bind_fail; - } - dev_dbg(udc->dev, "registered gadget driver '%s'\n", - driver->driver.name); - if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, &udc->gadget); if (retval) { dev_err(udc->dev, "can't bind to transceiver\n"); - goto transceiver_fail; + goto fail; } } @@ -1848,14 +1827,8 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver, udc_enable(udc); return 0; -transceiver_fail: - if (driver->unbind) - driver->unbind(&udc->gadget); -bind_fail: - device_del(&udc->gadget.dev); -add_fail: +fail: udc->driver = NULL; - udc->gadget.dev.driver = NULL; return retval; } @@ -1878,9 +1851,6 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) for (i = 0; i < NR_USB_ENDPOINTS; i++) pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep); - - if (driver) - driver->disconnect(&udc->gadget); } /** @@ -1889,26 +1859,17 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) * * Returns 0 if no error, -ENODEV, -EINVAL otherwise */ -static int pxa27x_udc_stop(struct usb_gadget_driver *driver) +static int pxa27x_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct pxa_udc *udc = the_controller; - - if (!udc) - return -ENODEV; - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; + struct pxa_udc *udc = to_pxa(g); stop_activity(udc, driver); udc_disable(udc); dplus_pullup(udc, 0); - driver->unbind(&udc->gadget); udc->driver = NULL; - device_del(&udc->gadget.dev); - dev_info(udc->dev, "unregistered gadget driver '%s'\n", - driver->driver.name); - if (!IS_ERR_OR_NULL(udc->transceiver)) return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; @@ -2450,7 +2411,7 @@ static struct pxa_udc memory = { * Perform basic init : allocates udc clock, creates sysfs files, requests * irq. */ -static int __init pxa_udc_probe(struct platform_device *pdev) +static int pxa_udc_probe(struct platform_device *pdev) { struct resource *regs; struct pxa_udc *udc = &memory; @@ -2464,7 +2425,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) return udc->irq; udc->dev = &pdev->dev; - udc->mach = pdev->dev.platform_data; + udc->mach = dev_get_platdata(&pdev->dev); udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; @@ -2493,9 +2454,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev) goto err_map; } - device_initialize(&udc->gadget.dev); - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = NULL; udc->vbus_sensed = 0; the_controller = udc; @@ -2511,12 +2469,15 @@ static int __init pxa_udc_probe(struct platform_device *pdev) driver_name, udc->irq, retval); goto err_irq; } + retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (retval) goto err_add_udc; pxa_init_debugfs(udc); + return 0; + err_add_udc: free_irq(udc->irq, udc); err_irq: @@ -2532,7 +2493,7 @@ err_clk: * pxa_udc_remove - removes the udc device driver * @_dev: platform device */ -static int __exit pxa_udc_remove(struct platform_device *_dev) +static int pxa_udc_remove(struct platform_device *_dev) { struct pxa_udc *udc = platform_get_drvdata(_dev); int gpio = udc->mach->gpio_pullup; @@ -2547,7 +2508,6 @@ static int __exit 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); @@ -2650,7 +2610,8 @@ static struct platform_driver udc_driver = { .name = "pxa27x-udc", .owner = THIS_MODULE, }, - .remove = __exit_p(pxa_udc_remove), + .probe = pxa_udc_probe, + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, @@ -2658,22 +2619,7 @@ static struct platform_driver udc_driver = { #endif }; -static int __init udc_init(void) -{ - if (!cpu_is_pxa27x() && !cpu_is_pxa3xx()) - return -ENODEV; - - printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); - return platform_driver_probe(&udc_driver, pxa_udc_probe); -} -module_init(udc_init); - - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} -module_exit(udc_exit); +module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Robert Jarzmik"); diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index 79d81a4b234..28f2b53530f 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -473,6 +473,7 @@ struct pxa_udc { struct dentry *debugfs_eps; #endif }; +#define to_pxa(g) (container_of((g), struct pxa_udc, gadget)) static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget) { diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 5a80751accb..b698a490cc7 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); @@ -1812,7 +1812,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self) return 0; } -static struct usb_gadget_ops r8a66597_gadget_ops = { +static const struct usb_gadget_ops r8a66597_gadget_ops = { .get_frame = r8a66597_get_frame, .udc_start = r8a66597_start, .udc_stop = r8a66597_stop, @@ -1822,7 +1822,7 @@ static 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); @@ -1833,11 +1833,10 @@ static int __exit r8a66597_remove(struct platform_device *pdev) r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { - clk_disable(r8a66597->clk); + clk_disable_unprepare(r8a66597->clk); clk_put(r8a66597->clk); } - device_unregister(&r8a66597->gadget.dev); kfree(r8a66597); return 0; } @@ -1905,27 +1904,17 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); if (r8a66597 == NULL) { ret = -ENOMEM; - dev_err(&pdev->dev, "kzalloc error\n"); goto clean_up; } spin_lock_init(&r8a66597->lock); - dev_set_drvdata(&pdev->dev, r8a66597); - r8a66597->pdata = pdev->dev.platform_data; + platform_set_drvdata(pdev, r8a66597); + r8a66597->pdata = dev_get_platdata(&pdev->dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; - dev_set_name(&r8a66597->gadget.dev, "gadget"); r8a66597->gadget.max_speed = USB_SPEED_HIGH; - r8a66597->gadget.dev.parent = &pdev->dev; - r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; - r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; - ret = device_register(&r8a66597->gadget.dev); - if (ret < 0) { - dev_err(&pdev->dev, "device_register failed\n"); - goto clean_up; - } init_timer(&r8a66597->timer); r8a66597->timer.function = r8a66597_timer; @@ -1939,9 +1928,9 @@ static int __init r8a66597_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(r8a66597->clk); - goto clean_up_dev; + goto clean_up; } - clk_enable(r8a66597->clk); + clk_prepare_enable(r8a66597->clk); } if (r8a66597->pdata->sudmac) { @@ -1974,9 +1963,9 @@ static int __init r8a66597_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = r8a66597_ep_name[i]; ep->ep.ops = &r8a66597_ep_ops; - ep->ep.maxpacket = 512; + usb_ep_set_maxpacket_limit(&ep->ep, 512); } - r8a66597->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&r8a66597->ep[0].ep, 64); r8a66597->ep[0].pipenum = 0; r8a66597->ep[0].fifoaddr = CFIFO; r8a66597->ep[0].fifosel = CFIFOSEL; @@ -1987,8 +1976,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, GFP_KERNEL); - if (r8a66597->ep0_req == NULL) + if (r8a66597->ep0_req == NULL) { + ret = -ENOMEM; goto clean_up3; + } r8a66597->ep0_req->complete = nop_completion; ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget); @@ -2004,11 +1995,9 @@ clean_up3: free_irq(irq, r8a66597); clean_up2: if (r8a66597->pdata->on_chip) { - clk_disable(r8a66597->clk); + clk_disable_unprepare(r8a66597->clk); clk_put(r8a66597->clk); } -clean_up_dev: - device_unregister(&r8a66597->gadget.dev); clean_up: if (r8a66597) { if (r8a66597->sudmac_reg) @@ -2031,21 +2020,10 @@ static struct platform_driver r8a66597_driver = { .name = (char *) udc_name, }, }; -MODULE_ALIAS("platform:r8a66597_udc"); - -static int __init r8a66597_udc_init(void) -{ - return platform_driver_probe(&r8a66597_driver, r8a66597_probe); -} -module_init(r8a66597_udc_init); -static void __exit r8a66597_udc_cleanup(void) -{ - platform_driver_unregister(&r8a66597_driver); -} -module_exit(r8a66597_udc_cleanup); +module_platform_driver_probe(r8a66597_driver, r8a66597_probe); MODULE_DESCRIPTION("R8A66597 USB gadget driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); - +MODULE_ALIAS("platform:r8a66597_udc"); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index e4192b887de..95d2324f697 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -25,7 +25,6 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -36,6 +35,7 @@ #include <asm/byteorder.h> #include <asm/unaligned.h> +#include "u_rndis.h" #undef VERBOSE_DEBUG @@ -761,6 +761,7 @@ int rndis_signal_connect(int configNr) return rndis_indicate_status_msg(configNr, RNDIS_STATUS_MEDIA_CONNECT); } +EXPORT_SYMBOL_GPL(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_GPL(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_GPL(rndis_uninit); void rndis_set_host_mac(int configNr, const u8 *addr) { rndis_per_dev_params[configNr].host_mac = addr; } +EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser @@ -870,6 +874,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return -ENOTSUPP; } +EXPORT_SYMBOL_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(rndis_rm_hdr); #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -1054,7 +1068,7 @@ static int rndis_proc_show(struct seq_file *m, void *v) s = "RNDIS_INITIALIZED"; break; case RNDIS_DATA_INITIALIZED: s = "RNDIS_DATA_INITIALIZED"; break; - }; s; }), + } s; }), param->medium, (param->media_state) ? 0 : param->speed*100, (param->media_state) ? "disconnected" : "connected", @@ -1065,7 +1079,7 @@ static int rndis_proc_show(struct seq_file *m, void *v) static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - rndis_params *p = PDE(file->f_path.dentry->d_inode)->data; + rndis_params *p = PDE_DATA(file_inode(file)); u32 speed = 0; int i, fl_speed = 0; @@ -1109,7 +1123,7 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, static int rndis_proc_open(struct inode *inode, struct file *file) { - return single_open(file, rndis_proc_show, PDE(inode)->data); + return single_open(file, rndis_proc_show, PDE_DATA(inode)); } static const struct file_operations rndis_proc_fops = { @@ -1173,3 +1187,4 @@ void rndis_exit(void) } #endif } + diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 0647f2f34e8..0f4abb4c377 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/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c deleted file mode 100644 index 439c3f972f8..00000000000 --- a/drivers/usb/gadget/s3c-hsotg.c +++ /dev/null @@ -1,3720 +0,0 @@ -/** - * linux/drivers/usb/gadget/s3c-hsotg.c - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * http://armlinux.simtec.co.uk/ - * - * S3C USB2.0 High-speed / OtG driver - * - * 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. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/dma-mapping.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> - -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/platform_data/s3c-hsotg.h> - -#include <mach/map.h> - -#include "s3c-hsotg.h" - -#define DMA_ADDR_INVALID (~((dma_addr_t)0)) - -static const char * const s3c_hsotg_supply_names[] = { - "vusb_d", /* digital USB supply, 1.2V */ - "vusb_a", /* analog USB supply, 1.1V */ -}; - -/* - * EP0_MPS_LIMIT - * - * Unfortunately there seems to be a limit of the amount of data that can - * be transferred by IN transactions on EP0. This is either 127 bytes or 3 - * packets (which practically means 1 packet and 63 bytes of data) when the - * MPS is set to 64. - * - * This means if we are wanting to move >127 bytes of data, we need to - * split the transactions up, but just doing one packet at a time does - * not work (this may be an implicit DATA0 PID on first packet of the - * transaction) and doing 2 packets is outside the controller's limits. - * - * If we try to lower the MPS size for EP0, then no transfers work properly - * for EP0, and the system will fail basic enumeration. As no cause for this - * has currently been found, we cannot support any large IN transfers for - * EP0. - */ -#define EP0_MPS_LIMIT 64 - -struct s3c_hsotg; -struct s3c_hsotg_req; - -/** - * struct s3c_hsotg_ep - driver endpoint definition. - * @ep: The gadget layer representation of the endpoint. - * @name: The driver generated name for the endpoint. - * @queue: Queue of requests for this endpoint. - * @parent: Reference back to the parent device structure. - * @req: The current request that the endpoint is processing. This is - * used to indicate an request has been loaded onto the endpoint - * and has yet to be completed (maybe due to data move, or simply - * awaiting an ack from the core all the data has been completed). - * @debugfs: File entry for debugfs file for this endpoint. - * @lock: State lock to protect contents of endpoint. - * @dir_in: Set to true if this endpoint is of the IN direction, which - * means that it is sending data to the Host. - * @index: The index for the endpoint registers. - * @name: The name array passed to the USB core. - * @halted: Set if the endpoint has been halted. - * @periodic: Set if this is a periodic ep, such as Interrupt - * @sent_zlp: Set if we've sent a zero-length packet. - * @total_data: The total number of data bytes done. - * @fifo_size: The size of the FIFO (for periodic IN endpoints) - * @fifo_load: The amount of data loaded into the FIFO (periodic IN) - * @last_load: The offset of data for the last start of request. - * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN - * - * This is the driver's state for each registered enpoint, allowing it - * to keep track of transactions that need doing. Each endpoint has a - * lock to protect the state, to try and avoid using an overall lock - * for the host controller as much as possible. - * - * For periodic IN endpoints, we have fifo_size and fifo_load to try - * and keep track of the amount of data in the periodic FIFO for each - * of these as we don't have a status register that tells us how much - * is in each of them. (note, this may actually be useless information - * as in shared-fifo mode periodic in acts like a single-frame packet - * buffer than a fifo) - */ -struct s3c_hsotg_ep { - struct usb_ep ep; - struct list_head queue; - struct s3c_hsotg *parent; - struct s3c_hsotg_req *req; - struct dentry *debugfs; - - - unsigned long total_data; - unsigned int size_loaded; - unsigned int last_load; - unsigned int fifo_load; - unsigned short fifo_size; - - unsigned char dir_in; - unsigned char index; - - unsigned int halted:1; - unsigned int periodic:1; - unsigned int sent_zlp:1; - - char name[10]; -}; - -/** - * struct s3c_hsotg - driver state. - * @dev: The parent device supplied to the probe function - * @driver: USB gadget driver - * @plat: The platform specific configuration data. - * @regs: The memory area mapped for accessing registers. - * @irq: The IRQ number we are using - * @supplies: Definition of USB power supplies - * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. - * @num_of_eps: Number of available EPs (excluding EP0) - * @debug_root: root directrory for debugfs. - * @debug_file: main status file for debugfs. - * @debug_fifo: FIFO status file for debugfs. - * @ep0_reply: Request used for ep0 reply. - * @ep0_buff: Buffer for EP0 reply data, if needed. - * @ctrl_buff: Buffer for EP0 control requests. - * @ctrl_req: Request for EP0 control packets. - * @setup: NAK management for EP0 SETUP - * @last_rst: Time of last reset - * @eps: The endpoints being supplied to the gadget framework - */ -struct s3c_hsotg { - struct device *dev; - struct usb_gadget_driver *driver; - struct s3c_hsotg_plat *plat; - - spinlock_t lock; - - void __iomem *regs; - int irq; - struct clk *clk; - - struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; - - unsigned int dedicated_fifos:1; - unsigned char num_of_eps; - - struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_fifo; - - struct usb_request *ep0_reply; - struct usb_request *ctrl_req; - u8 ep0_buff[8]; - u8 ctrl_buff[8]; - - struct usb_gadget gadget; - unsigned int setup; - unsigned long last_rst; - struct s3c_hsotg_ep *eps; -}; - -/** - * struct s3c_hsotg_req - data transfer request - * @req: The USB gadget request - * @queue: The list of requests for the endpoint this is queued for. - * @in_progress: Has already had size/packets written to core - * @mapped: DMA buffer for this request has been mapped via dma_map_single(). - */ -struct s3c_hsotg_req { - struct usb_request req; - struct list_head queue; - unsigned char in_progress; - unsigned char mapped; -}; - -/* conversion functions */ -static inline struct s3c_hsotg_req *our_req(struct usb_request *req) -{ - return container_of(req, struct s3c_hsotg_req, req); -} - -static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep) -{ - return container_of(ep, struct s3c_hsotg_ep, ep); -} - -static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget) -{ - return container_of(gadget, struct s3c_hsotg, gadget); -} - -static inline void __orr32(void __iomem *ptr, u32 val) -{ - writel(readl(ptr) | val, ptr); -} - -static inline void __bic32(void __iomem *ptr, u32 val) -{ - writel(readl(ptr) & ~val, ptr); -} - -/* forward decleration of functions */ -static void s3c_hsotg_dump(struct s3c_hsotg *hsotg); - -/** - * using_dma - return the DMA status of the driver. - * @hsotg: The driver state. - * - * Return true if we're using DMA. - * - * Currently, we have the DMA support code worked into everywhere - * that needs it, but the AMBA DMA implementation in the hardware can - * only DMA from 32bit aligned addresses. This means that gadgets such - * as the CDC Ethernet cannot work as they often pass packets which are - * not 32bit aligned. - * - * Unfortunately the choice to use DMA or not is global to the controller - * and seems to be only settable when the controller is being put through - * a core reset. This means we either need to fix the gadgets to take - * account of DMA alignment, or add bounce buffers (yuerk). - * - * Until this issue is sorted out, we always return 'false'. - */ -static inline bool using_dma(struct s3c_hsotg *hsotg) -{ - return false; /* support is not complete */ -} - -/** - * s3c_hsotg_en_gsint - enable one or more of the general interrupt - * @hsotg: The device state - * @ints: A bitmask of the interrupts to enable - */ -static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints) -{ - u32 gsintmsk = readl(hsotg->regs + GINTMSK); - u32 new_gsintmsk; - - new_gsintmsk = gsintmsk | ints; - - if (new_gsintmsk != gsintmsk) { - dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk); - writel(new_gsintmsk, hsotg->regs + GINTMSK); - } -} - -/** - * s3c_hsotg_disable_gsint - disable one or more of the general interrupt - * @hsotg: The device state - * @ints: A bitmask of the interrupts to enable - */ -static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints) -{ - u32 gsintmsk = readl(hsotg->regs + GINTMSK); - u32 new_gsintmsk; - - new_gsintmsk = gsintmsk & ~ints; - - if (new_gsintmsk != gsintmsk) - writel(new_gsintmsk, hsotg->regs + GINTMSK); -} - -/** - * s3c_hsotg_ctrl_epint - enable/disable an endpoint irq - * @hsotg: The device state - * @ep: The endpoint index - * @dir_in: True if direction is in. - * @en: The enable value, true to enable - * - * Set or clear the mask for an individual endpoint's interrupt - * request. - */ -static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg, - unsigned int ep, unsigned int dir_in, - unsigned int en) -{ - unsigned long flags; - u32 bit = 1 << ep; - u32 daint; - - if (!dir_in) - bit <<= 16; - - local_irq_save(flags); - daint = readl(hsotg->regs + DAINTMSK); - if (en) - daint |= bit; - else - daint &= ~bit; - writel(daint, hsotg->regs + DAINTMSK); - local_irq_restore(flags); -} - -/** - * s3c_hsotg_init_fifo - initialise non-periodic FIFOs - * @hsotg: The device instance. - */ -static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) -{ - unsigned int ep; - unsigned int addr; - unsigned int size; - int timeout; - u32 val; - - /* set FIFO sizes to 2048/1024 */ - - writel(2048, hsotg->regs + GRXFSIZ); - writel(GNPTXFSIZ_NPTxFStAddr(2048) | - GNPTXFSIZ_NPTxFDep(1024), - hsotg->regs + GNPTXFSIZ); - - /* - * arange all the rest of the TX FIFOs, as some versions of this - * block have overlapping default addresses. This also ensures - * that if the settings have been changed, then they are set to - * known values. - */ - - /* start at the end of the GNPTXFSIZ, rounded up */ - addr = 2048 + 1024; - size = 768; - - /* - * currently we allocate TX FIFOs for all possible endpoints, - * and assume that they are all the same size. - */ - - for (ep = 1; ep <= 15; ep++) { - val = addr; - val |= size << DPTXFSIZn_DPTxFSize_SHIFT; - addr += size; - - writel(val, hsotg->regs + DPTXFSIZn(ep)); - } - - /* - * according to p428 of the design guide, we need to ensure that - * all fifos are flushed before continuing - */ - - writel(GRSTCTL_TxFNum(0x10) | GRSTCTL_TxFFlsh | - GRSTCTL_RxFFlsh, hsotg->regs + GRSTCTL); - - /* wait until the fifos are both flushed */ - timeout = 100; - while (1) { - val = readl(hsotg->regs + GRSTCTL); - - if ((val & (GRSTCTL_TxFFlsh | GRSTCTL_RxFFlsh)) == 0) - break; - - if (--timeout == 0) { - dev_err(hsotg->dev, - "%s: timeout flushing fifos (GRSTCTL=%08x)\n", - __func__, val); - } - - udelay(1); - } - - dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout); -} - -/** - * @ep: USB endpoint to allocate request for. - * @flags: Allocation flags - * - * Allocate a new USB request structure appropriate for the specified endpoint - */ -static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep, - gfp_t flags) -{ - struct s3c_hsotg_req *req; - - req = kzalloc(sizeof(struct s3c_hsotg_req), flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - - req->req.dma = DMA_ADDR_INVALID; - return &req->req; -} - -/** - * is_ep_periodic - return true if the endpoint is in periodic mode. - * @hs_ep: The endpoint to query. - * - * Returns true if the endpoint is in periodic mode, meaning it is being - * used for an Interrupt or ISO transfer. - */ -static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep) -{ - return hs_ep->periodic; -} - -/** - * s3c_hsotg_unmap_dma - unmap the DMA memory being used for the request - * @hsotg: The device state. - * @hs_ep: The endpoint for the request - * @hs_req: The request being processed. - * - * This is the reverse of s3c_hsotg_map_dma(), called for the completion - * of a request to ensure the buffer is ready for access by the caller. - */ -static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req) -{ - struct usb_request *req = &hs_req->req; - enum dma_data_direction dir; - - dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - - /* ignore this if we're not moving any data */ - if (hs_req->req.length == 0) - return; - - if (hs_req->mapped) { - /* we mapped this, so unmap and remove the dma */ - - dma_unmap_single(hsotg->dev, req->dma, req->length, dir); - - req->dma = DMA_ADDR_INVALID; - hs_req->mapped = 0; - } else { - dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir); - } -} - -/** - * s3c_hsotg_write_fifo - write packet Data to the TxFIFO - * @hsotg: The controller state. - * @hs_ep: The endpoint we're going to write for. - * @hs_req: The request to write data for. - * - * This is called when the TxFIFO has some space in it to hold a new - * transmission and we have something to give it. The actual setup of - * the data size is done elsewhere, so all we have to do is to actually - * write the data. - * - * The return value is zero if there is more space (or nothing was done) - * otherwise -ENOSPC is returned if the FIFO space was used up. - * - * This routine is only needed for PIO - */ -static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req) -{ - bool periodic = is_ep_periodic(hs_ep); - u32 gnptxsts = readl(hsotg->regs + GNPTXSTS); - int buf_pos = hs_req->req.actual; - int to_write = hs_ep->size_loaded; - void *data; - int can_write; - int pkt_round; - - to_write -= (buf_pos - hs_ep->last_load); - - /* if there's nothing to write, get out early */ - if (to_write == 0) - return 0; - - if (periodic && !hsotg->dedicated_fifos) { - u32 epsize = readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); - int size_left; - int size_done; - - /* - * work out how much data was loaded so we can calculate - * how much data is left in the fifo. - */ - - size_left = DxEPTSIZ_XferSize_GET(epsize); - - /* - * if shared fifo, we cannot write anything until the - * previous data has been completely sent. - */ - if (hs_ep->fifo_load != 0) { - s3c_hsotg_en_gsint(hsotg, GINTSTS_PTxFEmp); - return -ENOSPC; - } - - dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n", - __func__, size_left, - hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size); - - /* how much of the data has moved */ - size_done = hs_ep->size_loaded - size_left; - - /* how much data is left in the fifo */ - can_write = hs_ep->fifo_load - size_done; - dev_dbg(hsotg->dev, "%s: => can_write1=%d\n", - __func__, can_write); - - can_write = hs_ep->fifo_size - can_write; - dev_dbg(hsotg->dev, "%s: => can_write2=%d\n", - __func__, can_write); - - if (can_write <= 0) { - s3c_hsotg_en_gsint(hsotg, GINTSTS_PTxFEmp); - return -ENOSPC; - } - } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { - can_write = readl(hsotg->regs + DTXFSTS(hs_ep->index)); - - can_write &= 0xffff; - can_write *= 4; - } else { - if (GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) { - dev_dbg(hsotg->dev, - "%s: no queue slots available (0x%08x)\n", - __func__, gnptxsts); - - s3c_hsotg_en_gsint(hsotg, GINTSTS_NPTxFEmp); - return -ENOSPC; - } - - can_write = GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts); - can_write *= 4; /* fifo size is in 32bit quantities. */ - } - - dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", - __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket); - - /* - * limit to 512 bytes of data, it seems at least on the non-periodic - * FIFO, requests of >512 cause the endpoint to get stuck with a - * fragment of the end of the transfer in it. - */ - if (can_write > 512) - can_write = 512; - - /* - * limit the write to one max-packet size worth of data, but allow - * the transfer to return that it did not run out of fifo space - * doing it. - */ - if (to_write > hs_ep->ep.maxpacket) { - to_write = hs_ep->ep.maxpacket; - - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); - } - - /* see if we can write data */ - - if (to_write > can_write) { - to_write = can_write; - pkt_round = to_write % hs_ep->ep.maxpacket; - - /* - * Round the write down to an - * exact number of packets. - * - * Note, we do not currently check to see if we can ever - * write a full packet or not to the FIFO. - */ - - if (pkt_round) - to_write -= pkt_round; - - /* - * enable correct FIFO interrupt to alert us when there - * is more room left. - */ - - s3c_hsotg_en_gsint(hsotg, - periodic ? GINTSTS_PTxFEmp : - GINTSTS_NPTxFEmp); - } - - dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", - to_write, hs_req->req.length, can_write, buf_pos); - - if (to_write <= 0) - return -ENOSPC; - - hs_req->req.actual = buf_pos + to_write; - hs_ep->total_data += to_write; - - if (periodic) - hs_ep->fifo_load += to_write; - - to_write = DIV_ROUND_UP(to_write, 4); - data = hs_req->req.buf + buf_pos; - - writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); - - return (to_write >= can_write) ? -ENOSPC : 0; -} - -/** - * get_ep_limit - get the maximum data legnth for this endpoint - * @hs_ep: The endpoint - * - * Return the maximum data that can be queued in one go on a given endpoint - * so that transfers that are too long can be split. - */ -static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) -{ - int index = hs_ep->index; - unsigned maxsize; - unsigned maxpkt; - - if (index != 0) { - maxsize = DxEPTSIZ_XferSize_LIMIT + 1; - maxpkt = DxEPTSIZ_PktCnt_LIMIT + 1; - } else { - maxsize = 64+64; - if (hs_ep->dir_in) - maxpkt = DIEPTSIZ0_PktCnt_LIMIT + 1; - else - maxpkt = 2; - } - - /* we made the constant loading easier above by using +1 */ - maxpkt--; - maxsize--; - - /* - * constrain by packet count if maxpkts*pktsize is greater - * than the length register size. - */ - - if ((maxpkt * hs_ep->ep.maxpacket) < maxsize) - maxsize = maxpkt * hs_ep->ep.maxpacket; - - return maxsize; -} - -/** - * s3c_hsotg_start_req - start a USB request from an endpoint's queue - * @hsotg: The controller state. - * @hs_ep: The endpoint to process a request for - * @hs_req: The request to start. - * @continuing: True if we are doing more for the current request. - * - * Start the given request running by setting the endpoint registers - * appropriately, and writing any data to the FIFOs. - */ -static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req, - bool continuing) -{ - struct usb_request *ureq = &hs_req->req; - int index = hs_ep->index; - int dir_in = hs_ep->dir_in; - u32 epctrl_reg; - u32 epsize_reg; - u32 epsize; - u32 ctrl; - unsigned length; - unsigned packets; - unsigned maxreq; - - if (index != 0) { - if (hs_ep->req && !continuing) { - dev_err(hsotg->dev, "%s: active request\n", __func__); - WARN_ON(1); - return; - } else if (hs_ep->req != hs_req && continuing) { - dev_err(hsotg->dev, - "%s: continue different req\n", __func__); - WARN_ON(1); - return; - } - } - - epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); - - dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", - __func__, readl(hsotg->regs + epctrl_reg), index, - hs_ep->dir_in ? "in" : "out"); - - /* If endpoint is stalled, we will restart request later */ - ctrl = readl(hsotg->regs + epctrl_reg); - - if (ctrl & DxEPCTL_Stall) { - dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); - return; - } - - length = ureq->length - ureq->actual; - dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n", - ureq->length, ureq->actual); - if (0) - dev_dbg(hsotg->dev, - "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n", - ureq->buf, length, ureq->dma, - ureq->no_interrupt, ureq->zero, ureq->short_not_ok); - - maxreq = get_ep_limit(hs_ep); - if (length > maxreq) { - int round = maxreq % hs_ep->ep.maxpacket; - - dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n", - __func__, length, maxreq, round); - - /* round down to multiple of packets */ - if (round) - maxreq -= round; - - length = maxreq; - } - - if (length) - packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket); - else - packets = 1; /* send one packet if length is zero. */ - - if (dir_in && index != 0) - epsize = DxEPTSIZ_MC(1); - else - epsize = 0; - - if (index != 0 && ureq->zero) { - /* - * test for the packets being exactly right for the - * transfer - */ - - if (length == (packets * hs_ep->ep.maxpacket)) - packets++; - } - - epsize |= DxEPTSIZ_PktCnt(packets); - epsize |= DxEPTSIZ_XferSize(length); - - dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n", - __func__, packets, length, ureq->length, epsize, epsize_reg); - - /* store the request as the current one we're doing */ - hs_ep->req = hs_req; - - /* write size / packets */ - writel(epsize, hsotg->regs + epsize_reg); - - if (using_dma(hsotg) && !continuing) { - unsigned int dma_reg; - - /* - * write DMA address to control register, buffer already - * synced by s3c_hsotg_ep_queue(). - */ - - dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); - writel(ureq->dma, hsotg->regs + dma_reg); - - dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n", - __func__, ureq->dma, dma_reg); - } - - ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */ - ctrl |= DxEPCTL_USBActEp; - - dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup); - - /* For Setup request do not clear NAK */ - if (hsotg->setup && index == 0) - hsotg->setup = 0; - else - ctrl |= DxEPCTL_CNAK; /* clear NAK set by core */ - - - dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - writel(ctrl, hsotg->regs + epctrl_reg); - - /* - * set these, it seems that DMA support increments past the end - * of the packet buffer so we need to calculate the length from - * this information. - */ - hs_ep->size_loaded = length; - hs_ep->last_load = ureq->actual; - - if (dir_in && !using_dma(hsotg)) { - /* set these anyway, we may need them for non-periodic in */ - hs_ep->fifo_load = 0; - - s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req); - } - - /* - * clear the INTknTXFEmpMsk when we start request, more as a aide - * to debugging to see what is going on. - */ - if (dir_in) - writel(DIEPMSK_INTknTXFEmpMsk, - hsotg->regs + DIEPINT(index)); - - /* - * Note, trying to clear the NAK here causes problems with transmit - * on the S3C6400 ending up with the TXFIFO becoming full. - */ - - /* check ep is enabled */ - if (!(readl(hsotg->regs + epctrl_reg) & DxEPCTL_EPEna)) - dev_warn(hsotg->dev, - "ep%d: failed to become enabled (DxEPCTL=0x%08x)?\n", - index, readl(hsotg->regs + epctrl_reg)); - - dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", - __func__, readl(hsotg->regs + epctrl_reg)); -} - -/** - * s3c_hsotg_map_dma - map the DMA memory being used for the request - * @hsotg: The device state. - * @hs_ep: The endpoint the request is on. - * @req: The request being processed. - * - * We've been asked to queue a request, so ensure that the memory buffer - * is correctly setup for DMA. If we've been passed an extant DMA address - * then ensure the buffer has been synced to memory. If our buffer has no - * DMA memory, then we map the memory and mark our request to allow us to - * cleanup on completion. - */ -static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct usb_request *req) -{ - enum dma_data_direction dir; - struct s3c_hsotg_req *hs_req = our_req(req); - - dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - - /* if the length is zero, ignore the DMA data */ - if (hs_req->req.length == 0) - return 0; - - if (req->dma == DMA_ADDR_INVALID) { - dma_addr_t dma; - - dma = dma_map_single(hsotg->dev, req->buf, req->length, dir); - - if (unlikely(dma_mapping_error(hsotg->dev, dma))) - goto dma_error; - - if (dma & 3) { - dev_err(hsotg->dev, "%s: unaligned dma buffer\n", - __func__); - - dma_unmap_single(hsotg->dev, dma, req->length, dir); - return -EINVAL; - } - - hs_req->mapped = 1; - req->dma = dma; - } else { - dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir); - hs_req->mapped = 0; - } - - return 0; - -dma_error: - dev_err(hsotg->dev, "%s: failed to map buffer %p, %d bytes\n", - __func__, req->buf, req->length); - - return -EIO; -} - -static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, - gfp_t gfp_flags) -{ - struct s3c_hsotg_req *hs_req = our_req(req); - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; - bool first; - - dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", - ep->name, req, req->length, req->buf, req->no_interrupt, - req->zero, req->short_not_ok); - - /* initialise status of the request */ - INIT_LIST_HEAD(&hs_req->queue); - req->actual = 0; - req->status = -EINPROGRESS; - - /* if we're using DMA, sync the buffers as necessary */ - if (using_dma(hs)) { - int ret = s3c_hsotg_map_dma(hs, hs_ep, req); - if (ret) - return ret; - } - - first = list_empty(&hs_ep->queue); - list_add_tail(&hs_req->queue, &hs_ep->queue); - - if (first) - s3c_hsotg_start_req(hs, hs_ep, hs_req, false); - - return 0; -} - -static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, - gfp_t gfp_flags) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; - unsigned long flags = 0; - int ret = 0; - - spin_lock_irqsave(&hs->lock, flags); - ret = s3c_hsotg_ep_queue(ep, req, gfp_flags); - spin_unlock_irqrestore(&hs->lock, flags); - - return ret; -} - -static void s3c_hsotg_ep_free_request(struct usb_ep *ep, - struct usb_request *req) -{ - struct s3c_hsotg_req *hs_req = our_req(req); - - kfree(hs_req); -} - -/** - * s3c_hsotg_complete_oursetup - setup completion callback - * @ep: The endpoint the request was on. - * @req: The request completed. - * - * Called on completion of any requests the driver itself - * submitted that need cleaning up. - */ -static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, - struct usb_request *req) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; - - dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req); - - s3c_hsotg_ep_free_request(ep, req); -} - -/** - * ep_from_windex - convert control wIndex value to endpoint - * @hsotg: The driver state. - * @windex: The control request wIndex field (in host order). - * - * Convert the given wIndex into a pointer to an driver endpoint - * structure, or return NULL if it is not a valid endpoint. - */ -static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg, - u32 windex) -{ - struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F]; - int dir = (windex & USB_DIR_IN) ? 1 : 0; - int idx = windex & 0x7F; - - if (windex >= 0x100) - return NULL; - - if (idx > hsotg->num_of_eps) - return NULL; - - if (idx && ep->dir_in != dir) - return NULL; - - return ep; -} - -/** - * s3c_hsotg_send_reply - send reply to control request - * @hsotg: The device state - * @ep: Endpoint 0 - * @buff: Buffer for request - * @length: Length of reply. - * - * Create a request and queue it on the given endpoint. This is useful as - * an internal method of sending replies to certain control requests, etc. - */ -static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *ep, - void *buff, - int length) -{ - struct usb_request *req; - int ret; - - dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length); - - req = s3c_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC); - hsotg->ep0_reply = req; - if (!req) { - dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__); - return -ENOMEM; - } - - req->buf = hsotg->ep0_buff; - req->length = length; - req->zero = 1; /* always do zero-length final transfer */ - req->complete = s3c_hsotg_complete_oursetup; - - if (length) - memcpy(req->buf, buff, length); - else - ep->sent_zlp = 1; - - ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC); - if (ret) { - dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__); - return ret; - } - - return 0; -} - -/** - * s3c_hsotg_process_req_status - process request GET_STATUS - * @hsotg: The device state - * @ctrl: USB control request - */ -static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg, - struct usb_ctrlrequest *ctrl) -{ - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; - struct s3c_hsotg_ep *ep; - __le16 reply; - int ret; - - dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__); - - if (!ep0->dir_in) { - dev_warn(hsotg->dev, "%s: direction out?\n", __func__); - return -EINVAL; - } - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - reply = cpu_to_le16(0); /* bit 0 => self powered, - * bit 1 => remote wakeup */ - break; - - case USB_RECIP_INTERFACE: - /* currently, the data result should be zero */ - reply = cpu_to_le16(0); - break; - - case USB_RECIP_ENDPOINT: - ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex)); - if (!ep) - return -ENOENT; - - reply = cpu_to_le16(ep->halted ? 1 : 0); - break; - - default: - return 0; - } - - if (le16_to_cpu(ctrl->wLength) != 2) - return -EINVAL; - - ret = s3c_hsotg_send_reply(hsotg, ep0, &reply, 2); - if (ret) { - dev_err(hsotg->dev, "%s: failed to send reply\n", __func__); - return ret; - } - - return 1; -} - -static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); - -/** - * get_ep_head - return the first request on the endpoint - * @hs_ep: The controller endpoint to get - * - * Get the first request on the endpoint. - */ -static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) -{ - if (list_empty(&hs_ep->queue)) - return NULL; - - return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); -} - -/** - * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE - * @hsotg: The device state - * @ctrl: USB control request - */ -static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, - struct usb_ctrlrequest *ctrl) -{ - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; - struct s3c_hsotg_req *hs_req; - bool restart; - bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); - struct s3c_hsotg_ep *ep; - int ret; - - dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", - __func__, set ? "SET" : "CLEAR"); - - if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { - ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex)); - if (!ep) { - dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n", - __func__, le16_to_cpu(ctrl->wIndex)); - return -ENOENT; - } - - switch (le16_to_cpu(ctrl->wValue)) { - case USB_ENDPOINT_HALT: - s3c_hsotg_ep_sethalt(&ep->ep, set); - - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); - if (ret) { - dev_err(hsotg->dev, - "%s: failed to send reply\n", __func__); - return ret; - } - - if (!set) { - /* - * If we have request in progress, - * then complete it - */ - if (ep->req) { - hs_req = ep->req; - ep->req = NULL; - list_del_init(&hs_req->queue); - hs_req->req.complete(&ep->ep, - &hs_req->req); - } - - /* If we have pending request, then start it */ - restart = !list_empty(&ep->queue); - if (restart) { - hs_req = get_ep_head(ep); - s3c_hsotg_start_req(hsotg, ep, - hs_req, false); - } - } - - break; - - default: - return -ENOENT; - } - } else - return -ENOENT; /* currently only deal with endpoint */ - - return 1; -} - -/** - * s3c_hsotg_process_control - process a control request - * @hsotg: The device state - * @ctrl: The control request received - * - * The controller has received the SETUP phase of a control request, and - * needs to work out what to do next (and whether to pass it on to the - * gadget driver). - */ -static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, - struct usb_ctrlrequest *ctrl) -{ - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; - int ret = 0; - u32 dcfg; - - ep0->sent_zlp = 0; - - dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n", - ctrl->bRequest, ctrl->bRequestType, - ctrl->wValue, ctrl->wLength); - - /* - * record the direction of the request, for later use when enquing - * packets onto EP0. - */ - - ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0; - dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in); - - /* - * if we've no data with this request, then the last part of the - * transaction is going to implicitly be IN. - */ - if (ctrl->wLength == 0) - ep0->dir_in = 1; - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - switch (ctrl->bRequest) { - case USB_REQ_SET_ADDRESS: - dcfg = readl(hsotg->regs + DCFG); - dcfg &= ~DCFG_DevAddr_MASK; - dcfg |= ctrl->wValue << DCFG_DevAddr_SHIFT; - writel(dcfg, hsotg->regs + DCFG); - - dev_info(hsotg->dev, "new address %d\n", ctrl->wValue); - - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); - return; - - case USB_REQ_GET_STATUS: - ret = s3c_hsotg_process_req_status(hsotg, ctrl); - break; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - ret = s3c_hsotg_process_req_feature(hsotg, ctrl); - break; - } - } - - /* as a fallback, try delivering it to the driver to deal with */ - - if (ret == 0 && hsotg->driver) { - ret = hsotg->driver->setup(&hsotg->gadget, ctrl); - if (ret < 0) - dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); - } - - /* - * the request is either unhandlable, or is not formatted correctly - * so respond with a STALL for the status stage to indicate failure. - */ - - if (ret < 0) { - u32 reg; - u32 ctrl; - - dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); - reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; - - /* - * DxEPCTL_Stall will be cleared by EP once it has - * taken effect, so no need to clear later. - */ - - ctrl = readl(hsotg->regs + reg); - ctrl |= DxEPCTL_Stall; - ctrl |= DxEPCTL_CNAK; - writel(ctrl, hsotg->regs + reg); - - dev_dbg(hsotg->dev, - "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n", - ctrl, reg, readl(hsotg->regs + reg)); - - /* - * don't believe we need to anything more to get the EP - * to reply with a STALL packet - */ - } -} - -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); - -/** - * s3c_hsotg_complete_setup - completion of a setup transfer - * @ep: The endpoint the request was on. - * @req: The request completed. - * - * Called on completion of any requests the driver itself submitted for - * EP0 setup packets - */ -static void s3c_hsotg_complete_setup(struct usb_ep *ep, - struct usb_request *req) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; - - if (req->status < 0) { - dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status); - return; - } - - if (req->actual == 0) - s3c_hsotg_enqueue_setup(hsotg); - else - s3c_hsotg_process_control(hsotg, req->buf); -} - -/** - * s3c_hsotg_enqueue_setup - start a request for EP0 packets - * @hsotg: The device state. - * - * Enqueue a request on EP0 if necessary to received any SETUP packets - * received from the host. - */ -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg) -{ - struct usb_request *req = hsotg->ctrl_req; - struct s3c_hsotg_req *hs_req = our_req(req); - int ret; - - dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__); - - req->zero = 0; - req->length = 8; - req->buf = hsotg->ctrl_buff; - req->complete = s3c_hsotg_complete_setup; - - if (!list_empty(&hs_req->queue)) { - dev_dbg(hsotg->dev, "%s already queued???\n", __func__); - return; - } - - hsotg->eps[0].dir_in = 0; - - ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC); - if (ret < 0) { - dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret); - /* - * Don't think there's much we can do other than watch the - * driver fail. - */ - } -} - -/** - * s3c_hsotg_complete_request - complete a request given to us - * @hsotg: The device state. - * @hs_ep: The endpoint the request was on. - * @hs_req: The request to complete. - * @result: The result code (0 => Ok, otherwise errno) - * - * The given request has finished, so call the necessary completion - * if it has one and then look to see if we can start a new request - * on the endpoint. - * - * Note, expects the ep to already be locked as appropriate. - */ -static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req, - int result) -{ - bool restart; - - if (!hs_req) { - dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__); - return; - } - - dev_dbg(hsotg->dev, "complete: ep %p %s, req %p, %d => %p\n", - hs_ep, hs_ep->ep.name, hs_req, result, hs_req->req.complete); - - /* - * only replace the status if we've not already set an error - * from a previous transaction - */ - - if (hs_req->req.status == -EINPROGRESS) - hs_req->req.status = result; - - hs_ep->req = NULL; - list_del_init(&hs_req->queue); - - if (using_dma(hsotg)) - s3c_hsotg_unmap_dma(hsotg, hs_ep, hs_req); - - /* - * call the complete request with the locks off, just in case the - * request tries to queue more work for this endpoint. - */ - - if (hs_req->req.complete) { - spin_unlock(&hsotg->lock); - hs_req->req.complete(&hs_ep->ep, &hs_req->req); - spin_lock(&hsotg->lock); - } - - /* - * Look to see if there is anything else to do. Note, the completion - * of the previous request may have caused a new request to be started - * so be careful when doing this. - */ - - if (!hs_ep->req && result >= 0) { - restart = !list_empty(&hs_ep->queue); - if (restart) { - hs_req = get_ep_head(hs_ep); - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, false); - } - } -} - -/** - * s3c_hsotg_rx_data - receive data from the FIFO for an endpoint - * @hsotg: The device state. - * @ep_idx: The endpoint index for the data - * @size: The size of data in the fifo, in bytes - * - * The FIFO status shows there is data to read from the FIFO for a given - * endpoint, so sort out whether we need to read the data into a request - * that has been made for that endpoint. - */ -static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) -{ - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx]; - struct s3c_hsotg_req *hs_req = hs_ep->req; - void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx); - int to_read; - int max_req; - int read_ptr; - - - if (!hs_req) { - u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx)); - int ptr; - - dev_warn(hsotg->dev, - "%s: FIFO %d bytes on ep%d but no req (DxEPCTl=0x%08x)\n", - __func__, size, ep_idx, epctl); - - /* dump the data from the FIFO, we've nothing we can do */ - for (ptr = 0; ptr < size; ptr += 4) - (void)readl(fifo); - - return; - } - - to_read = size; - read_ptr = hs_req->req.actual; - max_req = hs_req->req.length - read_ptr; - - dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", - __func__, to_read, max_req, read_ptr, hs_req->req.length); - - if (to_read > max_req) { - /* - * more data appeared than we where willing - * to deal with in this request. - */ - - /* currently we don't deal this */ - WARN_ON_ONCE(1); - } - - hs_ep->total_data += to_read; - hs_req->req.actual += to_read; - to_read = DIV_ROUND_UP(to_read, 4); - - /* - * note, we might over-write the buffer end by 3 bytes depending on - * alignment of the data. - */ - readsl(fifo, hs_req->req.buf + read_ptr, to_read); -} - -/** - * s3c_hsotg_send_zlp - send zero-length packet on control endpoint - * @hsotg: The device instance - * @req: The request currently on this endpoint - * - * Generate a zero-length IN packet request for terminating a SETUP - * transaction. - * - * Note, since we don't write any data to the TxFIFO, then it is - * currently believed that we do not need to wait for any space in - * the TxFIFO. - */ -static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg, - struct s3c_hsotg_req *req) -{ - u32 ctrl; - - if (!req) { - dev_warn(hsotg->dev, "%s: no request?\n", __func__); - return; - } - - if (req->req.length == 0) { - hsotg->eps[0].sent_zlp = 1; - s3c_hsotg_enqueue_setup(hsotg); - return; - } - - hsotg->eps[0].dir_in = 1; - hsotg->eps[0].sent_zlp = 1; - - dev_dbg(hsotg->dev, "sending zero-length packet\n"); - - /* issue a zero-sized packet to terminate this */ - writel(DxEPTSIZ_MC(1) | DxEPTSIZ_PktCnt(1) | - DxEPTSIZ_XferSize(0), hsotg->regs + DIEPTSIZ(0)); - - ctrl = readl(hsotg->regs + DIEPCTL0); - ctrl |= DxEPCTL_CNAK; /* clear NAK set by core */ - ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */ - ctrl |= DxEPCTL_USBActEp; - writel(ctrl, hsotg->regs + DIEPCTL0); -} - -/** - * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO - * @hsotg: The device instance - * @epnum: The endpoint received from - * @was_setup: Set if processing a SetupDone event. - * - * The RXFIFO has delivered an OutDone event, which means that the data - * transfer for an OUT endpoint has been completed, either by a short - * packet or by the finish of a transfer. - */ -static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, - int epnum, bool was_setup) -{ - u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum)); - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum]; - struct s3c_hsotg_req *hs_req = hs_ep->req; - struct usb_request *req = &hs_req->req; - unsigned size_left = DxEPTSIZ_XferSize_GET(epsize); - int result = 0; - - if (!hs_req) { - dev_dbg(hsotg->dev, "%s: no request active\n", __func__); - return; - } - - if (using_dma(hsotg)) { - unsigned size_done; - - /* - * Calculate the size of the transfer by checking how much - * is left in the endpoint size register and then working it - * out from the amount we loaded for the transfer. - * - * We need to do this as DMA pointers are always 32bit aligned - * so may overshoot/undershoot the transfer. - */ - - size_done = hs_ep->size_loaded - size_left; - size_done += hs_ep->last_load; - - req->actual = size_done; - } - - /* if there is more request to do, schedule new transfer */ - if (req->actual < req->length && size_left == 0) { - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); - return; - } else if (epnum == 0) { - /* - * After was_setup = 1 => - * set CNAK for non Setup requests - */ - hsotg->setup = was_setup ? 0 : 1; - } - - if (req->actual < req->length && req->short_not_ok) { - dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n", - __func__, req->actual, req->length); - - /* - * todo - what should we return here? there's no one else - * even bothering to check the status. - */ - } - - if (epnum == 0) { - /* - * Condition req->complete != s3c_hsotg_complete_setup says: - * send ZLP when we have an asynchronous request from gadget - */ - if (!was_setup && req->complete != s3c_hsotg_complete_setup) - s3c_hsotg_send_zlp(hsotg, hs_req); - } - - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); -} - -/** - * s3c_hsotg_read_frameno - read current frame number - * @hsotg: The device instance - * - * Return the current frame number - */ -static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg) -{ - u32 dsts; - - dsts = readl(hsotg->regs + DSTS); - dsts &= DSTS_SOFFN_MASK; - dsts >>= DSTS_SOFFN_SHIFT; - - return dsts; -} - -/** - * s3c_hsotg_handle_rx - RX FIFO has data - * @hsotg: The device instance - * - * The IRQ handler has detected that the RX FIFO has some data in it - * that requires processing, so find out what is in there and do the - * appropriate read. - * - * The RXFIFO is a true FIFO, the packets coming out are still in packet - * chunks, so if you have x packets received on an endpoint you'll get x - * FIFO events delivered, each with a packet's worth of data in it. - * - * When using DMA, we should not be processing events from the RXFIFO - * as the actual data should be sent to the memory directly and we turn - * on the completion interrupts to get notifications of transfer completion. - */ -static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg) -{ - u32 grxstsr = readl(hsotg->regs + GRXSTSP); - u32 epnum, status, size; - - WARN_ON(using_dma(hsotg)); - - epnum = grxstsr & GRXSTS_EPNum_MASK; - status = grxstsr & GRXSTS_PktSts_MASK; - - size = grxstsr & GRXSTS_ByteCnt_MASK; - size >>= GRXSTS_ByteCnt_SHIFT; - - if (1) - dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n", - __func__, grxstsr, size, epnum); - -#define __status(x) ((x) >> GRXSTS_PktSts_SHIFT) - - switch (status >> GRXSTS_PktSts_SHIFT) { - case __status(GRXSTS_PktSts_GlobalOutNAK): - dev_dbg(hsotg->dev, "GlobalOutNAK\n"); - break; - - case __status(GRXSTS_PktSts_OutDone): - dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg)); - - if (!using_dma(hsotg)) - s3c_hsotg_handle_outdone(hsotg, epnum, false); - break; - - case __status(GRXSTS_PktSts_SetupDone): - dev_dbg(hsotg->dev, - "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg), - readl(hsotg->regs + DOEPCTL(0))); - - s3c_hsotg_handle_outdone(hsotg, epnum, true); - break; - - case __status(GRXSTS_PktSts_OutRX): - s3c_hsotg_rx_data(hsotg, epnum, size); - break; - - case __status(GRXSTS_PktSts_SetupRX): - dev_dbg(hsotg->dev, - "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg), - readl(hsotg->regs + DOEPCTL(0))); - - s3c_hsotg_rx_data(hsotg, epnum, size); - break; - - default: - dev_warn(hsotg->dev, "%s: unknown status %08x\n", - __func__, grxstsr); - - s3c_hsotg_dump(hsotg); - break; - } -} - -/** - * s3c_hsotg_ep0_mps - turn max packet size into register setting - * @mps: The maximum packet size in bytes. - */ -static u32 s3c_hsotg_ep0_mps(unsigned int mps) -{ - switch (mps) { - case 64: - return D0EPCTL_MPS_64; - case 32: - return D0EPCTL_MPS_32; - case 16: - return D0EPCTL_MPS_16; - case 8: - return D0EPCTL_MPS_8; - } - - /* bad max packet size, warn and return invalid result */ - WARN_ON(1); - return (u32)-1; -} - -/** - * s3c_hsotg_set_ep_maxpacket - set endpoint's max-packet field - * @hsotg: The driver state. - * @ep: The index number of the endpoint - * @mps: The maximum packet size in bytes - * - * Configure the maximum packet size for the given endpoint, updating - * the hardware control registers to reflect this. - */ -static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, - unsigned int ep, unsigned int mps) -{ - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; - void __iomem *regs = hsotg->regs; - u32 mpsval; - u32 reg; - - if (ep == 0) { - /* EP0 is a special case */ - mpsval = s3c_hsotg_ep0_mps(mps); - if (mpsval > 3) - goto bad_mps; - } else { - if (mps >= DxEPCTL_MPS_LIMIT+1) - goto bad_mps; - - mpsval = mps; - } - - hs_ep->ep.maxpacket = mps; - - /* - * update both the in and out endpoint controldir_ registers, even - * if one of the directions may not be in use. - */ - - reg = readl(regs + DIEPCTL(ep)); - reg &= ~DxEPCTL_MPS_MASK; - reg |= mpsval; - writel(reg, regs + DIEPCTL(ep)); - - if (ep) { - reg = readl(regs + DOEPCTL(ep)); - reg &= ~DxEPCTL_MPS_MASK; - reg |= mpsval; - writel(reg, regs + DOEPCTL(ep)); - } - - return; - -bad_mps: - dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps); -} - -/** - * s3c_hsotg_txfifo_flush - flush Tx FIFO - * @hsotg: The driver state - * @idx: The index for the endpoint (0..15) - */ -static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) -{ - int timeout; - int val; - - writel(GRSTCTL_TxFNum(idx) | GRSTCTL_TxFFlsh, - hsotg->regs + GRSTCTL); - - /* wait until the fifo is flushed */ - timeout = 100; - - while (1) { - val = readl(hsotg->regs + GRSTCTL); - - if ((val & (GRSTCTL_TxFFlsh)) == 0) - break; - - if (--timeout == 0) { - dev_err(hsotg->dev, - "%s: timeout flushing fifo (GRSTCTL=%08x)\n", - __func__, val); - } - - udelay(1); - } -} - -/** - * s3c_hsotg_trytx - check to see if anything needs transmitting - * @hsotg: The driver state - * @hs_ep: The driver endpoint to check. - * - * Check to see if there is a request that has data to send, and if so - * make an attempt to write data into the FIFO. - */ -static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep) -{ - struct s3c_hsotg_req *hs_req = hs_ep->req; - - if (!hs_ep->dir_in || !hs_req) - return 0; - - if (hs_req->req.actual < hs_req->req.length) { - dev_dbg(hsotg->dev, "trying to write more for ep%d\n", - hs_ep->index); - return s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req); - } - - return 0; -} - -/** - * s3c_hsotg_complete_in - complete IN transfer - * @hsotg: The device state. - * @hs_ep: The endpoint that has just completed. - * - * An IN transfer has been completed, update the transfer's state and then - * call the relevant completion routines. - */ -static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep) -{ - struct s3c_hsotg_req *hs_req = hs_ep->req; - u32 epsize = readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); - int size_left, size_done; - - if (!hs_req) { - dev_dbg(hsotg->dev, "XferCompl but no req\n"); - return; - } - - /* Finish ZLP handling for IN EP0 transactions */ - if (hsotg->eps[0].sent_zlp) { - dev_dbg(hsotg->dev, "zlp packet received\n"); - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); - return; - } - - /* - * Calculate the size of the transfer by checking how much is left - * in the endpoint size register and then working it out from - * the amount we loaded for the transfer. - * - * We do this even for DMA, as the transfer may have incremented - * past the end of the buffer (DMA transfers are always 32bit - * aligned). - */ - - size_left = DxEPTSIZ_XferSize_GET(epsize); - - size_done = hs_ep->size_loaded - size_left; - size_done += hs_ep->last_load; - - if (hs_req->req.actual != size_done) - dev_dbg(hsotg->dev, "%s: adjusting size done %d => %d\n", - __func__, hs_req->req.actual, size_done); - - hs_req->req.actual = size_done; - dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n", - hs_req->req.length, hs_req->req.actual, hs_req->req.zero); - - /* - * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0 - * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B - * ,256B ... ), after last MPS sized packet send IN ZLP packet to - * inform the host that no more data is available. - * The state of req.zero member is checked to be sure that the value to - * send is smaller than wValue expected from host. - * Check req.length to NOT send another ZLP when the current one is - * under completion (the one for which this completion has been called). - */ - if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero && - hs_req->req.length == hs_req->req.actual && - !(hs_req->req.length % hs_ep->ep.maxpacket)) { - - dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n"); - s3c_hsotg_send_zlp(hsotg, hs_req); - - return; - } - - if (!size_left && hs_req->req.actual < hs_req->req.length) { - dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); - } else - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); -} - -/** - * s3c_hsotg_epint - handle an in/out endpoint interrupt - * @hsotg: The driver state - * @idx: The index for the endpoint (0..15) - * @dir_in: Set if this is an IN endpoint - * - * Process and clear any interrupt pending for an individual endpoint - */ -static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, - int dir_in) -{ - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx]; - u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); - u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); - u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); - u32 ints; - - ints = readl(hsotg->regs + epint_reg); - - /* Clear endpoint interrupts */ - writel(ints, hsotg->regs + epint_reg); - - dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", - __func__, idx, dir_in ? "in" : "out", ints); - - if (ints & DxEPINT_XferCompl) { - dev_dbg(hsotg->dev, - "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n", - __func__, readl(hsotg->regs + epctl_reg), - readl(hsotg->regs + epsiz_reg)); - - /* - * we get OutDone from the FIFO, so we only need to look - * at completing IN requests here - */ - if (dir_in) { - s3c_hsotg_complete_in(hsotg, hs_ep); - - if (idx == 0 && !hs_ep->req) - s3c_hsotg_enqueue_setup(hsotg); - } else if (using_dma(hsotg)) { - /* - * We're using DMA, we need to fire an OutDone here - * as we ignore the RXFIFO. - */ - - s3c_hsotg_handle_outdone(hsotg, idx, false); - } - } - - if (ints & DxEPINT_EPDisbld) { - dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); - - if (dir_in) { - int epctl = readl(hsotg->regs + epctl_reg); - - s3c_hsotg_txfifo_flush(hsotg, idx); - - if ((epctl & DxEPCTL_Stall) && - (epctl & DxEPCTL_EPType_Bulk)) { - int dctl = readl(hsotg->regs + DCTL); - - dctl |= DCTL_CGNPInNAK; - writel(dctl, hsotg->regs + DCTL); - } - } - } - - if (ints & DxEPINT_AHBErr) - dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); - - if (ints & DxEPINT_Setup) { /* Setup or Timeout */ - dev_dbg(hsotg->dev, "%s: Setup/Timeout\n", __func__); - - if (using_dma(hsotg) && idx == 0) { - /* - * this is the notification we've received a - * setup packet. In non-DMA mode we'd get this - * from the RXFIFO, instead we need to process - * the setup here. - */ - - if (dir_in) - WARN_ON_ONCE(1); - else - s3c_hsotg_handle_outdone(hsotg, 0, true); - } - } - - if (ints & DxEPINT_Back2BackSetup) - dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); - - if (dir_in) { - /* not sure if this is important, but we'll clear it anyway */ - if (ints & DIEPMSK_INTknTXFEmpMsk) { - dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", - __func__, idx); - } - - /* this probably means something bad is happening */ - if (ints & DIEPMSK_INTknEPMisMsk) { - dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n", - __func__, idx); - } - - /* FIFO has space or is empty (see GAHBCFG) */ - if (hsotg->dedicated_fifos && - ints & DIEPMSK_TxFIFOEmpty) { - dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", - __func__, idx); - if (!using_dma(hsotg)) - s3c_hsotg_trytx(hsotg, hs_ep); - } - } -} - -/** - * s3c_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done) - * @hsotg: The device state. - * - * Handle updating the device settings after the enumeration phase has - * been completed. - */ -static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) -{ - u32 dsts = readl(hsotg->regs + DSTS); - int ep0_mps = 0, ep_mps; - - /* - * This should signal the finish of the enumeration phase - * of the USB handshaking, so we should now know what rate - * we connected at. - */ - - dev_dbg(hsotg->dev, "EnumDone (DSTS=0x%08x)\n", dsts); - - /* - * note, since we're limited by the size of transfer on EP0, and - * it seems IN transfers must be a even number of packets we do - * not advertise a 64byte MPS on EP0. - */ - - /* catch both EnumSpd_FS and EnumSpd_FS48 */ - switch (dsts & DSTS_EnumSpd_MASK) { - case DSTS_EnumSpd_FS: - case DSTS_EnumSpd_FS48: - hsotg->gadget.speed = USB_SPEED_FULL; - ep0_mps = EP0_MPS_LIMIT; - ep_mps = 64; - break; - - case DSTS_EnumSpd_HS: - hsotg->gadget.speed = USB_SPEED_HIGH; - ep0_mps = EP0_MPS_LIMIT; - ep_mps = 512; - break; - - case DSTS_EnumSpd_LS: - hsotg->gadget.speed = USB_SPEED_LOW; - /* - * note, we don't actually support LS in this driver at the - * moment, and the documentation seems to imply that it isn't - * supported by the PHYs on some of the devices. - */ - break; - } - dev_info(hsotg->dev, "new device is %s\n", - usb_speed_string(hsotg->gadget.speed)); - - /* - * we should now know the maximum packet size for an - * endpoint, so set the endpoints to a default value. - */ - - if (ep0_mps) { - int i; - s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps); - for (i = 1; i < hsotg->num_of_eps; i++) - s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps); - } - - /* ensure after enumeration our EP0 is active */ - - s3c_hsotg_enqueue_setup(hsotg); - - dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); -} - -/** - * kill_all_requests - remove all requests from the endpoint's queue - * @hsotg: The device state. - * @ep: The endpoint the requests may be on. - * @result: The result code to use. - * @force: Force removal of any current requests - * - * Go through the requests on the given endpoint and mark them - * completed with the given result code. - */ -static void kill_all_requests(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *ep, - int result, bool force) -{ - struct s3c_hsotg_req *req, *treq; - - list_for_each_entry_safe(req, treq, &ep->queue, queue) { - /* - * currently, we can't do much about an already - * running request on an in endpoint - */ - - if (ep->req == req && ep->dir_in && !force) - continue; - - s3c_hsotg_complete_request(hsotg, ep, req, - result); - } -} - -#define call_gadget(_hs, _entry) \ - if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \ - (_hs)->driver && (_hs)->driver->_entry) { \ - spin_unlock(&_hs->lock); \ - (_hs)->driver->_entry(&(_hs)->gadget); \ - spin_lock(&_hs->lock); \ - } - -/** - * s3c_hsotg_disconnect - disconnect service - * @hsotg: The device state. - * - * The device has been disconnected. Remove all current - * transactions and signal the gadget driver that this - * has happened. - */ -static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg) -{ - unsigned ep; - - for (ep = 0; ep < hsotg->num_of_eps; ep++) - kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true); - - call_gadget(hsotg, disconnect); -} - -/** - * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler - * @hsotg: The device state: - * @periodic: True if this is a periodic FIFO interrupt - */ -static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic) -{ - struct s3c_hsotg_ep *ep; - int epno, ret; - - /* look through for any more data to transmit */ - - for (epno = 0; epno < hsotg->num_of_eps; epno++) { - ep = &hsotg->eps[epno]; - - if (!ep->dir_in) - continue; - - if ((periodic && !ep->periodic) || - (!periodic && ep->periodic)) - continue; - - ret = s3c_hsotg_trytx(hsotg, ep); - if (ret < 0) - break; - } -} - -/* IRQ flags which will trigger a retry around the IRQ loop */ -#define IRQ_RETRY_MASK (GINTSTS_NPTxFEmp | \ - GINTSTS_PTxFEmp | \ - GINTSTS_RxFLvl) - -/** - * s3c_hsotg_corereset - issue softreset to the core - * @hsotg: The device state - * - * Issue a soft reset to the core, and await the core finishing it. - */ -static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) -{ - int timeout; - u32 grstctl; - - dev_dbg(hsotg->dev, "resetting core\n"); - - /* issue soft reset */ - writel(GRSTCTL_CSftRst, hsotg->regs + GRSTCTL); - - timeout = 10000; - do { - grstctl = readl(hsotg->regs + GRSTCTL); - } while ((grstctl & GRSTCTL_CSftRst) && timeout-- > 0); - - if (grstctl & GRSTCTL_CSftRst) { - dev_err(hsotg->dev, "Failed to get CSftRst asserted\n"); - return -EINVAL; - } - - timeout = 10000; - - while (1) { - u32 grstctl = readl(hsotg->regs + GRSTCTL); - - if (timeout-- < 0) { - dev_info(hsotg->dev, - "%s: reset failed, GRSTCTL=%08x\n", - __func__, grstctl); - return -ETIMEDOUT; - } - - if (!(grstctl & GRSTCTL_AHBIdle)) - continue; - - break; /* reset done */ - } - - dev_dbg(hsotg->dev, "reset successful\n"); - return 0; -} - -/** - * s3c_hsotg_core_init - issue softreset to the core - * @hsotg: The device state - * - * Issue a soft reset to the core, and await the core finishing it. - */ -static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) -{ - s3c_hsotg_corereset(hsotg); - - /* - * we must now enable ep0 ready for host detection and then - * set configuration. - */ - - /* set the PLL on, remove the HNP/SRP and set the PHY */ - writel(GUSBCFG_PHYIf16 | GUSBCFG_TOutCal(7) | - (0x5 << 10), hsotg->regs + GUSBCFG); - - s3c_hsotg_init_fifo(hsotg); - - __orr32(hsotg->regs + DCTL, DCTL_SftDiscon); - - writel(1 << 18 | DCFG_DevSpd_HS, hsotg->regs + DCFG); - - /* Clear any pending OTG interrupts */ - writel(0xffffffff, hsotg->regs + GOTGINT); - - /* Clear any pending interrupts */ - writel(0xffffffff, hsotg->regs + GINTSTS); - - writel(GINTSTS_ErlySusp | GINTSTS_SessReqInt | - GINTSTS_GOUTNakEff | GINTSTS_GINNakEff | - GINTSTS_ConIDStsChng | GINTSTS_USBRst | - GINTSTS_EnumDone | GINTSTS_OTGInt | - GINTSTS_USBSusp | GINTSTS_WkUpInt, - hsotg->regs + GINTMSK); - - if (using_dma(hsotg)) - writel(GAHBCFG_GlblIntrEn | GAHBCFG_DMAEn | - GAHBCFG_HBstLen_Incr4, - hsotg->regs + GAHBCFG); - else - writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG); - - /* - * Enabling INTknTXFEmpMsk here seems to be a big mistake, we end - * up being flooded with interrupts if the host is polling the - * endpoint to try and read data. - */ - - writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) | - DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk | - DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | - DIEPMSK_INTknEPMisMsk, - hsotg->regs + DIEPMSK); - - /* - * don't need XferCompl, we get that from RXFIFO in slave mode. In - * DMA mode we may need this. - */ - writel((using_dma(hsotg) ? (DIEPMSK_XferComplMsk | - DIEPMSK_TimeOUTMsk) : 0) | - DOEPMSK_EPDisbldMsk | DOEPMSK_AHBErrMsk | - DOEPMSK_SetupMsk, - hsotg->regs + DOEPMSK); - - writel(0, hsotg->regs + DAINTMSK); - - dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); - - /* enable in and out endpoint interrupts */ - s3c_hsotg_en_gsint(hsotg, GINTSTS_OEPInt | GINTSTS_IEPInt); - - /* - * Enable the RXFIFO when in slave mode, as this is how we collect - * the data. In DMA mode, we get events from the FIFO but also - * things we cannot process, so do not use it. - */ - if (!using_dma(hsotg)) - s3c_hsotg_en_gsint(hsotg, GINTSTS_RxFLvl); - - /* Enable interrupts for EP0 in and out */ - s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1); - s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1); - - __orr32(hsotg->regs + DCTL, DCTL_PWROnPrgDone); - udelay(10); /* see openiboot */ - __bic32(hsotg->regs + DCTL, DCTL_PWROnPrgDone); - - dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL)); - - /* - * DxEPCTL_USBActEp says RO in manual, but seems to be set by - * writing to the EPCTL register.. - */ - - /* set to read 1 8byte packet */ - writel(DxEPTSIZ_MC(1) | DxEPTSIZ_PktCnt(1) | - DxEPTSIZ_XferSize(8), hsotg->regs + DOEPTSIZ0); - - writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) | - DxEPCTL_CNAK | DxEPCTL_EPEna | - DxEPCTL_USBActEp, - hsotg->regs + DOEPCTL0); - - /* enable, but don't activate EP0in */ - writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) | - DxEPCTL_USBActEp, hsotg->regs + DIEPCTL0); - - s3c_hsotg_enqueue_setup(hsotg); - - dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); - - /* clear global NAKs */ - writel(DCTL_CGOUTNak | DCTL_CGNPInNAK, - hsotg->regs + DCTL); - - /* must be at-least 3ms to allow bus to see disconnect */ - mdelay(3); - - /* remove the soft-disconnect and let's go */ - __bic32(hsotg->regs + DCTL, DCTL_SftDiscon); -} - -/** - * s3c_hsotg_irq - handle device interrupt - * @irq: The IRQ number triggered - * @pw: The pw value when registered the handler. - */ -static irqreturn_t s3c_hsotg_irq(int irq, void *pw) -{ - struct s3c_hsotg *hsotg = pw; - int retry_count = 8; - u32 gintsts; - u32 gintmsk; - - spin_lock(&hsotg->lock); -irq_retry: - gintsts = readl(hsotg->regs + GINTSTS); - gintmsk = readl(hsotg->regs + GINTMSK); - - dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n", - __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count); - - gintsts &= gintmsk; - - if (gintsts & GINTSTS_OTGInt) { - u32 otgint = readl(hsotg->regs + GOTGINT); - - dev_info(hsotg->dev, "OTGInt: %08x\n", otgint); - - writel(otgint, hsotg->regs + GOTGINT); - } - - if (gintsts & GINTSTS_SessReqInt) { - dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__); - writel(GINTSTS_SessReqInt, hsotg->regs + GINTSTS); - } - - if (gintsts & GINTSTS_EnumDone) { - writel(GINTSTS_EnumDone, hsotg->regs + GINTSTS); - - s3c_hsotg_irq_enumdone(hsotg); - } - - if (gintsts & GINTSTS_ConIDStsChng) { - dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n", - readl(hsotg->regs + DSTS), - readl(hsotg->regs + GOTGCTL)); - - writel(GINTSTS_ConIDStsChng, hsotg->regs + GINTSTS); - } - - if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) { - u32 daint = readl(hsotg->regs + DAINT); - u32 daint_out = daint >> DAINT_OutEP_SHIFT; - u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); - int ep; - - dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); - - for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { - if (daint_out & 1) - s3c_hsotg_epint(hsotg, ep, 0); - } - - for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) { - if (daint_in & 1) - s3c_hsotg_epint(hsotg, ep, 1); - } - } - - if (gintsts & GINTSTS_USBRst) { - - u32 usb_status = readl(hsotg->regs + GOTGCTL); - - dev_info(hsotg->dev, "%s: USBRst\n", __func__); - dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", - readl(hsotg->regs + GNPTXSTS)); - - writel(GINTSTS_USBRst, hsotg->regs + GINTSTS); - - if (usb_status & GOTGCTL_BSESVLD) { - if (time_after(jiffies, hsotg->last_rst + - msecs_to_jiffies(200))) { - - kill_all_requests(hsotg, &hsotg->eps[0], - -ECONNRESET, true); - - s3c_hsotg_core_init(hsotg); - hsotg->last_rst = jiffies; - } - } - } - - /* check both FIFOs */ - - if (gintsts & GINTSTS_NPTxFEmp) { - dev_dbg(hsotg->dev, "NPTxFEmp\n"); - - /* - * Disable the interrupt to stop it happening again - * unless one of these endpoint routines decides that - * it needs re-enabling - */ - - s3c_hsotg_disable_gsint(hsotg, GINTSTS_NPTxFEmp); - s3c_hsotg_irq_fifoempty(hsotg, false); - } - - if (gintsts & GINTSTS_PTxFEmp) { - dev_dbg(hsotg->dev, "PTxFEmp\n"); - - /* See note in GINTSTS_NPTxFEmp */ - - s3c_hsotg_disable_gsint(hsotg, GINTSTS_PTxFEmp); - s3c_hsotg_irq_fifoempty(hsotg, true); - } - - if (gintsts & GINTSTS_RxFLvl) { - /* - * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, - * we need to retry s3c_hsotg_handle_rx if this is still - * set. - */ - - s3c_hsotg_handle_rx(hsotg); - } - - if (gintsts & GINTSTS_ModeMis) { - dev_warn(hsotg->dev, "warning, mode mismatch triggered\n"); - writel(GINTSTS_ModeMis, hsotg->regs + GINTSTS); - } - - if (gintsts & GINTSTS_USBSusp) { - dev_info(hsotg->dev, "GINTSTS_USBSusp\n"); - writel(GINTSTS_USBSusp, hsotg->regs + GINTSTS); - - call_gadget(hsotg, suspend); - s3c_hsotg_disconnect(hsotg); - } - - if (gintsts & GINTSTS_WkUpInt) { - dev_info(hsotg->dev, "GINTSTS_WkUpIn\n"); - writel(GINTSTS_WkUpInt, hsotg->regs + GINTSTS); - - call_gadget(hsotg, resume); - } - - if (gintsts & GINTSTS_ErlySusp) { - dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n"); - writel(GINTSTS_ErlySusp, hsotg->regs + GINTSTS); - - s3c_hsotg_disconnect(hsotg); - } - - /* - * these next two seem to crop-up occasionally causing the core - * to shutdown the USB transfer, so try clearing them and logging - * the occurrence. - */ - - if (gintsts & GINTSTS_GOUTNakEff) { - dev_info(hsotg->dev, "GOUTNakEff triggered\n"); - - writel(DCTL_CGOUTNak, hsotg->regs + DCTL); - - s3c_hsotg_dump(hsotg); - } - - if (gintsts & GINTSTS_GINNakEff) { - dev_info(hsotg->dev, "GINNakEff triggered\n"); - - writel(DCTL_CGNPInNAK, hsotg->regs + DCTL); - - s3c_hsotg_dump(hsotg); - } - - /* - * if we've had fifo events, we should try and go around the - * loop again to see if there's any point in returning yet. - */ - - if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) - goto irq_retry; - - spin_unlock(&hsotg->lock); - - return IRQ_HANDLED; -} - -/** - * s3c_hsotg_ep_enable - enable the given endpoint - * @ep: The USB endpint to configure - * @desc: The USB endpoint descriptor to configure with. - * - * This is called from the USB gadget code's usb_ep_enable(). - */ -static int s3c_hsotg_ep_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; - unsigned long flags; - int index = hs_ep->index; - u32 epctrl_reg; - u32 epctrl; - u32 mps; - int dir_in; - int ret = 0; - - dev_dbg(hsotg->dev, - "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", - __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes, - desc->wMaxPacketSize, desc->bInterval); - - /* not to be called for EP0 */ - WARN_ON(index == 0); - - dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - if (dir_in != hs_ep->dir_in) { - dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__); - return -EINVAL; - } - - mps = usb_endpoint_maxp(desc); - - /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */ - - epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - epctrl = readl(hsotg->regs + epctrl_reg); - - dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", - __func__, epctrl, epctrl_reg); - - spin_lock_irqsave(&hsotg->lock, flags); - - epctrl &= ~(DxEPCTL_EPType_MASK | DxEPCTL_MPS_MASK); - epctrl |= DxEPCTL_MPS(mps); - - /* - * mark the endpoint as active, otherwise the core may ignore - * transactions entirely for this endpoint - */ - epctrl |= DxEPCTL_USBActEp; - - /* - * set the NAK status on the endpoint, otherwise we might try and - * do something with data that we've yet got a request to process - * since the RXFIFO will take data for an endpoint even if the - * size register hasn't been set. - */ - - epctrl |= DxEPCTL_SNAK; - - /* update the endpoint state */ - hs_ep->ep.maxpacket = mps; - - /* default, set to non-periodic */ - hs_ep->periodic = 0; - - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_ISOC: - dev_err(hsotg->dev, "no current ISOC support\n"); - ret = -EINVAL; - goto out; - - case USB_ENDPOINT_XFER_BULK: - epctrl |= DxEPCTL_EPType_Bulk; - break; - - case USB_ENDPOINT_XFER_INT: - if (dir_in) { - /* - * Allocate our TxFNum by simply using the index - * of the endpoint for the moment. We could do - * something better if the host indicates how - * many FIFOs we are expecting to use. - */ - - hs_ep->periodic = 1; - epctrl |= DxEPCTL_TxFNum(index); - } - - epctrl |= DxEPCTL_EPType_Intterupt; - break; - - case USB_ENDPOINT_XFER_CONTROL: - epctrl |= DxEPCTL_EPType_Control; - break; - } - - /* - * if the hardware has dedicated fifos, we must give each IN EP - * a unique tx-fifo even if it is non-periodic. - */ - if (dir_in && hsotg->dedicated_fifos) - epctrl |= DxEPCTL_TxFNum(index); - - /* for non control endpoints, set PID to D0 */ - if (index) - epctrl |= DxEPCTL_SetD0PID; - - dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n", - __func__, epctrl); - - writel(epctrl, hsotg->regs + epctrl_reg); - dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n", - __func__, readl(hsotg->regs + epctrl_reg)); - - /* enable the endpoint interrupt */ - s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); - -out: - spin_unlock_irqrestore(&hsotg->lock, flags); - return ret; -} - -/** - * s3c_hsotg_ep_disable - disable given endpoint - * @ep: The endpoint to disable. - */ -static int s3c_hsotg_ep_disable(struct usb_ep *ep) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; - int dir_in = hs_ep->dir_in; - int index = hs_ep->index; - unsigned long flags; - u32 epctrl_reg; - u32 ctrl; - - dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep); - - if (ep == &hsotg->eps[0].ep) { - dev_err(hsotg->dev, "%s: called for ep0\n", __func__); - return -EINVAL; - } - - epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - - spin_lock_irqsave(&hsotg->lock, flags); - /* terminate all requests with shutdown */ - kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); - - - ctrl = readl(hsotg->regs + epctrl_reg); - ctrl &= ~DxEPCTL_EPEna; - ctrl &= ~DxEPCTL_USBActEp; - ctrl |= DxEPCTL_SNAK; - - dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - writel(ctrl, hsotg->regs + epctrl_reg); - - /* disable endpoint interrupts */ - s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); - - spin_unlock_irqrestore(&hsotg->lock, flags); - return 0; -} - -/** - * on_list - check request is on the given endpoint - * @ep: The endpoint to check. - * @test: The request to test if it is on the endpoint. - */ -static bool on_list(struct s3c_hsotg_ep *ep, struct s3c_hsotg_req *test) -{ - struct s3c_hsotg_req *req, *treq; - - list_for_each_entry_safe(req, treq, &ep->queue, queue) { - if (req == test) - return true; - } - - return false; -} - -/** - * s3c_hsotg_ep_dequeue - dequeue given endpoint - * @ep: The endpoint to dequeue. - * @req: The request to be removed from a queue. - */ -static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) -{ - struct s3c_hsotg_req *hs_req = our_req(req); - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; - unsigned long flags; - - dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); - - spin_lock_irqsave(&hs->lock, flags); - - if (!on_list(hs_ep, hs_req)) { - spin_unlock_irqrestore(&hs->lock, flags); - return -EINVAL; - } - - s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET); - spin_unlock_irqrestore(&hs->lock, flags); - - return 0; -} - -/** - * s3c_hsotg_ep_sethalt - set halt on a given endpoint - * @ep: The endpoint to set halt. - * @value: Set or unset the halt. - */ -static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; - int index = hs_ep->index; - u32 epreg; - u32 epctl; - u32 xfertype; - - dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); - - /* write both IN and OUT control registers */ - - epreg = DIEPCTL(index); - epctl = readl(hs->regs + epreg); - - if (value) { - epctl |= DxEPCTL_Stall + DxEPCTL_SNAK; - if (epctl & DxEPCTL_EPEna) - epctl |= DxEPCTL_EPDis; - } else { - epctl &= ~DxEPCTL_Stall; - xfertype = epctl & DxEPCTL_EPType_MASK; - if (xfertype == DxEPCTL_EPType_Bulk || - xfertype == DxEPCTL_EPType_Intterupt) - epctl |= DxEPCTL_SetD0PID; - } - - writel(epctl, hs->regs + epreg); - - epreg = DOEPCTL(index); - epctl = readl(hs->regs + epreg); - - if (value) - epctl |= DxEPCTL_Stall; - else { - epctl &= ~DxEPCTL_Stall; - xfertype = epctl & DxEPCTL_EPType_MASK; - if (xfertype == DxEPCTL_EPType_Bulk || - xfertype == DxEPCTL_EPType_Intterupt) - epctl |= DxEPCTL_SetD0PID; - } - - writel(epctl, hs->regs + epreg); - - return 0; -} - -/** - * s3c_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held - * @ep: The endpoint to set halt. - * @value: Set or unset the halt. - */ -static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) -{ - struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; - unsigned long flags = 0; - int ret = 0; - - spin_lock_irqsave(&hs->lock, flags); - ret = s3c_hsotg_ep_sethalt(ep, value); - spin_unlock_irqrestore(&hs->lock, flags); - - return ret; -} - -static struct usb_ep_ops s3c_hsotg_ep_ops = { - .enable = s3c_hsotg_ep_enable, - .disable = s3c_hsotg_ep_disable, - .alloc_request = s3c_hsotg_ep_alloc_request, - .free_request = s3c_hsotg_ep_free_request, - .queue = s3c_hsotg_ep_queue_lock, - .dequeue = s3c_hsotg_ep_dequeue, - .set_halt = s3c_hsotg_ep_sethalt_lock, - /* note, don't believe we have any call for the fifo routines */ -}; - -/** - * s3c_hsotg_phy_enable - enable platform phy dev - * @hsotg: The driver state - * - * A wrapper for platform code responsible for controlling - * low-level USB code - */ -static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) -{ - struct platform_device *pdev = to_platform_device(hsotg->dev); - - dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->plat->phy_init) - hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); -} - -/** - * s3c_hsotg_phy_disable - disable platform phy dev - * @hsotg: The driver state - * - * A wrapper for platform code responsible for controlling - * low-level USB code - */ -static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) -{ - struct platform_device *pdev = to_platform_device(hsotg->dev); - - if (hsotg->plat->phy_exit) - hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); -} - -/** - * s3c_hsotg_init - initalize the usb core - * @hsotg: The driver state - */ -static void s3c_hsotg_init(struct s3c_hsotg *hsotg) -{ - /* unmask subset of endpoint interrupts */ - - writel(DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | - DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk, - hsotg->regs + DIEPMSK); - - writel(DOEPMSK_SetupMsk | DOEPMSK_AHBErrMsk | - DOEPMSK_EPDisbldMsk | DOEPMSK_XferComplMsk, - hsotg->regs + DOEPMSK); - - writel(0, hsotg->regs + DAINTMSK); - - /* Be in disconnected state until gadget is registered */ - __orr32(hsotg->regs + DCTL, DCTL_SftDiscon); - - if (0) { - /* post global nak until we're ready */ - writel(DCTL_SGNPInNAK | DCTL_SGOUTNak, - hsotg->regs + DCTL); - } - - /* setup fifos */ - - dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - readl(hsotg->regs + GRXFSIZ), - readl(hsotg->regs + GNPTXFSIZ)); - - s3c_hsotg_init_fifo(hsotg); - - /* set the PLL on, remove the HNP/SRP and set the PHY */ - writel(GUSBCFG_PHYIf16 | GUSBCFG_TOutCal(7) | (0x5 << 10), - hsotg->regs + GUSBCFG); - - writel(using_dma(hsotg) ? GAHBCFG_DMAEn : 0x0, - hsotg->regs + GAHBCFG); -} - -/** - * s3c_hsotg_udc_start - prepare the udc for work - * @gadget: The usb gadget state - * @driver: The usb gadget driver - * - * Perform initialization to prepare udc device and driver - * to work. - */ -static int s3c_hsotg_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct s3c_hsotg *hsotg = to_hsotg(gadget); - int ret; - - if (!hsotg) { - printk(KERN_ERR "%s: called with no device\n", __func__); - return -ENODEV; - } - - if (!driver) { - dev_err(hsotg->dev, "%s: no driver\n", __func__); - return -EINVAL; - } - - if (driver->max_speed < USB_SPEED_FULL) - dev_err(hsotg->dev, "%s: bad speed\n", __func__); - - if (!driver->setup) { - dev_err(hsotg->dev, "%s: missing entry points\n", __func__); - return -EINVAL; - } - - WARN_ON(hsotg->driver); - - driver->driver.bus = NULL; - hsotg->driver = driver; - hsotg->gadget.dev.driver = &driver->driver; - hsotg->gadget.dev.of_node = hsotg->dev->of_node; - hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask; - hsotg->gadget.speed = USB_SPEED_UNKNOWN; - - ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret); - goto err; - } - - hsotg->last_rst = jiffies; - dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); - return 0; - -err: - hsotg->driver = NULL; - hsotg->gadget.dev.driver = NULL; - return ret; -} - -/** - * s3c_hsotg_udc_stop - stop the udc - * @gadget: The usb gadget state - * @driver: The usb gadget driver - * - * Stop udc hw block and stay tunned for future transmissions - */ -static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct s3c_hsotg *hsotg = to_hsotg(gadget); - unsigned long flags = 0; - int ep; - - if (!hsotg) - return -ENODEV; - - if (!driver || driver != hsotg->driver || !driver->unbind) - return -EINVAL; - - /* all endpoints should be shutdown */ - for (ep = 0; ep < hsotg->num_of_eps; ep++) - s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); - - spin_lock_irqsave(&hsotg->lock, flags); - - s3c_hsotg_phy_disable(hsotg); - regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - - hsotg->driver = NULL; - hsotg->gadget.speed = USB_SPEED_UNKNOWN; - hsotg->gadget.dev.driver = NULL; - - spin_unlock_irqrestore(&hsotg->lock, flags); - - dev_info(hsotg->dev, "unregistered gadget driver '%s'\n", - driver->driver.name); - - return 0; -} - -/** - * s3c_hsotg_gadget_getframe - read the frame number - * @gadget: The usb gadget state - * - * Read the {micro} frame number - */ -static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) -{ - return s3c_hsotg_read_frameno(to_hsotg(gadget)); -} - -/** - * s3c_hsotg_pullup - connect/disconnect the USB PHY - * @gadget: The usb gadget state - * @is_on: Current state of the USB PHY - * - * Connect/Disconnect the USB PHY pullup - */ -static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) -{ - struct s3c_hsotg *hsotg = to_hsotg(gadget); - unsigned long flags = 0; - - dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on); - - spin_lock_irqsave(&hsotg->lock, flags); - if (is_on) { - s3c_hsotg_phy_enable(hsotg); - s3c_hsotg_core_init(hsotg); - } else { - s3c_hsotg_disconnect(hsotg); - s3c_hsotg_phy_disable(hsotg); - } - - hsotg->gadget.speed = USB_SPEED_UNKNOWN; - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static struct usb_gadget_ops s3c_hsotg_gadget_ops = { - .get_frame = s3c_hsotg_gadget_getframe, - .udc_start = s3c_hsotg_udc_start, - .udc_stop = s3c_hsotg_udc_stop, - .pullup = s3c_hsotg_pullup, -}; - -/** - * s3c_hsotg_initep - initialise a single endpoint - * @hsotg: The device state. - * @hs_ep: The endpoint to be initialised. - * @epnum: The endpoint number - * - * Initialise the given endpoint (as part of the probe and device state - * creation) to give to the gadget driver. Setup the endpoint name, any - * direction information and other state that may be required. - */ -static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - int epnum) -{ - u32 ptxfifo; - char *dir; - - if (epnum == 0) - dir = ""; - else if ((epnum % 2) == 0) { - dir = "out"; - } else { - dir = "in"; - hs_ep->dir_in = 1; - } - - hs_ep->index = epnum; - - snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir); - - INIT_LIST_HEAD(&hs_ep->queue); - INIT_LIST_HEAD(&hs_ep->ep.ep_list); - - /* add to the list of endpoints known by the gadget driver */ - if (epnum) - list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list); - - hs_ep->parent = hsotg; - hs_ep->ep.name = hs_ep->name; - hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT; - hs_ep->ep.ops = &s3c_hsotg_ep_ops; - - /* - * Read the FIFO size for the Periodic TX FIFO, even if we're - * an OUT endpoint, we may as well do this if in future the - * code is changed to make each endpoint's direction changeable. - */ - - ptxfifo = readl(hsotg->regs + DPTXFSIZn(epnum)); - hs_ep->fifo_size = DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4; - - /* - * if we're using dma, we need to set the next-endpoint pointer - * to be something valid. - */ - - if (using_dma(hsotg)) { - u32 next = DxEPCTL_NextEp((epnum + 1) % 15); - writel(next, hsotg->regs + DIEPCTL(epnum)); - writel(next, hsotg->regs + DOEPCTL(epnum)); - } -} - -/** - * s3c_hsotg_hw_cfg - read HW configuration registers - * @param: The device state - * - * Read the USB core HW configuration registers - */ -static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) -{ - u32 cfg2, cfg4; - /* check hardware configuration */ - - cfg2 = readl(hsotg->regs + 0x48); - hsotg->num_of_eps = (cfg2 >> 10) & 0xF; - - dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps); - - cfg4 = readl(hsotg->regs + 0x50); - hsotg->dedicated_fifos = (cfg4 >> 25) & 1; - - dev_info(hsotg->dev, "%s fifos\n", - hsotg->dedicated_fifos ? "dedicated" : "shared"); -} - -/** - * s3c_hsotg_dump - dump state of the udc - * @param: The device state - */ -static void s3c_hsotg_dump(struct s3c_hsotg *hsotg) -{ -#ifdef DEBUG - struct device *dev = hsotg->dev; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n", - readl(regs + DCFG), readl(regs + DCTL), - readl(regs + DIEPMSK)); - - dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n", - readl(regs + GAHBCFG), readl(regs + 0x44)); - - dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ)); - - /* show periodic fifo settings */ - - for (idx = 1; idx <= 15; idx++) { - val = readl(regs + DPTXFSIZn(idx)); - dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, - val >> DPTXFSIZn_DPTxFSize_SHIFT, - val & DPTXFSIZn_DPTxFStAddr_MASK); - } - - for (idx = 0; idx < 15; idx++) { - dev_info(dev, - "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, - readl(regs + DIEPCTL(idx)), - readl(regs + DIEPTSIZ(idx)), - readl(regs + DIEPDMA(idx))); - - val = readl(regs + DOEPCTL(idx)); - dev_info(dev, - "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", - idx, readl(regs + DOEPCTL(idx)), - readl(regs + DOEPTSIZ(idx)), - readl(regs + DOEPDMA(idx))); - - } - - dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n", - readl(regs + DVBUSDIS), readl(regs + DVBUSPULSE)); -#endif -} - -/** - * state_show - debugfs: show overall driver and device state. - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows the overall state of the hardware and - * some general information about each of the endpoints available - * to the system. - */ -static int state_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - int idx; - - seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); - - seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); - - seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); - - seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); - - seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - readl(regs + GRXSTSR)); - - seq_printf(seq, "\nEndpoint status:\n"); - - for (idx = 0; idx < 15; idx++) { - u32 in, out; - - in = readl(regs + DIEPCTL(idx)); - out = readl(regs + DOEPCTL(idx)); - - seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", - idx, in, out); - - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); - - seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", - in, out); - - seq_printf(seq, "\n"); - } - - return 0; -} - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * fifo_show - debugfs: show the fifo information - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * Show the FIFO information for the overall fifo and all the - * periodic transmission FIFOs. - */ -static int fifo_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - seq_printf(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); - - val = readl(regs + GNPTXFSIZ); - seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", - val >> GNPTXFSIZ_NPTxFDep_SHIFT, - val & GNPTXFSIZ_NPTxFStAddr_MASK); - - seq_printf(seq, "\nPeriodic TXFIFOs:\n"); - - for (idx = 1; idx <= 15; idx++) { - val = readl(regs + DPTXFSIZn(idx)); - - seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, - val >> DPTXFSIZn_DPTxFSize_SHIFT, - val & DPTXFSIZn_DPTxFStAddr_MASK); - } - - return 0; -} - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static const char *decode_direction(int is_in) -{ - return is_in ? "in" : "out"; -} - -/** - * ep_show - debugfs: show the state of an endpoint. - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * This debugfs entry shows the state of the given endpoint (one is - * registered for each available). - */ -static int ep_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg_ep *ep = seq->private; - struct s3c_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; - void __iomem *regs = hsotg->regs; - int index = ep->index; - int show_limit = 15; - unsigned long flags; - - seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", - ep->index, ep->ep.name, decode_direction(ep->dir_in)); - - /* first show the register state */ - - seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); - - seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); - - seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); - - seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); - - seq_printf(seq, "\n"); - seq_printf(seq, "mps %d\n", ep->ep.maxpacket); - seq_printf(seq, "total_data=%ld\n", ep->total_data); - - seq_printf(seq, "request list (%p,%p):\n", - ep->queue.next, ep->queue.prev); - - spin_lock_irqsave(&hsotg->lock, flags); - - list_for_each_entry(req, &ep->queue, queue) { - if (--show_limit < 0) { - seq_printf(seq, "not showing more requests...\n"); - break; - } - - seq_printf(seq, "%c req %p: %d bytes @%p, ", - req == ep->req ? '*' : ' ', - req, req->req.length, req->req.buf); - seq_printf(seq, "%d done, res %d\n", - req->req.actual, req->req.status); - } - - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * s3c_hsotg_create_debug - create debugfs directory and files - * @hsotg: The driver state - * - * Create the debugfs files to allow the user to get information - * about the state of the system. The directory name is created - * with the same name as the device itself, in case we end up - * with multiple blocks in future systems. - */ -static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg) -{ - struct dentry *root; - unsigned epidx; - - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); - hsotg->debug_root = root; - if (IS_ERR(root)) { - dev_err(hsotg->dev, "cannot create debug root\n"); - return; - } - - /* create general state file */ - - hsotg->debug_file = debugfs_create_file("state", 0444, root, - hsotg, &state_fops); - - if (IS_ERR(hsotg->debug_file)) - dev_err(hsotg->dev, "%s: failed to create state\n", __func__); - - hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root, - hsotg, &fifo_fops); - - if (IS_ERR(hsotg->debug_fifo)) - dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - - /* create one file for each endpoint */ - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep = &hsotg->eps[epidx]; - - ep->debugfs = debugfs_create_file(ep->name, 0444, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } -} - -/** - * s3c_hsotg_delete_debug - cleanup debugfs entries - * @hsotg: The driver state - * - * Cleanup (remove) the debugfs files for use on module exit. - */ -static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) -{ - unsigned epidx; - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep = &hsotg->eps[epidx]; - debugfs_remove(ep->debugfs); - } - - debugfs_remove(hsotg->debug_file); - debugfs_remove(hsotg->debug_fifo); - debugfs_remove(hsotg->debug_root); -} - -/** - * s3c_hsotg_release - release callback for hsotg device - * @dev: Device to for which release is called - * - * Nothing to do as the resource is allocated using devm_ API. - */ -static void s3c_hsotg_release(struct device *dev) -{ -} - -/** - * s3c_hsotg_probe - probe function for hsotg driver - * @pdev: The platform information for the driver - */ - -static int s3c_hsotg_probe(struct platform_device *pdev) -{ - struct s3c_hsotg_plat *plat = pdev->dev.platform_data; - struct device *dev = &pdev->dev; - struct s3c_hsotg_ep *eps; - struct s3c_hsotg *hsotg; - struct resource *res; - int epnum; - int ret; - int i; - - plat = pdev->dev.platform_data; - if (!plat) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); - if (!hsotg) { - dev_err(dev, "cannot get memory\n"); - return -ENOMEM; - } - - hsotg->dev = dev; - hsotg->plat = plat; - - hsotg->clk = devm_clk_get(&pdev->dev, "otg"); - if (IS_ERR(hsotg->clk)) { - dev_err(dev, "cannot get otg clock\n"); - return PTR_ERR(hsotg->clk); - } - - platform_set_drvdata(pdev, hsotg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - hsotg->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!hsotg->regs) { - dev_err(dev, "cannot map registers\n"); - ret = -ENXIO; - goto err_clk; - } - - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "cannot find IRQ\n"); - goto err_clk; - } - - spin_lock_init(&hsotg->lock); - - hsotg->irq = ret; - - ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, - dev_name(dev), hsotg); - if (ret < 0) { - dev_err(dev, "cannot claim IRQ\n"); - goto err_clk; - } - - dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); - - device_initialize(&hsotg->gadget.dev); - - dev_set_name(&hsotg->gadget.dev, "gadget"); - - hsotg->gadget.max_speed = USB_SPEED_HIGH; - hsotg->gadget.ops = &s3c_hsotg_gadget_ops; - hsotg->gadget.name = dev_name(dev); - - hsotg->gadget.dev.parent = dev; - hsotg->gadget.dev.dma_mask = dev->dma_mask; - hsotg->gadget.dev.release = s3c_hsotg_release; - - /* reset the system */ - - clk_prepare_enable(hsotg->clk); - - /* regulators */ - - for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++) - hsotg->supplies[i].supply = s3c_hsotg_supply_names[i]; - - ret = regulator_bulk_get(dev, ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(dev, "failed to request supplies: %d\n", ret); - goto err_clk; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - - if (ret) { - dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret); - goto err_supplies; - } - - /* usb phy enable */ - s3c_hsotg_phy_enable(hsotg); - - s3c_hsotg_corereset(hsotg); - s3c_hsotg_init(hsotg); - s3c_hsotg_hw_cfg(hsotg); - - /* hsotg->num_of_eps holds number of EPs other than ep0 */ - - if (hsotg->num_of_eps == 0) { - dev_err(dev, "wrong number of EPs (zero)\n"); - ret = -EINVAL; - goto err_supplies; - } - - eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep), - GFP_KERNEL); - if (!eps) { - dev_err(dev, "cannot get memory\n"); - ret = -ENOMEM; - goto err_supplies; - } - - hsotg->eps = eps; - - /* setup endpoint information */ - - INIT_LIST_HEAD(&hsotg->gadget.ep_list); - hsotg->gadget.ep0 = &hsotg->eps[0].ep; - - /* allocate EP0 request */ - - hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep, - GFP_KERNEL); - if (!hsotg->ctrl_req) { - dev_err(dev, "failed to allocate ctrl req\n"); - ret = -ENOMEM; - goto err_ep_mem; - } - - /* initialise the endpoints now the core has been initialised */ - for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) - s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum); - - /* disable power and clock */ - - ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret); - goto err_ep_mem; - } - - s3c_hsotg_phy_disable(hsotg); - - ret = device_add(&hsotg->gadget.dev); - if (ret) { - put_device(&hsotg->gadget.dev); - goto err_ep_mem; - } - - ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget); - if (ret) - goto err_ep_mem; - - s3c_hsotg_create_debug(hsotg); - - s3c_hsotg_dump(hsotg); - - return 0; - -err_ep_mem: - kfree(eps); -err_supplies: - s3c_hsotg_phy_disable(hsotg); - regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - -err_clk: - clk_disable_unprepare(hsotg->clk); - - return ret; -} - -/** - * s3c_hsotg_remove - remove function for hsotg driver - * @pdev: The platform information for the driver - */ -static int s3c_hsotg_remove(struct platform_device *pdev) -{ - struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); - - usb_del_gadget_udc(&hsotg->gadget); - - s3c_hsotg_delete_debug(hsotg); - - if (hsotg->driver) { - /* should have been done already by driver model core */ - usb_gadget_unregister_driver(hsotg->driver); - } - - s3c_hsotg_phy_disable(hsotg); - regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - - clk_disable_unprepare(hsotg->clk); - - device_unregister(&hsotg->gadget.dev); - return 0; -} - -#if 1 -#define s3c_hsotg_suspend NULL -#define s3c_hsotg_resume NULL -#endif - -static struct platform_driver s3c_hsotg_driver = { - .driver = { - .name = "s3c-hsotg", - .owner = THIS_MODULE, - }, - .probe = s3c_hsotg_probe, - .remove = s3c_hsotg_remove, - .suspend = s3c_hsotg_suspend, - .resume = s3c_hsotg_resume, -}; - -module_platform_driver(s3c_hsotg_driver); - -MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device"); -MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c-hsotg"); diff --git a/drivers/usb/gadget/s3c-hsotg.h b/drivers/usb/gadget/s3c-hsotg.h deleted file mode 100644 index d650b129583..00000000000 --- a/drivers/usb/gadget/s3c-hsotg.h +++ /dev/null @@ -1,377 +0,0 @@ -/* drivers/usb/gadget/s3c-hsotg.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks <ben@simtec.co.uk> - * - * USB2.0 Highspeed/OtG Synopsis DWC2 device block registers - * - * 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 __REGS_USB_HSOTG_H -#define __REGS_USB_HSOTG_H __FILE__ - -#define HSOTG_REG(x) (x) - -#define GOTGCTL HSOTG_REG(0x000) -#define GOTGCTL_BSESVLD (1 << 19) -#define GOTGCTL_ASESVLD (1 << 18) -#define GOTGCTL_DBNC_SHORT (1 << 17) -#define GOTGCTL_CONID_B (1 << 16) -#define GOTGCTL_DEVHNPEN (1 << 11) -#define GOTGCTL_HSSETHNPEN (1 << 10) -#define GOTGCTL_HNPREQ (1 << 9) -#define GOTGCTL_HSTNEGSCS (1 << 8) -#define GOTGCTL_SESREQ (1 << 1) -#define GOTGCTL_SESREQSCS (1 << 0) - -#define GOTGINT HSOTG_REG(0x004) -#define GOTGINT_DbnceDone (1 << 19) -#define GOTGINT_ADevTOUTChg (1 << 18) -#define GOTGINT_HstNegDet (1 << 17) -#define GOTGINT_HstnegSucStsChng (1 << 9) -#define GOTGINT_SesReqSucStsChng (1 << 8) -#define GOTGINT_SesEndDet (1 << 2) - -#define GAHBCFG HSOTG_REG(0x008) -#define GAHBCFG_PTxFEmpLvl (1 << 8) -#define GAHBCFG_NPTxFEmpLvl (1 << 7) -#define GAHBCFG_DMAEn (1 << 5) -#define GAHBCFG_HBstLen_MASK (0xf << 1) -#define GAHBCFG_HBstLen_SHIFT (1) -#define GAHBCFG_HBstLen_Single (0x0 << 1) -#define GAHBCFG_HBstLen_Incr (0x1 << 1) -#define GAHBCFG_HBstLen_Incr4 (0x3 << 1) -#define GAHBCFG_HBstLen_Incr8 (0x5 << 1) -#define GAHBCFG_HBstLen_Incr16 (0x7 << 1) -#define GAHBCFG_GlblIntrEn (1 << 0) - -#define GUSBCFG HSOTG_REG(0x00C) -#define GUSBCFG_PHYLPClkSel (1 << 15) -#define GUSBCFG_HNPCap (1 << 9) -#define GUSBCFG_SRPCap (1 << 8) -#define GUSBCFG_PHYIf16 (1 << 3) -#define GUSBCFG_TOutCal_MASK (0x7 << 0) -#define GUSBCFG_TOutCal_SHIFT (0) -#define GUSBCFG_TOutCal_LIMIT (0x7) -#define GUSBCFG_TOutCal(_x) ((_x) << 0) - -#define GRSTCTL HSOTG_REG(0x010) - -#define GRSTCTL_AHBIdle (1 << 31) -#define GRSTCTL_DMAReq (1 << 30) -#define GRSTCTL_TxFNum_MASK (0x1f << 6) -#define GRSTCTL_TxFNum_SHIFT (6) -#define GRSTCTL_TxFNum_LIMIT (0x1f) -#define GRSTCTL_TxFNum(_x) ((_x) << 6) -#define GRSTCTL_TxFFlsh (1 << 5) -#define GRSTCTL_RxFFlsh (1 << 4) -#define GRSTCTL_INTknQFlsh (1 << 3) -#define GRSTCTL_FrmCntrRst (1 << 2) -#define GRSTCTL_HSftRst (1 << 1) -#define GRSTCTL_CSftRst (1 << 0) - -#define GINTSTS HSOTG_REG(0x014) -#define GINTMSK HSOTG_REG(0x018) - -#define GINTSTS_WkUpInt (1 << 31) -#define GINTSTS_SessReqInt (1 << 30) -#define GINTSTS_DisconnInt (1 << 29) -#define GINTSTS_ConIDStsChng (1 << 28) -#define GINTSTS_PTxFEmp (1 << 26) -#define GINTSTS_HChInt (1 << 25) -#define GINTSTS_PrtInt (1 << 24) -#define GINTSTS_FetSusp (1 << 22) -#define GINTSTS_incompIP (1 << 21) -#define GINTSTS_IncomplSOIN (1 << 20) -#define GINTSTS_OEPInt (1 << 19) -#define GINTSTS_IEPInt (1 << 18) -#define GINTSTS_EPMis (1 << 17) -#define GINTSTS_EOPF (1 << 15) -#define GINTSTS_ISOutDrop (1 << 14) -#define GINTSTS_EnumDone (1 << 13) -#define GINTSTS_USBRst (1 << 12) -#define GINTSTS_USBSusp (1 << 11) -#define GINTSTS_ErlySusp (1 << 10) -#define GINTSTS_GOUTNakEff (1 << 7) -#define GINTSTS_GINNakEff (1 << 6) -#define GINTSTS_NPTxFEmp (1 << 5) -#define GINTSTS_RxFLvl (1 << 4) -#define GINTSTS_SOF (1 << 3) -#define GINTSTS_OTGInt (1 << 2) -#define GINTSTS_ModeMis (1 << 1) -#define GINTSTS_CurMod_Host (1 << 0) - -#define GRXSTSR HSOTG_REG(0x01C) -#define GRXSTSP HSOTG_REG(0x020) - -#define GRXSTS_FN_MASK (0x7f << 25) -#define GRXSTS_FN_SHIFT (25) - -#define GRXSTS_PktSts_MASK (0xf << 17) -#define GRXSTS_PktSts_SHIFT (17) -#define GRXSTS_PktSts_GlobalOutNAK (0x1 << 17) -#define GRXSTS_PktSts_OutRX (0x2 << 17) -#define GRXSTS_PktSts_OutDone (0x3 << 17) -#define GRXSTS_PktSts_SetupDone (0x4 << 17) -#define GRXSTS_PktSts_SetupRX (0x6 << 17) - -#define GRXSTS_DPID_MASK (0x3 << 15) -#define GRXSTS_DPID_SHIFT (15) -#define GRXSTS_ByteCnt_MASK (0x7ff << 4) -#define GRXSTS_ByteCnt_SHIFT (4) -#define GRXSTS_EPNum_MASK (0xf << 0) -#define GRXSTS_EPNum_SHIFT (0) - -#define GRXFSIZ HSOTG_REG(0x024) - -#define GNPTXFSIZ HSOTG_REG(0x028) - -#define GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16) -#define GNPTXFSIZ_NPTxFDep_SHIFT (16) -#define GNPTXFSIZ_NPTxFDep_LIMIT (0xffff) -#define GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16) -#define GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0) -#define GNPTXFSIZ_NPTxFStAddr_SHIFT (0) -#define GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff) -#define GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0) - -#define GNPTXSTS HSOTG_REG(0x02C) - -#define GNPTXSTS_NPtxQTop_MASK (0x7f << 24) -#define GNPTXSTS_NPtxQTop_SHIFT (24) - -#define GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16) -#define GNPTXSTS_NPTxQSpcAvail_SHIFT (16) -#define GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff) - -#define GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0) -#define GNPTXSTS_NPTxFSpcAvail_SHIFT (0) -#define GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff) - - -#define HPTXFSIZ HSOTG_REG(0x100) - -#define DPTXFSIZn(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) - -#define DPTXFSIZn_DPTxFSize_MASK (0xffff << 16) -#define DPTXFSIZn_DPTxFSize_SHIFT (16) -#define DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff) -#define DPTXFSIZn_DPTxFSize_LIMIT (0xffff) -#define DPTXFSIZn_DPTxFSize(_x) ((_x) << 16) - -#define DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0) -#define DPTXFSIZn_DPTxFStAddr_SHIFT (0) - -/* Device mode registers */ -#define DCFG HSOTG_REG(0x800) - -#define DCFG_EPMisCnt_MASK (0x1f << 18) -#define DCFG_EPMisCnt_SHIFT (18) -#define DCFG_EPMisCnt_LIMIT (0x1f) -#define DCFG_EPMisCnt(_x) ((_x) << 18) - -#define DCFG_PerFrInt_MASK (0x3 << 11) -#define DCFG_PerFrInt_SHIFT (11) -#define DCFG_PerFrInt_LIMIT (0x3) -#define DCFG_PerFrInt(_x) ((_x) << 11) - -#define DCFG_DevAddr_MASK (0x7f << 4) -#define DCFG_DevAddr_SHIFT (4) -#define DCFG_DevAddr_LIMIT (0x7f) -#define DCFG_DevAddr(_x) ((_x) << 4) - -#define DCFG_NZStsOUTHShk (1 << 2) - -#define DCFG_DevSpd_MASK (0x3 << 0) -#define DCFG_DevSpd_SHIFT (0) -#define DCFG_DevSpd_HS (0x0 << 0) -#define DCFG_DevSpd_FS (0x1 << 0) -#define DCFG_DevSpd_LS (0x2 << 0) -#define DCFG_DevSpd_FS48 (0x3 << 0) - -#define DCTL HSOTG_REG(0x804) - -#define DCTL_PWROnPrgDone (1 << 11) -#define DCTL_CGOUTNak (1 << 10) -#define DCTL_SGOUTNak (1 << 9) -#define DCTL_CGNPInNAK (1 << 8) -#define DCTL_SGNPInNAK (1 << 7) -#define DCTL_TstCtl_MASK (0x7 << 4) -#define DCTL_TstCtl_SHIFT (4) -#define DCTL_GOUTNakSts (1 << 3) -#define DCTL_GNPINNakSts (1 << 2) -#define DCTL_SftDiscon (1 << 1) -#define DCTL_RmtWkUpSig (1 << 0) - -#define DSTS HSOTG_REG(0x808) - -#define DSTS_SOFFN_MASK (0x3fff << 8) -#define DSTS_SOFFN_SHIFT (8) -#define DSTS_SOFFN_LIMIT (0x3fff) -#define DSTS_SOFFN(_x) ((_x) << 8) -#define DSTS_ErraticErr (1 << 3) -#define DSTS_EnumSpd_MASK (0x3 << 1) -#define DSTS_EnumSpd_SHIFT (1) -#define DSTS_EnumSpd_HS (0x0 << 1) -#define DSTS_EnumSpd_FS (0x1 << 1) -#define DSTS_EnumSpd_LS (0x2 << 1) -#define DSTS_EnumSpd_FS48 (0x3 << 1) - -#define DSTS_SuspSts (1 << 0) - -#define DIEPMSK HSOTG_REG(0x810) - -#define DIEPMSK_TxFIFOEmpty (1 << 7) -#define DIEPMSK_INEPNakEffMsk (1 << 6) -#define DIEPMSK_INTknEPMisMsk (1 << 5) -#define DIEPMSK_INTknTXFEmpMsk (1 << 4) -#define DIEPMSK_TimeOUTMsk (1 << 3) -#define DIEPMSK_AHBErrMsk (1 << 2) -#define DIEPMSK_EPDisbldMsk (1 << 1) -#define DIEPMSK_XferComplMsk (1 << 0) - -#define DOEPMSK HSOTG_REG(0x814) - -#define DOEPMSK_Back2BackSetup (1 << 6) -#define DOEPMSK_OUTTknEPdisMsk (1 << 4) -#define DOEPMSK_SetupMsk (1 << 3) -#define DOEPMSK_AHBErrMsk (1 << 2) -#define DOEPMSK_EPDisbldMsk (1 << 1) -#define DOEPMSK_XferComplMsk (1 << 0) - -#define DAINT HSOTG_REG(0x818) -#define DAINTMSK HSOTG_REG(0x81C) - -#define DAINT_OutEP_SHIFT (16) -#define DAINT_OutEP(x) (1 << ((x) + 16)) -#define DAINT_InEP(x) (1 << (x)) - -#define DTKNQR1 HSOTG_REG(0x820) -#define DTKNQR2 HSOTG_REG(0x824) -#define DTKNQR3 HSOTG_REG(0x830) -#define DTKNQR4 HSOTG_REG(0x834) - -#define DVBUSDIS HSOTG_REG(0x828) -#define DVBUSPULSE HSOTG_REG(0x82C) - -#define DIEPCTL0 HSOTG_REG(0x900) -#define DOEPCTL0 HSOTG_REG(0xB00) -#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) -#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) - -/* EP0 specialness: - * bits[29..28] - reserved (no SetD0PID, SetD1PID) - * bits[25..22] - should always be zero, this isn't a periodic endpoint - * bits[10..0] - MPS setting differenct for EP0 - */ -#define D0EPCTL_MPS_MASK (0x3 << 0) -#define D0EPCTL_MPS_SHIFT (0) -#define D0EPCTL_MPS_64 (0x0 << 0) -#define D0EPCTL_MPS_32 (0x1 << 0) -#define D0EPCTL_MPS_16 (0x2 << 0) -#define D0EPCTL_MPS_8 (0x3 << 0) - -#define DxEPCTL_EPEna (1 << 31) -#define DxEPCTL_EPDis (1 << 30) -#define DxEPCTL_SetD1PID (1 << 29) -#define DxEPCTL_SetOddFr (1 << 29) -#define DxEPCTL_SetD0PID (1 << 28) -#define DxEPCTL_SetEvenFr (1 << 28) -#define DxEPCTL_SNAK (1 << 27) -#define DxEPCTL_CNAK (1 << 26) -#define DxEPCTL_TxFNum_MASK (0xf << 22) -#define DxEPCTL_TxFNum_SHIFT (22) -#define DxEPCTL_TxFNum_LIMIT (0xf) -#define DxEPCTL_TxFNum(_x) ((_x) << 22) - -#define DxEPCTL_Stall (1 << 21) -#define DxEPCTL_Snp (1 << 20) -#define DxEPCTL_EPType_MASK (0x3 << 18) -#define DxEPCTL_EPType_SHIFT (18) -#define DxEPCTL_EPType_Control (0x0 << 18) -#define DxEPCTL_EPType_Iso (0x1 << 18) -#define DxEPCTL_EPType_Bulk (0x2 << 18) -#define DxEPCTL_EPType_Intterupt (0x3 << 18) - -#define DxEPCTL_NAKsts (1 << 17) -#define DxEPCTL_DPID (1 << 16) -#define DxEPCTL_EOFrNum (1 << 16) -#define DxEPCTL_USBActEp (1 << 15) -#define DxEPCTL_NextEp_MASK (0xf << 11) -#define DxEPCTL_NextEp_SHIFT (11) -#define DxEPCTL_NextEp_LIMIT (0xf) -#define DxEPCTL_NextEp(_x) ((_x) << 11) - -#define DxEPCTL_MPS_MASK (0x7ff << 0) -#define DxEPCTL_MPS_SHIFT (0) -#define DxEPCTL_MPS_LIMIT (0x7ff) -#define DxEPCTL_MPS(_x) ((_x) << 0) - -#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) -#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) - -#define DxEPINT_INEPNakEff (1 << 6) -#define DxEPINT_Back2BackSetup (1 << 6) -#define DxEPINT_INTknEPMis (1 << 5) -#define DxEPINT_INTknTXFEmp (1 << 4) -#define DxEPINT_OUTTknEPdis (1 << 4) -#define DxEPINT_Timeout (1 << 3) -#define DxEPINT_Setup (1 << 3) -#define DxEPINT_AHBErr (1 << 2) -#define DxEPINT_EPDisbld (1 << 1) -#define DxEPINT_XferCompl (1 << 0) - -#define DIEPTSIZ0 HSOTG_REG(0x910) - -#define DIEPTSIZ0_PktCnt_MASK (0x3 << 19) -#define DIEPTSIZ0_PktCnt_SHIFT (19) -#define DIEPTSIZ0_PktCnt_LIMIT (0x3) -#define DIEPTSIZ0_PktCnt(_x) ((_x) << 19) - -#define DIEPTSIZ0_XferSize_MASK (0x7f << 0) -#define DIEPTSIZ0_XferSize_SHIFT (0) -#define DIEPTSIZ0_XferSize_LIMIT (0x7f) -#define DIEPTSIZ0_XferSize(_x) ((_x) << 0) - -#define DOEPTSIZ0 HSOTG_REG(0xB10) -#define DOEPTSIZ0_SUPCnt_MASK (0x3 << 29) -#define DOEPTSIZ0_SUPCnt_SHIFT (29) -#define DOEPTSIZ0_SUPCnt_LIMIT (0x3) -#define DOEPTSIZ0_SUPCnt(_x) ((_x) << 29) - -#define DOEPTSIZ0_PktCnt (1 << 19) -#define DOEPTSIZ0_XferSize_MASK (0x7f << 0) -#define DOEPTSIZ0_XferSize_SHIFT (0) - -#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) -#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) - -#define DxEPTSIZ_MC_MASK (0x3 << 29) -#define DxEPTSIZ_MC_SHIFT (29) -#define DxEPTSIZ_MC_LIMIT (0x3) -#define DxEPTSIZ_MC(_x) ((_x) << 29) - -#define DxEPTSIZ_PktCnt_MASK (0x3ff << 19) -#define DxEPTSIZ_PktCnt_SHIFT (19) -#define DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff) -#define DxEPTSIZ_PktCnt_LIMIT (0x3ff) -#define DxEPTSIZ_PktCnt(_x) ((_x) << 19) - -#define DxEPTSIZ_XferSize_MASK (0x7ffff << 0) -#define DxEPTSIZ_XferSize_SHIFT (0) -#define DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff) -#define DxEPTSIZ_XferSize_LIMIT (0x7ffff) -#define DxEPTSIZ_XferSize(_x) ((_x) << 0) - -#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) -#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) -#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) - -#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) - -#endif /* __REGS_USB_HSOTG_H */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 52379b11f08..10c6a128250 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -283,7 +283,6 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) /** * s3c_hsudc_stop_activity - Stop activity on all endpoints. * @hsudc: Device controller for which EP activity is to be stopped. - * @driver: Reference to the gadget driver which is currently active. * * All the endpoints are stopped and any pending transfer requests if any on * the endpoint are terminated. @@ -435,7 +434,7 @@ static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx) struct s3c_hsudc_req *hsreq; u32 csr; - csr = readl((u32)hsudc->regs + S3C_ESR); + csr = readl(hsudc->regs + S3C_ESR); if (csr & S3C_ESR_STALL) { writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); return; @@ -468,7 +467,7 @@ static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx) struct s3c_hsudc_req *hsreq; u32 csr; - csr = readl((u32)hsudc->regs + S3C_ESR); + csr = readl(hsudc->regs + S3C_ESR); if (csr & S3C_ESR_STALL) { writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); return; @@ -901,12 +900,12 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, if (list_empty(&hsep->queue) && !hsep->stopped) { offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; if (ep_is_in(hsep)) { - csr = readl((u32)hsudc->regs + offset); + csr = readl(hsudc->regs + offset); if (!(csr & S3C_ESR_TX_SUCCESS) && (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) hsreq = NULL; } else { - csr = readl((u32)hsudc->regs + offset); + csr = readl(hsudc->regs + offset); if ((csr & S3C_ESR_RX_SUCCESS) && (s3c_hsudc_read_fifo(hsep, hsreq) == 1)) hsreq = NULL; @@ -1000,7 +999,7 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, hsep->dev = hsudc; hsep->ep.name = hsep->name; - hsep->ep.maxpacket = epnum ? 512 : 64; + usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64); hsep->ep.ops = &s3c_hsudc_ep_ops; hsep->fifo = hsudc->regs + S3C_BR(epnum); hsep->ep.desc = NULL; @@ -1154,7 +1153,6 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, return -EBUSY; hsudc->driver = driver; - hsudc->gadget.dev.driver = &driver->driver; ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); @@ -1190,7 +1188,6 @@ err_otg: regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: hsudc->driver = NULL; - hsudc->gadget.dev.driver = NULL; return ret; } @@ -1208,7 +1205,6 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&hsudc->lock, flags); hsudc->driver = NULL; - hsudc->gadget.dev.driver = NULL; hsudc->gadget.speed = USB_SPEED_UNKNOWN; s3c_hsudc_uninit_phy(); @@ -1254,7 +1250,7 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) return -EOPNOTSUPP; } -static struct usb_gadget_ops s3c_hsudc_gadget_ops = { +static const struct usb_gadget_ops s3c_hsudc_gadget_ops = { .get_frame = s3c_hsudc_gadget_getframe, .udc_start = s3c_hsudc_start, .udc_stop = s3c_hsudc_stop, @@ -1266,7 +1262,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct s3c_hsudc *hsudc; - struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; + struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev); int ret, i; hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) + @@ -1279,14 +1275,14 @@ static int s3c_hsudc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); hsudc->dev = dev; - hsudc->pd = pdev->dev.platform_data; + hsudc->pd = dev_get_platdata(&pdev->dev); hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; - ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies), + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies), hsudc->supplies); if (ret != 0) { dev_err(dev, "failed to request supplies: %d\n", ret); @@ -1295,24 +1291,18 @@ static int s3c_hsudc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hsudc->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!hsudc->regs) { - dev_err(dev, "error mapping device register area\n"); - ret = -EBUSY; + hsudc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hsudc->regs)) { + ret = PTR_ERR(hsudc->regs); goto err_res; } spin_lock_init(&hsudc->lock); - dev_set_name(&hsudc->gadget.dev, "gadget"); - hsudc->gadget.max_speed = USB_SPEED_HIGH; hsudc->gadget.ops = &s3c_hsudc_gadget_ops; hsudc->gadget.name = dev_name(dev); - hsudc->gadget.dev.parent = dev; - hsudc->gadget.dev.dma_mask = dev->dma_mask; hsudc->gadget.ep0 = &hsudc->ep[0].ep; - hsudc->gadget.is_otg = 0; hsudc->gadget.is_a_peripheral = 0; hsudc->gadget.speed = USB_SPEED_UNKNOWN; @@ -1346,12 +1336,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) disable_irq(hsudc->irq); local_irq_enable(); - ret = device_register(&hsudc->gadget.dev); - if (ret) { - put_device(&hsudc->gadget.dev); - goto err_add_device; - } - ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); if (ret) goto err_add_udc; @@ -1360,14 +1344,11 @@ static int s3c_hsudc_probe(struct platform_device *pdev) return 0; err_add_udc: - device_unregister(&hsudc->gadget.dev); -err_add_device: clk_disable(hsudc->uclk); err_res: if (!IS_ERR_OR_NULL(hsudc->transceiver)) usb_put_phy(hsudc->transceiver); - regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: return ret; } diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index a2fa6e16d01..7987aa049fa 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -117,7 +117,8 @@ static int dprintk(int level, const char *fmt, ...) sizeof(printk_buf)-len, fmt, args); va_end(args); - return pr_debug("%s", printk_buf); + pr_debug("%s", printk_buf); + return len; } #else static int dprintk(int level, const char *fmt, ...) @@ -1538,9 +1539,10 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) return -ENOTSUPP; } -static int s3c2410_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)); -static int s3c2410_udc_stop(struct usb_gadget_driver *driver); +static int s3c2410_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int s3c2410_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops s3c2410_ops = { .get_frame = s3c2410_udc_get_frame, @@ -1549,8 +1551,8 @@ static const struct usb_gadget_ops s3c2410_ops = { .pullup = s3c2410_udc_pullup, .vbus_session = s3c2410_udc_vbus_session, .vbus_draw = s3c2410_vbus_draw, - .start = s3c2410_udc_start, - .stop = s3c2410_udc_stop, + .udc_start = s3c2410_udc_start, + .udc_stop = s3c2410_udc_stop, }; static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd) @@ -1628,6 +1630,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev) ep->ep.desc = NULL; ep->halted = 0; INIT_LIST_HEAD(&ep->queue); + usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); } } @@ -1664,84 +1667,27 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev) s3c2410_udc_command(S3C2410_UDC_P_ENABLE); } -static int s3c2410_udc_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static int s3c2410_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct s3c2410_udc *udc = the_controller; - int retval; + struct s3c2410_udc *udc = to_s3c2410(g); dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name); - /* Sanity checks */ - if (!udc) - return -ENODEV; - - if (udc->driver) - return -EBUSY; - - if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) { - dev_err(&udc->gadget.dev, "Invalid driver: bind %p setup %p speed %d\n", - bind, driver->setup, driver->max_speed); - return -EINVAL; - } -#if defined(MODULE) - if (!driver->unbind) { - dev_err(&udc->gadget.dev, "Invalid driver: no unbind method\n"); - return -EINVAL; - } -#endif - /* Hook the driver */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; - - /* Bind the driver */ - retval = device_add(&udc->gadget.dev); - if (retval) { - dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval); - goto register_error; - } - - dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", - driver->driver.name); - - retval = bind(&udc->gadget, driver); - if (retval) { - device_del(&udc->gadget.dev); - goto register_error; - } /* Enable udc */ s3c2410_udc_enable(udc); return 0; - -register_error: - udc->driver = NULL; - udc->gadget.dev.driver = NULL; - return retval; } -static int s3c2410_udc_stop(struct usb_gadget_driver *driver) +static int s3c2410_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct s3c2410_udc *udc = the_controller; - - if (!udc) - return -ENODEV; - - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; + struct s3c2410_udc *udc = to_s3c2410(g); - dprintk(DEBUG_NORMAL, "usb_gadget_unregister_driver() '%s'\n", - driver->driver.name); - - /* report disconnect */ - if (driver->disconnect) - driver->disconnect(&udc->gadget); - - driver->unbind(&udc->gadget); - - device_del(&udc->gadget.dev); udc->driver = NULL; /* Disable udc */ @@ -1865,7 +1811,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } spin_lock_init(&udc->lock); - udc_info = pdev->dev.platform_data; + udc_info = dev_get_platdata(&pdev->dev); rsrc_start = S3C2410_PA_USBDEV; rsrc_len = S3C24XX_SZ_USBDEV; @@ -1879,10 +1825,6 @@ static int s3c2410_udc_probe(struct platform_device *pdev) goto err_mem; } - device_initialize(&udc->gadget.dev); - udc->gadget.dev.parent = &pdev->dev; - udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - the_controller = udc; platform_set_drvdata(pdev, udc); @@ -1911,6 +1853,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) irq = gpio_to_irq(udc_info->vbus_pin); if (irq < 0) { dev_err(dev, "no irq for gpio vbus pin\n"); + retval = irq; goto err_gpio_claim; } @@ -1988,10 +1931,10 @@ static int s3c2410_udc_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s()\n", __func__); - usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; + usb_del_gadget_udc(&udc->gadget); debugfs_remove(udc->regs_info); if (udc_info && !udc_info->udc_command && @@ -2008,8 +1951,6 @@ static int s3c2410_udc_remove(struct platform_device *pdev) iounmap(base_addr); release_mem_region(rsrc_start, rsrc_len); - platform_set_drvdata(pdev, NULL); - if (!IS_ERR(udc_clock) && udc_clock != NULL) { clk_disable(udc_clock); clk_put(udc_clock); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h index 3e80fd5c820..93bf225f196 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -95,5 +95,6 @@ struct s3c2410_udc { u8 vbus; struct dentry *regs_info; }; +#define to_s3c2410(g) (container_of((g), struct s3c2410_udc, gadget)) #endif diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 44752f531e8..1f5f978d35d 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/device.h> +#include <linux/module.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -28,20 +29,6 @@ #define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR /*-------------------------------------------------------------------------*/ - -/* - * 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_acm.c" -#include "f_obex.c" -#include "f_serial.c" -#include "u_serial.c" - -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); /* Thanks to NetChip Technologies for donating this product ID. @@ -129,22 +116,6 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ -static int __init serial_bind_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) { - if (use_acm) - status = acm_bind_config(c, i); - else if (use_obex) - status = obex_bind_config(c, i); - else - status = gser_bind_config(c, i); - } - return status; -} - static struct usb_configuration serial_config_driver = { /* .label = f(use_acm) */ /* .bConfigurationValue = f(use_acm) */ @@ -152,14 +123,61 @@ static struct usb_configuration serial_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; +static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS]; +static struct usb_function *f_serial[MAX_U_SERIAL_PORTS]; + +static int serial_register_ports(struct usb_composite_dev *cdev, + struct usb_configuration *c, const char *f_name) +{ + int i; + int ret; + + ret = usb_add_config_only(cdev, c); + if (ret) + goto out; + + for (i = 0; i < n_ports; i++) { + + fi_serial[i] = usb_get_function_instance(f_name); + if (IS_ERR(fi_serial[i])) { + ret = PTR_ERR(fi_serial[i]); + goto fail; + } + + f_serial[i] = usb_get_function(fi_serial[i]); + if (IS_ERR(f_serial[i])) { + ret = PTR_ERR(f_serial[i]); + goto err_get_func; + } + + ret = usb_add_function(c, f_serial[i]); + if (ret) + goto err_add_func; + } + + return 0; + +err_add_func: + usb_put_function(f_serial[i]); +err_get_func: + usb_put_function_instance(fi_serial[i]); + +fail: + i--; + while (i >= 0) { + usb_remove_function(c, f_serial[i]); + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + i--; + } +out: + return ret; +} + static int __init gs_bind(struct usb_composite_dev *cdev) { int status; - status = gserial_setup(cdev->gadget, n_ports); - if (status < 0) - return status; - /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ @@ -178,8 +196,17 @@ static int __init gs_bind(struct usb_composite_dev *cdev) } /* register our configuration */ - status = usb_add_config(cdev, &serial_config_driver, - serial_bind_config); + if (use_acm) { + status = serial_register_ports(cdev, &serial_config_driver, + "acm"); + usb_ep_autoconfig_reset(cdev->gadget); + } else if (use_obex) + status = serial_register_ports(cdev, &serial_config_driver, + "obex"); + else { + status = serial_register_ports(cdev, &serial_config_driver, + "gser"); + } if (status < 0) goto fail; @@ -189,16 +216,27 @@ static int __init gs_bind(struct usb_composite_dev *cdev) return 0; fail: - gserial_cleanup(); return status; } +static int gs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + for (i = 0; i < n_ports; i++) { + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + } + return 0; +} + static __refdata struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_SUPER, .bind = gs_bind, + .unbind = gs_unbind, }; static int __init init(void) @@ -234,6 +272,5 @@ module_init(init); static void __exit cleanup(void) { usb_composite_unregister(&gserial_driver); - gserial_cleanup(); } module_exit(cleanup); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 0e3ae43454a..648f9e489b3 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -23,254 +23,17 @@ * The valid range of num_buffers is: num >= 2 && num <= 4. */ +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/usb/composite.h> -#include <linux/usb/storage.h> -#include <scsi/scsi.h> -#include <asm/unaligned.h> - - -/* - * Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with any other driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. - */ -#define FSG_VENDOR_ID 0x0525 /* NetChip */ -#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ - - -/*-------------------------------------------------------------------------*/ - - -#ifndef DEBUG -#undef VERBOSE_DEBUG -#undef DUMP_MSGS -#endif /* !DEBUG */ - -#ifdef VERBOSE_DEBUG -#define VLDBG LDBG -#else -#define VLDBG(lun, fmt, args...) do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) -#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) -#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) -#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) - - -#ifdef DUMP_MSGS - -# define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { \ - if (length < 512) { \ - DBG(fsg, "%s, length %u:\n", label, length); \ - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ - 16, 1, buf, length, 0); \ - } \ -} while (0) - -# define dump_cdb(fsg) do { } while (0) - -#else - -# define dump_msg(fsg, /* const char * */ label, \ - /* const u8 * */ buf, /* unsigned */ length) do { } while (0) - -# ifdef VERBOSE_DEBUG - -# define dump_cdb(fsg) \ - print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ - 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ - -# else - -# define dump_cdb(fsg) do { } while (0) - -# endif /* VERBOSE_DEBUG */ - -#endif /* DUMP_MSGS */ - -/*-------------------------------------------------------------------------*/ - -/* CBI Interrupt data structure */ -struct interrupt_data { - u8 bType; - u8 bValue; -}; - -#define CBI_INTERRUPT_DATA_LEN 2 - -/* CBI Accept Device-Specific Command request */ -#define USB_CBI_ADSC_REQUEST 0x00 - - -/* Length of a SCSI Command Data Block */ -#define MAX_COMMAND_SIZE 16 - -/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ -#define SS_NO_SENSE 0 -#define SS_COMMUNICATION_FAILURE 0x040800 -#define SS_INVALID_COMMAND 0x052000 -#define SS_INVALID_FIELD_IN_CDB 0x052400 -#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 -#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 -#define SS_MEDIUM_NOT_PRESENT 0x023a00 -#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 -#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 -#define SS_RESET_OCCURRED 0x062900 -#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 -#define SS_UNRECOVERED_READ_ERROR 0x031100 -#define SS_WRITE_ERROR 0x030c02 -#define SS_WRITE_PROTECTED 0x072700 - -#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ -#define ASC(x) ((u8) ((x) >> 8)) -#define ASCQ(x) ((u8) (x)) - - -/*-------------------------------------------------------------------------*/ - - -struct fsg_lun { - struct file *filp; - loff_t file_length; - loff_t num_sectors; - - unsigned int initially_ro:1; - unsigned int ro:1; - unsigned int removable:1; - unsigned int cdrom:1; - unsigned int prevent_medium_removal:1; - unsigned int registered:1; - unsigned int info_valid:1; - unsigned int nofua:1; - - u32 sense_data; - u32 sense_data_info; - u32 unit_attention_data; - - unsigned int blkbits; /* Bits of logical block size of bound block device */ - unsigned int blksize; /* logical block size of bound block device */ - struct device dev; -}; - -static inline bool fsg_lun_is_open(struct fsg_lun *curlun) -{ - return curlun->filp != NULL; -} - -static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) -{ - return container_of(dev, struct fsg_lun, dev); -} - - -/* Big enough to hold our biggest descriptor */ -#define EP0_BUFSIZE 256 -#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; -module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); -MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); - -#else - -/* - * Number of buffers we will use. - * 2 is usually enough for good buffering pipeline - */ -#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS - -#endif /* CONFIG_USB_DEBUG */ - -/* check if fsg_num_buffers is within a valid range */ -static inline int fsg_num_buffers_validate(void) -{ - if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) - return 0; - pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", - fsg_num_buffers, 2 ,4); - return -EINVAL; -} - -/* Default size of buffer length. */ -#define FSG_BUFLEN ((u32)16384) - -/* Maximal number of LUNs supported in mass storage function */ -#define FSG_MAX_LUNS 8 - -enum fsg_buffer_state { - BUF_STATE_EMPTY = 0, - BUF_STATE_FULL, - BUF_STATE_BUSY -}; - -struct fsg_buffhd { - void *buf; - enum fsg_buffer_state state; - struct fsg_buffhd *next; - - /* - * The NetChip 2280 is faster, and handles some protocol faults - * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. - */ - unsigned int bulk_out_intended_length; - - struct usb_request *inreq; - int inreq_busy; - struct usb_request *outreq; - int outreq_busy; -}; - -enum fsg_state { - /* This one isn't used anywhere */ - FSG_STATE_COMMAND_PHASE = -10, - FSG_STATE_DATA_PHASE, - FSG_STATE_STATUS_PHASE, - - FSG_STATE_IDLE = 0, - FSG_STATE_ABORT_BULK_OUT, - FSG_STATE_RESET, - FSG_STATE_INTERFACE_CHANGE, - FSG_STATE_CONFIG_CHANGE, - FSG_STATE_DISCONNECT, - FSG_STATE_EXIT, - FSG_STATE_TERMINATED -}; - -enum data_direction { - DATA_DIR_UNKNOWN = 0, - DATA_DIR_FROM_HOST, - DATA_DIR_TO_HOST, - DATA_DIR_NONE -}; - - -/*-------------------------------------------------------------------------*/ - - -static inline u32 get_unaligned_be24(u8 *buf) -{ - return 0xffffff & (u32) get_unaligned_be32(buf - 1); -} - - -/*-------------------------------------------------------------------------*/ - - -enum { - FSG_STRING_INTERFACE -}; - +#include "storage_common.h" /* There is only one interface. */ -static struct usb_interface_descriptor -fsg_intf_desc = { +struct usb_interface_descriptor fsg_intf_desc = { .bLength = sizeof fsg_intf_desc, .bDescriptorType = USB_DT_INTERFACE, @@ -280,14 +43,14 @@ fsg_intf_desc = { .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ .iInterface = FSG_STRING_INTERFACE, }; +EXPORT_SYMBOL_GPL(fsg_intf_desc); /* * Three full-speed endpoint descriptors: bulk-in, bulk-out, and * interrupt-in. */ -static struct usb_endpoint_descriptor -fsg_fs_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -295,9 +58,9 @@ fsg_fs_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; +EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc); -static struct usb_endpoint_descriptor -fsg_fs_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -305,13 +68,15 @@ fsg_fs_bulk_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; +EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc); -static struct usb_descriptor_header *fsg_fs_function[] = { +struct usb_descriptor_header *fsg_fs_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, NULL, }; +EXPORT_SYMBOL_GPL(fsg_fs_function); /* @@ -322,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = { * and a "device qualifier" ... plus more construction options * for the configuration descriptor. */ -static struct usb_endpoint_descriptor -fsg_hs_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -331,9 +95,9 @@ fsg_hs_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; +EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc); -static struct usb_endpoint_descriptor -fsg_hs_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -342,17 +106,18 @@ fsg_hs_bulk_out_desc = { .wMaxPacketSize = cpu_to_le16(512), .bInterval = 1, /* NAK every 1 uframe */ }; +EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc); -static struct usb_descriptor_header *fsg_hs_function[] = { +struct usb_descriptor_header *fsg_hs_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, NULL, }; +EXPORT_SYMBOL_GPL(fsg_hs_function); -static struct usb_endpoint_descriptor -fsg_ss_bulk_in_desc = { +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -360,16 +125,17 @@ fsg_ss_bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc); -static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { .bLength = sizeof(fsg_ss_bulk_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /*.bMaxBurst = DYNAMIC, */ }; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc); -static struct usb_endpoint_descriptor -fsg_ss_bulk_out_desc = { +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -377,50 +143,17 @@ fsg_ss_bulk_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc); -static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { .bLength = sizeof(fsg_ss_bulk_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /*.bMaxBurst = DYNAMIC, */ }; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc); -static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = { - .bLength = USB_DT_USB_EXT_CAP_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_CAP_TYPE_EXT, - - .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT), -}; - -static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { - .bLength = USB_DT_USB_SS_CAP_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_SS_CAP_TYPE, - - /* .bmAttributes = LTM is not supported yet */ - - .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION - | USB_FULL_SPEED_OPERATION - | USB_HIGH_SPEED_OPERATION - | USB_5GBPS_OPERATION), - .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, - .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, - .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT), -}; - -static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { - .bLength = USB_DT_BOS_SIZE, - .bDescriptorType = USB_DT_BOS, - - .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE - + USB_DT_USB_EXT_CAP_SIZE - + USB_DT_USB_SS_CAP_SIZE), - - .bNumDeviceCaps = 2, -}; - -static struct usb_descriptor_header *fsg_ss_function[] = { +struct usb_descriptor_header *fsg_ss_function[] = { (struct usb_descriptor_header *) &fsg_intf_desc, (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, @@ -428,31 +161,7 @@ static struct usb_descriptor_header *fsg_ss_function[] = { (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, NULL, }; - -/* Maxpacket and other transfer characteristics vary by speed. */ -static __maybe_unused struct usb_endpoint_descriptor * -fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *ss) -{ - if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) - return ss; - else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} - - -/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -static struct usb_string fsg_strings[] = { - {FSG_STRING_INTERFACE, fsg_string_interface}, - {} -}; - -static struct usb_gadget_strings fsg_stringtab = { - .language = 0x0409, /* en-us */ - .strings = fsg_strings, -}; +EXPORT_SYMBOL_GPL(fsg_ss_function); /*-------------------------------------------------------------------------*/ @@ -462,7 +171,7 @@ static struct usb_gadget_strings fsg_stringtab = { * the caller must own fsg->filesem for writing. */ -static void fsg_lun_close(struct fsg_lun *curlun) +void fsg_lun_close(struct fsg_lun *curlun) { if (curlun->filp) { LDBG(curlun, "close backing file\n"); @@ -470,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun) curlun->filp = NULL; } } +EXPORT_SYMBOL_GPL(fsg_lun_close); - -static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { int ro; struct file *filp = NULL; @@ -501,7 +210,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) if (!(filp->f_mode & FMODE_WRITE)) ro = 1; - inode = filp->f_path.dentry->d_inode; + inode = file_inode(filp); if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { LINFO(curlun, "invalid file type: %s\n", filename); goto out; @@ -511,11 +220,11 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) * If we can't read the file, it's no good. * If we can't write the file, use it read-only. */ - if (!(filp->f_op->read || filp->f_op->aio_read)) { + if (!(filp->f_mode & FMODE_CAN_READ)) { LINFO(curlun, "file not readable: %s\n", filename); goto out; } - if (!(filp->f_op->write || filp->f_op->aio_write)) + if (!(filp->f_mode & FMODE_CAN_WRITE)) ro = 1; size = i_size_read(inode->i_mapping->host); @@ -569,6 +278,7 @@ out: fput(filp); return rc; } +EXPORT_SYMBOL_GPL(fsg_lun_open); /*-------------------------------------------------------------------------*/ @@ -577,7 +287,7 @@ out: * Sync the file data, don't bother with the metadata. * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +int fsg_lun_fsync_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; @@ -585,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun) return 0; return vfs_fsync(filp, 1); } +EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub); -static void store_cdrom_address(u8 *dest, int msf, u32 addr) +void store_cdrom_address(u8 *dest, int msf, u32 addr) { if (msf) { /* Convert to Minutes-Seconds-Frames */ @@ -603,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr) put_unaligned_be32(addr, dest); } } - +EXPORT_SYMBOL_GPL(store_cdrom_address); /*-------------------------------------------------------------------------*/ -static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) ? curlun->ro : curlun->initially_ro); } +EXPORT_SYMBOL_GPL(fsg_show_ro); -static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - return sprintf(buf, "%u\n", curlun->nofua); } +EXPORT_SYMBOL_GPL(fsg_show_nofua); -static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, - char *buf) +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); char *p; ssize_t rc; @@ -652,17 +357,44 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, up_read(filesem); return rc; } +EXPORT_SYMBOL_GPL(fsg_show_file); + +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->cdrom); +} +EXPORT_SYMBOL_GPL(fsg_show_cdrom); +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->removable); +} +EXPORT_SYMBOL_GPL(fsg_show_removable); -static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +/* + * The caller must hold fsg->filesem for reading when calling this function. + */ +static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) +{ + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + return -EBUSY; + } + + curlun->ro = ro; + curlun->initially_ro = ro; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + + return 0; +} + +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) { ssize_t rc; - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); - unsigned ro; + bool ro; - rc = kstrtouint(buf, 2, &ro); + rc = strtobool(buf, &ro); if (rc) return rc; @@ -671,28 +403,21 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, * backing file is closed. */ down_read(filesem); - if (fsg_lun_is_open(curlun)) { - LDBG(curlun, "read-only status change prevented\n"); - rc = -EBUSY; - } else { - curlun->ro = ro; - curlun->initially_ro = ro; - LDBG(curlun, "read-only status set to %d\n", curlun->ro); + rc = _fsg_store_ro(curlun, ro); + if (!rc) rc = count; - } up_read(filesem); + return rc; } +EXPORT_SYMBOL_GPL(fsg_store_ro); -static ssize_t fsg_store_nofua(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - unsigned nofua; + bool nofua; int ret; - ret = kstrtouint(buf, 2, &nofua); + ret = strtobool(buf, &nofua); if (ret) return ret; @@ -704,12 +429,11 @@ static ssize_t fsg_store_nofua(struct device *dev, return count; } +EXPORT_SYMBOL_GPL(fsg_store_nofua); -static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) { - struct fsg_lun *curlun = fsg_lun_from_dev(dev); - struct rw_semaphore *filesem = dev_get_drvdata(dev); int rc = 0; if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { @@ -736,3 +460,45 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, up_write(filesem); return (rc < 0 ? rc : count); } +EXPORT_SYMBOL_GPL(fsg_store_file); + +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + bool cdrom; + int ret; + + ret = strtobool(buf, &cdrom); + if (ret) + return ret; + + down_read(filesem); + ret = cdrom ? _fsg_store_ro(curlun, true) : 0; + + if (!ret) { + curlun->cdrom = cdrom; + ret = count; + } + up_read(filesem); + + return ret; +} +EXPORT_SYMBOL_GPL(fsg_store_cdrom); + +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count) +{ + bool removable; + int ret; + + ret = strtobool(buf, &removable); + if (ret) + return ret; + + curlun->removable = removable; + + return count; +} +EXPORT_SYMBOL_GPL(fsg_store_removable); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/storage_common.h new file mode 100644 index 00000000000..70c891469f5 --- /dev/null +++ b/drivers/usb/gadget/storage_common.h @@ -0,0 +1,225 @@ +#ifndef USB_STORAGE_COMMON_H +#define USB_STORAGE_COMMON_H + +#include <linux/device.h> +#include <linux/usb/storage.h> +#include <scsi/scsi.h> +#include <asm/unaligned.h> + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define _LMSG(func, lun, fmt, args...) \ + do { \ + if ((lun)->name_pfx && *(lun)->name_pfx) \ + func("%s/%s: " fmt, *(lun)->name_pfx, \ + (lun)->name, ## args); \ + else \ + func("%s: " fmt, (lun)->name, ## args); \ + } while (0) + +#define LDBG(lun, fmt, args...) _LMSG(pr_debug, lun, fmt, ## args) +#define LERROR(lun, fmt, args...) _LMSG(pr_err, lun, fmt, ## args) +#define LWARN(lun, fmt, args...) _LMSG(pr_warn, lun, fmt, ## args) +#define LINFO(lun, fmt, args...) _LMSG(pr_info, lun, fmt, ## args) + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) \ +do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +struct fsg_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + unsigned int blkbits; /* Bits of logical block size + of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ + struct device dev; + const char *name; /* "lun.name" */ + const char **name_pfx; /* "function.name" */ +}; + +static inline bool fsg_lun_is_open(struct fsg_lun *curlun) +{ + return curlun->filp != NULL; +} + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} + +enum { + FSG_STRING_INTERFACE +}; + +extern struct usb_interface_descriptor fsg_intf_desc; + +extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_fs_function[]; + +extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_hs_function[]; + +extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; +extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; +extern struct usb_descriptor_header *fsg_ss_function[]; + +void fsg_lun_close(struct fsg_lun *curlun); +int fsg_lun_open(struct fsg_lun *curlun, const char *filename); +int fsg_lun_fsync_sub(struct fsg_lun *curlun); +void store_cdrom_address(u8 *dest, int msf, u32 addr); +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf); +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count); + +#endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 7cacd6ae818..6cdb7a534f2 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -370,7 +370,7 @@ err: return -ENOMEM; } -void bot_cleanup_old_alt(struct f_uas *fu) +static void bot_cleanup_old_alt(struct f_uas *fu) { if (!(fu->flags & USBG_ENABLED)) return; @@ -472,7 +472,7 @@ static int usbg_bot_setup(struct usb_function *f, bot_enqueue_cmd_cbw(fu); return 0; break; - }; + } return -ENOTSUPP; } @@ -617,7 +617,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) default: BUG(); - }; + } return; cleanup: @@ -1383,10 +1383,8 @@ static struct se_node_acl *usbg_alloc_fabric_acl(struct se_portal_group *se_tpg) struct usbg_nacl *nacl; nacl = kzalloc(sizeof(struct usbg_nacl), GFP_KERNEL); - if (!nacl) { - printk(KERN_ERR "Unable to allocate struct usbg_nacl\n"); + if (!nacl) return NULL; - } return &nacl->se_node_acl; } @@ -1467,9 +1465,13 @@ static int usbg_get_cmd_state(struct se_cmd *se_cmd) return 0; } -static int usbg_queue_tm_rsp(struct se_cmd *se_cmd) +static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) { - return 0; +} + +static void usbg_aborted_task(struct se_cmd *se_cmd) +{ + return; } static const char *usbg_check_wwn(const char *name) @@ -1557,10 +1559,8 @@ static struct se_portal_group *usbg_make_tpg( } tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL); - if (!tpg) { - printk(KERN_ERR "Unable to allocate struct usbg_tpg"); + if (!tpg) return ERR_PTR(-ENOMEM); - } mutex_init(&tpg->tpg_mutex); atomic_set(&tpg->tpg_port_count, 0); tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1); @@ -1609,12 +1609,10 @@ static struct se_wwn *usbg_make_tport( return ERR_PTR(-EINVAL); tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL); - if (!(tport)) { - printk(KERN_ERR "Unable to allocate struct usbg_tport"); + if (!(tport)) return ERR_PTR(-ENOMEM); - } tport->tport_wwpn = wwpn; - snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name); + snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name); return &tport->tport_wwn; } @@ -1723,11 +1721,9 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name) ret = -ENOMEM; tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL); - if (!tv_nexus) { - pr_err("Unable to allocate struct tcm_vhost_nexus\n"); + if (!tv_nexus) goto err_unlock; - } - tv_nexus->tvn_se_sess = transport_init_session(); + tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL); if (IS_ERR(tv_nexus->tvn_se_sess)) goto err_free; @@ -1847,7 +1843,7 @@ static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun) struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); atomic_inc(&tpg->tpg_port_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return 0; } @@ -1857,7 +1853,7 @@ static void usbg_port_unlink(struct se_portal_group *se_tpg, struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); atomic_dec(&tpg->tpg_port_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static int usbg_check_stop_free(struct se_cmd *se_cmd) @@ -1898,6 +1894,7 @@ static struct target_core_fabric_ops usbg_ops = { .queue_data_in = usbg_send_read_response, .queue_status = usbg_send_status_response, .queue_tm_rsp = usbg_queue_tm_rsp, + .aborted_task = usbg_aborted_task, .check_stop_free = usbg_check_stop_free, .fabric_make_wwn = usbg_make_tport, @@ -1924,15 +1921,15 @@ static int usbg_register_configfs(void) } fabric->tf_ops = usbg_ops; - TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = usbg_wwn_attrs; - TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = usbg_base_attrs; - TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = usbg_wwn_attrs; + fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = usbg_base_attrs; + fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; ret = target_fabric_configfs_register(fabric); if (ret < 0) { printk(KERN_ERR "target_fabric_configfs_register() failed" diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/u_ecm.h new file mode 100644 index 00000000000..262cc03cc2c --- /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 00000000000..e3ae97874c4 --- /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 4ec3c0d7a18..97b027724ee 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -50,7 +50,6 @@ struct eth_dev { /* lock is held while accessing port_usb - * or updating its backlink port_usb->ioport */ spinlock_t lock; struct gether *port_usb; @@ -64,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 *, @@ -77,6 +78,7 @@ struct eth_dev { bool zlp; u8 host_mac[ETH_ALEN]; + u8 dev_mac[ETH_ALEN]; }; /*-------------------------------------------------------------------------*/ @@ -85,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)) @@ -159,12 +157,12 @@ static int ueth_change_mtu(struct net_device *net, int new_mtu) static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, "g_ether", sizeof p->driver); - strlcpy(p->version, UETH__VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); + strlcpy(p->driver, "g_ether", sizeof(p->driver)); + strlcpy(p->version, UETH__VERSION, sizeof(p->version)); + strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); } /* REVISIT can also support: @@ -589,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); @@ -698,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) { @@ -729,7 +717,16 @@ static int get_ether_addr(const char *str, u8 *dev_addr) return 1; } -static struct eth_dev *the_dev; +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, @@ -756,21 +753,19 @@ static struct device_type gadget_type = { * gadget driver using this framework. The link layer addresses are * set up using module parameters. * - * Returns negative errno, or zero on success + * Returns an eth_dev pointer on success, or an ERR_PTR on failure. */ -int 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; int status; - if (the_dev) - return -EBUSY; - net = alloc_etherdev(sizeof *dev); if (!net) - return -ENOMEM; + return ERR_PTR(-ENOMEM); dev = netdev_priv(net); spin_lock_init(&dev->lock); @@ -783,6 +778,7 @@ int 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)) @@ -797,7 +793,7 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], net->netdev_ops = ð_netdev_ops; - SET_ETHTOOL_OPS(net, &ops); + net->ethtool_ops = &ops; dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); @@ -807,11 +803,77 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], if (status < 0) { dev_dbg(&g->dev, "register_netdev failed, %d\n", status); free_netdev(net); + dev = ERR_PTR(status); } else { INFO(dev, "MAC %pM\n", net->dev_addr); INFO(dev, "HOST MAC %pM\n", dev->host_mac); - the_dev = dev; + /* + * two kinds of host-initiated state changes: + * - iff DATA transfer is active, carrier is "on" + * - tx queueing enabled if open *and* carrier is "on" + */ + netif_carrier_off(net); + } + + return dev; +} +EXPORT_SYMBOL_GPL(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; + + net->ethtool_ops = &ops; + SET_NETDEV_DEVTYPE(net, &gadget_type); + + return net; +} +EXPORT_SYMBOL_GPL(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" @@ -819,9 +881,123 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], */ 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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(gether_set_qmult); + +unsigned gether_get_qmult(struct net_device *net) +{ + struct eth_dev *dev; + + dev = netdev_priv(net); + return dev->qmult; +} +EXPORT_SYMBOL_GPL(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_GPL(gether_get_ifname); /** * gether_cleanup - remove Ethernet-over-USB device @@ -829,18 +1005,16 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], * * This is called to free all resources allocated by @gether_setup(). */ -void gether_cleanup(void) +void gether_cleanup(struct eth_dev *dev) { - if (!the_dev) + if (!dev) return; - unregister_netdev(the_dev->net); - flush_work(&the_dev->work); - free_netdev(the_dev->net); - - the_dev = NULL; + unregister_netdev(dev->net); + flush_work(&dev->work); + free_netdev(dev->net); } - +EXPORT_SYMBOL_GPL(gether_cleanup); /** * gether_connect - notify network layer that USB link is active @@ -860,7 +1034,7 @@ void gether_cleanup(void) */ struct net_device *gether_connect(struct gether *link) { - struct eth_dev *dev = the_dev; + struct eth_dev *dev = link->ioport; int result = 0; if (!dev) @@ -883,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; @@ -895,7 +1070,6 @@ struct net_device *gether_connect(struct gether *link) spin_lock(&dev->lock); dev->port_usb = link; - link->ioport = dev; if (netif_running(dev->net)) { if (link->open) link->open(link); @@ -921,6 +1095,7 @@ fail0: return ERR_PTR(result); return dev->net; } +EXPORT_SYMBOL_GPL(gether_connect); /** * gether_disconnect - notify network layer that USB link is inactive @@ -945,7 +1120,10 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); + netif_tx_lock(dev->net); netif_stop_queue(dev->net); + netif_tx_unlock(dev->net); + netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion @@ -989,6 +1167,9 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->lock); dev->port_usb = NULL; - link->ioport = NULL; spin_unlock(&dev->lock); } +EXPORT_SYMBOL_GPL(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 6f4a1623d85..0f0290acea7 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -21,6 +21,27 @@ #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; /* * This represents the USB side of an "ethernet" link, managed by a USB @@ -70,8 +91,9 @@ struct gether { |USB_CDC_PACKET_TYPE_DIRECTED) /* variant of gether_setup that allows customizing network device name */ -int 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 @@ -84,14 +106,149 @@ int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], * gadget driver using this framework. The link layer addresses are * set up using module parameters. * + * Returns a eth_dev pointer on success, or an ERR_PTR on failure + */ +static inline struct eth_dev *gether_setup(struct usb_gadget *g, + const char *dev_addr, const char *host_addr, + u8 ethaddr[ETH_ALEN], unsigned qmult) +{ + 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 int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +static inline struct net_device *gether_setup_default(void) { - return gether_setup_name(g, ethaddr, "usb"); + return gether_setup_name_default("usb"); } -void gether_cleanup(void); +/** + * 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 */ struct net_device *gether_connect(struct gether *); @@ -110,45 +267,4 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) return true; } -/* each configuration may bind one instance of an ethernet link */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); -int eem_bind_config(struct usb_configuration *c); - -#ifdef USB_ETH_RNDIS - -int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer); - -#else - -static inline int -rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer) -{ - return 0; -} - -#endif - -/** - * rndis_bind_config - add RNDIS network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -static inline int rndis_bind_config(struct usb_configuration *c, - u8 ethaddr[ETH_ALEN]) -{ - return rndis_bind_config_vendor(c, ethaddr, 0, NULL); -} - - #endif /* __U_ETHER_H */ diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/u_ether_configfs.h new file mode 100644 index 00000000000..bcbd30146cf --- /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_f.c b/drivers/usb/gadget/u_f.c new file mode 100644 index 00000000000..c6276f0268a --- /dev/null +++ b/drivers/usb/gadget/u_f.c @@ -0,0 +1,32 @@ +/* + * u_f.c -- USB function utilities for Gadget stack + * + * 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. + */ + +#include <linux/usb/gadget.h> +#include "u_f.h" + +struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = len ?: default_len; + req->buf = kmalloc(req->length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} +EXPORT_SYMBOL_GPL(alloc_ep_req); diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h new file mode 100644 index 00000000000..1d5f0eb6855 --- /dev/null +++ b/drivers/usb/gadget/u_f.h @@ -0,0 +1,52 @@ +/* + * u_f.h + * + * Utility definitions for USB 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_F_H__ +#define __U_F_H__ + +/* Variable Length Array Macros **********************************************/ +#define vla_group(groupname) size_t groupname##__next = 0 +#define vla_group_size(groupname) groupname##__next + +#define vla_item(groupname, type, name, n) \ + size_t groupname##_##name##__offset = ({ \ + size_t align_mask = __alignof__(type) - 1; \ + size_t offset = (groupname##__next + align_mask) & ~align_mask;\ + size_t size = (n) * sizeof(type); \ + groupname##__next = offset + size; \ + offset; \ + }) + +#define vla_item_with_sz(groupname, type, name, n) \ + size_t groupname##_##name##__sz = (n) * sizeof(type); \ + size_t groupname##_##name##__offset = ({ \ + size_t align_mask = __alignof__(type) - 1; \ + size_t offset = (groupname##__next + align_mask) & ~align_mask;\ + size_t size = groupname##_##name##__sz; \ + groupname##__next = offset + size; \ + offset; \ + }) + +#define vla_ptr(ptr, groupname, name) \ + ((void *) ((char *)ptr + groupname##_##name##__offset)) + +struct usb_ep; +struct usb_request; + +struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len); + +#endif /* __U_F_H__ */ + + diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h new file mode 100644 index 00000000000..bf0ba375d45 --- /dev/null +++ b/drivers/usb/gadget/u_fs.h @@ -0,0 +1,263 @@ +/* + * u_fs.h + * + * Utility definitions for the FunctionFS + * + * 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_FFS_H +#define U_FFS_H + +#include <linux/usb/composite.h> +#include <linux/list.h> +#include <linux/mutex.h> + +#ifdef VERBOSE_DEBUG +#ifndef pr_vdebug +# define pr_vdebug pr_debug +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) \ + print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) +#else +#ifndef pr_vdebug +# define pr_vdebug(...) do { } while (0) +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define ENTER() pr_vdebug("%s()\n", __func__) + +struct f_fs_opts; + +struct ffs_dev { + const char *name; + bool name_allocated; + bool mounted; + bool desc_ready; + bool single; + struct ffs_data *ffs_data; + struct f_fs_opts *opts; + struct list_head entry; + + int (*ffs_ready_callback)(struct ffs_data *ffs); + void (*ffs_closed_callback)(struct ffs_data *ffs); + void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev); + void (*ffs_release_dev_callback)(struct ffs_dev *dev); +}; + +extern struct mutex ffs_lock; + +static inline void ffs_dev_lock(void) +{ + mutex_lock(&ffs_lock); +} + +static inline void ffs_dev_unlock(void) +{ + mutex_unlock(&ffs_lock); +} + +int ffs_name_dev(struct ffs_dev *dev, const char *name); +int ffs_single_dev(struct ffs_dev *dev); + +struct ffs_epfile; +struct ffs_function; + +enum ffs_state { + /* + * Waiting for descriptors and strings. + * + * In this state no open(2), read(2) or write(2) on epfiles + * may succeed (which should not be the problem as there + * should be no such files opened in the first place). + */ + FFS_READ_DESCRIPTORS, + FFS_READ_STRINGS, + + /* + * We've got descriptors and strings. We are or have called + * functionfs_ready_callback(). functionfs_bind() may have + * been called but we don't know. + * + * This is the only state in which operations on epfiles may + * succeed. + */ + FFS_ACTIVE, + + /* + * All endpoints have been closed. This state is also set if + * we encounter an unrecoverable error. The only + * unrecoverable error is situation when after reading strings + * from user space we fail to initialise epfiles or + * functionfs_ready_callback() returns with error (<0). + * + * In this state no open(2), read(2) or write(2) (both on ep0 + * as well as epfile) may succeed (at this point epfiles are + * unlinked and all closed so this is not a problem; ep0 is + * also closed but ep0 file exists and so open(2) on ep0 must + * fail). + */ + FFS_CLOSING +}; + +enum ffs_setup_state { + /* There is no setup request pending. */ + FFS_NO_SETUP, + /* + * User has read events and there was a setup request event + * there. The next read/write on ep0 will handle the + * request. + */ + FFS_SETUP_PENDING, + /* + * There was event pending but before user space handled it + * some other event was introduced which canceled existing + * setup. If this state is set read/write on ep0 return + * -EIDRM. This state is only set when adding event. + */ + FFS_SETUP_CANCELLED +}; + +struct ffs_data { + struct usb_gadget *gadget; + + /* + * Protect access read/write operations, only one read/write + * at a time. As a consequence protects ep0req and company. + * While setup request is being processed (queued) this is + * held. + */ + struct mutex mutex; + + /* + * Protect access to endpoint related structures (basically + * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for + * endpoint zero. + */ + spinlock_t eps_lock; + + /* + * XXX REVISIT do we need our own request? Since we are not + * handling setup requests immediately user space may be so + * slow that another setup will be sent to the gadget but this + * time not to us but another function and then there could be + * a race. Is that the case? Or maybe we can use cdev->req + * after all, maybe we just need some spinlock for that? + */ + struct usb_request *ep0req; /* P: mutex */ + struct completion ep0req_completion; /* P: mutex */ + + /* reference counter */ + atomic_t ref; + /* how many files are opened (EP0 and others) */ + atomic_t opened; + + /* EP0 state */ + enum ffs_state state; + + /* + * Possible transitions: + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * happens only in ep0 read which is P: mutex + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * happens only in ep0 i/o which is P: mutex + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg + * + * This field should never be accessed directly and instead + * ffs_setup_state_clear_cancelled function should be used. + */ + enum ffs_setup_state setup_state; + + /* Events & such. */ + struct { + u8 types[4]; + unsigned short count; + /* XXX REVISIT need to update it in some places, or do we? */ + unsigned short can_stall; + struct usb_ctrlrequest setup; + + wait_queue_head_t waitq; + } ev; /* the whole structure, P: ev.waitq.lock */ + + /* Flags */ + unsigned long flags; +#define FFS_FL_CALL_CLOSED_CALLBACK 0 +#define FFS_FL_BOUND 1 + + /* Active function */ + struct ffs_function *func; + + /* + * Device name, write once when file system is mounted. + * Intended for user to read if she wants. + */ + const char *dev_name; + /* Private data for our user (ie. gadget). Managed by user. */ + void *private_data; + + /* filled by __ffs_data_got_descs() */ + /* + * raw_descs is what you kfree, real_descs points inside of raw_descs, + * where full speed, high speed and super speed descriptors start. + * real_descs_length is the length of all those descriptors. + */ + const void *raw_descs_data; + const void *raw_descs; + unsigned raw_descs_length; + unsigned fs_descs_count; + unsigned hs_descs_count; + unsigned ss_descs_count; + + unsigned short strings_count; + unsigned short interfaces_count; + unsigned short eps_count; + unsigned short _pad1; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ + const void *raw_strings; + struct usb_gadget_strings **stringtabs; + + /* + * File system's super block, write once when file system is + * mounted. + */ + struct super_block *sb; + + /* File permissions, written once when fs is mounted */ + struct ffs_file_perms { + umode_t mode; + kuid_t uid; + kgid_t gid; + } file_perms; + + /* + * The endpoint files, filled by ffs_epfiles_create(), + * destroyed by ffs_epfiles_destroy(). + */ + struct ffs_epfile *epfiles; +}; + + +struct f_fs_opts { + struct usb_function_instance func_inst; + struct ffs_dev *dev; + unsigned refcnt; + bool no_configfs; +}; + +static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi) +{ + return container_of(fi, struct f_fs_opts, func_inst); +} + +#endif /* U_FFS_H */ diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h new file mode 100644 index 00000000000..d4078426ba5 --- /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 00000000000..ce0f3a78ca1 --- /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_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 00000000000..ea5cf8c2da2 --- /dev/null +++ b/drivers/usb/gadget/u_os_desc.h @@ -0,0 +1,90 @@ +/* + * u_os_desc.h + * + * Utility definitions for "OS Descriptors" support + * + * Copyright (c) 2014 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_OS_DESC_H__ +#define __U_OS_DESC_H__ + +#include <asm/unaligned.h> +#include <linux/nls.h> + +#define USB_EXT_PROP_DW_SIZE 0 +#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4 +#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8 +#define USB_EXT_PROP_B_PROPERTY_NAME 10 +#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10 +#define USB_EXT_PROP_B_PROPERTY_DATA 14 + +#define USB_EXT_PROP_RESERVED 0 +#define USB_EXT_PROP_UNICODE 1 +#define USB_EXT_PROP_UNICODE_ENV 2 +#define USB_EXT_PROP_BINARY 3 +#define USB_EXT_PROP_LE32 4 +#define USB_EXT_PROP_BE32 5 +#define USB_EXT_PROP_UNICODE_LINK 6 +#define USB_EXT_PROP_UNICODE_MULTI 7 + +static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) +{ + put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]); +} + +static inline void usb_ext_prop_put_type(u8 *buf, int type) +{ + put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]); +} + +static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) +{ + int result; + + put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]); + result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, + (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]); + + return pnl; +} + +static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, + int data_len) +{ + put_unaligned_le32(data_len, + &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); + memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len); +} + +static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, + int data_len) +{ + int result; + put_unaligned_le32(data_len, + &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); + + result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, + (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], + data_len - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]); + + return data_len; +} + +#endif /* __U_OS_DESC_H__ */ diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h index 09a75259b6c..98ced18779e 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 00000000000..e902aa42a29 --- /dev/null +++ b/drivers/usb/gadget/u_rndis.h @@ -0,0 +1,46 @@ +/* + * 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; + + struct usb_os_desc rndis_os_desc; + char rndis_ext_compat_id[16]; + + /* + * 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; +}; + +int rndis_init(void); +void rndis_exit(void); +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); + +#endif /* U_RNDIS_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 598dcc1212f..ad0aca81200 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -26,6 +26,7 @@ #include <linux/tty_flip.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/module.h> #include "u_serial.h" @@ -35,11 +36,12 @@ * "serial port" functionality through the USB gadget stack. Each such * port is exposed through a /dev/ttyGS* node. * - * After initialization (gserial_setup), these TTY port devices stay - * available until they are removed (gserial_cleanup). Each one may be - * connected to a USB function (gserial_connect), or disconnected (with - * gserial_disconnect) when the USB host issues a config change event. - * Data can only flow when the port is connected to the host. + * After this module has been loaded, the individual TTY port can be requested + * (gserial_alloc_line()) and it will stay available until they are removed + * (gserial_free_line()). Each one may be connected to a USB function + * (gserial_connect), or disconnected (with gserial_disconnect) when the USB + * host issues a config change event. Data can only flow when the port is + * connected to the host. * * A given TTY port can be made available in multiple configurations. * For example, each one might expose a ttyGS0 node which provides a @@ -119,13 +121,10 @@ struct gs_port { struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ }; -/* increase N_PORTS if you need more */ -#define N_PORTS 4 static struct portmaster { struct mutex lock; /* protect open/close */ struct gs_port *port; -} ports[N_PORTS]; -static unsigned n_ports; +} ports[MAX_U_SERIAL_PORTS]; #define GS_CLOSE_TIMEOUT 15 /* seconds */ @@ -137,7 +136,7 @@ static unsigned n_ports; pr_debug(fmt, ##arg) #endif /* pr_vdebug */ #else -#ifndef pr_vdebig +#ifndef pr_vdebug #define pr_vdebug(fmt, arg...) \ ({ if (0) pr_debug(fmt, ##arg); }) #endif /* pr_vdebug */ @@ -309,6 +308,7 @@ gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) return req; } +EXPORT_SYMBOL_GPL(gs_alloc_req); /* * gs_free_req @@ -320,6 +320,7 @@ void gs_free_req(struct usb_ep *ep, struct usb_request *req) kfree(req->buf); usb_ep_free_request(ep, req); } +EXPORT_SYMBOL_GPL(gs_free_req); /* * gs_send_packet @@ -495,12 +496,8 @@ static void gs_rx_push(unsigned long _port) req = list_first_entry(queue, struct usb_request, list); - /* discard data if tty was closed */ - if (!tty) - goto recycle; - /* leave data queued if tty was rx throttled */ - if (test_bit(TTY_THROTTLED, &tty->flags)) + if (tty && test_bit(TTY_THROTTLED, &tty->flags)) break; switch (req->status) { @@ -533,7 +530,8 @@ static void gs_rx_push(unsigned long _port) size -= n; } - count = tty_insert_flip_string(tty, packet, size); + count = tty_insert_flip_string(&port->port, packet, + size); if (count) do_push = true; if (count != size) { @@ -546,16 +544,16 @@ static void gs_rx_push(unsigned long _port) } port->n_read = 0; } -recycle: + list_move(&req->list, &port->read_pool); port->read_started--; } - /* Push from tty to ldisc; without low_latency set this is handled by - * a workqueue, so we won't get callbacks and can hold port_lock + /* Push from tty to ldisc; this is handled by a workqueue, + * so we won't get callbacks and can hold port_lock */ - if (tty && do_push) - tty_flip_buffer_push(tty); + if (do_push) + tty_flip_buffer_push(&port->port); /* We want our data queue to become empty ASAP, keeping data @@ -1030,10 +1028,19 @@ static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { struct gs_port *port; + int ret = 0; + + mutex_lock(&ports[port_num].lock); + if (ports[port_num].port) { + ret = -EBUSY; + goto out; + } port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); - if (port == NULL) - return -ENOMEM; + if (port == NULL) { + ret = -ENOMEM; + goto out; + } tty_port_init(&port->port); spin_lock_init(&port->port_lock); @@ -1049,109 +1056,9 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) port->port_line_coding = *coding; ports[port_num].port = port; - - return 0; -} - -/** - * gserial_setup - initialize TTY driver for one or more ports - * @g: gadget to associate with these ports - * @count: how many ports to support - * Context: may sleep - * - * The TTY stack needs to know in advance how many devices it should - * plan to manage. Use this call to set up the ports you will be - * exporting through USB. Later, connect them to functions based - * on what configuration is activated by the USB host; and disconnect - * them as appropriate. - * - * An example would be a two-configuration device in which both - * configurations expose port 0, but through different functions. - * One configuration could even expose port 1 while the other - * one doesn't. - * - * Returns negative errno or zero. - */ -int gserial_setup(struct usb_gadget *g, unsigned count) -{ - unsigned i; - struct usb_cdc_line_coding coding; - int status; - - if (count == 0 || count > N_PORTS) - return -EINVAL; - - gs_tty_driver = alloc_tty_driver(count); - if (!gs_tty_driver) - return -ENOMEM; - - gs_tty_driver->driver_name = "g_serial"; - gs_tty_driver->name = PREFIX; - /* uses dynamically assigned dev_t values */ - - gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gs_tty_driver->init_termios = tty_std_termios; - - /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on - * MS-Windows. Otherwise, most of these flags shouldn't affect - * anything unless we were to actually hook up to a serial line. - */ - gs_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - gs_tty_driver->init_termios.c_ispeed = 9600; - gs_tty_driver->init_termios.c_ospeed = 9600; - - coding.dwDTERate = cpu_to_le32(9600); - coding.bCharFormat = 8; - coding.bParityType = USB_CDC_NO_PARITY; - coding.bDataBits = USB_CDC_1_STOP_BITS; - - tty_set_operations(gs_tty_driver, &gs_tty_ops); - - /* make devices be openable */ - for (i = 0; i < count; i++) { - mutex_init(&ports[i].lock); - status = gs_port_alloc(i, &coding); - if (status) { - count = i; - goto fail; - } - } - n_ports = count; - - /* export the driver ... */ - status = tty_register_driver(gs_tty_driver); - if (status) { - pr_err("%s: cannot register, err %d\n", - __func__, status); - goto fail; - } - - /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - for (i = 0; i < count; i++) { - struct device *tty_dev; - - tty_dev = tty_port_register_device(&ports[i].port->port, - gs_tty_driver, i, &g->dev); - if (IS_ERR(tty_dev)) - pr_warning("%s: no classdev for port %d, err %ld\n", - __func__, i, PTR_ERR(tty_dev)); - } - - pr_debug("%s: registered %d ttyGS* device%s\n", __func__, - count, (count == 1) ? "" : "s"); - - return status; -fail: - while (count--) { - tty_port_destroy(&ports[count].port->port); - kfree(ports[count].port); - } - put_tty_driver(gs_tty_driver); - gs_tty_driver = NULL; - return status; +out: + mutex_unlock(&ports[port_num].lock); + return ret; } static int gs_closed(struct gs_port *port) @@ -1164,55 +1071,77 @@ static int gs_closed(struct gs_port *port) return cond; } -/** - * gserial_cleanup - remove TTY-over-USB driver and devices - * Context: may sleep - * - * This is called to free all resources allocated by @gserial_setup(). - * Accordingly, it may need to wait until some open /dev/ files have - * closed. - * - * The caller must have issued @gserial_disconnect() for any ports - * that had previously been connected, so that there is never any - * I/O pending when it's called. - */ -void gserial_cleanup(void) +static void gserial_free_port(struct gs_port *port) +{ + tasklet_kill(&port->push); + /* wait for old opens to finish */ + wait_event(port->port.close_wait, gs_closed(port)); + WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); + kfree(port); +} + +void gserial_free_line(unsigned char port_num) { - unsigned i; struct gs_port *port; - if (!gs_tty_driver) + mutex_lock(&ports[port_num].lock); + if (WARN_ON(!ports[port_num].port)) { + mutex_unlock(&ports[port_num].lock); return; + } + port = ports[port_num].port; + ports[port_num].port = NULL; + mutex_unlock(&ports[port_num].lock); - /* start sysfs and /dev/ttyGS* node removal */ - for (i = 0; i < n_ports; i++) - tty_unregister_device(gs_tty_driver, i); - - for (i = 0; i < n_ports; i++) { - /* prevent new opens */ - mutex_lock(&ports[i].lock); - port = ports[i].port; - ports[i].port = NULL; - mutex_unlock(&ports[i].lock); - - tasklet_kill(&port->push); + gserial_free_port(port); + tty_unregister_device(gs_tty_driver, port_num); +} +EXPORT_SYMBOL_GPL(gserial_free_line); - /* wait for old opens to finish */ - wait_event(port->port.close_wait, gs_closed(port)); +int gserial_alloc_line(unsigned char *line_num) +{ + struct usb_cdc_line_coding coding; + struct device *tty_dev; + int ret; + int port_num; - WARN_ON(port->port_usb != NULL); + coding.dwDTERate = cpu_to_le32(9600); + coding.bCharFormat = 8; + coding.bParityType = USB_CDC_NO_PARITY; + coding.bDataBits = USB_CDC_1_STOP_BITS; - tty_port_destroy(&port->port); - kfree(port); + for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) { + ret = gs_port_alloc(port_num, &coding); + if (ret == -EBUSY) + continue; + if (ret) + return ret; + break; } - n_ports = 0; + if (ret) + return ret; - tty_unregister_driver(gs_tty_driver); - put_tty_driver(gs_tty_driver); - gs_tty_driver = NULL; + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - pr_debug("%s: cleaned up ttyGS* support\n", __func__); + tty_dev = tty_port_register_device(&ports[port_num].port->port, + gs_tty_driver, port_num, NULL); + if (IS_ERR(tty_dev)) { + struct gs_port *port; + pr_err("%s: failed to register tty for port %d, err %ld\n", + __func__, port_num, PTR_ERR(tty_dev)); + + ret = PTR_ERR(tty_dev); + port = ports[port_num].port; + ports[port_num].port = NULL; + gserial_free_port(port); + goto err; + } + *line_num = port_num; +err: + return ret; } +EXPORT_SYMBOL_GPL(gserial_alloc_line); /** * gserial_connect - notify TTY I/O glue that USB link is active @@ -1229,8 +1158,8 @@ void gserial_cleanup(void) * * Caller needs to have set up the endpoints and USB function in @dev * before calling this, as well as the appropriate (speed-specific) - * endpoint descriptors, and also have set up the TTY driver by calling - * @gserial_setup(). + * endpoint descriptors, and also have allocate @port_num by calling + * @gserial_alloc_line(). * * Returns negative errno or zero. * On success, ep->driver_data will be overwritten. @@ -1241,11 +1170,18 @@ int gserial_connect(struct gserial *gser, u8 port_num) unsigned long flags; int status; - if (!gs_tty_driver || port_num >= n_ports) + if (port_num >= MAX_U_SERIAL_PORTS) return -ENXIO; - /* we "know" gserial_cleanup() hasn't been called */ port = ports[port_num].port; + if (!port) { + pr_err("serial line %d not allocated.\n", port_num); + return -EINVAL; + } + if (port->port_usb) { + pr_err("serial line %d is in use.\n", port_num); + return -EBUSY; + } /* activate the endpoints */ status = usb_ep_enable(gser->in); @@ -1292,7 +1228,7 @@ fail_out: gser->in->driver_data = NULL; return status; } - +EXPORT_SYMBOL_GPL(gserial_connect); /** * gserial_disconnect - notify TTY I/O glue that USB link is inactive * @gser: the function, on which gserial_connect() was called @@ -1347,3 +1283,65 @@ void gserial_disconnect(struct gserial *gser) spin_unlock_irqrestore(&port->port_lock, flags); } +EXPORT_SYMBOL_GPL(gserial_disconnect); + +static int userial_init(void) +{ + unsigned i; + int status; + + gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS); + if (!gs_tty_driver) + return -ENOMEM; + + gs_tty_driver->driver_name = "g_serial"; + gs_tty_driver->name = PREFIX; + /* uses dynamically assigned dev_t values */ + + gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gs_tty_driver->init_termios = tty_std_termios; + + /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on + * MS-Windows. Otherwise, most of these flags shouldn't affect + * anything unless we were to actually hook up to a serial line. + */ + gs_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + gs_tty_driver->init_termios.c_ispeed = 9600; + gs_tty_driver->init_termios.c_ospeed = 9600; + + tty_set_operations(gs_tty_driver, &gs_tty_ops); + for (i = 0; i < MAX_U_SERIAL_PORTS; i++) + mutex_init(&ports[i].lock); + + /* export the driver ... */ + status = tty_register_driver(gs_tty_driver); + if (status) { + pr_err("%s: cannot register, err %d\n", + __func__, status); + goto fail; + } + + pr_debug("%s: registered %d ttyGS* device%s\n", __func__, + MAX_U_SERIAL_PORTS, + (MAX_U_SERIAL_PORTS == 1) ? "" : "s"); + + return status; +fail: + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; + return status; +} +module_init(userial_init); + +static void userial_cleanup(void) +{ + tty_unregister_driver(gs_tty_driver); + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; +} +module_exit(userial_cleanup); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index 9b0fe6450fb..c20210c0bab 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -15,6 +15,13 @@ #include <linux/usb/composite.h> #include <linux/usb/cdc.h> +#define MAX_U_SERIAL_PORTS 4 + +struct f_serial_opts { + struct usb_function_instance func_inst; + u8 port_num; +}; + /* * One non-multiplexed "serial" I/O port ... there can be several of these * on any given USB peripheral device, if it provides enough endpoints. @@ -49,16 +56,15 @@ struct gserial { struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); void gs_free_req(struct usb_ep *, struct usb_request *req); -/* port setup/teardown is handled by gadget driver */ -int gserial_setup(struct usb_gadget *g, unsigned n_ports); -void gserial_cleanup(void); +/* management of individual TTY ports */ +int gserial_alloc_line(unsigned char *port_line); +void gserial_free_line(unsigned char port_line); /* connect/disconnect is handled by individual functions */ int gserial_connect(struct gserial *, u8 port_num); void gserial_disconnect(struct gserial *); /* functions are bound to configurations by a config or gadget driver */ -int acm_bind_config(struct usb_configuration *c, u8 port_num); int gser_bind_config(struct usb_configuration *c, u8 port_num); int obex_bind_config(struct usb_configuration *c, u8 port_num); diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c index e0c5e88e03e..7a55fea4343 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/u_uac1.c @@ -191,7 +191,7 @@ try_again: frames = bytes_to_frames(runtime, count); old_fs = get_fs(); set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, buf, frames); + result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames); if (result != frames) { ERROR(card, "Playback error: %d\n", (int)result); set_fs(old_fs); @@ -240,8 +240,11 @@ static int gaudio_open_snd_dev(struct gaudio *card) snd = &card->playback; snd->filp = filp_open(fn_play, O_WRONLY, 0); if (IS_ERR(snd->filp)) { + int ret = PTR_ERR(snd->filp); + ERROR(card, "No such PCM playback device: %s\n", fn_play); snd->filp = NULL; + return ret; } pcm_file = snd->filp->private_data; snd->substream = pcm_file->substream; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 4d90a800063..b0d98172bc0 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -23,6 +23,7 @@ #include <linux/list.h> #include <linux/err.h> #include <linux/dma-mapping.h> +#include <linux/workqueue.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -50,6 +51,8 @@ static DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ +#ifdef CONFIG_HAS_DMA + int usb_gadget_map_request(struct usb_gadget *gadget, struct usb_request *req, int is_in) { @@ -99,29 +102,26 @@ void usb_gadget_unmap_request(struct usb_gadget *gadget, } EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); +#endif /* CONFIG_HAS_DMA */ + /* ------------------------------------------------------------------------- */ -/** - * usb_gadget_start - tells usb device controller to start up - * @gadget: The gadget we want to get started - * @driver: The driver we want to bind to @gadget - * @bind: The bind function for @driver - * - * This call is issued by the UDC Class driver when it's about - * to register a gadget driver to the device controller, before - * calling gadget driver's bind() method. - * - * It allows the controller to be powered off until strictly - * necessary to have it powered on. - * - * Returns zero on success, else negative errno. - */ -static inline int usb_gadget_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *, struct usb_gadget_driver *)) +static void usb_gadget_state_work(struct work_struct *work) +{ + struct usb_gadget *gadget = work_to_gadget(work); + + sysfs_notify(&gadget->dev.kobj, NULL, "state"); +} + +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) { - return gadget->ops->start(driver, bind); + gadget->state = state; + schedule_work(&gadget->work); } +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ /** * usb_gadget_udc_start - tells usb device controller to start up @@ -144,24 +144,6 @@ static inline int usb_gadget_udc_start(struct usb_gadget *gadget, } /** - * usb_gadget_stop - tells usb device controller we don't need it anymore - * @gadget: The device we want to stop activity - * @driver: The driver to unbind from @gadget - * - * This call is issued by the UDC Class driver after calling - * gadget driver's unbind() method. - * - * The details are implementation specific, but it can go as - * far as powering off UDC completely and disable its data - * line pullups. - */ -static inline void usb_gadget_stop(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - gadget->ops->stop(driver); -} - -/** * usb_gadget_udc_stop - tells usb device controller we don't need it anymore * @gadget: The device we want to stop activity * @driver: The driver to unbind from @gadget @@ -196,15 +178,23 @@ static void usb_udc_release(struct device *dev) } static const struct attribute_group *usb_udc_attr_groups[]; + +static void usb_udc_nop_release(struct device *dev) +{ + dev_vdbg(dev, "%s\n", __func__); +} + /** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. * * Returns zero on success, negative errno otherwise. */ -int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) { struct usb_udc *udc; int ret = -ENOMEM; @@ -213,6 +203,25 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (!udc) goto err1; + dev_set_name(&gadget->dev, "gadget"); + INIT_WORK(&gadget->work, usb_gadget_state_work); + gadget->dev.parent = parent; + +#ifdef CONFIG_HAS_DMA + dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask); + gadget->dev.dma_parms = parent->dma_parms; + gadget->dev.dma_mask = parent->dma_mask; +#endif + + if (release) + gadget->dev.release = release; + else + gadget->dev.release = usb_udc_nop_release; + + ret = device_register(&gadget->dev); + if (ret) + goto err2; + device_initialize(&udc->dev); udc->dev.release = usb_udc_release; udc->dev.class = udc_class; @@ -220,7 +229,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) udc->dev.parent = parent; ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); if (ret) - goto err2; + goto err3; udc->gadget = gadget; @@ -229,30 +238,43 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) ret = device_add(&udc->dev); if (ret) - goto err3; + goto err4; + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); mutex_unlock(&udc_lock); return 0; -err3: + +err4: list_del(&udc->list); mutex_unlock(&udc_lock); -err2: +err3: put_device(&udc->dev); +err2: + put_device(&gadget->dev); + kfree(udc); + err1: return ret; } -EXPORT_SYMBOL_GPL(usb_add_gadget_udc); +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); -static int udc_is_newstyle(struct usb_udc *udc) +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) { - if (udc->gadget->ops->udc_start && udc->gadget->ops->udc_stop) - return 1; - return 0; + return usb_add_gadget_udc_release(parent, gadget, NULL); } - +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); static void usb_gadget_remove_driver(struct usb_udc *udc) { @@ -261,17 +283,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - if (udc_is_newstyle(udc)) { - usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc->gadget, udc->driver); - } else { - usb_gadget_stop(udc->gadget, udc->driver); - } + usb_gadget_disconnect(udc->gadget); + udc->driver->disconnect(udc->gadget); + udc->driver->unbind(udc->gadget); + usb_gadget_udc_stop(udc->gadget, NULL); udc->driver = NULL; udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; } /** @@ -305,12 +324,73 @@ found: usb_gadget_remove_driver(udc); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + flush_work(&gadget->work); device_unregister(&udc->dev); + device_unregister(&gadget->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ +static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +{ + int ret; + + dev_dbg(&udc->dev, "registering UDC driver [%s]\n", + driver->function); + + udc->driver = driver; + udc->dev.driver = &driver->driver; + udc->gadget->dev.driver = &driver->driver; + + ret = driver->bind(udc->gadget, driver); + if (ret) + goto err1; + ret = usb_gadget_udc_start(udc->gadget, driver); + if (ret) { + driver->unbind(udc->gadget); + goto err1; + } + usb_gadget_connect(udc->gadget); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + return 0; +err1: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + udc->driver->function, ret); + udc->driver = NULL; + udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; + return ret; +} + +int udc_attach_driver(const char *name, struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + ret = strcmp(name, dev_name(&udc->dev)); + if (!ret) + break; + } + if (ret) { + ret = -ENODEV; + goto out; + } + if (udc->driver) { + ret = -EBUSY; + goto out; + } + ret = udc_bind_to_driver(udc, driver); +out: + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(udc_attach_driver); + int usb_gadget_probe_driver(struct usb_gadget_driver *driver) { struct usb_udc *udc = NULL; @@ -329,41 +409,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver) pr_debug("couldn't find an available UDC\n"); mutex_unlock(&udc_lock); return -ENODEV; - found: - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); - - udc->driver = driver; - udc->dev.driver = &driver->driver; - - if (udc_is_newstyle(udc)) { - ret = driver->bind(udc->gadget, driver); - if (ret) - goto err1; - ret = usb_gadget_udc_start(udc->gadget, driver); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } - usb_gadget_connect(udc->gadget); - } else { - - ret = usb_gadget_start(udc->gadget, driver, driver->bind); - if (ret) - goto err1; - - } - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - mutex_unlock(&udc_lock); - return 0; - -err1: - dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); - udc->driver = NULL; - udc->dev.driver = NULL; + ret = udc_bind_to_driver(udc, driver); mutex_unlock(&udc_lock); return ret; } @@ -381,6 +428,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) list_for_each_entry(udc, &udc_list, list) if (udc->driver == driver) { usb_gadget_remove_driver(udc); + usb_gadget_set_state(udc->gadget, + USB_STATE_NOTATTACHED); ret = 0; break; } @@ -410,13 +459,11 @@ static ssize_t usb_udc_softconn_store(struct device *dev, struct usb_udc *udc = container_of(dev, struct usb_udc, dev); if (sysfs_streq(buf, "connect")) { - if (udc_is_newstyle(udc)) - usb_gadget_udc_start(udc->gadget, udc->driver); + usb_gadget_udc_start(udc->gadget, udc->driver); usb_gadget_connect(udc->gadget); } else if (sysfs_streq(buf, "disconnect")) { usb_gadget_disconnect(udc->gadget); - if (udc_is_newstyle(udc)) - usb_gadget_udc_stop(udc->gadget, udc->driver); + usb_gadget_udc_stop(udc->gadget, udc->driver); } else { dev_err(dev, "unsupported command '%s'\n", buf); return -EINVAL; @@ -426,21 +473,31 @@ static ssize_t usb_udc_softconn_store(struct device *dev, } static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + struct usb_gadget *gadget = udc->gadget; + + return sprintf(buf, "%s\n", usb_state_string(gadget->state)); +} +static DEVICE_ATTR_RO(state); + #define USB_UDC_SPEED_ATTR(name, param) \ -ssize_t usb_udc_##param##_show(struct device *dev, \ +ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ return snprintf(buf, PAGE_SIZE, "%s\n", \ usb_speed_string(udc->gadget->param)); \ } \ -static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL) +static DEVICE_ATTR_RO(name) static USB_UDC_SPEED_ATTR(current_speed, speed); static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); #define USB_UDC_ATTR(name) \ -ssize_t usb_udc_##name##_show(struct device *dev, \ +ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ @@ -448,7 +505,7 @@ ssize_t usb_udc_##name##_show(struct device *dev, \ \ return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ } \ -static DEVICE_ATTR(name, S_IRUGO, usb_udc_##name##_show, NULL) +static DEVICE_ATTR_RO(name) static USB_UDC_ATTR(is_otg); static USB_UDC_ATTR(is_a_peripheral); @@ -459,6 +516,7 @@ static USB_UDC_ATTR(a_alt_hnp_support); static struct attribute *usb_udc_attrs[] = { &dev_attr_srp.attr, &dev_attr_soft_connect.attr, + &dev_attr_state.attr, &dev_attr_current_speed.attr, &dev_attr_maximum_speed.attr, diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 1f49fce0f0b..73a4dfba0ed 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -13,7 +13,6 @@ #include <linux/list.h> #include <linux/string.h> #include <linux/device.h> -#include <linux/init.h> #include <linux/nls.h> #include <linux/usb/ch9.h> diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 93b0c119111..7a9111de805 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -57,6 +57,7 @@ struct uvc_event #include <linux/videodev2.h> #include <linux/version.h> #include <media/v4l2-fh.h> +#include <media/v4l2-device.h> #include "uvc_queue.h" @@ -98,8 +99,6 @@ extern unsigned int uvc_gadget_trace_param; #define DRIVER_VERSION "0.1.0" #define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - #define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 @@ -147,6 +146,7 @@ enum uvc_state struct uvc_device { struct video_device *vdev; + struct v4l2_device v4l2_dev; enum uvc_state state; struct usb_function func; struct uvc_video video; @@ -190,6 +190,7 @@ struct uvc_file_handle * Functions */ +extern void uvc_function_setup_continue(struct uvc_device *uvc); extern void uvc_endpoint_stream(struct uvc_device *dev); extern void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 104ae9c8125..1c29bc954db 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -10,6 +10,7 @@ * (at your option) any later version. */ +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/list.h> @@ -18,7 +19,9 @@ #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> -#include <linux/atomic.h> + +#include <media/v4l2-common.h> +#include <media/videobuf2-vmalloc.h> #include "uvc.h" @@ -28,253 +31,177 @@ * Video queues is initialized by uvc_queue_init(). The function performs * basic initialization of the uvc_video_queue struct and never fails. * - * Video buffer allocation and freeing are performed by uvc_alloc_buffers and - * uvc_free_buffers respectively. The former acquires the video queue lock, - * while the later must be called with the lock held (so that allocation can - * free previously allocated buffers). Trying to free buffers that are mapped - * to user space will return -EBUSY. - * - * Video buffers are managed using two queues. However, unlike most USB video - * drivers that use an in queue and an out queue, we use a main queue to hold - * all queued buffers (both 'empty' and 'done' buffers), and an irq queue to - * hold empty buffers. This design (copied from video-buf) minimizes locking - * in interrupt, as only one queue is shared between interrupt and user - * contexts. - * - * Use cases - * --------- - * - * Unless stated otherwise, all operations that modify the irq buffers queue - * are protected by the irq spinlock. - * - * 1. The user queues the buffers, starts streaming and dequeues a buffer. - * - * The buffers are added to the main and irq queues. Both operations are - * protected by the queue lock, and the later is protected by the irq - * spinlock as well. - * - * The completion handler fetches a buffer from the irq queue and fills it - * with video data. If no buffer is available (irq queue empty), the handler - * returns immediately. - * - * When the buffer is full, the completion handler removes it from the irq - * queue, marks it as ready (UVC_BUF_STATE_DONE) and wakes its wait queue. - * At that point, any process waiting on the buffer will be woken up. If a - * process tries to dequeue a buffer after it has been marked ready, the - * dequeing will succeed immediately. - * - * 2. Buffers are queued, user is waiting on a buffer and the device gets - * disconnected. - * - * When the device is disconnected, the kernel calls the completion handler - * with an appropriate status code. The handler marks all buffers in the - * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so - * that any process waiting on a buffer gets woken up. - * - * Waking up up the first buffer on the irq list is not enough, as the - * process waiting on the buffer might restart the dequeue operation - * immediately. - * + * Video buffers are managed by videobuf2. The driver uses a mutex to protect + * the videobuf2 queue operations by serializing calls to videobuf2 and a + * spinlock to protect the IRQ queue that holds the buffers to be processed by + * the driver. */ -static void -uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) -{ - mutex_init(&queue->mutex); - spin_lock_init(&queue->irqlock); - INIT_LIST_HEAD(&queue->mainqueue); - INIT_LIST_HEAD(&queue->irqqueue); - queue->type = type; -} - -/* - * Free the video buffers. - * - * This function must be called with the queue lock held. +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations */ -static int uvc_free_buffers(struct uvc_video_queue *queue) + +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - unsigned int i; + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); - for (i = 0; i < queue->count; ++i) { - if (queue->buffer[i].vma_use_count != 0) - return -EBUSY; - } + if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) + *nbuffers = UVC_MAX_VIDEO_BUFFERS; - if (queue->count) { - vfree(queue->mem); - queue->count = 0; - } + *nplanes = 1; + + sizes[0] = video->imagesize; return 0; } -/* - * Allocate the video buffers. - * - * Pages are reserved to make sure they will not be swapped, as they will be - * filled in the URB completion handler. - * - * Buffers will be individually mapped, so they must all be page aligned. - */ -static int -uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, - unsigned int buflength) +static int uvc_buffer_prepare(struct vb2_buffer *vb) { - unsigned int bufsize = PAGE_ALIGN(buflength); - unsigned int i; - void *mem = NULL; - int ret; + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); - if (nbuffers > UVC_MAX_VIDEO_BUFFERS) - nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { + uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); + return -EINVAL; + } - mutex_lock(&queue->mutex); + if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) + return -ENODEV; - if ((ret = uvc_free_buffers(queue)) < 0) - goto done; + buf->state = UVC_BUF_STATE_QUEUED; + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + buf->bytesused = 0; + else + buf->bytesused = vb2_get_plane_payload(vb, 0); - /* Bail out if no buffers should be allocated. */ - if (nbuffers == 0) - goto done; + return 0; +} - /* Decrement the number of buffers until allocation succeeds. */ - for (; nbuffers > 0; --nbuffers) { - mem = vmalloc_32(nbuffers * bufsize); - if (mem != NULL) - break; - } +static void uvc_buffer_queue(struct vb2_buffer *vb) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); + unsigned long flags; - if (mem == NULL) { - ret = -ENOMEM; - goto done; - } + spin_lock_irqsave(&queue->irqlock, flags); - for (i = 0; i < nbuffers; ++i) { - memset(&queue->buffer[i], 0, sizeof queue->buffer[i]); - queue->buffer[i].buf.index = i; - queue->buffer[i].buf.m.offset = i * bufsize; - queue->buffer[i].buf.length = buflength; - queue->buffer[i].buf.type = queue->type; - queue->buffer[i].buf.sequence = 0; - queue->buffer[i].buf.field = V4L2_FIELD_NONE; - queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; - queue->buffer[i].buf.flags = 0; - init_waitqueue_head(&queue->buffer[i].wait); + if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { + list_add_tail(&buf->queue, &queue->irqqueue); + } else { + /* If the device is disconnected return the buffer to userspace + * directly. The next QBUF call will fail with -ENODEV. + */ + buf->state = UVC_BUF_STATE_ERROR; + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); } - queue->mem = mem; - queue->count = nbuffers; - queue->buf_size = bufsize; - ret = nbuffers; + 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); -done: mutex_unlock(&queue->mutex); - return ret; } -static void __uvc_query_buffer(struct uvc_buffer *buf, - struct v4l2_buffer *v4l2_buf) +static void uvc_wait_finish(struct vb2_queue *vq) { - memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); - - if (buf->vma_use_count) - v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED; - - switch (buf->state) { - case UVC_BUF_STATE_ERROR: - case UVC_BUF_STATE_DONE: - v4l2_buf->flags |= V4L2_BUF_FLAG_DONE; - break; - case UVC_BUF_STATE_QUEUED: - case UVC_BUF_STATE_ACTIVE: - v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; - break; - case UVC_BUF_STATE_IDLE: - default: - break; - } + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_lock(&queue->mutex); } -static int -uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf) +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, + enum v4l2_buf_type type) { - int ret = 0; + int ret; - mutex_lock(&queue->mutex); - if (v4l2_buf->index >= queue->count) { - ret = -EINVAL; - goto done; - } + queue->queue.type = type; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.drv_priv = queue; + queue->queue.buf_struct_size = sizeof(struct uvc_buffer); + queue->queue.ops = &uvc_queue_qops; + queue->queue.mem_ops = &vb2_vmalloc_memops; + queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC + | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; + ret = vb2_queue_init(&queue->queue); + if (ret) + return ret; - __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf); + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = 0; -done: + return 0; +} + +/* + * Free the video buffers. + */ +static void uvc_free_buffers(struct uvc_video_queue *queue) +{ + mutex_lock(&queue->mutex); + vb2_queue_release(&queue->queue); mutex_unlock(&queue->mutex); - return ret; } /* - * Queue a video buffer. Attempting to queue a buffer that has already been - * queued will return -EINVAL. + * Allocate the video buffers. */ -static int -uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf) +static int uvc_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) { - struct uvc_buffer *buf; - unsigned long flags; - int ret = 0; + int ret; - uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index); + mutex_lock(&queue->mutex); + ret = vb2_reqbufs(&queue->queue, rb); + mutex_unlock(&queue->mutex); - if (v4l2_buf->type != queue->type || - v4l2_buf->memory != V4L2_MEMORY_MMAP) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " - "and/or memory (%u).\n", v4l2_buf->type, - v4l2_buf->memory); - return -EINVAL; - } + return ret ? ret : rb->count; +} - mutex_lock(&queue->mutex); - if (v4l2_buf->index >= queue->count) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n"); - ret = -EINVAL; - goto done; - } +static int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + int ret; - buf = &queue->buffer[v4l2_buf->index]; - if (buf->state != UVC_BUF_STATE_IDLE) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state " - "(%u).\n", buf->state); - ret = -EINVAL; - goto done; - } + mutex_lock(&queue->mutex); + ret = vb2_querybuf(&queue->queue, buf); + mutex_unlock(&queue->mutex); - if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - v4l2_buf->bytesused > buf->buf.length) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); - ret = -EINVAL; - goto done; - } + return ret; +} - if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - buf->buf.bytesused = 0; - else - buf->buf.bytesused = v4l2_buf->bytesused; +static int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + unsigned long flags; + int ret; - spin_lock_irqsave(&queue->irqlock, flags); - if (queue->flags & UVC_QUEUE_DISCONNECTED) { - spin_unlock_irqrestore(&queue->irqlock, flags); - ret = -ENODEV; + mutex_lock(&queue->mutex); + ret = vb2_qbuf(&queue->queue, buf); + if (ret < 0) goto done; - } - buf->state = UVC_BUF_STATE_QUEUED; + spin_lock_irqsave(&queue->irqlock, flags); ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; queue->flags &= ~UVC_QUEUE_PAUSED; - - list_add_tail(&buf->stream, &queue->mainqueue); - list_add_tail(&buf->queue, &queue->irqqueue); spin_unlock_irqrestore(&queue->irqlock, flags); done: @@ -282,76 +209,19 @@ done: return ret; } -static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking) -{ - if (nonblocking) { - return (buf->state != UVC_BUF_STATE_QUEUED && - buf->state != UVC_BUF_STATE_ACTIVE) - ? 0 : -EAGAIN; - } - - return wait_event_interruptible(buf->wait, - buf->state != UVC_BUF_STATE_QUEUED && - buf->state != UVC_BUF_STATE_ACTIVE); -} - /* * Dequeue a video buffer. If nonblocking is false, block until a buffer is * available. */ -static int -uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf, - int nonblocking) +static int uvc_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf, int nonblocking) { - struct uvc_buffer *buf; - int ret = 0; - - if (v4l2_buf->type != queue->type || - v4l2_buf->memory != V4L2_MEMORY_MMAP) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " - "and/or memory (%u).\n", v4l2_buf->type, - v4l2_buf->memory); - return -EINVAL; - } + int ret; mutex_lock(&queue->mutex); - if (list_empty(&queue->mainqueue)) { - uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n"); - ret = -EINVAL; - goto done; - } - - buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); - if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0) - goto done; - - uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n", - buf->buf.index, buf->state, buf->buf.bytesused); - - switch (buf->state) { - case UVC_BUF_STATE_ERROR: - uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data " - "(transmission error).\n"); - ret = -EIO; - case UVC_BUF_STATE_DONE: - buf->state = UVC_BUF_STATE_IDLE; - break; - - case UVC_BUF_STATE_IDLE: - case UVC_BUF_STATE_QUEUED: - case UVC_BUF_STATE_ACTIVE: - default: - uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u " - "(driver bug?).\n", buf->state); - ret = -EINVAL; - goto done; - } - - list_del(&buf->stream); - __uvc_query_buffer(buf, v4l2_buf); - -done: + ret = vb2_dqbuf(&queue->queue, buf, nonblocking); mutex_unlock(&queue->mutex); + return ret; } @@ -361,105 +231,47 @@ done: * This function implements video queue polling and is intended to be used by * the device poll handler. */ -static unsigned int -uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, - poll_table *wait) +static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait) { - struct uvc_buffer *buf; - unsigned int mask = 0; + unsigned int ret; mutex_lock(&queue->mutex); - if (list_empty(&queue->mainqueue)) - goto done; - - buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream); - - poll_wait(file, &buf->wait, wait); - if (buf->state == UVC_BUF_STATE_DONE || - buf->state == UVC_BUF_STATE_ERROR) - mask |= POLLOUT | POLLWRNORM; - -done: + ret = vb2_poll(&queue->queue, file, wait); mutex_unlock(&queue->mutex); - return mask; -} -/* - * VMA operations. - */ -static void uvc_vm_open(struct vm_area_struct *vma) -{ - struct uvc_buffer *buffer = vma->vm_private_data; - buffer->vma_use_count++; + return ret; } -static void uvc_vm_close(struct vm_area_struct *vma) +static int uvc_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma) { - struct uvc_buffer *buffer = vma->vm_private_data; - buffer->vma_use_count--; -} + int ret; -static struct vm_operations_struct uvc_vm_ops = { - .open = uvc_vm_open, - .close = uvc_vm_close, -}; + mutex_lock(&queue->mutex); + ret = vb2_mmap(&queue->queue, vma); + mutex_unlock(&queue->mutex); + + return ret; +} +#ifndef CONFIG_MMU /* - * Memory-map a buffer. + * Get unmapped area. * - * This function implements video buffer memory mapping and is intended to be - * used by the device mmap handler. + * NO-MMU arch need this function to make mmap() work correctly. */ -static int -uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) +static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) { - struct uvc_buffer *uninitialized_var(buffer); - struct page *page; - unsigned long addr, start, size; - unsigned int i; - int ret = 0; - - start = vma->vm_start; - size = vma->vm_end - vma->vm_start; + unsigned long ret; mutex_lock(&queue->mutex); - - for (i = 0; i < queue->count; ++i) { - buffer = &queue->buffer[i]; - if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) - break; - } - - if (i == queue->count || size != queue->buf_size) { - ret = -EINVAL; - goto done; - } - - /* - * VM_IO marks the area as being an mmaped region for I/O to a - * device. It also prevents the region from being core dumped. - */ - vma->vm_flags |= VM_IO; - - addr = (unsigned long)queue->mem + buffer->buf.m.offset; - while (size > 0) { - page = vmalloc_to_page((void *)addr); - if ((ret = vm_insert_page(vma, start, page)) < 0) - goto done; - - start += PAGE_SIZE; - addr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &uvc_vm_ops; - vma->vm_private_data = buffer; - uvc_vm_open(vma); - -done: + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); mutex_unlock(&queue->mutex); return ret; } +#endif /* * Cancel the video buffers queue. @@ -484,7 +296,7 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) queue); list_del(&buf->queue); buf->state = UVC_BUF_STATE_ERROR; - wake_up(&buf->wait); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); } /* This must be protected by the irqlock spinlock to avoid race * conditions between uvc_queue_buffer and the disconnection event that @@ -516,26 +328,33 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) */ static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) { - unsigned int i; + unsigned long flags; int ret = 0; mutex_lock(&queue->mutex); if (enable) { - if (uvc_queue_streaming(queue)) { - ret = -EBUSY; + ret = vb2_streamon(&queue->queue, queue->queue.type); + if (ret < 0) goto done; - } + queue->sequence = 0; - queue->flags |= UVC_QUEUE_STREAMING; queue->buf_used = 0; } else { - uvc_queue_cancel(queue, 0); - INIT_LIST_HEAD(&queue->mainqueue); + ret = vb2_streamoff(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; - for (i = 0; i < queue->count; ++i) - queue->buffer[i].state = UVC_BUF_STATE_IDLE; + spin_lock_irqsave(&queue->irqlock, flags); + INIT_LIST_HEAD(&queue->irqqueue); - queue->flags &= ~UVC_QUEUE_STREAMING; + /* + * FIXME: We need to clear the DISCONNECTED flag to ensure that + * applications will be able to queue buffers for the next + * streaming run. However, clearing it here doesn't guarantee + * that the device will be reconnected in the meantime. + */ + queue->flags &= ~UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); } done: @@ -544,15 +363,15 @@ done: } /* called with &queue_irqlock held.. */ -static struct uvc_buffer * -uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) +static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && - buf->buf.length != buf->buf.bytesused) { + buf->length != buf->bytesused) { buf->state = UVC_BUF_STATE_QUEUED; - buf->buf.bytesused = 0; + vb2_set_plane_payload(&buf->buf, 0, 0); return buf; } @@ -563,10 +382,13 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) else nextbuf = NULL; - buf->buf.sequence = queue->sequence++; - do_gettimeofday(&buf->buf.timestamp); + buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; + buf->buf.v4l2_buf.sequence = queue->sequence++; + v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp); + + vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); - wake_up(&buf->wait); return nextbuf; } diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h index 1812a8ecc5d..8e76ce982f1 100644 --- a/drivers/usb/gadget/uvc_queue.h +++ b/drivers/usb/gadget/uvc_queue.h @@ -6,6 +6,7 @@ #include <linux/kernel.h> #include <linux/poll.h> #include <linux/videodev2.h> +#include <media/videobuf2-core.h> /* Maximum frame size in bytes, for sanity checking. */ #define UVC_MAX_FRAME_SIZE (16*1024*1024) @@ -25,42 +26,35 @@ enum uvc_buffer_state { }; struct uvc_buffer { - unsigned long vma_use_count; - struct list_head stream; - - /* Touched by interrupt handler. */ - struct v4l2_buffer buf; + struct vb2_buffer buf; struct list_head queue; - wait_queue_head_t wait; + enum uvc_buffer_state state; + void *mem; + unsigned int length; + unsigned int bytesused; }; -#define UVC_QUEUE_STREAMING (1 << 0) -#define UVC_QUEUE_DISCONNECTED (1 << 1) -#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) -#define UVC_QUEUE_PAUSED (1 << 3) +#define UVC_QUEUE_DISCONNECTED (1 << 0) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1) +#define UVC_QUEUE_PAUSED (1 << 2) struct uvc_video_queue { - enum v4l2_buf_type type; + struct vb2_queue queue; + struct mutex mutex; /* Protects queue */ - void *mem; unsigned int flags; __u32 sequence; - unsigned int count; - unsigned int buf_size; unsigned int buf_used; - struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; - struct mutex mutex; /* protects buffers and mainqueue */ - spinlock_t irqlock; /* protects irqqueue */ - struct list_head mainqueue; + spinlock_t irqlock; /* Protects flags and irqqueue */ struct list_head irqqueue; }; static inline int uvc_queue_streaming(struct uvc_video_queue *queue) { - return queue->flags & UVC_QUEUE_STREAMING; + return vb2_is_streaming(&queue->queue); } #endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 2ca9386d655..ad48e81155e 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -41,9 +41,8 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) req->length = min_t(unsigned int, uvc->event_length, data->length); req->zero = data->length < uvc->event_length; - req->dma = DMA_ADDR_INVALID; - memcpy(req->buf, data->data, data->length); + memcpy(req->buf, data->data, req->length); return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL); } @@ -148,16 +147,13 @@ uvc_v4l2_release(struct file *file) uvc_function_disconnect(uvc); uvc_video_enable(video, 0); - mutex_lock(&video->queue.mutex); - if (uvc_free_buffers(&video->queue) < 0) - printk(KERN_ERR "uvc_v4l2_release: Unable to free " - "buffers.\n"); - mutex_unlock(&video->queue.mutex); + uvc_free_buffers(&video->queue); file->private_data = NULL; v4l2_fh_del(&handle->vfh); v4l2_fh_exit(&handle->vfh); kfree(handle); + return 0; } @@ -178,9 +174,9 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_capability *cap = arg; memset(cap, 0, sizeof *cap); - strncpy(cap->driver, "g_uvc", sizeof(cap->driver)); - strncpy(cap->card, cdev->gadget->name, sizeof(cap->card)); - strncpy(cap->bus_info, dev_name(&cdev->gadget->dev), + strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); + strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -192,7 +188,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_format *fmt = arg; - if (fmt->type != video->queue.type) + if (fmt->type != video->queue.queue.type) return -EINVAL; return uvc_v4l2_get_format(video, fmt); @@ -202,7 +198,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_format *fmt = arg; - if (fmt->type != video->queue.type) + if (fmt->type != video->queue.queue.type) return -EINVAL; return uvc_v4l2_set_format(video, fmt); @@ -213,16 +209,13 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_requestbuffers *rb = arg; - if (rb->type != video->queue.type || - rb->memory != V4L2_MEMORY_MMAP) + if (rb->type != video->queue.queue.type) return -EINVAL; - ret = uvc_alloc_buffers(&video->queue, rb->count, - video->imagesize); + ret = uvc_alloc_buffers(&video->queue, rb); if (ret < 0) return ret; - rb->count = ret; ret = 0; break; } @@ -231,9 +224,6 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_buffer *buf = arg; - if (buf->type != video->queue.type) - return -EINVAL; - return uvc_query_buffer(&video->queue, buf); } @@ -251,24 +241,36 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->queue.type) + if (*type != video->queue.queue.type) return -EINVAL; - return uvc_video_enable(video, 1); + /* Enable UVC video. */ + ret = uvc_video_enable(video, 1); + if (ret < 0) + return ret; + + /* + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ + uvc_function_setup_continue(uvc); + uvc->state = UVC_STATE_STREAMING; + + return 0; } case VIDIOC_STREAMOFF: { int *type = arg; - if (*type != video->queue.type) + if (*type != video->queue.queue.type) return -EINVAL; return uvc_video_enable(video, 0); } /* Events */ - case VIDIOC_DQEVENT: + case VIDIOC_DQEVENT: { struct v4l2_event *event = arg; @@ -333,17 +335,21 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - unsigned int mask = 0; - poll_wait(file, &handle->vfh.wait, wait); - if (v4l2_event_pending(&handle->vfh)) - mask |= POLLPRI; + return uvc_queue_poll(&uvc->video.queue, file, wait); +} - mask |= uvc_queue_poll(&uvc->video.queue, file, wait); +#ifndef CONFIG_MMU +static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); - return mask; + return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff); } +#endif static struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, @@ -352,5 +358,8 @@ static struct v4l2_file_operations uvc_v4l2_fops = { .ioctl = uvc_v4l2_ioctl, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, +#ifndef CONFIG_MMU + .get_unmapped_area = uvc_v4l2_get_unmapped_area, +#endif }; diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index b0e53a8ea4f..71e896d4c5a 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -32,7 +32,7 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, data[0] = 2; data[1] = UVC_STREAM_EOH | video->fid; - if (buf->buf.bytesused - video->queue.buf_used <= len - 2) + if (buf->bytesused - video->queue.buf_used <= len - 2) data[1] |= UVC_STREAM_EOF; return 2; @@ -47,8 +47,8 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, void *mem; /* Copy video data to the USB buffer. */ - mem = queue->mem + buf->buf.m.offset + queue->buf_used; - nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); + mem = buf->mem + queue->buf_used; + nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); memcpy(data, mem, nbytes); queue->buf_used += nbytes; @@ -82,7 +82,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, req->length = video->req_size - len; req->zero = video->payload_size == video->max_payload_size; - if (buf->buf.bytesused == video->queue.buf_used) { + if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; uvc_queue_next_buffer(&video->queue, buf); @@ -92,7 +92,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } if (video->payload_size == video->max_payload_size || - buf->buf.bytesused == video->queue.buf_used) + buf->bytesused == video->queue.buf_used) video->payload_size = 0; } @@ -115,7 +115,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, req->length = video->req_size - len; - if (buf->buf.bytesused == video->queue.buf_used) { + if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; uvc_queue_next_buffer(&video->queue, buf); @@ -161,6 +161,7 @@ static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { struct uvc_video *video = req->context; + struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *buf; unsigned long flags; int ret; @@ -169,13 +170,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) case 0: break; - case -ESHUTDOWN: + case -ESHUTDOWN: /* disconnect from host. */ printk(KERN_INFO "VS request cancelled.\n"); + uvc_queue_cancel(queue, 1); goto requeue; default: printk(KERN_INFO "VS request completed with status %d.\n", req->status); + uvc_queue_cancel(queue, 0); goto requeue; } @@ -229,13 +232,18 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + unsigned int req_size; unsigned int i; int ret = -ENOMEM; BUG_ON(video->req_size); + req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult + 1); + for (i = 0; i < UVC_NUM_REQUESTS; ++i) { - video->req_buffer[i] = kmalloc(video->ep->maxpacket, GFP_KERNEL); + video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); if (video->req_buffer[i] == NULL) goto error; @@ -245,14 +253,14 @@ uvc_video_alloc_requests(struct uvc_video *video) video->req[i]->buf = video->req_buffer[i]; video->req[i]->length = 0; - video->req[i]->dma = DMA_ADDR_INVALID; video->req[i]->complete = uvc_video_complete; video->req[i]->context = video; list_add_tail(&video->req[i]->list, &video->req_free); } - video->req_size = video->ep->maxpacket; + video->req_size = req_size; + return 0; error: @@ -309,7 +317,8 @@ uvc_video_pump(struct uvc_video *video) video->encode(req, video, buf); /* Queue the USB request */ - if ((ret = usb_ep_queue(video->ep, req, GFP_KERNEL)) < 0) { + ret = usb_ep_queue(video->ep, req, GFP_ATOMIC); + if (ret < 0) { printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index 69cf5c2cd33..8cef1e658c2 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -336,7 +336,7 @@ static struct usb_configuration webcam_config_driver = { .bConfigurationValue = 1, .iConfiguration = 0, /* dynamic */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, + .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, }; static int /* __init_or_exit */ diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 6bf4c061136..134f354ede6 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -10,7 +10,6 @@ * (at your option) any later version. */ - /* * Gadget Zero only needs two bulk endpoints, and is an example of how you * can write a hardware-agnostic gadget driver running inside a USB device. @@ -43,23 +42,11 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/device.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/usb/composite.h> #include "g_zero.h" -#include "gadget_chips.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_sourcesink.c" -#include "f_loopback.c" - /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -67,9 +54,6 @@ USB_GADGET_COMPOSITE_OPTIONS(); static const char longname[] = "Gadget Zero"; -unsigned buflen = 4096; /* only used for bulk endpoints */ -module_param(buflen, uint, 0); - /* * Normally the "loopback" configuration is second (index 1) so * it's not the default. Here's where to change that order, to @@ -79,6 +63,13 @@ module_param(buflen, uint, 0); static bool loopdefault = 0; module_param(loopdefault, bool, S_IRUGO|S_IWUSR); +static struct usb_zero_options gzero_options = { + .isoc_interval = GZERO_ISOC_INTERVAL, + .isoc_maxpacket = GZERO_ISOC_MAXPACKET, + .bulk_buflen = GZERO_BULK_BUFLEN, + .qlen = GZERO_QLEN, +}; + /*-------------------------------------------------------------------------*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -100,10 +91,22 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR); * functional coverage for the "USBCV" test harness from USB-IF. * It's always set if OTG mode is enabled. */ -unsigned autoresume = DEFAULT_AUTORESUME; +static unsigned autoresume = DEFAULT_AUTORESUME; module_param(autoresume, uint, S_IRUGO); MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); +/* Maximum Autoresume time */ +static unsigned max_autoresume; +module_param(max_autoresume, uint, S_IRUGO); +MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup"); + +/* Interval between two remote wakeups */ +static unsigned autoresume_interval_ms; +module_param(autoresume_interval_ms, uint, S_IRUGO); +MODULE_PARM_DESC(autoresume_interval_ms, + "milliseconds to increase successive wakeup delays"); + +static unsigned autoresume_step_ms; /*-------------------------------------------------------------------------*/ static struct usb_device_descriptor device_desc = { @@ -129,20 +132,27 @@ static struct usb_otg_descriptor otg_descriptor = { .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, }; -const struct usb_descriptor_header *otg_desc[] = { +static const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, NULL, }; +#else +#define otg_desc NULL #endif /* string IDs are assigned dynamically */ /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; +#define USB_GZERO_SS_DESC (USB_GADGET_FIRST_AVAIL_IDX + 0) +#define USB_GZERO_LB_DESC (USB_GADGET_FIRST_AVAIL_IDX + 1) + static struct usb_string strings_dev[] = { [USB_GADGET_MANUFACTURER_IDX].s = "", [USB_GADGET_PRODUCT_IDX].s = longname, [USB_GADGET_SERIAL_IDX].s = serial, + [USB_GZERO_SS_DESC].s = "source and sink data", + [USB_GZERO_LB_DESC].s = "loop input to output", { } /* end of list */ }; @@ -158,58 +168,6 @@ static struct usb_gadget_strings *dev_strings[] = { /*-------------------------------------------------------------------------*/ -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - if (len) - req->length = len; - else - req->length = buflen; - req->buf = kmalloc(req->length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; -} - -void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) -{ - int value; - - if (ep->driver_data) { - value = usb_ep_disable(ep); - if (value < 0) - DBG(cdev, "disable %s --> %d\n", - ep->name, value); - ep->driver_data = NULL; - } -} - -void disable_endpoints(struct usb_composite_dev *cdev, - struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out) -{ - disable_ep(cdev, in); - disable_ep(cdev, out); - if (iso_in) - disable_ep(cdev, iso_in); - if (iso_out) - disable_ep(cdev, iso_out); -} - -/*-------------------------------------------------------------------------*/ - static struct timer_list autoresume_timer; static void zero_autoresume(unsigned long _c) @@ -237,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev) return; if (autoresume) { - mod_timer(&autoresume_timer, jiffies + (HZ * autoresume)); - DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); + if (max_autoresume && + (autoresume_step_ms > max_autoresume * 1000)) + autoresume_step_ms = autoresume * 1000; + + mod_timer(&autoresume_timer, jiffies + + msecs_to_jiffies(autoresume_step_ms)); + DBG(cdev, "suspend, wakeup in %d milliseconds\n", + autoresume_step_ms); + + autoresume_step_ms += autoresume_interval_ms; } else DBG(cdev, "%s\n", __func__); } @@ -251,8 +217,65 @@ static void zero_resume(struct usb_composite_dev *cdev) /*-------------------------------------------------------------------------*/ +static struct usb_configuration loopback_driver = { + .label = "loopback", + .bConfigurationValue = 2, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + /* .iConfiguration = DYNAMIC */ +}; + +static struct usb_function *func_ss; +static struct usb_function_instance *func_inst_ss; + +static int ss_config_setup(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequest) { + case 0x5b: + case 0x5c: + return func_ss->setup(func_ss, ctrl); + default: + return -EOPNOTSUPP; + } +} + +static struct usb_configuration sourcesink_driver = { + .label = "source/sink", + .setup = ss_config_setup, + .bConfigurationValue = 3, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + /* .iConfiguration = DYNAMIC */ +}; + +module_param_named(buflen, gzero_options.bulk_buflen, uint, 0); +module_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none"); + +module_param_named(isoc_interval, gzero_options.isoc_interval, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_interval, "1 - 16"); + +module_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); + +module_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)"); + +module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); + +static struct usb_function *func_lb; +static struct usb_function_instance *func_inst_lb; + +module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(qlen, "depth of loopback queue"); + static int __init zero_bind(struct usb_composite_dev *cdev) { + struct f_ss_opts *ss_opts; + struct f_lb_opts *lb_opts; int status; /* Allocate string descriptor numbers ... note that string @@ -268,27 +291,112 @@ static int __init zero_bind(struct usb_composite_dev *cdev) setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev); + func_inst_ss = usb_get_function_instance("SourceSink"); + if (IS_ERR(func_inst_ss)) + return PTR_ERR(func_inst_ss); + + ss_opts = container_of(func_inst_ss, struct f_ss_opts, func_inst); + ss_opts->pattern = gzero_options.pattern; + ss_opts->isoc_interval = gzero_options.isoc_interval; + ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; + ss_opts->isoc_mult = gzero_options.isoc_mult; + ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; + ss_opts->bulk_buflen = gzero_options.bulk_buflen; + + func_ss = usb_get_function(func_inst_ss); + if (IS_ERR(func_ss)) { + status = PTR_ERR(func_ss); + goto err_put_func_inst_ss; + } + + func_inst_lb = usb_get_function_instance("Loopback"); + if (IS_ERR(func_inst_lb)) { + status = PTR_ERR(func_inst_lb); + goto err_put_func_ss; + } + + lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst); + lb_opts->bulk_buflen = gzero_options.bulk_buflen; + lb_opts->qlen = gzero_options.qlen; + + func_lb = usb_get_function(func_inst_lb); + if (IS_ERR(func_lb)) { + status = PTR_ERR(func_lb); + goto err_put_func_inst_lb; + } + + sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id; + loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id; + + /* support autoresume for remote wakeup testing */ + sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; + loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; + sourcesink_driver.descriptors = NULL; + loopback_driver.descriptors = NULL; + if (autoresume) { + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + autoresume_step_ms = autoresume * 1000; + } + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + sourcesink_driver.descriptors = otg_desc; + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_driver.descriptors = otg_desc; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + /* Register primary, then secondary configuration. Note that * SH3 only allows one config... */ if (loopdefault) { - loopback_add(cdev, autoresume != 0); - sourcesink_add(cdev, autoresume != 0); + usb_add_config_only(cdev, &loopback_driver); + usb_add_config_only(cdev, &sourcesink_driver); } else { - sourcesink_add(cdev, autoresume != 0); - loopback_add(cdev, autoresume != 0); + usb_add_config_only(cdev, &sourcesink_driver); + usb_add_config_only(cdev, &loopback_driver); } + status = usb_add_function(&sourcesink_driver, func_ss); + if (status) + goto err_conf_flb; + usb_ep_autoconfig_reset(cdev->gadget); + status = usb_add_function(&loopback_driver, func_lb); + if (status) + goto err_conf_flb; + + usb_ep_autoconfig_reset(cdev->gadget); usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); return 0; + +err_conf_flb: + usb_put_function(func_lb); + func_lb = NULL; +err_put_func_inst_lb: + usb_put_function_instance(func_inst_lb); + func_inst_lb = NULL; +err_put_func_ss: + usb_put_function(func_ss); + func_ss = NULL; +err_put_func_inst_ss: + usb_put_function_instance(func_inst_ss); + func_inst_ss = NULL; + return status; } static int zero_unbind(struct usb_composite_dev *cdev) { del_timer_sync(&autoresume_timer); + if (!IS_ERR_OR_NULL(func_ss)) + usb_put_function(func_ss); + usb_put_function_instance(func_inst_ss); + if (!IS_ERR_OR_NULL(func_lb)) + usb_put_function(func_lb); + usb_put_function_instance(func_inst_lb); return 0; } |
