diff options
Diffstat (limited to 'drivers/usb/gadget')
129 files changed, 64712 insertions, 20806 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 080bb1e4b84..ba18e9c110c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -15,6 +15,7 @@ menuconfig USB_GADGET tristate "USB Gadget Support" + select NLS help USB is a master/slave protocol, organized with one master host (such as a PC) controlling up to 127 peripheral devices. @@ -57,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 @@ -96,8 +111,21 @@ config USB_GADGET_VBUS_DRAW This value will be used except for system-specific gadget drivers that have more specific information. -config USB_GADGET_SELECTED - boolean +config USB_GADGET_STORAGE_NUM_BUFFERS + int "Number of storage pipeline buffers" + range 2 4 + default 2 + help + Usually 2 buffers are enough to establish a good buffering + pipeline. The number may be increased in order to compensate + for a bursty VFS behaviour. For instance there may be CPU wake up + latencies that makes the VFS to appear bursty in a system with + an CPU on-demand governor. Especially if DMA is doing IO to + offload the CPU. In this case the CPU will go into power + save often and spin up occasionally to move data within VFS. + If selecting USB_GADGET_DEBUG_FILES this value may be set by + a module parameter as well. + If unsure, say 2. # # USB Peripheral Controller Support @@ -109,23 +137,15 @@ config USB_GADGET_SELECTED # - discrete ones (including all PCI-only controllers) # - debug/dummy gadget+hcd is last. # -choice - prompt "USB Peripheral Controller" - depends on USB_GADGET - help - A USB device uses a controller to talk to its host. - Systems should have only one such upstream link. - Many controller drivers are platform-specific; these - often need board-specific hooks. +menu "USB Peripheral Controller" # # Integrated controllers # -config USB_GADGET_AT91 - boolean "Atmel AT91 USB Device Port" - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 - select USB_GADGET_SELECTED +config USB_AT91 + tristate "Atmel AT91 USB Device Port" + depends on ARCH_AT91 help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable @@ -135,31 +155,41 @@ config USB_GADGET_AT91 dynamically linked module called "at91_udc" and force all gadget drivers to also be dynamically linked. -config USB_AT91 - tristate - depends on USB_GADGET_AT91 - default USB_GADGET +config USB_LPC32XX + tristate "LPC32XX USB Peripheral Controller" + depends on ARCH_LPC32XX && I2C + select USB_ISP1301 + help + This option selects the USB device controller in the LPC32xx SoC. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "lpc32xx_udc" and force all + gadget drivers to also be dynamically linked. -config USB_GADGET_ATMEL_USBA - boolean "Atmel USBA" - select USB_GADGET_DUALSPEED - depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL +config USB_ATMEL_USBA + tristate "Atmel USBA" + 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. -config USB_ATMEL_USBA - tristate - depends on USB_GADGET_ATMEL_USBA - default USB_GADGET - select USB_GADGET_SELECTED +config USB_BCM63XX_UDC + tristate "Broadcom BCM63xx Peripheral Controller" + depends on BCM63XX + help + Many Broadcom BCM63xx chipsets (such as the BCM6328) have a + high speed USB Device Port with support for four fixed endpoints + (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "bcm63xx_udc". -config USB_GADGET_FSL_USB2 - boolean "Freescale Highspeed USB DR Peripheral Controller" - depends on FSL_SOC - select USB_GADGET_DUALSPEED +config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" + depends on FSL_SOC || ARCH_MXC + select USB_FSL_MPH_DR_OF if OF help - Some of Freescale PowerPC processors have a High Speed + Some of Freescale PowerPC and i.MX processors have a High Speed Dual-Role(DR) USB controller, which supports device mode. The number of programmable endpoints is different through @@ -169,29 +199,34 @@ config USB_GADGET_FSL_USB2 dynamically linked module called "fsl_usb2_udc" and force all gadget drivers to also be dynamically linked. -config USB_FSL_USB2 - tristate - depends on USB_GADGET_FSL_USB2 - default USB_GADGET - select USB_GADGET_SELECTED +config USB_FUSB300 + tristate "Faraday FUSB300 USB Peripheral Controller" + depends on !PHYS_ADDR_T_64BIT && HAS_DMA + help + Faraday usb device controller FUSB300 driver -config USB_GADGET_LH7A40X - boolean "LH7A40X" - depends on ARCH_LH7A40X +config USB_FOTG210_UDC + depends on HAS_DMA + tristate "Faraday FOTG210 USB Peripheral Controller" help - This driver provides USB Device Controller driver for LH7A40x + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. -config USB_LH7A40X - tristate - depends on USB_GADGET_LH7A40X - default USB_GADGET - select USB_GADGET_SELECTED + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". -config USB_GADGET_OMAP - boolean "OMAP USB Device Controller" - depends on ARCH_OMAP - select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG - select USB_OTG_UTILS if ARCH_OMAP +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 + 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 @@ -203,25 +238,8 @@ config USB_GADGET_OMAP dynamically linked module called "omap_udc" and force all gadget drivers to also be dynamically linked. -config USB_OMAP - tristate - depends on USB_GADGET_OMAP - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_OTG - boolean "OTG Support" - depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD - help - The most notable feature of USB OTG is support for a - "Dual-Role" device, which can act as either a device - or a host. The initial role choice can be changed - later, when two dual-role devices talk to each other. - - Select this only if your OMAP board has a Mini-AB connector. - -config USB_GADGET_PXA25X - boolean "PXA 25x or IXP 4xx" +config USB_PXA25X + tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX help Intel's PXA 25x series XScale ARM-5TE processors include @@ -235,26 +253,42 @@ config USB_GADGET_PXA25X dynamically linked module called "pxa25x_udc" and force all gadget drivers to also be dynamically linked. -config USB_PXA25X - tristate - depends on USB_GADGET_PXA25X - default USB_GADGET - select USB_GADGET_SELECTED - # if there's only one gadget driver, using only two bulk endpoints, # don't waste memory for the other endpoints config USB_PXA25X_SMALL - depends on USB_GADGET_PXA25X + depends on USB_PXA25X bool default n if USB_ETH_RNDIS default y if USB_ZERO default y if USB_ETH default y if USB_G_SERIAL -config USB_GADGET_PXA27X - boolean "PXA 27x" - depends on ARCH_PXA && PXA27x - select USB_OTG_UTILS +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. + It has nine configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "r8a66597_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_RENESAS_USBHS_UDC + tristate 'Renesas USBHS controller' + depends on USB_RENESAS_USBHS + help + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA27X + tristate "PXA 27x" help Intel's PXA 27x series XScale ARM v5TE processors include an integrated full speed USB 1.1 device controller. @@ -266,15 +300,9 @@ config USB_GADGET_PXA27X dynamically linked module called "pxa27x_udc" and force all gadget drivers to also be dynamically linked. -config USB_PXA27X - tristate - depends on USB_GADGET_PXA27X - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_S3C2410 - boolean "S3C2410 USB Device Controller" - depends on ARCH_S3C2410 +config USB_S3C2410 + tristate "S3C2410 USB Device Controller" + depends on ARCH_S3C24XX help Samsung's S3C2410 is an ARM-4 processor with an integrated full speed USB 1.1 device controller. It has 4 configurable @@ -283,54 +311,41 @@ config USB_GADGET_S3C2410 This driver has been tested on the S3C2410, S3C2412, and S3C2440 processors. -config USB_S3C2410 - tristate - depends on USB_GADGET_S3C2410 - default USB_GADGET - select USB_GADGET_SELECTED - config USB_S3C2410_DEBUG boolean "S3C2410 udc debug messages" - depends on USB_GADGET_S3C2410 - -# -# Controllers available in both integrated and discrete versions -# + depends on USB_S3C2410 -# musb builds in ../musb along with host support -config USB_GADGET_MUSB_HDRC - boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)" - depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) - select USB_GADGET_DUALSPEED - select USB_GADGET_SELECTED +config USB_S3C_HSUDC + tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C24XX help - This OTG-capable silicon IP is used in dual designs including - the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. -config USB_GADGET_IMX - boolean "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 - help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. + This driver has been tested on S3C2416 and S3C2450 processors. - It has Six fixed-function endpoints, as well as endpoint - zero (for control transfers). +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. - 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_MV_U3D + depends on HAS_DMA + tristate "MARVELL PXA2128 USB 3.0 controller" + help + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. -config USB_IMX - tristate - depends on USB_GADGET_IMX - default USB_GADGET - select USB_GADGET_SELECTED +# +# Controllers available in both integrated and discrete versions +# -config USB_GADGET_M66592 - boolean "Renesas M66592 USB Peripheral Controller" - select USB_GADGET_DUALSPEED +config USB_M66592 + tristate "Renesas M66592 USB Peripheral Controller" help M66592 is a discrete USB peripheral controller chip that supports both full and high speed USB 2.0 data transfers. @@ -340,30 +355,13 @@ config USB_GADGET_M66592 dynamically linked module called "m66592_udc" and force all gadget drivers to also be dynamically linked. -config USB_M66592 - tristate - depends on USB_GADGET_M66592 - default USB_GADGET - select USB_GADGET_SELECTED - -config SUPERH_BUILT_IN_M66592 - boolean "Enable SuperH built-in USB like the M66592" - depends on USB_GADGET_M66592 && CPU_SUBTYPE_SH7722 - help - SH7722 has USB like the M66592. - - The transfer rate is very slow when use "Ethernet Gadget". - However, this problem is improved if change a value of - NET_IP_ALIGN to 4. - # # Controllers available only in discrete form (and all PCI controllers) # -config USB_GADGET_AMD5536UDC - boolean "AMD5536 UDC" +config USB_AMD5536UDC + tristate "AMD5536 UDC" depends on PCI - select USB_GADGET_DUALSPEED help The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. It is a USB Highspeed DMA capable USB device controller. Beside ep0 @@ -375,14 +373,8 @@ config USB_GADGET_AMD5536UDC dynamically linked module called "amd5536udc" and force all gadget drivers to also be dynamically linked. -config USB_AMD5536UDC - tristate - depends on USB_GADGET_AMD5536UDC - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_FSL_QE - boolean "Freescale QE/CPM USB Device Controller" +config USB_FSL_QE + tristate "Freescale QE/CPM USB Device Controller" depends on FSL_SOC && (QUICC_ENGINE || CPM) help Some of Freescale PowerPC processors have a Full Speed @@ -394,34 +386,31 @@ config USB_GADGET_FSL_QE Set CONFIG_USB_GADGET to "m" to build this driver as a dynamically linked module called "fsl_qe_udc". -config USB_FSL_QE - tristate - depends on USB_GADGET_FSL_QE - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_CI13XXX - boolean "MIPS USB CI13xxx" - depends on PCI - select USB_GADGET_DUALSPEED +config USB_NET2272 + tristate "PLX NET2272" help - MIPS USB IP core family device controller - Currently it only supports IP part number CI13412 + PLX NET2272 is a USB peripheral controller which supports + both full and high speed USB 2.0 data transfers. + It has three configurable endpoints, as well as endpoint zero + (for control transfer). Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "ci13xxx_udc" and force all + dynamically linked module called "net2272" and force all gadget drivers to also be dynamically linked. -config USB_CI13XXX - tristate - depends on USB_GADGET_CI13XXX - default USB_GADGET - select USB_GADGET_SELECTED +config USB_NET2272_DMA + boolean "Support external DMA controller" + 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 + driver itself. + + If unsure, say "N" here. The driver works fine in PIO mode. -config USB_GADGET_NET2280 - boolean "NetChip 228x" +config USB_NET2280 + tristate "NetChip 228x" depends on PCI - select USB_GADGET_DUALSPEED help NetChip 2280 / 2282 is a PCI based USB peripheral controller which supports both full and high speed USB 2.0 data transfers. @@ -434,14 +423,8 @@ config USB_GADGET_NET2280 dynamically linked module called "net2280" and force all gadget drivers to also be dynamically linked. -config USB_NET2280 - tristate - depends on USB_GADGET_NET2280 - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_GOKU - boolean "Toshiba TC86C001 'Goku-S'" +config USB_GOKU + tristate "Toshiba TC86C001 'Goku-S'" depends on PCI help The Toshiba TC86C001 is a PCI device which includes controllers @@ -454,32 +437,46 @@ config USB_GADGET_GOKU dynamically linked module called "goku_udc" and to force all gadget drivers to also be dynamically linked. -config USB_GOKU - tristate - depends on USB_GADGET_GOKU - default USB_GADGET - select USB_GADGET_SELECTED - +config USB_EG20T + tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" + depends on PCI + help + This is a USB device driver for EG20T PCH. + EG20T PCH is the platform controller hub that is used in Intel's + general embedded platform. EG20T PCH has USB device interface. + Using this interface, it is able to access system devices connected + to USB device. + This driver enables USB device function. + USB device is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + This driver supports both control transfer and bulk transfer modes. + This driver dose not support interrupt transfer or isochronous + transfer modes. + + This driver also can be used for LAPIS Semiconductor's ML7213 which is + for IVI(In-Vehicle Infotainment) use. + ML7831 is for general purpose use. + ML7213/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7831 is completely compatible for Intel EG20T PCH. # # LAST -- dummy/emulated controller # -config USB_GADGET_DUMMY_HCD - boolean "Dummy HCD (DEVELOPMENT)" +config USB_DUMMY_HCD + tristate "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) - select USB_GADGET_DUALSPEED help This host controller driver emulates USB, looping all data transfer requests back to a USB "gadget driver" in the same host. The host side is the master; the gadget side is the slave. Gadget drivers can be high, full, or low speed; and they have access to endpoints like those from NET2280, PXA2xx, or SA1100 hardware. - + This may help in some stages of creating a driver to embed in a Linux device, since it lets you debug several parts of the gadget driver without its hardware or drivers being involved. - + Since such a gadget side driver needs to interoperate with a host side Linux-USB device driver, this may help to debug both sides of a USB protocol stack. @@ -488,31 +485,65 @@ config USB_GADGET_DUMMY_HCD dynamically linked module called "dummy_hcd" and force all gadget drivers to also be dynamically linked. -config USB_DUMMY_HCD - tristate - depends on USB_GADGET_DUMMY_HCD - default USB_GADGET - select USB_GADGET_SELECTED - # NOTE: Please keep dummy_hcd LAST so that "real hardware" appears # first and will be selected by default. -endchoice - -config USB_GADGET_DUALSPEED - bool - depends on USB_GADGET - default n - help - Means that gadget drivers should include extra descriptors - and code to handle dual-speed controllers. +endmenu # # USB Gadget Drivers # + +# 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" - depends on USB_GADGET && USB_GADGET_SELECTED default USB_ETH help A Linux "Gadget Driver" talks to the USB Peripheral Controller @@ -532,8 +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 @@ -566,12 +750,46 @@ config USB_ZERO_HNPTEST the "B-Peripheral" role, that device will use HNP to let this one serve as the USB host instead (in the "B-Host" role). +config USB_AUDIO + tristate "Audio Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_PCM + help + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, + 1 AudioStreaming Interface each for USB-OUT and USB-IN. + Number of channels, sample rate and sample size can be + specified as module parameters. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to and + sourced from a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host and choose to provide whatever it + wants as audio data to the USB Host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_audio". + +config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work + without one. + 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 either - of two ways: + This driver implements Ethernet style communication, in one of + several ways: - The "Communication Device Class" (CDC) Ethernet Control Model. That protocol is often avoided with pure Ethernet adapters, in @@ -581,7 +799,11 @@ config USB_ETH - On hardware can't implement that protocol, a simple CDC subset is used, placing fewer demands on USB. - RNDIS support is a third option, more demanding than that subset. + - CDC Ethernet Emulation Model (EEM) is a newer standard that has + a simpler interface that can be used by more USB hardware. + + RNDIS support is an additional option, more demanding than than + subset. Within the USB device, this gadget driver exposes a network device "usbX", where X depends on what other networking devices you have. @@ -599,6 +821,8 @@ config USB_ETH 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, @@ -614,9 +838,42 @@ config USB_ETH_RNDIS XP, you'll need to download drivers from Microsoft's website; a URL is given in comments found in that info file. +config USB_ETH_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 + 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. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". + +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 + an advanced protocol for Ethernet encapsulation, allows grouping + of several ethernet frames into one USB transfer and different + alignment possibilities. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ncm". + config USB_GADGETFS - tristate "Gadget Filesystem (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Gadget Filesystem" help This driver provides a filesystem based API that lets user mode programs implement a single-configuration USB device, including @@ -624,36 +881,89 @@ config USB_GADGETFS All endpoints, transfer speeds, and transfer types supported by the hardware are available, through read() and write() calls. - Currently, this option is still labelled as EXPERIMENTAL because - of existing race conditions in the underlying in-kernel AIO core. - Say "y" to link the driver statically, or "m" to build a dynamically linked module called "gadgetfs". -config USB_FILE_STORAGE - tristate "File-backed Storage Gadget" +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 + 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. + + If you say "y" or "m" here you will be able what kind of + configurations the gadget will provide. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_ffs". + +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. + +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. + +config USB_FUNCTIONFS_GENERIC + bool "Include 'pure' configuration" + depends on USB_FUNCTIONFS + help + Include a configuration with the Function Filesystem alone with + no Ethernet interface. + +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" depends on BLOCK + select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE help - The File-backed 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. + 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. - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_file_storage". + This driver is a replacement for now removed File-backed + Storage Gadget (g_file_storage). -config USB_FILE_STORAGE_TEST - bool "File-backed Storage Gadget testing version" - depends on USB_FILE_STORAGE - default n + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_mass_storage". + +config USB_GADGET_TARGET + tristate "USB Gadget Target Fabric Module" + depends on TARGET_CORE + select USB_LIBCOMPOSITE help - Say "y" to generate the larger testing version of the - File-backed Storage Gadget, useful for probing the - behavior of USB Mass Storage hosts. Not needed for - normal operation. + This fabric is an USB gadget. Two USB protocols are supported that is + BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is + advertised on alternative interface 0 (primary) and UAS is on + alternative interface 1. Both protocols can work on USB2.0 and USB3.0. + UAS utilizes the USB 3.0 feature called streams support. 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. This driver supports a CDC-ACM module option, which can be used @@ -672,8 +982,9 @@ config USB_G_SERIAL make MS-Windows work with CDC ACM. config USB_MIDI_GADGET - tristate "MIDI Gadget (EXPERIMENTAL)" - depends on SND && EXPERIMENTAL + tristate "MIDI Gadget" + depends on SND + select USB_LIBCOMPOSITE select SND_RAWMIDI help The MIDI Gadget acts as a USB Audio device, with one MIDI @@ -687,6 +998,7 @@ config USB_MIDI_GADGET config USB_G_PRINTER tristate "Printer Gadget" + select USB_LIBCOMPOSITE help The Printer Gadget channels data between the USB host and a userspace program driving the print engine. The user space @@ -700,9 +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. @@ -714,10 +1033,147 @@ config USB_CDC_COMPOSITE Say "y" to link the driver statically, or "m" to build a dynamically linked module. +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. + + It's only really useful for N900 hardware. If you're building + a kernel for N900, say Y or M here. If unsure, say N. + +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. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + +config USB_G_MULTI + tristate "Multifunction Composite Gadget" + 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 + interfaces. + + You will be asked to choose which of the two configurations is + to be available in the gadget. At least one configuration must + be chosen to make the gadget usable. Selecting more than one + configuration will prevent Windows from automatically detecting + the gadget as a composite gadget, so an INF file will be needed to + use the gadget. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "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 + Mass Storage functions available in the Multifunction Composite + Gadget. This is the configuration dedicated for Windows since RNDIS + is Microsoft's protocol. + + If unsure, say "y". + +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 + Composite Gadget. + + If unsure, say "y". + +endif # TTY + +config USB_G_HID + tristate "HID Gadget" + select USB_LIBCOMPOSITE + help + The HID gadget driver provides generic emulation of USB + Human Interface Devices (HID). + + For more information, see Documentation/usb/gadget_hid.txt which + includes sample code for accessing the device files. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "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 + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + +if USB_G_DBGP +choice + prompt "EHCI Debug Device mode" + default USB_G_DBGP_SERIAL + +config USB_G_DBGP_PRINTK + depends on USB_G_DBGP + bool "printk" + help + Directly printk() received data. No interaction. + +config USB_G_DBGP_SERIAL + depends on USB_G_DBGP + select USB_U_SERIAL + bool "serial" + help + Userland can interact using /dev/ttyGSxxx. +endchoice +endif + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. +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 + and stream video data to the host. -# - none yet + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_webcam". endchoice diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 39a51d746cb..49514ea60a9 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,45 +1,105 @@ # # USB peripheral controller drivers # -ifeq ($(CONFIG_USB_GADGET_DEBUG),y) - EXTRA_CFLAGS += -DDEBUG -endif +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 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_LH7A40X) += lh7a40x_udc.o obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o +fsl_usb2_udc-y := fsl_udc_core.o +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_CI13XXX) += ci13xxx_udc.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 # -g_zero-objs := zero.o -g_ether-objs := ether.o -g_serial-objs := serial.o -g_midi-objs := gmidi.o -gadgetfs-objs := inode.o -g_file_storage-objs := file_storage.o -g_printer-objs := printer.o -g_cdc-objs := cdc2.o +g_zero-y := zero.o +g_audio-y := audio.o +g_ether-y := ether.o +g_serial-y := serial.o +g_midi-y := gmidi.o +gadgetfs-y := inode.o +g_mass_storage-y := mass_storage.o +g_printer-y := printer.o +g_cdc-y := cdc2.o +g_multi-y := multi.o +g_hid-y := hid.o +g_dbgp-y := dbgp.o +g_nokia-y := nokia.o +g_webcam-y := webcam.o +g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o +g_tcm_usb_gadget-y := tcm_usb_gadget.o obj-$(CONFIG_USB_ZERO) += g_zero.o +obj-$(CONFIG_USB_AUDIO) += g_audio.o obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o -obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o +obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o - +obj-$(CONFIG_USB_G_HID) += g_hid.o +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o +obj-$(CONFIG_USB_G_MULTI) += g_multi.o +obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o +obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o +obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c new file mode 100644 index 00000000000..a252444cc0a --- /dev/null +++ b/drivers/usb/gadget/acm_ms.c @@ -0,0 +1,284 @@ +/* + * acm_ms.c -- Composite driver, with ACM and mass storage support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Author: David Brownell + * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de> + * + * Heavily based on multi.c and cdc2.c + * + * 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> +#include <linux/module.h> + +#include "u_serial.h" + +#define DRIVER_DESC "Composite Gadget (ACM + MS)" +#define DRIVER_VERSION "2011/10/10" + +/*-------------------------------------------------------------------------*/ + +/* + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ + +#include "f_mass_storage.h" + +/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM), + .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + /*.bNumConfigurations = DYNAMIC*/ +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + +/* string IDs are assigned dynamically */ +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_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 */, 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)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + opts = fsg_opts_from_func_inst(fi_msg); + + f_acm = usb_get_function(f_acm_inst); + if (IS_ERR(f_acm)) + return PTR_ERR(f_acm); + + 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) + 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 = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +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; + + f_acm_inst = usb_get_function_instance("acm"); + if (IS_ERR(f_acm_inst)) + return PTR_ERR(f_acm_inst); + + 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 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 fail_string_ids; + + usb_composite_overwrite_options(cdev, &coverwrite); + dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", + DRIVER_DESC); + return 0; + + /* error recovery */ +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) +{ + 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; +} + +static __refdata struct usb_composite_driver acm_ms_driver = { + .name = "g_acm_ms", + .dev = &device_desc, + .max_speed = USB_SPEED_SUPER, + .strings = dev_strings, + .bind = acm_ms_bind, + .unbind = __exit_p(acm_ms_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); +MODULE_LICENSE("GPL v2"); + +static int __init init(void) +{ + return usb_composite_probe(&acm_ms_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&acm_ms_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 826f3adde5d..41b062eb4de 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* @@ -38,7 +29,7 @@ /* Driver strings */ #define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" -#define UDC_DRIVER_VERSION_STRING "01.00.0206 - $Revision: #3 $" +#define UDC_DRIVER_VERSION_STRING "01.00.0206" /* system */ #include <linux/module.h> @@ -48,9 +39,7 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -61,9 +50,9 @@ #include <linux/device.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/prefetch.h> #include <asm/byteorder.h> -#include <asm/system.h> #include <asm/unaligned.h> /* gadget stack */ @@ -149,7 +138,7 @@ static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, /* endpoint names used for print */ static const char ep0_string[] = "ep0in"; -static const char *ep_string[] = { +static const char *const ep_string[] = { ep0_string, "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk", @@ -161,15 +150,15 @@ static const char *ep_string[] = { }; /* DMA usage flag */ -static int use_dma = 1; +static bool use_dma = 1; /* packet per buffer dma */ -static int use_dma_ppb = 1; +static bool use_dma_ppb = 1; /* with per descr. update */ -static int use_dma_ppb_du; +static bool use_dma_ppb_du; /* buffer fill mode */ static int use_dma_bufferfill_mode; /* full speed only mode */ -static int use_fullspeed; +static bool use_fullspeed; /* tx buffer size for high speed */ static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE; @@ -204,7 +193,7 @@ static void print_regs(struct udc *dev) DBG(dev, "DMA mode = PPBNDU (packet per buffer " "WITHOUT desc. update)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU"); - } else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) { + } else if (use_dma && use_dma_ppb && use_dma_ppb_du) { DBG(dev, "DMA mode = PPBDU (packet per buffer " "WITH desc. update)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU"); @@ -213,9 +202,8 @@ static void print_regs(struct udc *dev) DBG(dev, "DMA mode = BF (buffer fill mode)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF"); } - if (!use_dma) { + if (!use_dma) dev_info(&dev->pdev->dev, "FIFO mode\n"); - } DBG(dev, "-------------------------------------------------------\n"); } @@ -279,7 +267,7 @@ static int udc_enable_dev_setup_interrupts(struct udc *dev) return 0; } -/* Calculates fifo start of endpoint based on preceeding endpoints */ +/* Calculates fifo start of endpoint based on preceding endpoints */ static int udc_set_txfifo_addr(struct udc_ep *ep) { struct udc *dev; @@ -344,7 +332,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) return -ESHUTDOWN; spin_lock_irqsave(&dev->lock, iflags); - ep->desc = desc; + ep->ep.desc = desc; ep->halted = 0; @@ -354,7 +342,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) writel(tmp, &dev->ep[ep->num].regs->ctl); /* set max packet size */ - maxpacket = le16_to_cpu(desc->wMaxPacketSize); + maxpacket = usb_endpoint_maxp(desc); tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE); ep->ep.maxpacket = maxpacket; @@ -453,11 +441,11 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep) u32 tmp; VDBG(ep->dev, "ep-%d reset\n", ep->num); - ep->desc = NULL; + ep->ep.desc = NULL; 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); @@ -499,7 +487,7 @@ static int udc_ep_disable(struct usb_ep *usbep) return -EINVAL; ep = container_of(usbep, struct udc_ep, ep); - if (usbep->name == ep0_string || !ep->desc) + if (usbep->name == ep0_string || !ep->ep.desc) return -EINVAL; DBG(ep->dev, "Disable ep-%d\n", ep->num); @@ -578,9 +566,8 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq) VDBG(ep->dev, "req->td_data=%p\n", req->td_data); /* free dma chain if created */ - if (req->chain_len > 1) { + if (req->chain_len > 1) udc_free_dma_chain(ep->dev, req); - } pci_pool_free(ep->dev->data_requests, req->td_data, req->td_phys); @@ -648,9 +635,8 @@ udc_txfifo_write(struct udc_ep *ep, struct usb_request *req) bytes = remaining; /* dwords first */ - for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) writel(*(buf + i), ep->txfifo); - } /* remaining bytes must be written by byte access */ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { @@ -669,9 +655,8 @@ static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords) VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords); - for (i = 0; i < dwords; i++) { + for (i = 0; i < dwords; i++) *(buf + i) = readl(dev->rxfifo); - } return 0; } @@ -684,9 +669,8 @@ static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes) VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes); /* dwords first */ - for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo); - } /* remaining bytes must be read by byte access */ if (bytes % UDC_DWORD_BYTES) { @@ -840,20 +824,8 @@ __acquires(ep->dev->lock) dev = ep->dev; /* unmap DMA */ - if (req->dma_mapping) { - if (ep->in) - pci_unmap_single(dev->pdev, - req->req.dma, - req->req.length, - PCI_DMA_TODEVICE); - else - pci_unmap_single(dev->pdev, - req->req.dma, - req->req.length, - PCI_DMA_FROMDEVICE); - req->dma_mapping = 0; - req->req.dma = DMA_DONT_USE; - } + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in); halted = ep->halted; ep->halted = 1; @@ -906,9 +878,8 @@ static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req) struct udc_data_dma *td; td = req->td_data; - while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) td = phys_to_virt(td->next); - } return td; @@ -958,21 +929,18 @@ static int udc_create_dma_chain( dma_addr = DMA_DONT_USE; /* unset L bit in first desc for OUT */ - if (!ep->in) { + if (!ep->in) req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); - } /* alloc only new desc's if not already available */ len = req->req.length / ep->ep.maxpacket; - if (req->req.length % ep->ep.maxpacket) { + if (req->req.length % ep->ep.maxpacket) len++; - } if (len > req->chain_len) { /* shorter chain already allocated before */ - if (req->chain_len > 1) { + if (req->chain_len > 1) udc_free_dma_chain(ep->dev, req); - } req->chain_len = len; create_new_chain = 1; } @@ -1015,11 +983,12 @@ static int udc_create_dma_chain( /* link td and assign tx bytes */ if (i == buf_len) { - if (create_new_chain) { + if (create_new_chain) req->td_data->next = dma_addr; - } else { - /* req->td_data->next = virt_to_phys(td); */ - } + /* + else + req->td_data->next = virt_to_phys(td); + */ /* write tx bytes */ if (ep->in) { /* first desc */ @@ -1033,11 +1002,12 @@ static int udc_create_dma_chain( UDC_DMA_IN_STS_TXBYTES); } } else { - if (create_new_chain) { + if (create_new_chain) last->next = dma_addr; - } else { - /* last->next = virt_to_phys(td); */ - } + /* + else + last->next = virt_to_phys(td); + */ if (ep->in) { /* write tx bytes */ td->status = AMD_ADDBITS(td->status, @@ -1094,7 +1064,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) return -EINVAL; ep = container_of(usbep, struct udc_ep, ep); - if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) return -EINVAL; VDBG(ep->dev, "udc_queue(): ep%d-in=%d\n", ep->num, ep->in); @@ -1104,20 +1074,11 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) return -ESHUTDOWN; /* map dma (usually done before) */ - if (ep->dma && usbreq->length != 0 - && (usbreq->dma == DMA_DONT_USE || usbreq->dma == 0)) { + if (ep->dma) { VDBG(dev, "DMA map req %p\n", req); - if (ep->in) - usbreq->dma = pci_map_single(dev->pdev, - usbreq->buf, - usbreq->length, - PCI_DMA_TODEVICE); - else - usbreq->dma = pci_map_single(dev->pdev, - usbreq->buf, - usbreq->length, - PCI_DMA_FROMDEVICE); - req->dma_mapping = 1; + retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in); + if (retval) + return retval; } VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n", @@ -1160,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 */ @@ -1214,7 +1175,12 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) tmp &= AMD_UNMASK_BIT(ep->num); writel(tmp, &dev->regs->ep_irqmsk); } - } + } else if (ep->in) { + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + } } else if (ep->dma) { @@ -1223,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; } @@ -1289,7 +1255,7 @@ static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq) unsigned long iflags; ep = container_of(usbep, struct udc_ep, ep); - if (!usbep || !usbreq || (!ep->desc && (ep->num != 0 + if (!usbep || !usbreq || (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))) return -EINVAL; @@ -1349,7 +1315,7 @@ udc_set_halt(struct usb_ep *usbep, int halt) pr_debug("set_halt %s: halt=%d\n", usbep->name, halt); ep = container_of(usbep, struct udc_ep, ep); - if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) + if (!ep->ep.desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX)) return -EINVAL; if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; @@ -1433,10 +1399,16 @@ static int udc_wakeup(struct usb_gadget *gadget) return 0; } +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, + .udc_start = amd5536_udc_start, + .udc_stop = amd5536_udc_stop, }; /* Setups endpoint parameters, adds endpoints to linked list */ @@ -1478,11 +1450,10 @@ static int startup_registers(struct udc *dev) /* program speed */ tmp = readl(&dev->regs->cfg); - if (use_fullspeed) { + if (use_fullspeed) tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); - } else { + else tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); - } writel(tmp, &dev->regs->cfg); return 0; @@ -1503,9 +1474,8 @@ static void udc_basic_init(struct udc *dev) mod_timer(&udc_timer, jiffies - 1); } /* stop poll stall timer */ - if (timer_pending(&udc_pollstall_timer)) { + if (timer_pending(&udc_pollstall_timer)) mod_timer(&udc_pollstall_timer, jiffies - 1); - } /* disable DMA */ tmp = readl(&dev->regs->ctl); tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE); @@ -1539,11 +1509,10 @@ static void udc_setup_endpoints(struct udc *dev) /* read enum speed */ tmp = readl(&dev->regs->sts); tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED); - if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) { + if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) dev->gadget.speed = USB_SPEED_HIGH; - } else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) { + else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) dev->gadget.speed = USB_SPEED_FULL; - } /* set basic ep parameters */ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { @@ -1569,9 +1538,8 @@ static void udc_setup_endpoints(struct udc *dev) * disabling ep interrupts when ENUM interrupt occurs but ep is * not enabled by gadget driver */ - if (!ep->desc) { + if (!ep->ep.desc) ep_init(dev->regs, ep); - } if (use_dma) { /* @@ -1595,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); } /* @@ -1669,9 +1640,8 @@ static void udc_tasklet_disconnect(unsigned long par) spin_lock(&dev->lock); /* empty queues */ - for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) empty_req_queue(&dev->ep[tmp]); - } } @@ -1745,9 +1715,8 @@ static void udc_timer_function(unsigned long v) * open the fifo */ udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } else { /* * fifo contains data now, setup timer for opening @@ -1759,9 +1728,8 @@ static void udc_timer_function(unsigned long v) set_rde++; /* debug: lhadmot_timer_start = 221070 */ udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } else @@ -1906,19 +1874,17 @@ static void activate_control_endpoints(struct udc *dev) mod_timer(&udc_timer, jiffies - 1); } /* stop pollstall timer */ - if (timer_pending(&udc_pollstall_timer)) { + if (timer_pending(&udc_pollstall_timer)) mod_timer(&udc_pollstall_timer, jiffies - 1); - } /* enable DMA */ tmp = readl(&dev->regs->ctl); tmp |= AMD_BIT(UDC_DEVCTL_MODE) | AMD_BIT(UDC_DEVCTL_RDE) | AMD_BIT(UDC_DEVCTL_TDE); - if (use_dma_bufferfill_mode) { + if (use_dma_bufferfill_mode) tmp |= AMD_BIT(UDC_DEVCTL_BF); - } else if (use_dma_ppb_du) { + else if (use_dma_ppb_du) tmp |= AMD_BIT(UDC_DEVCTL_DU); - } writel(tmp, &dev->regs->ctl); } @@ -1950,25 +1916,14 @@ static int setup_ep0(struct udc *dev) } /* Called by gadget driver to register itself */ -int usb_gadget_register_driver(struct usb_gadget_driver *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 || !driver->bind || !driver->setup - || driver->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 = driver->bind(&dev->gadget); /* Some gadget drivers use both ep0 directions. * NOTE: to gadget driver, ep0 is just one endpoint... @@ -1976,14 +1931,6 @@ int usb_gadget_register_driver(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); @@ -1996,7 +1943,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_register_driver); /* shutdown requests and disconnect from gadget */ static void @@ -2008,38 +1954,26 @@ __acquires(dev->lock) /* empty queues and init hardware */ udc_basic_init(dev); - for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) empty_req_queue(&dev->ep[tmp]); - } - if (dev->gadget.speed != USB_SPEED_UNKNOWN) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - /* init */ udc_setup_endpoints(dev); } /* Called by gadget driver to unregister itself */ -int usb_gadget_unregister_driver(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 */ @@ -2047,13 +1981,8 @@ int usb_gadget_unregister_driver(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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /* Clear pending NAK bits */ static void udc_process_cnak_queue(struct udc *dev) @@ -2106,9 +2035,8 @@ static void udc_ep0_set_rde(struct udc *dev) udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; set_rde = 1; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } } @@ -2133,7 +2061,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) if (use_dma) { /* BNA event ? */ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { - DBG(dev, "BNA ep%dout occured - DESPTR = %x \n", + DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n", ep->num, readl(&ep->regs->desptr)); /* clear BNA */ writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); @@ -2147,7 +2075,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) } /* HE event ? */ if (tmp & AMD_BIT(UDC_EPSTS_HE)) { - dev_err(&dev->pdev->dev, "HE ep%dout occured\n", ep->num); + dev_err(&dev->pdev->dev, "HE ep%dout occurred\n", ep->num); /* clear HE */ writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); @@ -2296,9 +2224,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) jiffies + HZ*UDC_RDE_TIMER_SECONDS; set_rde = 1; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } if (ep->num != UDC_EP0OUT_IX) dev->data_ep_queued = 0; @@ -2320,9 +2247,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) /* check pending CNAKS */ if (cnak_pending) { /* CNAk processing when rxfifo empty only */ - if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) udc_process_cnak_queue(dev); - } } /* clear OUT bits in ep status */ @@ -2350,7 +2276,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* BNA ? */ if (epsts & AMD_BIT(UDC_EPSTS_BNA)) { dev_err(&dev->pdev->dev, - "BNA ep%din occured - DESPTR = %08lx \n", + "BNA ep%din occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); @@ -2363,7 +2289,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* HE event ? */ if (epsts & AMD_BIT(UDC_EPSTS_HE)) { dev_err(&dev->pdev->dev, - "HE ep%dn occured - DESPTR = %08lx \n", + "HE ep%dn occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); /* clear HE */ @@ -2379,40 +2305,34 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) if (!ep->cancel_transfer && !list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct udc_request, queue); - if (req) { - /* - * length bytes transfered - * check dma done of last desc. in PPBDU mode - */ - if (use_dma_ppb_du) { - td = udc_get_last_dma_desc(req); - if (td) { - dma_done = - AMD_GETBITS(td->status, - UDC_DMA_IN_STS_BS); - /* don't care DMA done */ - req->req.actual = - req->req.length; - } - } else { - /* assume all bytes transferred */ + /* + * length bytes transferred + * check dma done of last desc. in PPBDU mode + */ + if (use_dma_ppb_du) { + td = udc_get_last_dma_desc(req); + if (td) { + dma_done = + AMD_GETBITS(td->status, + UDC_DMA_IN_STS_BS); + /* don't care DMA done */ req->req.actual = req->req.length; } + } else { + /* assume all bytes transferred */ + req->req.actual = req->req.length; + } - if (req->req.actual == req->req.length) { - /* complete req */ - complete_req(ep, req, 0); - req->dma_going = 0; - /* further request available ? */ - if (list_empty(&ep->queue)) { - /* disable interrupt */ - tmp = readl( - &dev->regs->ep_irqmsk); - tmp |= AMD_BIT(ep->num); - writel(tmp, - &dev->regs->ep_irqmsk); - } - + if (req->req.actual == req->req.length) { + /* complete req */ + complete_req(ep, req, 0); + req->dma_going = 0; + /* further request available ? */ + if (list_empty(&ep->queue)) { + /* disable interrupt */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); } } } @@ -2435,9 +2355,9 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* write fifo */ udc_txfifo_write(ep, &req->req); len = req->req.length - req->req.actual; - if (len > ep->ep.maxpacket) - len = ep->ep.maxpacket; - req->req.actual += len; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; if (req->req.actual == req->req.length || (len != ep->ep.maxpacket)) { /* complete req */ @@ -2479,6 +2399,13 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) } } + } else if (!use_dma && ep->in) { + /* disable interrupt */ + tmp = readl( + &dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, + &dev->regs->ep_irqmsk); } } /* clear status bits */ @@ -2582,9 +2509,8 @@ __acquires(dev->lock) if (!timer_pending(&udc_timer)) { udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } @@ -2698,9 +2624,8 @@ __acquires(dev->lock) /* check pending CNAKS */ if (cnak_pending) { /* CNAk processing when rxfifo empty only */ - if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) udc_process_cnak_queue(dev); - } } finished: @@ -2724,7 +2649,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts); /* DMA completion */ if (tmp & AMD_BIT(UDC_EPSTS_TDC)) { - VDBG(dev, "isr: TDC clear \n"); + VDBG(dev, "isr: TDC clear\n"); ret_val = IRQ_HANDLED; /* clear TDC bit */ @@ -2779,7 +2704,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) /* write fifo */ udc_txfifo_write(ep, &req->req); - /* lengh bytes transfered */ + /* lengh bytes transferred */ len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; @@ -3006,13 +2931,8 @@ __acquires(dev->lock) /* link up all endpoints */ udc_setup_endpoints(dev); - if (dev->gadget.speed == USB_SPEED_HIGH) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "high"); - } else if (dev->gadget.speed == USB_SPEED_FULL) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "full"); - } + dev_info(&dev->pdev->dev, "Connect: %s\n", + usb_speed_string(dev->gadget.speed)); /* init ep 0 */ activate_control_endpoints(dev); @@ -3128,6 +3048,7 @@ static void udc_pci_remove(struct pci_dev *pdev) dev = pci_get_drvdata(pdev); + usb_del_gadget_udc(&udc->gadget); /* gadget driver must not be registered */ BUG_ON(dev->driver != NULL); @@ -3159,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); } @@ -3279,15 +3197,26 @@ static int udc_pci_probe( } if (!pdev->irq) { - dev_err(&dev->pdev->dev, "irq not set\n"); + dev_err(&pdev->dev, "irq not set\n"); kfree(dev); dev = NULL; retval = -ENODEV; goto finished; } + spin_lock_init(&dev->lock); + /* udc csr registers base */ + dev->csr = dev->virt_addr + UDC_CSR_ADDR; + /* dev registers base */ + dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; + /* ep registers base */ + dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; + /* fifo's base */ + dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); + dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); + if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { - dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq); + dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); kfree(dev); dev = NULL; retval = -EBUSY; @@ -3313,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) @@ -3338,24 +3265,11 @@ static int udc_probe(struct udc *dev) udc_pollstall_timer.data = 0; /* device struct setup */ - spin_lock_init(&dev->lock); dev->gadget.ops = &udc_ops; dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.dev.release = gadget_release; - dev->gadget.name = name; dev->gadget.name = name; - dev->gadget.is_dualspeed = 1; - - /* udc csr registers base */ - dev->csr = dev->virt_addr + UDC_CSR_ADDR; - /* dev registers base */ - dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; - /* ep registers base */ - dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; - /* fifo's base */ - dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); - dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); + dev->gadget.max_speed = USB_SPEED_HIGH; /* init registers, interrupts, ... */ startup_registers(dev); @@ -3377,7 +3291,8 @@ static int udc_probe(struct udc *dev) "driver version: %s(for Geode5536 B1)\n", tmp); udc = dev; - retval = device_register(&dev->gadget.dev); + retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, + gadget_release); if (retval) goto finished; @@ -3443,19 +3358,7 @@ static struct pci_driver udc_pci_driver = { .remove = udc_pci_remove, }; -/* Inits driver */ -static int __init init(void) -{ - return pci_register_driver(&udc_pci_driver); -} -module_init(init); - -/* Cleans driver */ -static void __exit cleanup(void) -{ - pci_unregister_driver(&udc_pci_driver); -} -module_exit(cleanup); +module_pci_driver(udc_pci_driver); MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); MODULE_AUTHOR("Thomas Dahlmann"); diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index 4bbabbbfc93..6744d3b8310 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AMD5536UDC_H @@ -481,7 +472,6 @@ struct udc_request { /* flags */ unsigned dma_going : 1, - dma_mapping : 1, dma_done : 1; /* phys. address */ dma_addr_t td_phys; @@ -521,7 +511,6 @@ struct udc_ep { /* queue for requests */ struct list_head queue; - const struct usb_endpoint_descriptor *desc; unsigned halted; unsigned cancel_transfer; unsigned num : 5, @@ -573,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]; @@ -584,7 +575,7 @@ union udc_setup_data { * SET and GET bitfields in u32 values * via constants for mask/offset: * <bit_field_stub_name> is the text between - * UDC_ and _MASK|_OFS of appropiate + * UDC_ and _MASK|_OFS of appropriate * constant * * set bitfield value in u32 u32Val diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 0b2bb8f0706..cfd18bcca72 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -9,16 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. */ #undef VERBOSE_DEBUG @@ -31,24 +21,26 @@ #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> +#include <linux/prefetch.h> #include <linux/clk.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/atmel.h> #include <asm/byteorder.h> #include <mach/hardware.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/gpio.h> -#include <mach/board.h> #include <mach/cpu.h> #include <mach/at91sam9261_matrix.h> +#include <mach/at91_matrix.h> #include "at91_udc.h" @@ -58,7 +50,7 @@ * full speed USB controllers, including the at91rm9200 (arm920T, with MMU), * at91sam926x (arm926ejs, with MMU), and several no-mmu versions. * - * This driver expects the board has been wired with two GPIOs suppporting + * This driver expects the board has been wired with two GPIOs supporting * a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the * testing hasn't covered such cases.) * @@ -76,11 +68,12 @@ static const char driver_name [] = "at91_udc"; static const char ep0name[] = "ep0"; +#define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000) -#define at91_udp_read(dev, reg) \ - __raw_readl((dev)->udp_baseaddr + (reg)) -#define at91_udp_write(dev, reg, val) \ - __raw_writel((val), (dev)->udp_baseaddr + (reg)) +#define at91_udp_read(udc, reg) \ + __raw_readl((udc)->udp_baseaddr + (reg)) +#define at91_udp_write(udc, reg, val) \ + __raw_writel((val), (udc)->udp_baseaddr + (reg)) /*-------------------------------------------------------------------------*/ @@ -102,8 +95,9 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep) u32 csr; struct at91_request *req; unsigned long flags; + struct at91_udc *udc = ep->udc; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); csr = __raw_readl(ep->creg); @@ -147,7 +141,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep) &req->req, length, req->req.length, req->req.buf); } - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); } static void proc_irq_show(struct seq_file *s, const char *label, u32 mask) @@ -217,7 +211,7 @@ static int proc_udc_show(struct seq_file *s, void *unused) if (udc->enabled && udc->vbus) { proc_ep_show(s, &udc->ep[0]); list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { - if (ep->desc) + if (ep->ep.desc) proc_ep_show(s, ep); } } @@ -226,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 = { @@ -272,7 +266,9 @@ static void done(struct at91_ep *ep, struct at91_request *req, int status) VDBG("%s done %p, status %d\n", ep->ep.name, req, status); ep->stopped = 1; + spin_unlock(&udc->lock); req->req.complete(&ep->ep, &req->req); + spin_lock(&udc->lock); ep->stopped = stopped; /* ep0 is always ready; other endpoints need a non-empty queue */ @@ -366,6 +362,13 @@ rescan: if (is_done) done(ep, req, 0); else if (ep->is_pingpong) { + /* + * One dummy read to delay the code because of a HW glitch: + * CSR returns bad RXCOUNT when read too soon after updating + * RX_DATA_BK flags. + */ + csr = __raw_readl(creg); + bufferspace -= count; buf += count; goto rescan; @@ -447,7 +450,7 @@ static void nuke(struct at91_ep *ep, int status) { struct at91_request *req; - // terminer chaque requete dans la queue + /* terminate any request in the queue */ ep->stopped = 1; if (list_empty(&ep->queue)) return; @@ -465,27 +468,27 @@ static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); - struct at91_udc *dev = ep->udc; + struct at91_udc *udc; u16 maxpacket; u32 tmp; unsigned long flags; if (!_ep || !ep - || !desc || ep->desc - || _ep->name == ep0name + || !desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT - || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 + || (maxpacket = usb_endpoint_maxp(desc)) == 0 || maxpacket > ep->maxpacket) { DBG("bad ep or descriptor\n"); return -EINVAL; } - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { DBG("bogus device state\n"); return -ESHUTDOWN; } - tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + tmp = usb_endpoint_type(desc); switch (tmp) { case USB_ENDPOINT_XFER_CONTROL: DBG("only one control endpoint\n"); @@ -514,10 +517,10 @@ bogus_max: } ok: - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); /* initialize endpoint to match this descriptor */ - ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep->is_in = usb_endpoint_dir_in(desc); ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); ep->stopped = 0; if (ep->is_in) @@ -526,17 +529,16 @@ ok: tmp |= AT91_UDP_EPEDS; __raw_writel(tmp, ep->creg); - ep->desc = desc; ep->ep.maxpacket = maxpacket; /* * reset/init endpoint fifo. NOTE: leaves fifo_bank alone, * since endpoint resets don't reset hw pingpong state. */ - at91_udp_write(dev, AT91_UDP_RST_EP, ep->int_mask); - at91_udp_write(dev, AT91_UDP_RST_EP, 0); + at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(udc, AT91_UDP_RST_EP, 0); - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -549,12 +551,12 @@ static int at91_ep_disable (struct usb_ep * _ep) if (ep == &ep->udc->ep[0]) return -EINVAL; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); nuke(ep, -ESHUTDOWN); /* restore the endpoint's pristine config */ - ep->desc = NULL; + ep->ep.desc = NULL; ep->ep.maxpacket = ep->maxpacket; /* reset fifos and endpoint */ @@ -564,7 +566,7 @@ static int at91_ep_disable (struct usb_ep * _ep) __raw_writel(0, ep->creg); } - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -600,7 +602,7 @@ static int at91_ep_queue(struct usb_ep *_ep, { struct at91_request *req; struct at91_ep *ep; - struct at91_udc *dev; + struct at91_udc *udc; int status; unsigned long flags; @@ -613,14 +615,14 @@ static int at91_ep_queue(struct usb_ep *_ep, return -EINVAL; } - if (!_ep || (!ep->desc && ep->ep.name != ep0name)) { + if (!_ep || (!ep->ep.desc && ep->ep.name != ep0name)) { DBG("invalid ep\n"); return -EINVAL; } - dev = ep->udc; + udc = ep->udc; - if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { DBG("invalid device\n"); return -EINVAL; } @@ -628,7 +630,7 @@ static int at91_ep_queue(struct usb_ep *_ep, _req->status = -EINPROGRESS; _req->actual = 0; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); /* try to kickstart any empty and idle queue */ if (list_empty(&ep->queue) && !ep->stopped) { @@ -646,7 +648,7 @@ static int at91_ep_queue(struct usb_ep *_ep, if (is_ep0) { u32 tmp; - if (!dev->req_pending) { + if (!udc->req_pending) { status = -EINVAL; goto done; } @@ -655,11 +657,11 @@ static int at91_ep_queue(struct usb_ep *_ep, * defer changing CONFG until after the gadget driver * reconfigures the endpoints. */ - if (dev->wait_for_config_ack) { - tmp = at91_udp_read(dev, AT91_UDP_GLB_STAT); + if (udc->wait_for_config_ack) { + tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT); tmp ^= AT91_UDP_CONFG; VDBG("toggle config\n"); - at91_udp_write(dev, AT91_UDP_GLB_STAT, tmp); + at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp); } if (req->req.length == 0) { ep0_in_status: @@ -669,7 +671,7 @@ ep0_in_status: tmp &= ~SET_FX; tmp |= CLR_FX | AT91_UDP_TXPKTRDY; __raw_writel(tmp, ep->creg); - dev->req_pending = 0; + udc->req_pending = 0; goto done; } } @@ -688,31 +690,40 @@ ep0_in_status: if (req && !status) { list_add_tail (&req->queue, &ep->queue); - at91_udp_write(dev, AT91_UDP_IER, ep->int_mask); + at91_udp_write(udc, AT91_UDP_IER, ep->int_mask); } done: - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return (status < 0) ? status : 0; } static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { - struct at91_ep *ep; + struct at91_ep *ep; struct at91_request *req; + unsigned long flags; + struct at91_udc *udc; ep = container_of(_ep, struct at91_ep, ep); if (!_ep || ep->ep.name == ep0name) return -EINVAL; + udc = ep->udc; + + spin_lock_irqsave(&udc->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) + if (&req->req != _req) { + spin_unlock_irqrestore(&udc->lock, flags); return -EINVAL; + } done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -729,7 +740,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value) return -EINVAL; creg = ep->creg; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); csr = __raw_readl(creg); @@ -754,7 +765,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value) __raw_writel(csr, creg); } - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return status; } @@ -766,7 +777,7 @@ static const struct usb_ep_ops at91_ep_ops = { .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, - // there's only imprecise fifo status reporting + /* there's only imprecise fifo status reporting */ }; /*-------------------------------------------------------------------------*/ @@ -788,7 +799,7 @@ static int at91_wakeup(struct usb_gadget *gadget) unsigned long flags; DBG("%s\n", __func__ ); - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); if (!udc->clocked || !udc->suspended) goto done; @@ -802,11 +813,11 @@ static int at91_wakeup(struct usb_gadget *gadget) at91_udp_write(udc, AT91_UDP_GLB_STAT, glbstate); done: - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return status; } -/* reinit == restore inital software state */ +/* reinit == restore initial software state */ static void udc_reinit(struct at91_udc *udc) { u32 i; @@ -819,12 +830,12 @@ static void udc_reinit(struct at91_udc *udc) if (i != 0) list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - ep->desc = NULL; + 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); - // initialiser une queue par endpoint + /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); } } @@ -844,8 +855,11 @@ static void stop_activity(struct at91_udc *udc) ep->stopped = 1; nuke(ep, -ESHUTDOWN); } - if (driver) + if (driver) { + spin_unlock(&udc->lock); driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } udc_reinit(udc); } @@ -855,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) @@ -865,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); } /* @@ -892,12 +913,12 @@ static void pullup(struct at91_udc *udc, int is_on) txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; - usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); + usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); usbpucr |= AT91_MATRIX_USBPUCR_PUON; - at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); + at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); } } else { stop_activity(udc); @@ -910,12 +931,12 @@ static void pullup(struct at91_udc *udc, int is_on) txvc &= ~AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; - usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); + usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR); usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; - at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); + at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr); } clk_off(udc); } @@ -927,14 +948,14 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - // VDBG("vbus %s\n", is_active ? "on" : "off"); - local_irq_save(flags); + /* VDBG("vbus %s\n", is_active ? "on" : "off"); */ + spin_lock_irqsave(&udc->lock, flags); udc->vbus = (is_active != 0); if (udc->driver) pullup(udc, is_active); else pullup(udc, 0); - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -943,10 +964,10 @@ static int at91_pullup(struct usb_gadget *gadget, int is_on) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); udc->enabled = is_on = !!is_on; pullup(udc, is_on); - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -955,24 +976,30 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - local_irq_save(flags); + spin_lock_irqsave(&udc->lock, flags); udc->selfpowered = (is_on != 0); - local_irq_restore(flags); + spin_unlock_irqrestore(&udc->lock, flags); return 0; } +static int at91_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static int at91_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops at91_udc_ops = { .get_frame = at91_get_frame, .wakeup = at91_wakeup, .set_selfpowered = at91_set_selfpowered, .vbus_session = at91_vbus_session, .pullup = at91_pullup, + .udc_start = at91_start, + .udc_stop = at91_stop, /* * VBUS-powered devices may also also want to support bigger * power budgets after an appropriate SET_CONFIGURATION. */ - // .vbus_power = at91_vbus_power, + /* .vbus_power = at91_vbus_power, */ }; /*-------------------------------------------------------------------------*/ @@ -1041,7 +1068,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) ep->is_in = 0; } } else { - // REVISIT this happens sometimes under load; why?? + /* REVISIT this happens sometimes under load; why?? */ ERR("SETUP len %d, csr %08x\n", rxcount, csr); status = -EINVAL; } @@ -1149,7 +1176,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) | USB_REQ_GET_STATUS: tmp = w_index & USB_ENDPOINT_NUMBER_MASK; ep = &udc->ep[tmp]; - if (tmp >= NUM_ENDPOINTS || (tmp && !ep->desc)) + if (tmp >= NUM_ENDPOINTS || (tmp && !ep->ep.desc)) goto stall; if (tmp) { @@ -1174,7 +1201,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) ep = &udc->ep[tmp]; if (w_value != USB_ENDPOINT_HALT || tmp >= NUM_ENDPOINTS) goto stall; - if (!ep->desc || ep->is_iso) + if (!ep->ep.desc || ep->is_iso) goto stall; if ((w_index & USB_DIR_IN)) { if (!ep->is_in) @@ -1195,7 +1222,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) goto stall; if (tmp == 0) goto succeed; - if (!ep->desc || ep->is_iso) + if (!ep->ep.desc || ep->is_iso) goto stall; if ((w_index & USB_DIR_IN)) { if (!ep->is_in) @@ -1219,8 +1246,11 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) #undef w_length /* pass request up to the gadget driver */ - if (udc->driver) + if (udc->driver) { + spin_unlock(&udc->lock); status = udc->driver->setup(&udc->gadget, &pkt.r); + spin_lock(&udc->lock); + } else status = -ENODEV; if (status < 0) { @@ -1240,7 +1270,6 @@ write_in: csr |= AT91_UDP_TXPKTRDY; __raw_writel(csr, creg); udc->req_pending = 0; - return; } static void handle_ep0(struct at91_udc *udc) @@ -1370,6 +1399,15 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) { struct at91_udc *udc = _udc; u32 rescans = 5; + int disable_clock = 0; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + if (!udc->clocked) { + clk_on(udc); + disable_clock = 1; + } while (rescans--) { u32 status; @@ -1409,7 +1447,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP); - // VDBG("bus suspend\n"); + /* VDBG("bus suspend\n"); */ if (udc->suspended) continue; udc->suspended = 1; @@ -1420,15 +1458,18 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) * and then into standby to avoid drawing more than * 500uA power (2500uA for some high-power configs). */ - if (udc->driver && udc->driver->suspend) + if (udc->driver && udc->driver->suspend) { + spin_unlock(&udc->lock); udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } /* host initiated resume */ } else if (status & AT91_UDP_RXRSM) { at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); - // VDBG("bus resume\n"); + /* VDBG("bus resume\n"); */ if (!udc->suspended) continue; udc->suspended = 0; @@ -1438,8 +1479,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) * would normally want to switch out of slow clock * mode into normal mode. */ - if (udc->driver && udc->driver->resume) + if (udc->driver && udc->driver->resume) { + spin_unlock(&udc->lock); udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } /* endpoint IRQs are cleared by handling them */ } else { @@ -1458,6 +1502,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) } } + if (disable_clock) + clk_off(udc); + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; } @@ -1540,125 +1589,131 @@ static struct at91_udc controller = { /* ep6 and ep7 are also reserved (custom silicon might use them) */ }; +static void at91_vbus_update(struct at91_udc *udc, unsigned value) +{ + value ^= udc->board.vbus_active_low; + if (value != udc->vbus) + at91_vbus_session(&udc->gadget, value); +} + static irqreturn_t at91_vbus_irq(int irq, void *_udc) { struct at91_udc *udc = _udc; - unsigned value; /* vbus needs at least brief debouncing */ udelay(10); - value = gpio_get_value(udc->board.vbus_pin); - if (value != udc->vbus) - at91_vbus_session(&udc->gadget, value); + at91_vbus_update(udc, gpio_get_value(udc->board.vbus_pin)); return IRQ_HANDLED; } -int usb_gadget_register_driver (struct usb_gadget_driver *driver) +static void at91_vbus_timer_work(struct work_struct *work) { - struct at91_udc *udc = &controller; - int retval; + struct at91_udc *udc = container_of(work, struct at91_udc, + vbus_timer_work); - if (!driver - || driver->speed < USB_SPEED_FULL - || !driver->bind - || !driver->setup) { - DBG("bad parameter.\n"); - return -EINVAL; - } + at91_vbus_update(udc, gpio_get_value_cansleep(udc->board.vbus_pin)); - if (udc->driver) { - DBG("UDC already has a gadget driver\n"); - return -EBUSY; - } + if (!timer_pending(&udc->vbus_timer)) + mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT); +} +static void at91_vbus_timer(unsigned long data) +{ + struct at91_udc *udc = (struct at91_udc *)data; + + /* + * If we are polling vbus it is likely that the gpio is on an + * bus such as i2c or spi which may sleep, so schedule some work + * to read the vbus gpio + */ + schedule_work(&udc->vbus_timer_work); +} + +static int at91_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct at91_udc *udc; + + udc = container_of(gadget, struct at91_udc, gadget); udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; - udc->gadget.dev.driver_data = &driver->driver; + udc->gadget.dev.of_node = udc->pdev->dev.of_node; udc->enabled = 1; udc->selfpowered = 1; - retval = driver->bind(&udc->gadget); - if (retval) { - DBG("driver->bind() returned %d\n", retval); - udc->driver = NULL; - udc->gadget.dev.driver = NULL; - udc->gadget.dev.driver_data = NULL; - udc->enabled = 0; - udc->selfpowered = 0; - return retval; - } - - local_irq_disable(); - pullup(udc, 1); - local_irq_enable(); - DBG("bound to %s\n", driver->driver.name); return 0; } -EXPORT_SYMBOL (usb_gadget_register_driver); -int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int at91_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct at91_udc *udc = &controller; - - if (!driver || driver != udc->driver || !driver->unbind) - return -EINVAL; + struct at91_udc *udc; + unsigned long flags; - local_irq_disable(); + udc = container_of(gadget, struct at91_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); udc->enabled = 0; at91_udp_write(udc, AT91_UDP_IDR, ~0); - pullup(udc, 0); - local_irq_enable(); + spin_unlock_irqrestore(&udc->lock, flags); - driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; - udc->gadget.dev.driver_data = NULL; udc->driver = NULL; DBG("unbound from %s\n", driver->driver.name); return 0; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ static void at91udc_shutdown(struct platform_device *dev) { + struct at91_udc *udc = platform_get_drvdata(dev); + unsigned long flags; + /* force disconnect on reboot */ + spin_lock_irqsave(&udc->lock, flags); pullup(platform_get_drvdata(dev), 0); + spin_unlock_irqrestore(&udc->lock, flags); +} + +static void at91udc_of_init(struct at91_udc *udc, + struct device_node *np) +{ + struct at91_udc_data *board = &udc->board; + u32 val; + enum of_gpio_flags flags; + + if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0) + board->vbus_polled = 1; + + board->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, + &flags); + board->vbus_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + + board->pullup_pin = of_get_named_gpio_flags(np, "atmel,pullup-gpio", 0, + &flags); + + board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; } -static int __init at91udc_probe(struct platform_device *pdev) +static int at91udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct at91_udc *udc; int retval; struct resource *res; - if (!dev->platform_data) { + 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; - if (!request_mem_region(res->start, - res->end - res->start + 1, - driver_name)) { + if (!request_mem_region(res->start, resource_size(res), driver_name)) { DBG("someone's using UDC memory\n"); return -EBUSY; } @@ -1666,13 +1721,18 @@ static int __init at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; - udc->board = *(struct at91_udc_data *) dev->platform_data; + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) + at91udc_of_init(udc, pdev->dev.of_node); + else + memcpy(&udc->board, dev_get_platdata(dev), + sizeof(struct at91_udc_data)); udc->pdev = pdev; udc->enabled = 0; + spin_lock_init(&udc->lock); /* rm9200 needs manual D+ pullup; off by default */ if (cpu_is_at91rm9200()) { - if (udc->board.pullup_pin <= 0) { + if (!gpio_is_valid(udc->board.pullup_pin)) { DBG("no D+ pullup?\n"); retval = -ENODEV; goto fail0; @@ -1687,19 +1747,19 @@ static int __init at91udc_probe(struct platform_device *pdev) } /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260()) { + if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { udc->ep[0].maxpacket = 64; udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; udc->ep[5].maxpacket = 512; - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { udc->ep[3].maxpacket = 64; } else if (cpu_is_at91sam9263()) { udc->ep[0].maxpacket = 64; udc->ep[3].maxpacket = 64; } - udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); + udc->udp_baseaddr = ioremap(res->start, resource_size(res)); if (!udc->udp_baseaddr) { retval = -ENOMEM; goto fail0a; @@ -1710,34 +1770,34 @@ static int __init 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; + goto fail1; } - retval = device_register(&udc->gadget.dev); - if (retval < 0) - goto fail0b; - /* 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); retval = request_irq(udc->udp_irq, at91_udc_irq, - IRQF_DISABLED, driver_name, udc); + 0, driver_name, udc); if (retval < 0) { DBG("request irq %d failed\n", udc->udp_irq); goto fail1; } - if (udc->board.vbus_pin > 0) { + if (gpio_is_valid(udc->board.vbus_pin)) { retval = gpio_request(udc->board.vbus_pin, "udc_vbus"); if (retval < 0) { DBG("request vbus pin failed\n"); @@ -1749,40 +1809,58 @@ static int __init at91udc_probe(struct platform_device *pdev) * Get the initial state of VBUS - we cannot expect * a pending interrupt. */ - udc->vbus = gpio_get_value(udc->board.vbus_pin); - if (request_irq(udc->board.vbus_pin, at91_vbus_irq, - IRQF_DISABLED, driver_name, udc)) { - DBG("request vbus irq %d failed\n", - udc->board.vbus_pin); - free_irq(udc->udp_irq, udc); - retval = -EBUSY; - goto fail3; + udc->vbus = gpio_get_value_cansleep(udc->board.vbus_pin) ^ + udc->board.vbus_active_low; + + if (udc->board.vbus_polled) { + INIT_WORK(&udc->vbus_timer_work, at91_vbus_timer_work); + setup_timer(&udc->vbus_timer, at91_vbus_timer, + (unsigned long)udc); + mod_timer(&udc->vbus_timer, + jiffies + VBUS_POLL_TIMEOUT); + } else { + if (request_irq(gpio_to_irq(udc->board.vbus_pin), + at91_vbus_irq, 0, driver_name, udc)) { + DBG("request vbus irq %d failed\n", + udc->board.vbus_pin); + retval = -EBUSY; + goto fail3; + } } } else { DBG("no VBUS detection, assuming always-on\n"); udc->vbus = 1; } + retval = usb_add_gadget_udc(dev, &udc->gadget); + if (retval) + goto fail4; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0; - +fail4: + if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled) + free_irq(gpio_to_irq(udc->board.vbus_pin), udc); fail3: - if (udc->board.vbus_pin > 0) + if (gpio_is_valid(udc->board.vbus_pin)) gpio_free(udc->board.vbus_pin); 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()) gpio_free(udc->board.pullup_pin); fail0: - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); DBG("%s probe failed, %d\n", driver_name, retval); return retval; } @@ -1791,33 +1869,37 @@ static int __exit at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); struct resource *res; + unsigned long flags; DBG("remove\n"); + usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; + spin_lock_irqsave(&udc->lock, flags); pullup(udc, 0); + spin_unlock_irqrestore(&udc->lock, flags); device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); - if (udc->board.vbus_pin > 0) { - free_irq(udc->board.vbus_pin, udc); + if (gpio_is_valid(udc->board.vbus_pin)) { + free_irq(gpio_to_irq(udc->board.vbus_pin), udc); 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()) gpio_free(udc->board.pullup_pin); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); clk_put(udc->iclk); clk_put(udc->fclk); + if (IS_ENABLED(CONFIG_COMMON_CLK)) + clk_put(udc->uclk); return 0; } @@ -1827,6 +1909,7 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg) { struct at91_udc *udc = platform_get_drvdata(pdev); int wake = udc->driver && device_may_wakeup(&pdev->dev); + unsigned long flags; /* Unless we can act normally to the host (letting it wake us up * whenever it has work for us) force disconnect. Wakeup requires @@ -1836,13 +1919,15 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg) if ((!udc->suspended && udc->addr) || !wake || at91_suspend_entering_slow_clock()) { + spin_lock_irqsave(&udc->lock, flags); pullup(udc, 0); wake = 0; + spin_unlock_irqrestore(&udc->lock, flags); } else enable_irq_wake(udc->udp_irq); udc->active_suspend = wake; - if (udc->board.vbus_pin > 0 && wake) + if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled && wake) enable_irq_wake(udc->board.vbus_pin); return 0; } @@ -1850,15 +1935,20 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg) static int at91udc_resume(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); + unsigned long flags; - if (udc->board.vbus_pin > 0 && udc->active_suspend) + if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled && + udc->active_suspend) disable_irq_wake(udc->board.vbus_pin); /* maybe reconnect to host; if so, clocks on */ if (udc->active_suspend) disable_irq_wake(udc->udp_irq); - else + else { + spin_lock_irqsave(&udc->lock, flags); pullup(udc, 1); + spin_unlock_irqrestore(&udc->lock, flags); + } return 0; } #else @@ -1866,6 +1956,15 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif +#if defined(CONFIG_OF) +static const struct of_device_id at91_udc_dt_ids[] = { + { .compatible = "atmel,at91rm9200-udc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91_udc_dt_ids); +#endif + static struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, @@ -1874,20 +1973,11 @@ static struct platform_driver at91_udc_driver = { .driver = { .name = (char *) driver_name, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_udc_dt_ids), }, }; -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 c65d6229589..01752466338 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -7,16 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef AT91_UDC_H @@ -115,9 +105,6 @@ struct at91_ep { unsigned is_in:1; unsigned is_iso:1; unsigned fifo_bank:1; - - const struct usb_endpoint_descriptor - *desc; }; /* @@ -139,11 +126,14 @@ 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; int udp_irq; + spinlock_t lock; + struct timer_list vbus_timer; + struct work_struct vbus_timer_work; }; static inline struct at91_udc *to_udc(struct usb_gadget *g) diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 05c913cc365..76023ce449a 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/slab.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/list.h> @@ -20,16 +21,14 @@ #include <linux/usb/gadget.h> #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 <mach/board.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> @@ -47,10 +46,9 @@ static int queue_dbg_open(struct inode *inode, struct file *file) spin_lock_irq(&ep->udc->lock); list_for_each_entry(req, &ep->queue, queue) { - req_copy = kmalloc(sizeof(*req_copy), GFP_ATOMIC); + req_copy = kmemdup(req, sizeof(*req_copy), GFP_ATOMIC); if (!req_copy) goto fail; - memcpy(req_copy, req, sizeof(*req_copy)); list_add_tail(&req_copy->queue, queue_data); } spin_unlock_irq(&ep->udc->lock); @@ -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; @@ -272,7 +270,7 @@ static void usba_init_debugfs(struct usba_udc *udc) regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - regs->d_inode->i_size = regs_resource->end - regs_resource->start + 1; + regs->d_inode->i_size = resource_size(regs_resource); udc->debugfs_regs = regs; usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0)); @@ -320,33 +318,33 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) static int vbus_is_present(struct usba_udc *udc) { if (gpio_is_valid(udc->vbus_pin)) - return gpio_get_value(udc->vbus_pin); + return gpio_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted; /* No Vbus detection: Assume always present */ return 1; } -#if defined(CONFIG_AVR32) +#if defined(CONFIG_ARCH_AT91SAM9RL) + +#include <linux/clk/at91_pmc.h> static void toggle_bias(int is_on) { -} + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); -#elif defined(CONFIG_ARCH_AT91) + if (is_on) + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); + else + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); +} -#include <mach/at91_pmc.h> +#else static void toggle_bias(int is_on) { - unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR); - - if (is_on) - at91_sys_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); - else - at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); } -#endif /* CONFIG_ARCH_AT91 */ +#endif /* CONFIG_ARCH_AT91SAM9RL */ static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) { @@ -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", @@ -527,7 +520,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); - maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index) || ep->index == 0 @@ -550,12 +543,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", ep->ep.name, ept_cfg, maxpacket); - if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + if (usb_endpoint_dir_in(desc)) { ep->is_in = 1; ept_cfg |= USBA_EPT_DIR_IN; } - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_CONTROL: ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL); ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE); @@ -571,7 +564,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * Bits 11:12 specify number of _additional_ * transactions per microframe. */ - nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1; + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; if (nr_trans > 3) return -EINVAL; @@ -599,13 +592,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) spin_lock_irqsave(&ep->udc->lock, flags); - if (ep->desc) { - spin_unlock_irqrestore(&ep->udc->lock, flags); - DBG(DBG_ERR, "ep%d already enabled\n", ep->index); - return -EBUSY; - } - - ep->desc = desc; + ep->ep.desc = desc; ep->ep.maxpacket = maxpacket; usba_ep_writel(ep, CFG, ept_cfg); @@ -647,7 +634,7 @@ static int usba_ep_disable(struct usb_ep *_ep) spin_lock_irqsave(&udc->lock, flags); - if (!ep->desc) { + if (!ep->ep.desc) { spin_unlock_irqrestore(&udc->lock, flags); /* REVISIT because this driver disables endpoints in * reset_all_endpoints() before calling disconnect(), @@ -658,7 +645,7 @@ static int usba_ep_disable(struct usb_ep *_ep) ep->ep.name); return -EINVAL; } - ep->desc = NULL; + ep->ep.desc = NULL; list_splice_init(&ep->queue, &req_list); if (ep->can_dma) { @@ -690,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; } @@ -723,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; @@ -751,7 +728,7 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, */ ret = -ESHUTDOWN; spin_lock_irqsave(&udc->lock, flags); - if (ep->desc) { + if (ep->ep.desc) { if (list_empty(&ep->queue)) submit_request(ep, req); @@ -775,7 +752,8 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, "%s: queue req %p, len %u\n", ep->ep.name, req, _req->length); - if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || !ep->desc) + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN || + !ep->ep.desc) return -ESHUTDOWN; req->submitted = 0; @@ -791,7 +769,7 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* May have received a reset since last time we checked */ ret = -ESHUTDOWN; spin_lock_irqsave(&udc->lock, flags); - if (ep->desc) { + if (ep->ep.desc) { list_add_tail(&req->queue, &ep->queue); if ((!ep_is_control(ep) && ep->is_in) || @@ -904,7 +882,7 @@ static int usba_ep_set_halt(struct usb_ep *_ep, int value) DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep->ep.name, value ? "set" : "clear"); - if (!ep->desc) { + if (!ep->ep.desc) { DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", ep->ep.name); return -ENODEV; @@ -1007,10 +985,16 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) return 0; } +static int atmel_usba_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static int atmel_usba_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops usba_udc_ops = { .get_frame = usba_udc_get_frame, .wakeup = usba_udc_wakeup, .set_selfpowered = usba_udc_set_selfpowered, + .udc_start = atmel_usba_start, + .udc_stop = atmel_usba_stop, }; static struct usb_endpoint_descriptor usba_ep0_desc = { @@ -1028,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), - .is_dualspeed = 1, - .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, }, }; @@ -1064,7 +1045,7 @@ static void reset_all_endpoints(struct usba_udc *udc) * FIXME remove this code ... and retest thoroughly. */ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { - if (ep->desc) { + if (ep->ep.desc) { spin_unlock(&udc->lock); usba_ep_disable(&ep->ep); spin_lock(&udc->lock); @@ -1082,9 +1063,9 @@ static struct usba_ep *get_ep_by_addr(struct usba_udc *udc, u16 wIndex) list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { u8 bEndpointAddress; - if (!ep->desc) + if (!ep->ep.desc) continue; - bEndpointAddress = ep->desc->bEndpointAddress; + bEndpointAddress = ep->ep.desc->bEndpointAddress; if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) continue; if ((bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) @@ -1161,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, @@ -1179,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 @@ -1680,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]); } } @@ -1705,23 +1686,22 @@ 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); spin_lock(&udc->lock); } - if (status & USBA_HIGH_SPEED) { - DBG(DBG_BUS, "High-speed bus reset detected\n"); + if (status & USBA_HIGH_SPEED) udc->gadget.speed = USB_SPEED_HIGH; - } else { - DBG(DBG_BUS, "Full-speed bus reset detected\n"); + else udc->gadget.speed = USB_SPEED_FULL; - } + DBG(DBG_BUS, "%s bus reset detected\n", + usb_speed_string(udc->gadget.speed)); - ep0 = &usba_ep[0]; - ep0->desc = &usba_ep0_desc; + ep0 = &udc->usba_ep[0]; + ep0->ep.desc = &usba_ep0_desc; ep0->state = WAIT_FOR_SETUP; usba_ep_writel(ep0, CFG, (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) @@ -1763,7 +1743,7 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) if (!udc->driver) goto out; - vbus = gpio_get_value(udc->vbus_pin); + vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { toggle_bias(1); @@ -1789,34 +1769,26 @@ out: return IRQ_HANDLED; } -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +static int atmel_usba_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usba_udc *udc = &the_udc; - unsigned long flags; int ret; - - if (!udc->pdev) - return -ENODEV; + struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); + unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - if (udc->driver) { - spin_unlock_irqrestore(&udc->lock, flags); - return -EBUSY; - } 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 = driver->bind(&udc->gadget); + ret = clk_prepare_enable(udc->pclk); + if (ret) + return ret; + ret = clk_prepare_enable(udc->hclk); if (ret) { - DBG(DBG_ERR, "Could not bind to driver %s: error %d\n", - driver->driver.name, ret); - goto err_driver_bind; + clk_disable_unprepare(udc->pclk); + return ret; } DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); @@ -1835,24 +1807,14 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) spin_unlock_irqrestore(&udc->lock, flags); return 0; - -err_driver_bind: - udc->driver = NULL; - udc->gadget.dev.driver = NULL; - return ret; } -EXPORT_SYMBOL(usb_gadget_register_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int atmel_usba_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usba_udc *udc = &the_udc; + struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); unsigned long flags; - if (!udc->pdev) - return -ENODEV; - if (driver != udc->driver || !driver->unbind) - return -EINVAL; - if (gpio_is_valid(udc->vbus_pin)) disable_irq(gpio_to_irq(udc->vbus_pin)); @@ -1865,47 +1827,187 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); - if (udc->driver->disconnect) - udc->driver->disconnect(&udc->gadget); + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); + + DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name); - driver->unbind(&udc->gadget); - udc->gadget.dev.driver = NULL; udc->driver = NULL; - clk_disable(udc->hclk); - clk_disable(udc->pclk); + return 0; +} - DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); +#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; - return 0; + 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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); 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; @@ -1914,98 +2016,57 @@ static int __init usba_udc_probe(struct platform_device *pdev) udc->vbus_pin = -ENODEV; ret = -ENOMEM; - udc->regs = ioremap(regs->start, regs->end - regs->start + 1); + 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, fifo->end - fifo->start + 1); + 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; - - 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; " @@ -2013,81 +2074,60 @@ static int __init usba_udc_probe(struct platform_device *pdev) } else { disable_irq(gpio_to_irq(udc->vbus_pin)); } + } else { + /* gpio_request fail so use -EINVAL for gpio_is_valid */ + udc->vbus_pin = -EINVAL; } } + ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (ret) + 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_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); - for (i = 1; i < pdata->num_ep; i++) - usba_ep_cleanup_debugfs(&usba_ep[i]); - usba_cleanup_debugfs(udc); - - if (gpio_is_valid(udc->vbus_pin)) - 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); + usb_del_gadget_udc(&udc->gadget); - device_unregister(&udc->gadget.dev); + for (i = 1; i < udc->num_ep; i++) + usba_ep_cleanup_debugfs(&udc->usba_ep[i]); + usba_cleanup_debugfs(udc); 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 <hskinnemoen@atmel.com>"); +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:atmel_usba_udc"); diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h index f7baea307f0..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 @@ -280,7 +274,6 @@ struct usba_ep { struct usba_udc *udc; struct list_head queue; - const struct usb_endpoint_descriptor *desc; u16 fifo_size; u8 nr_banks; @@ -323,8 +316,11 @@ struct usba_udc { struct platform_device *pdev; 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/audio.c b/drivers/usb/gadget/audio.c new file mode 100644 index 00000000000..231b0efe8fd --- /dev/null +++ b/drivers/usb/gadget/audio.c @@ -0,0 +1,190 @@ +/* + * audio.c -- Audio gadget driver + * + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/composite.h> + +#include "gadget_chips.h" +#define DRIVER_DESC "Linux USB Audio Gadget" +#define DRIVER_VERSION "Feb 2, 2012" + +USB_GADGET_COMPOSITE_OPTIONS(); + +/* string IDs are assigned dynamically */ + +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *audio_strings[] = { + &stringtab_dev, + NULL, +}; + +#ifdef CONFIG_GADGET_UAC1 +#include "u_uac1.h" +#include "u_uac1.c" +#include "f_uac1.c" +#else +#include "f_uac2.c" +#endif + +/*-------------------------------------------------------------------------*/ + +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +/* Thanks to Linux Foundation for donating this product ID. */ +#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */ + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x200), + +#ifdef CONFIG_GADGET_UAC1 + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, +#else + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, +#endif + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id defaults change according to what configs + * we support. (As does bNumConfigurations.) These values can + * also be overridden by module parameters. + */ + .idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init audio_do_config(struct usb_configuration *c) +{ + /* FIXME alloc iConfiguration string, set it in c->strings */ + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + audio_bind_config(c); + + return 0; +} + +static struct usb_configuration audio_config_driver = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +#ifndef CONFIG_GADGET_UAC1 + .unbind = uac2_unbind_config, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int __init audio_bind(struct usb_composite_dev *cdev) +{ + int status; + + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + goto fail; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + + status = usb_add_config(cdev, &audio_config_driver, audio_do_config); + if (status < 0) + goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); + + INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION); + return 0; + +fail: + return status; +} + +static int __exit audio_unbind(struct usb_composite_dev *cdev) +{ +#ifdef CONFIG_GADGET_UAC1 + gaudio_cleanup(); +#endif + return 0; +} + +static __refdata struct usb_composite_driver audio_driver = { + .name = "g_audio", + .dev = &device_desc, + .strings = audio_strings, + .max_speed = USB_SPEED_HIGH, + .bind = audio_bind, + .unbind = __exit_p(audio_unbind), +}; + +static int __init init(void) +{ + return usb_composite_probe(&audio_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&audio_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c new file mode 100644 index 00000000000..e969eb809a8 --- /dev/null +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -0,0 +1,2436 @@ +/* + * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller + * + * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com> + * Copyright (C) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kconfig.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/workqueue.h> + +#include <bcm63xx_cpu.h> +#include <bcm63xx_iudma.h> +#include <bcm63xx_dev_usb_usbd.h> +#include <bcm63xx_io.h> +#include <bcm63xx_regs.h> + +#define DRV_MODULE_NAME "bcm63xx_udc" + +static const char bcm63xx_ep0name[] = "ep0"; +static const char *const bcm63xx_ep_name[] = { + bcm63xx_ep0name, + "ep1in-bulk", "ep2out-bulk", "ep3in-int", "ep4out-int", +}; + +static bool use_fullspeed; +module_param(use_fullspeed, bool, S_IRUGO); +MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only"); + +/* + * RX IRQ coalescing options: + * + * false (default) - one IRQ per DATAx packet. Slow but reliable. The + * driver is able to pass the "testusb" suite and recover from conditions like: + * + * 1) Device queues up a 2048-byte RX IUDMA transaction on an OUT bulk ep + * 2) Host sends 512 bytes of data + * 3) Host decides to reconfigure the device and sends SET_INTERFACE + * 4) Device shuts down the endpoint and cancels the RX transaction + * + * true - one IRQ per transfer, for transfers <= 2048B. Generates + * considerably fewer IRQs, but error recovery is less robust. Does not + * reliably pass "testusb". + * + * TX always uses coalescing, because we can cancel partially complete TX + * transfers by repeatedly flushing the FIFO. The hardware doesn't allow + * this on RX. + */ +static bool irq_coalesce; +module_param(irq_coalesce, bool, S_IRUGO); +MODULE_PARM_DESC(irq_coalesce, "take one IRQ per RX transfer"); + +#define BCM63XX_NUM_EP 5 +#define BCM63XX_NUM_IUDMA 6 +#define BCM63XX_NUM_FIFO_PAIRS 3 + +#define IUDMA_RESET_TIMEOUT_US 10000 + +#define IUDMA_EP0_RXCHAN 0 +#define IUDMA_EP0_TXCHAN 1 + +#define IUDMA_MAX_FRAGMENT 2048 +#define BCM63XX_MAX_CTRL_PKT 64 + +#define BCMEP_CTRL 0x00 +#define BCMEP_ISOC 0x01 +#define BCMEP_BULK 0x02 +#define BCMEP_INTR 0x03 + +#define BCMEP_OUT 0x00 +#define BCMEP_IN 0x01 + +#define BCM63XX_SPD_FULL 1 +#define BCM63XX_SPD_HIGH 0 + +#define IUDMA_DMAC_OFFSET 0x200 +#define IUDMA_DMAS_OFFSET 0x400 + +enum bcm63xx_ep0_state { + EP0_REQUEUE, + EP0_IDLE, + EP0_IN_DATA_PHASE_SETUP, + EP0_IN_DATA_PHASE_COMPLETE, + EP0_OUT_DATA_PHASE_SETUP, + EP0_OUT_DATA_PHASE_COMPLETE, + EP0_OUT_STATUS_PHASE, + EP0_IN_FAKE_STATUS_PHASE, + EP0_SHUTDOWN, +}; + +static const char __maybe_unused bcm63xx_ep0_state_names[][32] = { + "REQUEUE", + "IDLE", + "IN_DATA_PHASE_SETUP", + "IN_DATA_PHASE_COMPLETE", + "OUT_DATA_PHASE_SETUP", + "OUT_DATA_PHASE_COMPLETE", + "OUT_STATUS_PHASE", + "IN_FAKE_STATUS_PHASE", + "SHUTDOWN", +}; + +/** + * struct iudma_ch_cfg - Static configuration for an IUDMA channel. + * @ep_num: USB endpoint number. + * @n_bds: Number of buffer descriptors in the ring. + * @ep_type: Endpoint type (control, bulk, interrupt). + * @dir: Direction (in, out). + * @n_fifo_slots: Number of FIFO entries to allocate for this channel. + * @max_pkt_hs: Maximum packet size in high speed mode. + * @max_pkt_fs: Maximum packet size in full speed mode. + */ +struct iudma_ch_cfg { + int ep_num; + int n_bds; + int ep_type; + int dir; + int n_fifo_slots; + int max_pkt_hs; + int max_pkt_fs; +}; + +static const struct iudma_ch_cfg iudma_defaults[] = { + + /* This controller was designed to support a CDC/RNDIS application. + It may be possible to reconfigure some of the endpoints, but + the hardware limitations (FIFO sizing and number of DMA channels) + may significantly impact flexibility and/or stability. Change + these values at your own risk. + + ep_num ep_type n_fifo_slots max_pkt_fs + idx | n_bds | dir | max_pkt_hs | + | | | | | | | | */ + [0] = { -1, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 }, + [1] = { 0, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 }, + [2] = { 2, 16, BCMEP_BULK, BCMEP_OUT, 128, 512, 64 }, + [3] = { 1, 16, BCMEP_BULK, BCMEP_IN, 128, 512, 64 }, + [4] = { 4, 4, BCMEP_INTR, BCMEP_OUT, 32, 64, 64 }, + [5] = { 3, 4, BCMEP_INTR, BCMEP_IN, 32, 64, 64 }, +}; + +struct bcm63xx_udc; + +/** + * struct iudma_ch - Represents the current state of a single IUDMA channel. + * @ch_idx: IUDMA channel index (0 to BCM63XX_NUM_IUDMA-1). + * @ep_num: USB endpoint number. -1 for ep0 RX. + * @enabled: Whether bcm63xx_ep_enable() has been called. + * @max_pkt: "Chunk size" on the USB interface. Based on interface speed. + * @is_tx: true for TX, false for RX. + * @bep: Pointer to the associated endpoint. NULL for ep0 RX. + * @udc: Reference to the device controller. + * @read_bd: Next buffer descriptor to reap from the hardware. + * @write_bd: Next BD available for a new packet. + * @end_bd: Points to the final BD in the ring. + * @n_bds_used: Number of BD entries currently occupied. + * @bd_ring: Base pointer to the BD ring. + * @bd_ring_dma: Physical (DMA) address of bd_ring. + * @n_bds: Total number of BDs in the ring. + * + * ep0 has two IUDMA channels (IUDMA_EP0_RXCHAN and IUDMA_EP0_TXCHAN), as it is + * bidirectional. The "struct usb_ep" associated with ep0 is for TX (IN) + * only. + * + * Each bulk/intr endpoint has a single IUDMA channel and a single + * struct usb_ep. + */ +struct iudma_ch { + unsigned int ch_idx; + int ep_num; + bool enabled; + int max_pkt; + bool is_tx; + struct bcm63xx_ep *bep; + struct bcm63xx_udc *udc; + + struct bcm_enet_desc *read_bd; + struct bcm_enet_desc *write_bd; + struct bcm_enet_desc *end_bd; + int n_bds_used; + + struct bcm_enet_desc *bd_ring; + dma_addr_t bd_ring_dma; + unsigned int n_bds; +}; + +/** + * struct bcm63xx_ep - Internal (driver) state of a single endpoint. + * @ep_num: USB endpoint number. + * @iudma: Pointer to IUDMA channel state. + * @ep: USB gadget layer representation of the EP. + * @udc: Reference to the device controller. + * @queue: Linked list of outstanding requests for this EP. + * @halted: 1 if the EP is stalled; 0 otherwise. + */ +struct bcm63xx_ep { + unsigned int ep_num; + struct iudma_ch *iudma; + struct usb_ep ep; + struct bcm63xx_udc *udc; + struct list_head queue; + unsigned halted:1; +}; + +/** + * struct bcm63xx_req - Internal (driver) state of a single request. + * @queue: Links back to the EP's request list. + * @req: USB gadget layer representation of the request. + * @offset: Current byte offset into the data buffer (next byte to queue). + * @bd_bytes: Number of data bytes in outstanding BD entries. + * @iudma: IUDMA channel used for the request. + */ +struct bcm63xx_req { + struct list_head queue; /* ep's requests */ + struct usb_request req; + unsigned int offset; + unsigned int bd_bytes; + struct iudma_ch *iudma; +}; + +/** + * struct bcm63xx_udc - Driver/hardware private context. + * @lock: Spinlock to mediate access to this struct, and (most) HW regs. + * @dev: Generic Linux device structure. + * @pd: Platform data (board/port info). + * @usbd_clk: Clock descriptor for the USB device block. + * @usbh_clk: Clock descriptor for the USB host block. + * @gadget: USB slave device. + * @driver: Driver for USB slave devices. + * @usbd_regs: Base address of the USBD/USB20D block. + * @iudma_regs: Base address of the USBD's associated IUDMA block. + * @bep: Array of endpoints, including ep0. + * @iudma: Array of all IUDMA channels used by this controller. + * @cfg: USB configuration number, from SET_CONFIGURATION wValue. + * @iface: USB interface number, from SET_INTERFACE wIndex. + * @alt_iface: USB alt interface number, from SET_INTERFACE wValue. + * @ep0_ctrl_req: Request object for bcm63xx_udc-initiated ep0 transactions. + * @ep0_ctrl_buf: Data buffer for ep0_ctrl_req. + * @ep0state: Current state of the ep0 state machine. + * @ep0_wq: Workqueue struct used to wake up the ep0 state machine. + * @wedgemap: Bitmap of wedged endpoints. + * @ep0_req_reset: USB reset is pending. + * @ep0_req_set_cfg: Need to spoof a SET_CONFIGURATION packet. + * @ep0_req_set_iface: Need to spoof a SET_INTERFACE packet. + * @ep0_req_shutdown: Driver is shutting down; requesting ep0 to halt activity. + * @ep0_req_completed: ep0 request has completed; worker has not seen it yet. + * @ep0_reply: Pending reply from gadget driver. + * @ep0_request: Outstanding ep0 request. + * @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>. + * @debugfs_usbd: debugfs file "usbd" for controller state. + * @debugfs_iudma: debugfs file "usbd" for IUDMA state. + */ +struct bcm63xx_udc { + spinlock_t lock; + + struct device *dev; + struct bcm63xx_usbd_platform_data *pd; + struct clk *usbd_clk; + struct clk *usbh_clk; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + void __iomem *usbd_regs; + void __iomem *iudma_regs; + + struct bcm63xx_ep bep[BCM63XX_NUM_EP]; + struct iudma_ch iudma[BCM63XX_NUM_IUDMA]; + + int cfg; + int iface; + int alt_iface; + + struct bcm63xx_req ep0_ctrl_req; + u8 *ep0_ctrl_buf; + + int ep0state; + struct work_struct ep0_wq; + + unsigned long wedgemap; + + unsigned ep0_req_reset:1; + unsigned ep0_req_set_cfg:1; + unsigned ep0_req_set_iface:1; + unsigned ep0_req_shutdown:1; + + unsigned ep0_req_completed:1; + struct usb_request *ep0_reply; + struct usb_request *ep0_request; + + struct dentry *debugfs_root; + struct dentry *debugfs_usbd; + struct dentry *debugfs_iudma; +}; + +static const struct usb_ep_ops bcm63xx_udc_ep_ops; + +/*********************************************************************** + * Convenience functions + ***********************************************************************/ + +static inline struct bcm63xx_udc *gadget_to_udc(struct usb_gadget *g) +{ + return container_of(g, struct bcm63xx_udc, gadget); +} + +static inline struct bcm63xx_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct bcm63xx_ep, ep); +} + +static inline struct bcm63xx_req *our_req(struct usb_request *req) +{ + return container_of(req, struct bcm63xx_req, req); +} + +static inline u32 usbd_readl(struct bcm63xx_udc *udc, u32 off) +{ + return bcm_readl(udc->usbd_regs + off); +} + +static inline void usbd_writel(struct bcm63xx_udc *udc, u32 val, u32 off) +{ + bcm_writel(val, udc->usbd_regs + off); +} + +static inline u32 usb_dma_readl(struct bcm63xx_udc *udc, u32 off) +{ + return bcm_readl(udc->iudma_regs + off); +} + +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, int chan) +{ + 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, + int chan) +{ + 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, int chan) +{ + 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, + int chan) +{ + 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) +{ + if (is_enabled) { + clk_enable(udc->usbh_clk); + clk_enable(udc->usbd_clk); + udelay(10); + } else { + clk_disable(udc->usbd_clk); + clk_disable(udc->usbh_clk); + } +} + +/*********************************************************************** + * Low-level IUDMA / FIFO operations + ***********************************************************************/ + +/** + * bcm63xx_ep_dma_select - Helper function to set up the init_sel signal. + * @udc: Reference to the device controller. + * @idx: Desired init_sel value. + * + * The "init_sel" signal is used as a selection index for both endpoints + * and IUDMA channels. Since these do not map 1:1, the use of this signal + * depends on the context. + */ +static void bcm63xx_ep_dma_select(struct bcm63xx_udc *udc, int idx) +{ + u32 val = usbd_readl(udc, USBD_CONTROL_REG); + + val &= ~USBD_CONTROL_INIT_SEL_MASK; + val |= idx << USBD_CONTROL_INIT_SEL_SHIFT; + usbd_writel(udc, val, USBD_CONTROL_REG); +} + +/** + * bcm63xx_set_stall - Enable/disable stall on one endpoint. + * @udc: Reference to the device controller. + * @bep: Endpoint on which to operate. + * @is_stalled: true to enable stall, false to disable. + * + * See notes in bcm63xx_update_wedge() regarding automatic clearing of + * halt/stall conditions. + */ +static void bcm63xx_set_stall(struct bcm63xx_udc *udc, struct bcm63xx_ep *bep, + bool is_stalled) +{ + u32 val; + + val = USBD_STALL_UPDATE_MASK | + (is_stalled ? USBD_STALL_ENABLE_MASK : 0) | + (bep->ep_num << USBD_STALL_EPNUM_SHIFT); + usbd_writel(udc, val, USBD_STALL_REG); +} + +/** + * bcm63xx_fifo_setup - (Re)initialize FIFO boundaries and settings. + * @udc: Reference to the device controller. + * + * These parameters depend on the USB link speed. Settings are + * per-IUDMA-channel-pair. + */ +static void bcm63xx_fifo_setup(struct bcm63xx_udc *udc) +{ + int is_hs = udc->gadget.speed == USB_SPEED_HIGH; + u32 i, val, rx_fifo_slot, tx_fifo_slot; + + /* set up FIFO boundaries and packet sizes; this is done in pairs */ + rx_fifo_slot = tx_fifo_slot = 0; + for (i = 0; i < BCM63XX_NUM_IUDMA; i += 2) { + const struct iudma_ch_cfg *rx_cfg = &iudma_defaults[i]; + const struct iudma_ch_cfg *tx_cfg = &iudma_defaults[i + 1]; + + bcm63xx_ep_dma_select(udc, i >> 1); + + val = (rx_fifo_slot << USBD_RXFIFO_CONFIG_START_SHIFT) | + ((rx_fifo_slot + rx_cfg->n_fifo_slots - 1) << + USBD_RXFIFO_CONFIG_END_SHIFT); + rx_fifo_slot += rx_cfg->n_fifo_slots; + usbd_writel(udc, val, USBD_RXFIFO_CONFIG_REG); + usbd_writel(udc, + is_hs ? rx_cfg->max_pkt_hs : rx_cfg->max_pkt_fs, + USBD_RXFIFO_EPSIZE_REG); + + val = (tx_fifo_slot << USBD_TXFIFO_CONFIG_START_SHIFT) | + ((tx_fifo_slot + tx_cfg->n_fifo_slots - 1) << + USBD_TXFIFO_CONFIG_END_SHIFT); + tx_fifo_slot += tx_cfg->n_fifo_slots; + usbd_writel(udc, val, USBD_TXFIFO_CONFIG_REG); + usbd_writel(udc, + is_hs ? tx_cfg->max_pkt_hs : tx_cfg->max_pkt_fs, + USBD_TXFIFO_EPSIZE_REG); + + usbd_readl(udc, USBD_TXFIFO_EPSIZE_REG); + } +} + +/** + * bcm63xx_fifo_reset_ep - Flush a single endpoint's FIFO. + * @udc: Reference to the device controller. + * @ep_num: Endpoint number. + */ +static void bcm63xx_fifo_reset_ep(struct bcm63xx_udc *udc, int ep_num) +{ + u32 val; + + bcm63xx_ep_dma_select(udc, ep_num); + + val = usbd_readl(udc, USBD_CONTROL_REG); + val |= USBD_CONTROL_FIFO_RESET_MASK; + usbd_writel(udc, val, USBD_CONTROL_REG); + usbd_readl(udc, USBD_CONTROL_REG); +} + +/** + * bcm63xx_fifo_reset - Flush all hardware FIFOs. + * @udc: Reference to the device controller. + */ +static void bcm63xx_fifo_reset(struct bcm63xx_udc *udc) +{ + int i; + + for (i = 0; i < BCM63XX_NUM_FIFO_PAIRS; i++) + bcm63xx_fifo_reset_ep(udc, i); +} + +/** + * bcm63xx_ep_init - Initial (one-time) endpoint initialization. + * @udc: Reference to the device controller. + */ +static void bcm63xx_ep_init(struct bcm63xx_udc *udc) +{ + u32 i, val; + + for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { + const struct iudma_ch_cfg *cfg = &iudma_defaults[i]; + + if (cfg->ep_num < 0) + continue; + + bcm63xx_ep_dma_select(udc, cfg->ep_num); + val = (cfg->ep_type << USBD_EPNUM_TYPEMAP_TYPE_SHIFT) | + ((i >> 1) << USBD_EPNUM_TYPEMAP_DMA_CH_SHIFT); + usbd_writel(udc, val, USBD_EPNUM_TYPEMAP_REG); + } +} + +/** + * bcm63xx_ep_setup - Configure per-endpoint settings. + * @udc: Reference to the device controller. + * + * This needs to be rerun if the speed/cfg/intf/altintf changes. + */ +static void bcm63xx_ep_setup(struct bcm63xx_udc *udc) +{ + u32 val, i; + + usbd_writel(udc, USBD_CSR_SETUPADDR_DEF, USBD_CSR_SETUPADDR_REG); + + for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { + const struct iudma_ch_cfg *cfg = &iudma_defaults[i]; + int max_pkt = udc->gadget.speed == USB_SPEED_HIGH ? + cfg->max_pkt_hs : cfg->max_pkt_fs; + int idx = cfg->ep_num; + + udc->iudma[i].max_pkt = max_pkt; + + if (idx < 0) + continue; + 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) | + (cfg->ep_type << USBD_CSR_EP_TYPE_SHIFT) | + (udc->cfg << USBD_CSR_EP_CFG_SHIFT) | + (udc->iface << USBD_CSR_EP_IFACE_SHIFT) | + (udc->alt_iface << USBD_CSR_EP_ALTIFACE_SHIFT) | + (max_pkt << USBD_CSR_EP_MAXPKT_SHIFT); + usbd_writel(udc, val, USBD_CSR_EP_REG(idx)); + } +} + +/** + * iudma_write - Queue a single IUDMA transaction. + * @udc: Reference to the device controller. + * @iudma: IUDMA channel to use. + * @breq: Request containing the transaction data. + * + * For RX IUDMA, this will queue a single buffer descriptor, as RX IUDMA + * does not honor SOP/EOP so the handling of multiple buffers is ambiguous. + * So iudma_write() may be called several times to fulfill a single + * usb_request. + * + * For TX IUDMA, this can queue multiple buffer descriptors if needed. + */ +static void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma, + struct bcm63xx_req *breq) +{ + int first_bd = 1, last_bd = 0, extra_zero_pkt = 0; + unsigned int bytes_left = breq->req.length - breq->offset; + const int max_bd_bytes = !irq_coalesce && !iudma->is_tx ? + iudma->max_pkt : IUDMA_MAX_FRAGMENT; + + iudma->n_bds_used = 0; + breq->bd_bytes = 0; + breq->iudma = iudma; + + if ((bytes_left % iudma->max_pkt == 0) && bytes_left && breq->req.zero) + extra_zero_pkt = 1; + + do { + struct bcm_enet_desc *d = iudma->write_bd; + u32 dmaflags = 0; + unsigned int n_bytes; + + if (d == iudma->end_bd) { + dmaflags |= DMADESC_WRAP_MASK; + iudma->write_bd = iudma->bd_ring; + } else { + iudma->write_bd++; + } + iudma->n_bds_used++; + + n_bytes = min_t(int, bytes_left, max_bd_bytes); + if (n_bytes) + dmaflags |= n_bytes << DMADESC_LENGTH_SHIFT; + else + dmaflags |= (1 << DMADESC_LENGTH_SHIFT) | + DMADESC_USB_ZERO_MASK; + + dmaflags |= DMADESC_OWNER_MASK; + if (first_bd) { + dmaflags |= DMADESC_SOP_MASK; + first_bd = 0; + } + + /* + * extra_zero_pkt forces one more iteration through the loop + * after all data is queued up, to send the zero packet + */ + if (extra_zero_pkt && !bytes_left) + extra_zero_pkt = 0; + + if (!iudma->is_tx || iudma->n_bds_used == iudma->n_bds || + (n_bytes == bytes_left && !extra_zero_pkt)) { + last_bd = 1; + dmaflags |= DMADESC_EOP_MASK; + } + + d->address = breq->req.dma + breq->offset; + mb(); + d->len_stat = dmaflags; + + breq->offset += n_bytes; + breq->bd_bytes += n_bytes; + bytes_left -= n_bytes; + } while (!last_bd); + + usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG_REG, iudma->ch_idx); +} + +/** + * iudma_read - Check for IUDMA buffer completion. + * @udc: Reference to the device controller. + * @iudma: IUDMA channel to use. + * + * This checks to see if ALL of the outstanding BDs on the DMA channel + * have been filled. If so, it returns the actual transfer length; + * otherwise it returns -EBUSY. + */ +static int iudma_read(struct bcm63xx_udc *udc, struct iudma_ch *iudma) +{ + int i, actual_len = 0; + struct bcm_enet_desc *d = iudma->read_bd; + + if (!iudma->n_bds_used) + return -EINVAL; + + for (i = 0; i < iudma->n_bds_used; i++) { + u32 dmaflags; + + dmaflags = d->len_stat; + + if (dmaflags & DMADESC_OWNER_MASK) + return -EBUSY; + + actual_len += (dmaflags & DMADESC_LENGTH_MASK) >> + DMADESC_LENGTH_SHIFT; + if (d == iudma->end_bd) + d = iudma->bd_ring; + else + d++; + } + + iudma->read_bd = d; + iudma->n_bds_used = 0; + return actual_len; +} + +/** + * iudma_reset_channel - Stop DMA on a single channel. + * @udc: Reference to the device controller. + * @iudma: IUDMA channel to reset. + */ +static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma) +{ + int timeout = IUDMA_RESET_TIMEOUT_US; + struct bcm_enet_desc *d; + int ch_idx = iudma->ch_idx; + + if (!iudma->is_tx) + 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); + + while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx) & + ENETDMAC_CHANCFG_EN_MASK) { + udelay(1); + + /* repeatedly flush the FIFO data until the BD completes */ + if (iudma->is_tx && iudma->ep_num >= 0) + bcm63xx_fifo_reset_ep(udc, iudma->ep_num); + + if (!timeout--) { + dev_err(udc->dev, "can't reset IUDMA channel %d\n", + ch_idx); + break; + } + if (timeout == IUDMA_RESET_TIMEOUT_US / 2) { + 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); + } + } + 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++) + d->len_stat = 0; + mb(); + + iudma->read_bd = iudma->write_bd = iudma->bd_ring; + iudma->n_bds_used = 0; + + /* 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); + + usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG, ch_idx); + usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG, ch_idx); +} + +/** + * iudma_init_channel - One-time IUDMA channel initialization. + * @udc: Reference to the device controller. + * @ch_idx: Channel to initialize. + */ +static int iudma_init_channel(struct bcm63xx_udc *udc, unsigned int ch_idx) +{ + struct iudma_ch *iudma = &udc->iudma[ch_idx]; + const struct iudma_ch_cfg *cfg = &iudma_defaults[ch_idx]; + unsigned int n_bds = cfg->n_bds; + struct bcm63xx_ep *bep = NULL; + + iudma->ep_num = cfg->ep_num; + iudma->ch_idx = ch_idx; + iudma->is_tx = !!(ch_idx & 0x01); + if (iudma->ep_num >= 0) { + bep = &udc->bep[iudma->ep_num]; + bep->iudma = iudma; + INIT_LIST_HEAD(&bep->queue); + } + + iudma->bep = bep; + iudma->udc = udc; + + /* ep0 is always active; others are controlled by the gadget driver */ + if (iudma->ep_num <= 0) + iudma->enabled = true; + + iudma->n_bds = n_bds; + iudma->bd_ring = dmam_alloc_coherent(udc->dev, + n_bds * sizeof(struct bcm_enet_desc), + &iudma->bd_ring_dma, GFP_KERNEL); + if (!iudma->bd_ring) + return -ENOMEM; + iudma->end_bd = &iudma->bd_ring[n_bds - 1]; + + return 0; +} + +/** + * iudma_init - One-time initialization of all IUDMA channels. + * @udc: Reference to the device controller. + * + * Enable DMA, flush channels, and enable global IUDMA IRQs. + */ +static int iudma_init(struct bcm63xx_udc *udc) +{ + int i, rc; + + usb_dma_writel(udc, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); + + for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { + rc = iudma_init_channel(udc, i); + if (rc) + return rc; + iudma_reset_channel(udc, &udc->iudma[i]); + } + + usb_dma_writel(udc, BIT(BCM63XX_NUM_IUDMA)-1, ENETDMA_GLB_IRQMASK_REG); + return 0; +} + +/** + * iudma_uninit - Uninitialize IUDMA channels. + * @udc: Reference to the device controller. + * + * Kill global IUDMA IRQs, flush channels, and kill DMA. + */ +static void iudma_uninit(struct bcm63xx_udc *udc) +{ + int i; + + usb_dma_writel(udc, 0, ENETDMA_GLB_IRQMASK_REG); + + for (i = 0; i < BCM63XX_NUM_IUDMA; i++) + iudma_reset_channel(udc, &udc->iudma[i]); + + usb_dma_writel(udc, 0, ENETDMA_CFG_REG); +} + +/*********************************************************************** + * Other low-level USBD operations + ***********************************************************************/ + +/** + * bcm63xx_set_ctrl_irqs - Mask/unmask control path interrupts. + * @udc: Reference to the device controller. + * @enable_irqs: true to enable, false to disable. + */ +static void bcm63xx_set_ctrl_irqs(struct bcm63xx_udc *udc, bool enable_irqs) +{ + u32 val; + + usbd_writel(udc, 0, USBD_STATUS_REG); + + val = BIT(USBD_EVENT_IRQ_USB_RESET) | + BIT(USBD_EVENT_IRQ_SETUP) | + BIT(USBD_EVENT_IRQ_SETCFG) | + BIT(USBD_EVENT_IRQ_SETINTF) | + BIT(USBD_EVENT_IRQ_USB_LINK); + usbd_writel(udc, enable_irqs ? val : 0, USBD_EVENT_IRQ_MASK_REG); + usbd_writel(udc, val, USBD_EVENT_IRQ_STATUS_REG); +} + +/** + * bcm63xx_select_phy_mode - Select between USB device and host mode. + * @udc: Reference to the device controller. + * @is_device: true for device, false for host. + * + * This should probably be reworked to use the drivers/usb/otg + * infrastructure. + * + * By default, the AFE/pullups are disabled in device mode, until + * bcm63xx_select_pullup() is called. + */ +static void bcm63xx_select_phy_mode(struct bcm63xx_udc *udc, bool is_device) +{ + u32 val, portmask = BIT(udc->pd->port_no); + + if (BCMCPU_IS_6328()) { + /* configure pinmux to sense VBUS signal */ + val = bcm_gpio_readl(GPIO_PINMUX_OTHR_REG); + val &= ~GPIO_PINMUX_OTHR_6328_USB_MASK; + val |= is_device ? GPIO_PINMUX_OTHR_6328_USB_DEV : + GPIO_PINMUX_OTHR_6328_USB_HOST; + bcm_gpio_writel(val, GPIO_PINMUX_OTHR_REG); + } + + val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG); + if (is_device) { + val |= (portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT); + val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT); + } else { + val &= ~(portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT); + val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT); + } + bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG); + + val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG); + if (is_device) + val |= USBH_PRIV_SWAP_USBD_MASK; + else + val &= ~USBH_PRIV_SWAP_USBD_MASK; + bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG); +} + +/** + * bcm63xx_select_pullup - Enable/disable the pullup on D+ + * @udc: Reference to the device controller. + * @is_on: true to enable the pullup, false to disable. + * + * If the pullup is active, the host will sense a FS/HS device connected to + * the port. If the pullup is inactive, the host will think the USB + * device has been disconnected. + */ +static void bcm63xx_select_pullup(struct bcm63xx_udc *udc, bool is_on) +{ + u32 val, portmask = BIT(udc->pd->port_no); + + val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG); + if (is_on) + val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT); + else + val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT); + bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG); +} + +/** + * bcm63xx_uninit_udc_hw - Shut down the hardware prior to driver removal. + * @udc: Reference to the device controller. + * + * This just masks the IUDMA IRQs and releases the clocks. It is assumed + * that bcm63xx_udc_stop() has already run, and the clocks are stopped. + */ +static void bcm63xx_uninit_udc_hw(struct bcm63xx_udc *udc) +{ + set_clocks(udc, true); + iudma_uninit(udc); + set_clocks(udc, false); + + clk_put(udc->usbd_clk); + clk_put(udc->usbh_clk); +} + +/** + * bcm63xx_init_udc_hw - Initialize the controller hardware and data structures. + * @udc: Reference to the device controller. + */ +static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc) +{ + int i, rc = 0; + u32 val; + + udc->ep0_ctrl_buf = devm_kzalloc(udc->dev, BCM63XX_MAX_CTRL_PKT, + GFP_KERNEL); + if (!udc->ep0_ctrl_buf) + return -ENOMEM; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + for (i = 0; i < BCM63XX_NUM_EP; i++) { + struct bcm63xx_ep *bep = &udc->bep[i]; + + bep->ep.name = bcm63xx_ep_name[i]; + bep->ep_num = i; + bep->ep.ops = &bcm63xx_udc_ep_ops; + list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list); + bep->halted = 0; + usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT); + bep->udc = udc; + bep->ep.desc = NULL; + INIT_LIST_HEAD(&bep->queue); + } + + udc->gadget.ep0 = &udc->bep[0].ep; + list_del(&udc->bep[0].ep.ep_list); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->ep0state = EP0_SHUTDOWN; + + udc->usbh_clk = clk_get(udc->dev, "usbh"); + if (IS_ERR(udc->usbh_clk)) + return -EIO; + + udc->usbd_clk = clk_get(udc->dev, "usbd"); + if (IS_ERR(udc->usbd_clk)) { + clk_put(udc->usbh_clk); + return -EIO; + } + + set_clocks(udc, true); + + val = USBD_CONTROL_AUTO_CSRS_MASK | + USBD_CONTROL_DONE_CSRS_MASK | + (irq_coalesce ? USBD_CONTROL_RXZSCFG_MASK : 0); + usbd_writel(udc, val, USBD_CONTROL_REG); + + val = USBD_STRAPS_APP_SELF_PWR_MASK | + USBD_STRAPS_APP_RAM_IF_MASK | + USBD_STRAPS_APP_CSRPRGSUP_MASK | + USBD_STRAPS_APP_8BITPHY_MASK | + USBD_STRAPS_APP_RMTWKUP_MASK; + + if (udc->gadget.max_speed == USB_SPEED_HIGH) + val |= (BCM63XX_SPD_HIGH << USBD_STRAPS_SPEED_SHIFT); + else + val |= (BCM63XX_SPD_FULL << USBD_STRAPS_SPEED_SHIFT); + usbd_writel(udc, val, USBD_STRAPS_REG); + + bcm63xx_set_ctrl_irqs(udc, false); + + usbd_writel(udc, 0, USBD_EVENT_IRQ_CFG_LO_REG); + + val = USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_ENUM_ON) | + USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_SET_CSRS); + usbd_writel(udc, val, USBD_EVENT_IRQ_CFG_HI_REG); + + rc = iudma_init(udc); + set_clocks(udc, false); + if (rc) + bcm63xx_uninit_udc_hw(udc); + + return 0; +} + +/*********************************************************************** + * Standard EP gadget operations + ***********************************************************************/ + +/** + * bcm63xx_ep_enable - Enable one endpoint. + * @ep: Endpoint to enable. + * @desc: Contains max packet, direction, etc. + * + * Most of the endpoint parameters are fixed in this controller, so there + * isn't much for this function to do. + */ +static int bcm63xx_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + struct iudma_ch *iudma = bep->iudma; + unsigned long flags; + + if (!ep || !desc || ep->name == bcm63xx_ep0name) + return -EINVAL; + + if (!udc->driver) + return -ESHUTDOWN; + + spin_lock_irqsave(&udc->lock, flags); + if (iudma->enabled) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + + iudma->enabled = true; + BUG_ON(!list_empty(&bep->queue)); + + iudma_reset_channel(udc, iudma); + + bep->halted = 0; + bcm63xx_set_stall(udc, bep, false); + clear_bit(bep->ep_num, &udc->wedgemap); + + ep->desc = desc; + ep->maxpacket = usb_endpoint_maxp(desc); + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * bcm63xx_ep_disable - Disable one endpoint. + * @ep: Endpoint to disable. + */ +static int bcm63xx_ep_disable(struct usb_ep *ep) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + struct iudma_ch *iudma = bep->iudma; + struct list_head *pos, *n; + unsigned long flags; + + if (!ep || !ep->desc) + return -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (!iudma->enabled) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + iudma->enabled = false; + + iudma_reset_channel(udc, iudma); + + if (!list_empty(&bep->queue)) { + list_for_each_safe(pos, n, &bep->queue) { + struct bcm63xx_req *breq = + list_entry(pos, struct bcm63xx_req, queue); + + usb_gadget_unmap_request(&udc->gadget, &breq->req, + iudma->is_tx); + list_del(&breq->queue); + breq->req.status = -ESHUTDOWN; + + spin_unlock_irqrestore(&udc->lock, flags); + breq->req.complete(&iudma->bep->ep, &breq->req); + spin_lock_irqsave(&udc->lock, flags); + } + } + ep->desc = NULL; + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * bcm63xx_udc_alloc_request - Allocate a new request. + * @ep: Endpoint associated with the request. + * @mem_flags: Flags to pass to kzalloc(). + */ +static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep, + gfp_t mem_flags) +{ + struct bcm63xx_req *breq; + + breq = kzalloc(sizeof(*breq), mem_flags); + if (!breq) + return NULL; + return &breq->req; +} + +/** + * bcm63xx_udc_free_request - Free a request. + * @ep: Endpoint associated with the request. + * @req: Request to free. + */ +static void bcm63xx_udc_free_request(struct usb_ep *ep, + struct usb_request *req) +{ + struct bcm63xx_req *breq = our_req(req); + kfree(breq); +} + +/** + * bcm63xx_udc_queue - Queue up a new request. + * @ep: Endpoint associated with the request. + * @req: Request to add. + * @mem_flags: Unused. + * + * If the queue is empty, start this request immediately. Otherwise, add + * it to the list. + * + * ep0 replies are sent through this function from the gadget driver, but + * they are treated differently because they need to be handled by the ep0 + * state machine. (Sometimes they are replies to control requests that + * were spoofed by this driver, and so they shouldn't be transmitted at all.) + */ +static int bcm63xx_udc_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t mem_flags) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + struct bcm63xx_req *breq = our_req(req); + unsigned long flags; + int rc = 0; + + if (unlikely(!req || !req->complete || !req->buf || !ep)) + return -EINVAL; + + req->actual = 0; + req->status = 0; + breq->offset = 0; + + if (bep == &udc->bep[0]) { + /* only one reply per request, please */ + if (udc->ep0_reply) + return -EINVAL; + + udc->ep0_reply = req; + schedule_work(&udc->ep0_wq); + return 0; + } + + spin_lock_irqsave(&udc->lock, flags); + if (!bep->iudma->enabled) { + rc = -ESHUTDOWN; + goto out; + } + + rc = usb_gadget_map_request(&udc->gadget, req, bep->iudma->is_tx); + if (rc == 0) { + list_add_tail(&breq->queue, &bep->queue); + if (list_is_singular(&bep->queue)) + iudma_write(udc, bep->iudma, breq); + } + +out: + spin_unlock_irqrestore(&udc->lock, flags); + return rc; +} + +/** + * bcm63xx_udc_dequeue - Remove a pending request from the queue. + * @ep: Endpoint associated with the request. + * @req: Request to remove. + * + * If the request is not at the head of the queue, this is easy - just nuke + * it. If the request is at the head of the queue, we'll need to stop the + * DMA transaction and then queue up the successor. + */ +static int bcm63xx_udc_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + struct bcm63xx_req *breq = our_req(req), *cur; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&udc->lock, flags); + if (list_empty(&bep->queue)) { + rc = -EINVAL; + goto out; + } + + cur = list_first_entry(&bep->queue, struct bcm63xx_req, queue); + usb_gadget_unmap_request(&udc->gadget, &breq->req, bep->iudma->is_tx); + + if (breq == cur) { + iudma_reset_channel(udc, bep->iudma); + list_del(&breq->queue); + + if (!list_empty(&bep->queue)) { + struct bcm63xx_req *next; + + next = list_first_entry(&bep->queue, + struct bcm63xx_req, queue); + iudma_write(udc, bep->iudma, next); + } + } else { + list_del(&breq->queue); + } + +out: + spin_unlock_irqrestore(&udc->lock, flags); + + req->status = -ESHUTDOWN; + req->complete(ep, req); + + return rc; +} + +/** + * bcm63xx_udc_set_halt - Enable/disable STALL flag in the hardware. + * @ep: Endpoint to halt. + * @value: Zero to clear halt; nonzero to set halt. + * + * See comments in bcm63xx_update_wedge(). + */ +static int bcm63xx_udc_set_halt(struct usb_ep *ep, int value) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + bcm63xx_set_stall(udc, bep, !!value); + bep->halted = value; + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * bcm63xx_udc_set_wedge - Stall the endpoint until the next reset. + * @ep: Endpoint to wedge. + * + * See comments in bcm63xx_update_wedge(). + */ +static int bcm63xx_udc_set_wedge(struct usb_ep *ep) +{ + struct bcm63xx_ep *bep = our_ep(ep); + struct bcm63xx_udc *udc = bep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + set_bit(bep->ep_num, &udc->wedgemap); + bcm63xx_set_stall(udc, bep, true); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_ep_ops bcm63xx_udc_ep_ops = { + .enable = bcm63xx_ep_enable, + .disable = bcm63xx_ep_disable, + + .alloc_request = bcm63xx_udc_alloc_request, + .free_request = bcm63xx_udc_free_request, + + .queue = bcm63xx_udc_queue, + .dequeue = bcm63xx_udc_dequeue, + + .set_halt = bcm63xx_udc_set_halt, + .set_wedge = bcm63xx_udc_set_wedge, +}; + +/*********************************************************************** + * EP0 handling + ***********************************************************************/ + +/** + * bcm63xx_ep0_setup_callback - Drop spinlock to invoke ->setup callback. + * @udc: Reference to the device controller. + * @ctrl: 8-byte SETUP request. + */ +static int bcm63xx_ep0_setup_callback(struct bcm63xx_udc *udc, + struct usb_ctrlrequest *ctrl) +{ + int rc; + + spin_unlock_irq(&udc->lock); + rc = udc->driver->setup(&udc->gadget, ctrl); + spin_lock_irq(&udc->lock); + return rc; +} + +/** + * bcm63xx_ep0_spoof_set_cfg - Synthesize a SET_CONFIGURATION request. + * @udc: Reference to the device controller. + * + * Many standard requests are handled automatically in the hardware, but + * we still need to pass them to the gadget driver so that it can + * reconfigure the interfaces/endpoints if necessary. + * + * Unfortunately we are not able to send a STALL response if the host + * requests an invalid configuration. If this happens, we'll have to be + * content with printing a warning. + */ +static int bcm63xx_ep0_spoof_set_cfg(struct bcm63xx_udc *udc) +{ + struct usb_ctrlrequest ctrl; + int rc; + + ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_SET_CONFIGURATION; + ctrl.wValue = cpu_to_le16(udc->cfg); + ctrl.wIndex = 0; + ctrl.wLength = 0; + + rc = bcm63xx_ep0_setup_callback(udc, &ctrl); + if (rc < 0) { + dev_warn_ratelimited(udc->dev, + "hardware auto-acked bad SET_CONFIGURATION(%d) request\n", + udc->cfg); + } + return rc; +} + +/** + * bcm63xx_ep0_spoof_set_iface - Synthesize a SET_INTERFACE request. + * @udc: Reference to the device controller. + */ +static int bcm63xx_ep0_spoof_set_iface(struct bcm63xx_udc *udc) +{ + struct usb_ctrlrequest ctrl; + int rc; + + ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_INTERFACE; + ctrl.bRequest = USB_REQ_SET_INTERFACE; + ctrl.wValue = cpu_to_le16(udc->alt_iface); + ctrl.wIndex = cpu_to_le16(udc->iface); + ctrl.wLength = 0; + + rc = bcm63xx_ep0_setup_callback(udc, &ctrl); + if (rc < 0) { + dev_warn_ratelimited(udc->dev, + "hardware auto-acked bad SET_INTERFACE(%d,%d) request\n", + udc->iface, udc->alt_iface); + } + return rc; +} + +/** + * bcm63xx_ep0_map_write - dma_map and iudma_write a single request. + * @udc: Reference to the device controller. + * @ch_idx: IUDMA channel number. + * @req: USB gadget layer representation of the request. + */ +static void bcm63xx_ep0_map_write(struct bcm63xx_udc *udc, int ch_idx, + struct usb_request *req) +{ + struct bcm63xx_req *breq = our_req(req); + struct iudma_ch *iudma = &udc->iudma[ch_idx]; + + BUG_ON(udc->ep0_request); + udc->ep0_request = req; + + req->actual = 0; + breq->offset = 0; + usb_gadget_map_request(&udc->gadget, req, iudma->is_tx); + iudma_write(udc, iudma, breq); +} + +/** + * bcm63xx_ep0_complete - Set completion status and "stage" the callback. + * @udc: Reference to the device controller. + * @req: USB gadget layer representation of the request. + * @status: Status to return to the gadget driver. + */ +static void bcm63xx_ep0_complete(struct bcm63xx_udc *udc, + struct usb_request *req, int status) +{ + req->status = status; + if (status) + req->actual = 0; + if (req->complete) { + spin_unlock_irq(&udc->lock); + req->complete(&udc->bep[0].ep, req); + spin_lock_irq(&udc->lock); + } +} + +/** + * bcm63xx_ep0_nuke_reply - Abort request from the gadget driver due to + * reset/shutdown. + * @udc: Reference to the device controller. + * @is_tx: Nonzero for TX (IN), zero for RX (OUT). + */ +static void bcm63xx_ep0_nuke_reply(struct bcm63xx_udc *udc, int is_tx) +{ + struct usb_request *req = udc->ep0_reply; + + udc->ep0_reply = NULL; + usb_gadget_unmap_request(&udc->gadget, req, is_tx); + if (udc->ep0_request == req) { + udc->ep0_req_completed = 0; + udc->ep0_request = NULL; + } + bcm63xx_ep0_complete(udc, req, -ESHUTDOWN); +} + +/** + * bcm63xx_ep0_read_complete - Close out the pending ep0 request; return + * transfer len. + * @udc: Reference to the device controller. + */ +static int bcm63xx_ep0_read_complete(struct bcm63xx_udc *udc) +{ + struct usb_request *req = udc->ep0_request; + + udc->ep0_req_completed = 0; + udc->ep0_request = NULL; + + return req->actual; +} + +/** + * bcm63xx_ep0_internal_request - Helper function to submit an ep0 request. + * @udc: Reference to the device controller. + * @ch_idx: IUDMA channel number. + * @length: Number of bytes to TX/RX. + * + * Used for simple transfers performed by the ep0 worker. This will always + * use ep0_ctrl_req / ep0_ctrl_buf. + */ +static void bcm63xx_ep0_internal_request(struct bcm63xx_udc *udc, int ch_idx, + int length) +{ + struct usb_request *req = &udc->ep0_ctrl_req.req; + + req->buf = udc->ep0_ctrl_buf; + req->length = length; + req->complete = NULL; + + bcm63xx_ep0_map_write(udc, ch_idx, req); +} + +/** + * bcm63xx_ep0_do_setup - Parse new SETUP packet and decide how to handle it. + * @udc: Reference to the device controller. + * + * EP0_IDLE probably shouldn't ever happen. EP0_REQUEUE means we're ready + * for the next packet. Anything else means the transaction requires multiple + * stages of handling. + */ +static enum bcm63xx_ep0_state bcm63xx_ep0_do_setup(struct bcm63xx_udc *udc) +{ + int rc; + struct usb_ctrlrequest *ctrl = (void *)udc->ep0_ctrl_buf; + + rc = bcm63xx_ep0_read_complete(udc); + + if (rc < 0) { + dev_err(udc->dev, "missing SETUP packet\n"); + return EP0_IDLE; + } + + /* + * Handle 0-byte IN STATUS acknowledgement. The hardware doesn't + * ALWAYS deliver these 100% of the time, so if we happen to see one, + * just throw it away. + */ + if (rc == 0) + return EP0_REQUEUE; + + /* Drop malformed SETUP packets */ + if (rc != sizeof(*ctrl)) { + dev_warn_ratelimited(udc->dev, + "malformed SETUP packet (%d bytes)\n", rc); + return EP0_REQUEUE; + } + + /* Process new SETUP packet arriving on ep0 */ + rc = bcm63xx_ep0_setup_callback(udc, ctrl); + if (rc < 0) { + bcm63xx_set_stall(udc, &udc->bep[0], true); + return EP0_REQUEUE; + } + + if (!ctrl->wLength) + return EP0_REQUEUE; + else if (ctrl->bRequestType & USB_DIR_IN) + return EP0_IN_DATA_PHASE_SETUP; + else + return EP0_OUT_DATA_PHASE_SETUP; +} + +/** + * bcm63xx_ep0_do_idle - Check for outstanding requests if ep0 is idle. + * @udc: Reference to the device controller. + * + * In state EP0_IDLE, the RX descriptor is either pending, or has been + * filled with a SETUP packet from the host. This function handles new + * SETUP packets, control IRQ events (which can generate fake SETUP packets), + * and reset/shutdown events. + * + * Returns 0 if work was done; -EAGAIN if nothing to do. + */ +static int bcm63xx_ep0_do_idle(struct bcm63xx_udc *udc) +{ + if (udc->ep0_req_reset) { + udc->ep0_req_reset = 0; + } else if (udc->ep0_req_set_cfg) { + udc->ep0_req_set_cfg = 0; + if (bcm63xx_ep0_spoof_set_cfg(udc) >= 0) + udc->ep0state = EP0_IN_FAKE_STATUS_PHASE; + } else if (udc->ep0_req_set_iface) { + udc->ep0_req_set_iface = 0; + if (bcm63xx_ep0_spoof_set_iface(udc) >= 0) + udc->ep0state = EP0_IN_FAKE_STATUS_PHASE; + } else if (udc->ep0_req_completed) { + udc->ep0state = bcm63xx_ep0_do_setup(udc); + return udc->ep0state == EP0_IDLE ? -EAGAIN : 0; + } else if (udc->ep0_req_shutdown) { + udc->ep0_req_shutdown = 0; + udc->ep0_req_completed = 0; + udc->ep0_request = NULL; + iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]); + usb_gadget_unmap_request(&udc->gadget, + &udc->ep0_ctrl_req.req, 0); + + /* bcm63xx_udc_pullup() is waiting for this */ + mb(); + udc->ep0state = EP0_SHUTDOWN; + } else if (udc->ep0_reply) { + /* + * This could happen if a USB RESET shows up during an ep0 + * transaction (especially if a laggy driver like gadgetfs + * is in use). + */ + dev_warn(udc->dev, "nuking unexpected reply\n"); + bcm63xx_ep0_nuke_reply(udc, 0); + } else { + return -EAGAIN; + } + + return 0; +} + +/** + * bcm63xx_ep0_one_round - Handle the current ep0 state. + * @udc: Reference to the device controller. + * + * Returns 0 if work was done; -EAGAIN if nothing to do. + */ +static int bcm63xx_ep0_one_round(struct bcm63xx_udc *udc) +{ + enum bcm63xx_ep0_state ep0state = udc->ep0state; + bool shutdown = udc->ep0_req_reset || udc->ep0_req_shutdown; + + switch (udc->ep0state) { + case EP0_REQUEUE: + /* set up descriptor to receive SETUP packet */ + bcm63xx_ep0_internal_request(udc, IUDMA_EP0_RXCHAN, + BCM63XX_MAX_CTRL_PKT); + ep0state = EP0_IDLE; + break; + case EP0_IDLE: + return bcm63xx_ep0_do_idle(udc); + case EP0_IN_DATA_PHASE_SETUP: + /* + * Normal case: TX request is in ep0_reply (queued by the + * callback), or will be queued shortly. When it's here, + * send it to the HW and go to EP0_IN_DATA_PHASE_COMPLETE. + * + * Shutdown case: Stop waiting for the reply. Just + * REQUEUE->IDLE. The gadget driver is NOT expected to + * queue anything else now. + */ + if (udc->ep0_reply) { + bcm63xx_ep0_map_write(udc, IUDMA_EP0_TXCHAN, + udc->ep0_reply); + ep0state = EP0_IN_DATA_PHASE_COMPLETE; + } else if (shutdown) { + ep0state = EP0_REQUEUE; + } + break; + case EP0_IN_DATA_PHASE_COMPLETE: { + /* + * Normal case: TX packet (ep0_reply) is in flight; wait for + * it to finish, then go back to REQUEUE->IDLE. + * + * Shutdown case: Reset the TX channel, send -ESHUTDOWN + * completion to the gadget driver, then REQUEUE->IDLE. + */ + if (udc->ep0_req_completed) { + udc->ep0_reply = NULL; + bcm63xx_ep0_read_complete(udc); + /* + * the "ack" sometimes gets eaten (see + * bcm63xx_ep0_do_idle) + */ + ep0state = EP0_REQUEUE; + } else if (shutdown) { + iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]); + bcm63xx_ep0_nuke_reply(udc, 1); + ep0state = EP0_REQUEUE; + } + break; + } + case EP0_OUT_DATA_PHASE_SETUP: + /* Similar behavior to EP0_IN_DATA_PHASE_SETUP */ + if (udc->ep0_reply) { + bcm63xx_ep0_map_write(udc, IUDMA_EP0_RXCHAN, + udc->ep0_reply); + ep0state = EP0_OUT_DATA_PHASE_COMPLETE; + } else if (shutdown) { + ep0state = EP0_REQUEUE; + } + break; + case EP0_OUT_DATA_PHASE_COMPLETE: { + /* Similar behavior to EP0_IN_DATA_PHASE_COMPLETE */ + if (udc->ep0_req_completed) { + udc->ep0_reply = NULL; + bcm63xx_ep0_read_complete(udc); + + /* send 0-byte ack to host */ + bcm63xx_ep0_internal_request(udc, IUDMA_EP0_TXCHAN, 0); + ep0state = EP0_OUT_STATUS_PHASE; + } else if (shutdown) { + iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]); + bcm63xx_ep0_nuke_reply(udc, 0); + ep0state = EP0_REQUEUE; + } + break; + } + case EP0_OUT_STATUS_PHASE: + /* + * Normal case: 0-byte OUT ack packet is in flight; wait + * for it to finish, then go back to REQUEUE->IDLE. + * + * Shutdown case: just cancel the transmission. Don't bother + * calling the completion, because it originated from this + * function anyway. Then go back to REQUEUE->IDLE. + */ + if (udc->ep0_req_completed) { + bcm63xx_ep0_read_complete(udc); + ep0state = EP0_REQUEUE; + } else if (shutdown) { + iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]); + udc->ep0_request = NULL; + ep0state = EP0_REQUEUE; + } + break; + case EP0_IN_FAKE_STATUS_PHASE: { + /* + * Normal case: we spoofed a SETUP packet and are now + * waiting for the gadget driver to send a 0-byte reply. + * This doesn't actually get sent to the HW because the + * HW has already sent its own reply. Once we get the + * response, return to IDLE. + * + * Shutdown case: return to IDLE immediately. + * + * Note that the ep0 RX descriptor has remained queued + * (and possibly unfilled) during this entire transaction. + * The HW datapath (IUDMA) never even sees SET_CONFIGURATION + * or SET_INTERFACE transactions. + */ + struct usb_request *r = udc->ep0_reply; + + if (!r) { + if (shutdown) + ep0state = EP0_IDLE; + break; + } + + bcm63xx_ep0_complete(udc, r, 0); + udc->ep0_reply = NULL; + ep0state = EP0_IDLE; + break; + } + case EP0_SHUTDOWN: + break; + } + + if (udc->ep0state == ep0state) + return -EAGAIN; + + udc->ep0state = ep0state; + return 0; +} + +/** + * bcm63xx_ep0_process - ep0 worker thread / state machine. + * @w: Workqueue struct. + * + * bcm63xx_ep0_process is triggered any time an event occurs on ep0. It + * is used to synchronize ep0 events and ensure that both HW and SW events + * occur in a well-defined order. When the ep0 IUDMA queues are idle, it may + * synthesize SET_CONFIGURATION / SET_INTERFACE requests that were consumed + * by the USBD hardware. + * + * The worker function will continue iterating around the state machine + * until there is nothing left to do. Usually "nothing left to do" means + * that we're waiting for a new event from the hardware. + */ +static void bcm63xx_ep0_process(struct work_struct *w) +{ + struct bcm63xx_udc *udc = container_of(w, struct bcm63xx_udc, ep0_wq); + spin_lock_irq(&udc->lock); + while (bcm63xx_ep0_one_round(udc) == 0) + ; + spin_unlock_irq(&udc->lock); +} + +/*********************************************************************** + * Standard UDC gadget operations + ***********************************************************************/ + +/** + * bcm63xx_udc_get_frame - Read current SOF frame number from the HW. + * @gadget: USB slave device. + */ +static int bcm63xx_udc_get_frame(struct usb_gadget *gadget) +{ + struct bcm63xx_udc *udc = gadget_to_udc(gadget); + + return (usbd_readl(udc, USBD_STATUS_REG) & + USBD_STATUS_SOF_MASK) >> USBD_STATUS_SOF_SHIFT; +} + +/** + * bcm63xx_udc_pullup - Enable/disable pullup on D+ line. + * @gadget: USB slave device. + * @is_on: 0 to disable pullup, 1 to enable. + * + * See notes in bcm63xx_select_pullup(). + */ +static int bcm63xx_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct bcm63xx_udc *udc = gadget_to_udc(gadget); + unsigned long flags; + int i, rc = -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (is_on && udc->ep0state == EP0_SHUTDOWN) { + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->ep0state = EP0_REQUEUE; + bcm63xx_fifo_setup(udc); + bcm63xx_fifo_reset(udc); + bcm63xx_ep_setup(udc); + + bitmap_zero(&udc->wedgemap, BCM63XX_NUM_EP); + for (i = 0; i < BCM63XX_NUM_EP; i++) + bcm63xx_set_stall(udc, &udc->bep[i], false); + + bcm63xx_set_ctrl_irqs(udc, true); + bcm63xx_select_pullup(gadget_to_udc(gadget), true); + rc = 0; + } else if (!is_on && udc->ep0state != EP0_SHUTDOWN) { + bcm63xx_select_pullup(gadget_to_udc(gadget), false); + + udc->ep0_req_shutdown = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + while (1) { + schedule_work(&udc->ep0_wq); + if (udc->ep0state == EP0_SHUTDOWN) + break; + msleep(50); + } + bcm63xx_set_ctrl_irqs(udc, false); + cancel_work_sync(&udc->ep0_wq); + return 0; + } + + spin_unlock_irqrestore(&udc->lock, flags); + return rc; +} + +/** + * bcm63xx_udc_start - Start the controller. + * @gadget: USB slave device. + * @driver: Driver for USB slave devices. + */ +static int bcm63xx_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct bcm63xx_udc *udc = gadget_to_udc(gadget); + unsigned long flags; + + if (!driver || driver->max_speed < USB_SPEED_HIGH || + !driver->setup) + return -EINVAL; + if (!udc) + return -ENODEV; + if (udc->driver) + return -EBUSY; + + spin_lock_irqsave(&udc->lock, flags); + + set_clocks(udc, true); + bcm63xx_fifo_setup(udc); + bcm63xx_ep_init(udc); + bcm63xx_ep_setup(udc); + bcm63xx_fifo_reset(udc); + bcm63xx_select_phy_mode(udc, true); + + udc->driver = driver; + driver->driver.bus = NULL; + udc->gadget.dev.of_node = udc->dev->of_node; + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * bcm63xx_udc_stop - Shut down the controller. + * @gadget: USB slave device. + * @driver: Driver for USB slave devices. + */ +static int bcm63xx_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct bcm63xx_udc *udc = gadget_to_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + udc->driver = NULL; + + /* + * If we switch the PHY too abruptly after dropping D+, the host + * will often complain: + * + * hub 1-0:1.0: port 1 disabled by hub (EMI?), re-enabling... + */ + msleep(100); + + bcm63xx_select_phy_mode(udc, false); + set_clocks(udc, false); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops bcm63xx_udc_ops = { + .get_frame = bcm63xx_udc_get_frame, + .pullup = bcm63xx_udc_pullup, + .udc_start = bcm63xx_udc_start, + .udc_stop = bcm63xx_udc_stop, +}; + +/*********************************************************************** + * IRQ handling + ***********************************************************************/ + +/** + * bcm63xx_update_cfg_iface - Read current configuration/interface settings. + * @udc: Reference to the device controller. + * + * This controller intercepts SET_CONFIGURATION and SET_INTERFACE messages. + * The driver never sees the raw control packets coming in on the ep0 + * IUDMA channel, but at least we get an interrupt event to tell us that + * new values are waiting in the USBD_STATUS register. + */ +static void bcm63xx_update_cfg_iface(struct bcm63xx_udc *udc) +{ + u32 reg = usbd_readl(udc, USBD_STATUS_REG); + + udc->cfg = (reg & USBD_STATUS_CFG_MASK) >> USBD_STATUS_CFG_SHIFT; + udc->iface = (reg & USBD_STATUS_INTF_MASK) >> USBD_STATUS_INTF_SHIFT; + udc->alt_iface = (reg & USBD_STATUS_ALTINTF_MASK) >> + USBD_STATUS_ALTINTF_SHIFT; + bcm63xx_ep_setup(udc); +} + +/** + * bcm63xx_update_link_speed - Check to see if the link speed has changed. + * @udc: Reference to the device controller. + * + * The link speed update coincides with a SETUP IRQ. Returns 1 if the + * speed has changed, so that the caller can update the endpoint settings. + */ +static int bcm63xx_update_link_speed(struct bcm63xx_udc *udc) +{ + u32 reg = usbd_readl(udc, USBD_STATUS_REG); + enum usb_device_speed oldspeed = udc->gadget.speed; + + switch ((reg & USBD_STATUS_SPD_MASK) >> USBD_STATUS_SPD_SHIFT) { + case BCM63XX_SPD_HIGH: + udc->gadget.speed = USB_SPEED_HIGH; + break; + case BCM63XX_SPD_FULL: + udc->gadget.speed = USB_SPEED_FULL; + break; + default: + /* this should never happen */ + udc->gadget.speed = USB_SPEED_UNKNOWN; + dev_err(udc->dev, + "received SETUP packet with invalid link speed\n"); + return 0; + } + + if (udc->gadget.speed != oldspeed) { + dev_info(udc->dev, "link up, %s-speed mode\n", + udc->gadget.speed == USB_SPEED_HIGH ? "high" : "full"); + return 1; + } else { + return 0; + } +} + +/** + * bcm63xx_update_wedge - Iterate through wedged endpoints. + * @udc: Reference to the device controller. + * @new_status: true to "refresh" wedge status; false to clear it. + * + * On a SETUP interrupt, we need to manually "refresh" the wedge status + * because the controller hardware is designed to automatically clear + * stalls in response to a CLEAR_FEATURE request from the host. + * + * On a RESET interrupt, we do want to restore all wedged endpoints. + */ +static void bcm63xx_update_wedge(struct bcm63xx_udc *udc, bool new_status) +{ + int i; + + for_each_set_bit(i, &udc->wedgemap, BCM63XX_NUM_EP) { + bcm63xx_set_stall(udc, &udc->bep[i], new_status); + if (!new_status) + clear_bit(i, &udc->wedgemap); + } +} + +/** + * bcm63xx_udc_ctrl_isr - ISR for control path events (USBD). + * @irq: IRQ number (unused). + * @dev_id: Reference to the device controller. + * + * This is where we handle link (VBUS) down, USB reset, speed changes, + * SET_CONFIGURATION, and SET_INTERFACE events. + */ +static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id) +{ + struct bcm63xx_udc *udc = dev_id; + u32 stat; + bool disconnected = false; + + stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) & + usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG); + + usbd_writel(udc, stat, USBD_EVENT_IRQ_STATUS_REG); + + spin_lock(&udc->lock); + if (stat & BIT(USBD_EVENT_IRQ_USB_LINK)) { + /* VBUS toggled */ + + if (!(usbd_readl(udc, USBD_EVENTS_REG) & + USBD_EVENTS_USB_LINK_MASK) && + udc->gadget.speed != USB_SPEED_UNKNOWN) + dev_info(udc->dev, "link down\n"); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + disconnected = true; + } + if (stat & BIT(USBD_EVENT_IRQ_USB_RESET)) { + bcm63xx_fifo_setup(udc); + bcm63xx_fifo_reset(udc); + bcm63xx_ep_setup(udc); + + bcm63xx_update_wedge(udc, false); + + udc->ep0_req_reset = 1; + schedule_work(&udc->ep0_wq); + disconnected = true; + } + if (stat & BIT(USBD_EVENT_IRQ_SETUP)) { + if (bcm63xx_update_link_speed(udc)) { + bcm63xx_fifo_setup(udc); + bcm63xx_ep_setup(udc); + } + bcm63xx_update_wedge(udc, true); + } + if (stat & BIT(USBD_EVENT_IRQ_SETCFG)) { + bcm63xx_update_cfg_iface(udc); + udc->ep0_req_set_cfg = 1; + schedule_work(&udc->ep0_wq); + } + if (stat & BIT(USBD_EVENT_IRQ_SETINTF)) { + bcm63xx_update_cfg_iface(udc); + udc->ep0_req_set_iface = 1; + schedule_work(&udc->ep0_wq); + } + spin_unlock(&udc->lock); + + if (disconnected && udc->driver) + udc->driver->disconnect(&udc->gadget); + + return IRQ_HANDLED; +} + +/** + * bcm63xx_udc_data_isr - ISR for data path events (IUDMA). + * @irq: IRQ number (unused). + * @dev_id: Reference to the IUDMA channel that generated the interrupt. + * + * For the two ep0 channels, we have special handling that triggers the + * ep0 worker thread. For normal bulk/intr channels, either queue up + * the next buffer descriptor for the transaction (incomplete transaction), + * or invoke the completion callback (complete transactions). + */ +static irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id) +{ + struct iudma_ch *iudma = dev_id; + struct bcm63xx_udc *udc = iudma->udc; + struct bcm63xx_ep *bep; + struct usb_request *req = NULL; + struct bcm63xx_req *breq = NULL; + int rc; + bool is_done = false; + + spin_lock(&udc->lock); + + usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK, + ENETDMAC_IR_REG, iudma->ch_idx); + bep = iudma->bep; + rc = iudma_read(udc, iudma); + + /* special handling for EP0 RX (0) and TX (1) */ + if (iudma->ch_idx == IUDMA_EP0_RXCHAN || + iudma->ch_idx == IUDMA_EP0_TXCHAN) { + req = udc->ep0_request; + breq = our_req(req); + + /* a single request could require multiple submissions */ + if (rc >= 0) { + req->actual += rc; + + if (req->actual >= req->length || breq->bd_bytes > rc) { + udc->ep0_req_completed = 1; + is_done = true; + schedule_work(&udc->ep0_wq); + + /* "actual" on a ZLP is 1 byte */ + req->actual = min(req->actual, req->length); + } else { + /* queue up the next BD (same request) */ + iudma_write(udc, iudma, breq); + } + } + } else if (!list_empty(&bep->queue)) { + breq = list_first_entry(&bep->queue, struct bcm63xx_req, queue); + req = &breq->req; + + if (rc >= 0) { + req->actual += rc; + + if (req->actual >= req->length || breq->bd_bytes > rc) { + is_done = true; + list_del(&breq->queue); + + req->actual = min(req->actual, req->length); + + if (!list_empty(&bep->queue)) { + struct bcm63xx_req *next; + + next = list_first_entry(&bep->queue, + struct bcm63xx_req, queue); + iudma_write(udc, iudma, next); + } + } else { + iudma_write(udc, iudma, breq); + } + } + } + spin_unlock(&udc->lock); + + if (is_done) { + usb_gadget_unmap_request(&udc->gadget, req, iudma->is_tx); + if (req->complete) + req->complete(&bep->ep, req); + } + + return IRQ_HANDLED; +} + +/*********************************************************************** + * Debug filesystem + ***********************************************************************/ + +/* + * bcm63xx_usbd_dbg_show - Show USBD controller state. + * @s: seq_file to which the information will be written. + * @p: Unused. + * + * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/usbd + */ +static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p) +{ + struct bcm63xx_udc *udc = s->private; + + if (!udc->driver) + return -ENODEV; + + seq_printf(s, "ep0 state: %s\n", + bcm63xx_ep0_state_names[udc->ep0state]); + seq_printf(s, " pending requests: %s%s%s%s%s%s%s\n", + udc->ep0_req_reset ? "reset " : "", + udc->ep0_req_set_cfg ? "set_cfg " : "", + udc->ep0_req_set_iface ? "set_iface " : "", + udc->ep0_req_shutdown ? "shutdown " : "", + udc->ep0_request ? "pending " : "", + udc->ep0_req_completed ? "completed " : "", + udc->ep0_reply ? "reply " : ""); + seq_printf(s, "cfg: %d; iface: %d; alt_iface: %d\n", + udc->cfg, udc->iface, udc->alt_iface); + seq_printf(s, "regs:\n"); + seq_printf(s, " control: %08x; straps: %08x; status: %08x\n", + usbd_readl(udc, USBD_CONTROL_REG), + usbd_readl(udc, USBD_STRAPS_REG), + usbd_readl(udc, USBD_STATUS_REG)); + seq_printf(s, " events: %08x; stall: %08x\n", + usbd_readl(udc, USBD_EVENTS_REG), + usbd_readl(udc, USBD_STALL_REG)); + + return 0; +} + +/* + * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors. + * @s: seq_file to which the information will be written. + * @p: Unused. + * + * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/iudma + */ +static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p) +{ + struct bcm63xx_udc *udc = s->private; + int ch_idx, i; + u32 sram2, sram3; + + if (!udc->driver) + return -ENODEV; + + for (ch_idx = 0; ch_idx < BCM63XX_NUM_IUDMA; ch_idx++) { + struct iudma_ch *iudma = &udc->iudma[ch_idx]; + struct list_head *pos; + + seq_printf(s, "IUDMA channel %d -- ", ch_idx); + switch (iudma_defaults[ch_idx].ep_type) { + case BCMEP_CTRL: + seq_printf(s, "control"); + break; + case BCMEP_BULK: + seq_printf(s, "bulk"); + break; + case BCMEP_INTR: + seq_printf(s, "interrupt"); + break; + } + seq_printf(s, ch_idx & 0x01 ? " tx" : " rx"); + 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)); + + 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), + sram2 >> 16, sram2 & 0xffff, + sram3 >> 16, sram3 & 0xffff, + usb_dmas_readl(udc, ENETDMAS_SRAM4_REG, ch_idx)); + seq_printf(s, " desc: %d/%d used", iudma->n_bds_used, + iudma->n_bds); + + if (iudma->bep) { + i = 0; + list_for_each(pos, &iudma->bep->queue) + i++; + seq_printf(s, "; %d queued\n", i); + } else { + seq_printf(s, "\n"); + } + + for (i = 0; i < iudma->n_bds; i++) { + struct bcm_enet_desc *d = &iudma->bd_ring[i]; + + seq_printf(s, " %03x (%02x): len_stat: %04x_%04x; pa %08x", + i * sizeof(*d), i, + d->len_stat >> 16, d->len_stat & 0xffff, + d->address); + if (d == iudma->read_bd) + seq_printf(s, " <<RD"); + if (d == iudma->write_bd) + seq_printf(s, " <<WR"); + seq_printf(s, "\n"); + } + + seq_printf(s, "\n"); + } + + return 0; +} + +static int bcm63xx_usbd_dbg_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm63xx_usbd_dbg_show, inode->i_private); +} + +static int bcm63xx_iudma_dbg_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm63xx_iudma_dbg_show, inode->i_private); +} + +static const struct file_operations usbd_dbg_fops = { + .owner = THIS_MODULE, + .open = bcm63xx_usbd_dbg_open, + .llseek = seq_lseek, + .read = seq_read, + .release = single_release, +}; + +static const struct file_operations iudma_dbg_fops = { + .owner = THIS_MODULE, + .open = bcm63xx_iudma_dbg_open, + .llseek = seq_lseek, + .read = seq_read, + .release = single_release, +}; + + +/** + * bcm63xx_udc_init_debugfs - Create debugfs entries. + * @udc: Reference to the device controller. + */ +static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc) +{ + struct dentry *root, *usbd, *iudma; + + if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS)) + return; + + root = debugfs_create_dir(udc->gadget.name, NULL); + if (IS_ERR(root) || !root) + goto err_root; + + usbd = debugfs_create_file("usbd", 0400, root, udc, + &usbd_dbg_fops); + if (!usbd) + goto err_usbd; + iudma = debugfs_create_file("iudma", 0400, root, udc, + &iudma_dbg_fops); + if (!iudma) + goto err_iudma; + + udc->debugfs_root = root; + udc->debugfs_usbd = usbd; + udc->debugfs_iudma = iudma; + return; +err_iudma: + debugfs_remove(usbd); +err_usbd: + debugfs_remove(root); +err_root: + dev_err(udc->dev, "debugfs is not available\n"); +} + +/** + * bcm63xx_udc_cleanup_debugfs - Remove debugfs entries. + * @udc: Reference to the device controller. + * + * debugfs_remove() is safe to call with a NULL argument. + */ +static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc) +{ + debugfs_remove(udc->debugfs_iudma); + debugfs_remove(udc->debugfs_usbd); + debugfs_remove(udc->debugfs_root); + udc->debugfs_iudma = NULL; + udc->debugfs_usbd = NULL; + udc->debugfs_root = NULL; +} + +/*********************************************************************** + * Driver init/exit + ***********************************************************************/ + +/** + * bcm63xx_udc_probe - Initialize a new instance of the UDC. + * @pdev: Platform device struct from the bcm63xx BSP code. + * + * Note that platform data is required, because pd.port_no varies from chip + * to chip and is used to switch the correct USB port to device mode. + */ +static int bcm63xx_udc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); + struct bcm63xx_udc *udc; + struct resource *res; + int rc = -ENOMEM, i, irq; + + udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); + if (!udc) { + dev_err(dev, "cannot allocate memory\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, udc); + udc->dev = dev; + udc->pd = pd; + + if (!pd) { + dev_err(dev, "missing platform data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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); + 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); + + udc->gadget.ops = &bcm63xx_udc_ops; + udc->gadget.name = dev_name(dev); + + if (!pd->use_fullspeed && !use_fullspeed) + udc->gadget.max_speed = USB_SPEED_HIGH; + else + udc->gadget.max_speed = USB_SPEED_FULL; + + /* request clocks, allocate buffers, and clear any pending IRQs */ + rc = bcm63xx_init_udc_hw(udc); + if (rc) + return rc; + + rc = -ENXIO; + + /* IRQ resource #0: control interrupt (VBUS, speed, etc.) */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "missing IRQ resource #0\n"); + goto out_uninit; + } + if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0, + dev_name(dev), udc) < 0) { + dev_err(dev, "error requesting IRQ #%d\n", irq); + goto out_uninit; + } + + /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */ + for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { + irq = platform_get_irq(pdev, i + 1); + if (irq < 0) { + dev_err(dev, "missing IRQ resource #%d\n", i + 1); + goto out_uninit; + } + if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0, + dev_name(dev), &udc->iudma[i]) < 0) { + dev_err(dev, "error requesting IRQ #%d\n", irq); + 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); +out_uninit: + bcm63xx_uninit_udc_hw(udc); + return rc; +} + +/** + * bcm63xx_udc_remove - Remove the device from the system. + * @pdev: Platform device struct from the bcm63xx BSP code. + */ +static int bcm63xx_udc_remove(struct platform_device *pdev) +{ + struct bcm63xx_udc *udc = platform_get_drvdata(pdev); + + bcm63xx_udc_cleanup_debugfs(udc); + usb_del_gadget_udc(&udc->gadget); + BUG_ON(udc->driver); + + bcm63xx_uninit_udc_hw(udc); + + return 0; +} + +static struct platform_driver bcm63xx_udc_driver = { + .probe = bcm63xx_udc_probe, + .remove = bcm63xx_udc_remove, + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + }, +}; +module_platform_driver(bcm63xx_udc_driver); + +MODULE_DESCRIPTION("BCM63xx USB Peripheral Controller"); +MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_MODULE_NAME); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 928137d3dbd..e126b6b248e 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -8,22 +8,14 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> -#include <linux/utsname.h> +#include <linux/module.h> #include "u_ether.h" #include "u_serial.h" +#include "u_ecm.h" #define DRIVER_DESC "CDC Composite Gadget" @@ -41,24 +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 "composite.c" -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" -#include "u_serial.c" -#include "f_acm.c" -#include "f_ecm.c" -#include "u_ether.c" +USB_ETHERNET_MODULE_PARAMETERS(); /*-------------------------------------------------------------------------*/ @@ -100,15 +77,10 @@ static const struct usb_descriptor_header *otg_desc[] = { /* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -122,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. @@ -138,20 +113,39 @@ 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 = { .label = "CDC Composite (ECM + ACM)", - .bind = cdc_do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -161,8 +155,8 @@ static struct usb_configuration cdc_config_driver = { static int __init cdc_bind(struct usb_composite_dev *cdev) { - int gcnum; struct usb_gadget *gadget = cdev->gadget; + struct f_ecm_opts *ecm_opts; int status; if (!can_support_ecm(cdev->gadget)) { @@ -171,80 +165,68 @@ 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; - - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - WARNING(cdev, "controller '%s' not recognized; trying %s\n", - gadget->name, - cdc_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } + 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. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - status = usb_string_id(cdev); - if (status < 0) - goto fail1; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - device_desc.iManufacturer = status; - - status = usb_string_id(cdev); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail1; - strings_dev[STRING_PRODUCT_IDX].id = status; - device_desc.iProduct = status; + 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, &cdc_config_driver); + status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config); if (status < 0) goto fail1; + usb_composite_overwrite_options(cdev, &coverwrite); dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); 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; } -static struct usb_composite_driver cdc_driver = { +static __refdata struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .bind = cdc_bind, .unbind = __exit_p(cdc_unbind), }; @@ -255,7 +237,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(&cdc_driver); + return usb_composite_probe(&cdc_driver); } module_init(init); diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c deleted file mode 100644 index 38e531ecae4..00000000000 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ /dev/null @@ -1,2832 +0,0 @@ -/* - * ci13xxx_udc.c - MIPS USB IP core family device controller - * - * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. - * - * Author: David Lopo - * - * 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. - */ - -/* - * Description: MIPS USB IP core family device controller - * Currently it only supports IP part number CI13412 - * - * This driver is composed of several blocks: - * - HW: hardware interface - * - DBG: debug facilities (optional) - * - UTIL: utilities - * - ISR: interrupts handling - * - ENDPT: endpoint operations (Gadget API) - * - GADGET: gadget operations (Gadget API) - * - BUS: bus glue code, bus abstraction layer - * - PCI: PCI core interface and PCI resources (interrupts, memory...) - * - * Compile Options - * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities - * - STALL_IN: non-empty bulk-in pipes cannot be halted - * if defined mass storage compliance succeeds but with warnings - * => case 4: Hi > Dn - * => case 5: Hi > Di - * => case 8: Hi <> Do - * if undefined usbtest 13 fails - * - TRACE: enable function tracing (depends on DEBUG) - * - * Main Features - * - Chapter 9 & Mass Storage Compliance with Gadget File Storage - * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) - * - Normal & LPM support - * - * USBTEST Report - * - OK: 0-12, 13 (STALL_IN defined) & 14 - * - Not Supported: 15 & 16 (ISO) - * - * TODO List - * - OTG - * - Isochronous & Interrupt Traffic - * - Handle requests which spawns into several TDs - * - GET_STATUS(device) - always reports 0 - * - Gadget API (majority of optional features) - * - Suspend & Remote Wakeup - */ -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dmapool.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -#include "ci13xxx_udc.h" - - -/****************************************************************************** - * DEFINE - *****************************************************************************/ -/* ctrl register bank access */ -static DEFINE_SPINLOCK(udc_lock); - -/* driver name */ -#define UDC_DRIVER_NAME "ci13xxx_udc" - -/* control endpoint description */ -static const struct usb_endpoint_descriptor -ctrl_endpt_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), -}; - -/* UDC descriptor */ -static struct ci13xxx *_udc; - -/* Interrupt statistics */ -#define ISR_MASK 0x1F -static struct { - u32 test; - u32 ui; - u32 uei; - u32 pci; - u32 uri; - u32 sli; - u32 none; - struct { - u32 cnt; - u32 buf[ISR_MASK+1]; - u32 idx; - } hndl; -} isr_statistics; - -/** - * ffs_nr: find first (least significant) bit set - * @x: the word to search - * - * This function returns bit number (instead of position) - */ -static int ffs_nr(u32 x) -{ - int n = ffs(x); - - return n ? n-1 : 32; -} - -/****************************************************************************** - * HW block - *****************************************************************************/ -/* register bank descriptor */ -static struct { - unsigned lpm; /* is LPM? */ - void __iomem *abs; /* bus map offset */ - void __iomem *cap; /* bus map offset + CAP offset + CAP data */ - size_t size; /* bank size */ -} hw_bank; - -/* UDC register map */ -#define ABS_CAPLENGTH (0x100UL) -#define ABS_HCCPARAMS (0x108UL) -#define ABS_DCCPARAMS (0x124UL) -#define ABS_TESTMODE (hw_bank.lpm ? 0x0FCUL : 0x138UL) -/* offset to CAPLENTGH (addr + data) */ -#define CAP_USBCMD (0x000UL) -#define CAP_USBSTS (0x004UL) -#define CAP_USBINTR (0x008UL) -#define CAP_DEVICEADDR (0x014UL) -#define CAP_ENDPTLISTADDR (0x018UL) -#define CAP_PORTSC (0x044UL) -#define CAP_DEVLC (0x084UL) -#define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL) -#define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL) -#define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL) -#define CAP_ENDPTFLUSH (hw_bank.lpm ? 0x0E0UL : 0x074UL) -#define CAP_ENDPTSTAT (hw_bank.lpm ? 0x0E4UL : 0x078UL) -#define CAP_ENDPTCOMPLETE (hw_bank.lpm ? 0x0E8UL : 0x07CUL) -#define CAP_ENDPTCTRL (hw_bank.lpm ? 0x0ECUL : 0x080UL) -#define CAP_LAST (hw_bank.lpm ? 0x12CUL : 0x0C0UL) - -/* maximum number of enpoints: valid only after hw_device_reset() */ -static unsigned hw_ep_max; - -/** - * hw_ep_bit: calculates the bit number - * @num: endpoint number - * @dir: endpoint direction - * - * This function returns bit number - */ -static inline int hw_ep_bit(int num, int dir) -{ - return num + (dir ? 16 : 0); -} - -/** - * hw_aread: reads from register bitfield - * @addr: address relative to bus map - * @mask: bitfield mask - * - * This function returns register bitfield data - */ -static u32 hw_aread(u32 addr, u32 mask) -{ - return ioread32(addr + hw_bank.abs) & mask; -} - -/** - * hw_awrite: writes to register bitfield - * @addr: address relative to bus map - * @mask: bitfield mask - * @data: new data - */ -static void hw_awrite(u32 addr, u32 mask, u32 data) -{ - iowrite32(hw_aread(addr, ~mask) | (data & mask), - addr + hw_bank.abs); -} - -/** - * hw_cread: reads from register bitfield - * @addr: address relative to CAP offset plus content - * @mask: bitfield mask - * - * This function returns register bitfield data - */ -static u32 hw_cread(u32 addr, u32 mask) -{ - return ioread32(addr + hw_bank.cap) & mask; -} - -/** - * hw_cwrite: writes to register bitfield - * @addr: address relative to CAP offset plus content - * @mask: bitfield mask - * @data: new data - */ -static void hw_cwrite(u32 addr, u32 mask, u32 data) -{ - iowrite32(hw_cread(addr, ~mask) | (data & mask), - addr + hw_bank.cap); -} - -/** - * hw_ctest_and_clear: tests & clears register bitfield - * @addr: address relative to CAP offset plus content - * @mask: bitfield mask - * - * This function returns register bitfield data - */ -static u32 hw_ctest_and_clear(u32 addr, u32 mask) -{ - u32 reg = hw_cread(addr, mask); - - iowrite32(reg, addr + hw_bank.cap); - return reg; -} - -/** - * hw_ctest_and_write: tests & writes register bitfield - * @addr: address relative to CAP offset plus content - * @mask: bitfield mask - * @data: new data - * - * This function returns register bitfield data - */ -static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data) -{ - u32 reg = hw_cread(addr, ~0); - - iowrite32((reg & ~mask) | (data & mask), addr + hw_bank.cap); - return (reg & mask) >> ffs_nr(mask); -} - -/** - * hw_device_reset: resets chip (execute without interruption) - * @base: register base address - * - * This function returns an error code - */ -static int hw_device_reset(void __iomem *base) -{ - u32 reg; - - /* bank is a module variable */ - hw_bank.abs = base; - - hw_bank.cap = hw_bank.abs; - hw_bank.cap += ABS_CAPLENGTH; - hw_bank.cap += ioread8(hw_bank.cap); - - reg = hw_aread(ABS_HCCPARAMS, HCCPARAMS_LEN) >> ffs_nr(HCCPARAMS_LEN); - hw_bank.lpm = reg; - hw_bank.size = hw_bank.cap - hw_bank.abs; - hw_bank.size += CAP_LAST; - hw_bank.size /= sizeof(u32); - - /* should flush & stop before reset */ - hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); - hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); - - hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST); - while (hw_cread(CAP_USBCMD, USBCMD_RST)) - udelay(10); /* not RTOS friendly */ - - /* USBMODE should be configured step by step */ - hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); - hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); - hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */ - - if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) { - pr_err("cannot enter in device mode"); - pr_err("lpm = %i", hw_bank.lpm); - return -ENODEV; - } - - reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); - if (reg == 0 || reg > ENDPT_MAX) - return -ENODEV; - - hw_ep_max = reg; /* cache hw ENDPT_MAX */ - - /* setup lock mode ? */ - - /* ENDPTSETUPSTAT is '0' by default */ - - /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ - - return 0; -} - -/** - * hw_device_state: enables/disables interrupts & starts/stops device (execute - * without interruption) - * @dma: 0 => disable, !0 => enable and set dma engine - * - * This function returns an error code - */ -static int hw_device_state(u32 dma) -{ - if (dma) { - hw_cwrite(CAP_ENDPTLISTADDR, ~0, dma); - /* interrupt, error, port change, reset, sleep/suspend */ - hw_cwrite(CAP_USBINTR, ~0, - USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_cwrite(CAP_USBCMD, USBCMD_RS, USBCMD_RS); - } else { - hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); - hw_cwrite(CAP_USBINTR, ~0, 0); - } - return 0; -} - -/** - * hw_ep_flush: flush endpoint fifo (execute without interruption) - * @num: endpoint number - * @dir: endpoint direction - * - * This function returns an error code - */ -static int hw_ep_flush(int num, int dir) -{ - int n = hw_ep_bit(num, dir); - - do { - /* flush any pending transfer */ - hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n)); - while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) - cpu_relax(); - } while (hw_cread(CAP_ENDPTSTAT, BIT(n))); - - return 0; -} - -/** - * hw_ep_disable: disables endpoint (execute without interruption) - * @num: endpoint number - * @dir: endpoint direction - * - * This function returns an error code - */ -static int hw_ep_disable(int num, int dir) -{ - hw_ep_flush(num, dir); - hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), - dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); - return 0; -} - -/** - * hw_ep_enable: enables endpoint (execute without interruption) - * @num: endpoint number - * @dir: endpoint direction - * @type: endpoint type - * - * This function returns an error code - */ -static int hw_ep_enable(int num, int dir, int type) -{ - u32 mask, data; - - if (dir) { - mask = ENDPTCTRL_TXT; /* type */ - data = type << ffs_nr(mask); - - mask |= ENDPTCTRL_TXS; /* unstall */ - mask |= ENDPTCTRL_TXR; /* reset data toggle */ - data |= ENDPTCTRL_TXR; - mask |= ENDPTCTRL_TXE; /* enable */ - data |= ENDPTCTRL_TXE; - } else { - mask = ENDPTCTRL_RXT; /* type */ - data = type << ffs_nr(mask); - - mask |= ENDPTCTRL_RXS; /* unstall */ - mask |= ENDPTCTRL_RXR; /* reset data toggle */ - data |= ENDPTCTRL_RXR; - mask |= ENDPTCTRL_RXE; /* enable */ - data |= ENDPTCTRL_RXE; - } - hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data); - return 0; -} - -/** - * hw_ep_get_halt: return endpoint halt status - * @num: endpoint number - * @dir: endpoint direction - * - * This function returns 1 if endpoint halted - */ -static int hw_ep_get_halt(int num, int dir) -{ - u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; - - return hw_cread(CAP_ENDPTCTRL + num * sizeof(u32), mask) ? 1 : 0; -} - -/** - * hw_ep_is_primed: test if endpoint is primed (execute without interruption) - * @num: endpoint number - * @dir: endpoint direction - * - * This function returns true if endpoint primed - */ -static int hw_ep_is_primed(int num, int dir) -{ - u32 reg = hw_cread(CAP_ENDPTPRIME, ~0) | hw_cread(CAP_ENDPTSTAT, ~0); - - return test_bit(hw_ep_bit(num, dir), (void *)®); -} - -/** - * hw_test_and_clear_setup_status: test & clear setup status (execute without - * interruption) - * @n: bit number (endpoint) - * - * This function returns setup status - */ -static int hw_test_and_clear_setup_status(int n) -{ - return hw_ctest_and_clear(CAP_ENDPTSETUPSTAT, BIT(n)); -} - -/** - * hw_ep_prime: primes endpoint (execute without interruption) - * @num: endpoint number - * @dir: endpoint direction - * @is_ctrl: true if control endpoint - * - * This function returns an error code - */ -static int hw_ep_prime(int num, int dir, int is_ctrl) -{ - int n = hw_ep_bit(num, dir); - - /* the caller should flush first */ - if (hw_ep_is_primed(num, dir)) - return -EBUSY; - - if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) - return -EAGAIN; - - hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n)); - - while (hw_cread(CAP_ENDPTPRIME, BIT(n))) - cpu_relax(); - if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) - return -EAGAIN; - - /* status shoult be tested according with manual but it doesn't work */ - return 0; -} - -/** - * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute - * without interruption) - * @num: endpoint number - * @dir: endpoint direction - * @value: true => stall, false => unstall - * - * This function returns an error code - */ -static int hw_ep_set_halt(int num, int dir, int value) -{ - if (value != 0 && value != 1) - return -EINVAL; - - do { - u32 addr = CAP_ENDPTCTRL + num * sizeof(u32); - u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; - u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; - - /* data toggle - reserved for EP0 but it's in ESS */ - hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr); - - } while (value != hw_ep_get_halt(num, dir)); - - return 0; -} - -/** - * hw_intr_clear: disables interrupt & clears interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_clear(int n) -{ - if (n >= REG_BITS) - return -EINVAL; - - hw_cwrite(CAP_USBINTR, BIT(n), 0); - hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); - return 0; -} - -/** - * hw_intr_force: enables interrupt & forces interrupt status (execute without - * interruption) - * @n: interrupt bit - * - * This function returns an error code - */ -static int hw_intr_force(int n) -{ - if (n >= REG_BITS) - return -EINVAL; - - hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_cwrite(CAP_USBINTR, BIT(n), BIT(n)); - hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); - hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, 0); - return 0; -} - -/** - * hw_is_port_high_speed: test if port is high speed - * - * This function returns true if high speed port - */ -static int hw_port_is_high_speed(void) -{ - return hw_bank.lpm ? hw_cread(CAP_DEVLC, DEVLC_PSPD) : - hw_cread(CAP_PORTSC, PORTSC_HSP); -} - -/** - * hw_port_test_get: reads port test mode value - * - * This function returns port test mode value - */ -static u8 hw_port_test_get(void) -{ - return hw_cread(CAP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); -} - -/** - * hw_port_test_set: writes port test mode (execute without interruption) - * @mode: new value - * - * This function returns an error code - */ -static int hw_port_test_set(u8 mode) -{ - const u8 TEST_MODE_MAX = 7; - - if (mode > TEST_MODE_MAX) - return -EINVAL; - - hw_cwrite(CAP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); - return 0; -} - -/** - * hw_read_intr_enable: returns interrupt enable register - * - * This function returns register data - */ -static u32 hw_read_intr_enable(void) -{ - return hw_cread(CAP_USBINTR, ~0); -} - -/** - * hw_read_intr_status: returns interrupt status register - * - * This function returns register data - */ -static u32 hw_read_intr_status(void) -{ - return hw_cread(CAP_USBSTS, ~0); -} - -/** - * hw_register_read: reads all device registers (execute without interruption) - * @buf: destination buffer - * @size: buffer size - * - * This function returns number of registers read - */ -static size_t hw_register_read(u32 *buf, size_t size) -{ - unsigned i; - - if (size > hw_bank.size) - size = hw_bank.size; - - for (i = 0; i < size; i++) - buf[i] = hw_aread(i * sizeof(u32), ~0); - - return size; -} - -/** - * hw_register_write: writes to register - * @addr: register address - * @data: register value - * - * This function returns an error code - */ -static int hw_register_write(u16 addr, u32 data) -{ - /* align */ - addr /= sizeof(u32); - - if (addr >= hw_bank.size) - return -EINVAL; - - /* align */ - addr *= sizeof(u32); - - hw_awrite(addr, ~0, data); - return 0; -} - -/** - * hw_test_and_clear_complete: test & clear complete status (execute without - * interruption) - * @n: bit number (endpoint) - * - * This function returns complete status - */ -static int hw_test_and_clear_complete(int n) -{ - return hw_ctest_and_clear(CAP_ENDPTCOMPLETE, BIT(n)); -} - -/** - * hw_test_and_clear_intr_active: test & clear active interrupts (execute - * without interruption) - * - * This function returns active interrutps - */ -static u32 hw_test_and_clear_intr_active(void) -{ - u32 reg = hw_read_intr_status() & hw_read_intr_enable(); - - hw_cwrite(CAP_USBSTS, ~0, reg); - return reg; -} - -/** - * hw_test_and_clear_setup_guard: test & clear setup guard (execute without - * interruption) - * - * This function returns guard value - */ -static int hw_test_and_clear_setup_guard(void) -{ - return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, 0); -} - -/** - * hw_test_and_set_setup_guard: test & set setup guard (execute without - * interruption) - * - * This function returns guard value - */ -static int hw_test_and_set_setup_guard(void) -{ - return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); -} - -/** - * hw_usb_set_address: configures USB address (execute without interruption) - * @value: new USB address - * - * This function returns an error code - */ -static int hw_usb_set_address(u8 value) -{ - /* advance */ - hw_cwrite(CAP_DEVICEADDR, DEVICEADDR_USBADR | DEVICEADDR_USBADRA, - value << ffs_nr(DEVICEADDR_USBADR) | DEVICEADDR_USBADRA); - return 0; -} - -/** - * hw_usb_reset: restart device after a bus reset (execute without - * interruption) - * - * This function returns an error code - */ -static int hw_usb_reset(void) -{ - hw_usb_set_address(0); - - /* ESS flushes only at end?!? */ - hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); /* flush all EPs */ - - /* clear setup token semaphores */ - hw_cwrite(CAP_ENDPTSETUPSTAT, 0, 0); /* writes its content */ - - /* clear complete status */ - hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */ - - /* wait until all bits cleared */ - while (hw_cread(CAP_ENDPTPRIME, ~0)) - udelay(10); /* not RTOS friendly */ - - /* reset all endpoints ? */ - - /* reset internal status and wait for further instructions - no need to verify the port reset status (ESS does it) */ - - return 0; -} - -/****************************************************************************** - * DBG block - *****************************************************************************/ -/** - * show_device: prints information about device capabilities and status - * - * Check "device.h" for details - */ -static ssize_t show_device(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &udc->gadget; - int n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", - gadget->speed); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n", - gadget->is_dualspeed); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", - gadget->is_otg); - n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", - gadget->is_a_peripheral); - n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", - gadget->b_hnp_enable); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", - gadget->a_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", - gadget->a_alt_hnp_support); - n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", - (gadget->name ? gadget->name : "")); - - return n; -} -static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); - -/** - * show_driver: prints information about attached gadget (if any) - * - * Check "device.h" for details - */ -static ssize_t show_driver(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = udc->driver; - int n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - if (driver == NULL) - return scnprintf(buf, PAGE_SIZE, - "There is no gadget attached!\n"); - - n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", - (driver->function ? driver->function : "")); - n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", - driver->speed); - - return n; -} -static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); - -/* Maximum event message length */ -#define DBG_DATA_MSG 64UL - -/* Maximum event messages */ -#define DBG_DATA_MAX 128UL - -/* Event buffer descriptor */ -static struct { - char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ - unsigned idx; /* index */ - unsigned tty; /* print to console? */ - rwlock_t lck; /* lock */ -} dbg_data = { - .idx = 0, - .tty = 0, - .lck = __RW_LOCK_UNLOCKED(lck) -}; - -/** - * dbg_dec: decrements debug event index - * @idx: buffer index - */ -static void dbg_dec(unsigned *idx) -{ - *idx = (*idx - 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_inc: increments debug event index - * @idx: buffer index - */ -static void dbg_inc(unsigned *idx) -{ - *idx = (*idx + 1) & (DBG_DATA_MAX-1); -} - -/** - * dbg_print: prints the common part of the event - * @addr: endpoint address - * @name: event name - * @status: status - * @extra: extra information - */ -static void dbg_print(u8 addr, const char *name, int status, const char *extra) -{ - struct timeval tval; - unsigned int stamp; - unsigned long flags; - - write_lock_irqsave(&dbg_data.lck, flags); - - do_gettimeofday(&tval); - stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ - stamp = stamp * 1000000 + tval.tv_usec; - - scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, - "%04X\t» %02X %-7.7s %4i «\t%s\n", - stamp, addr, name, status, extra); - - dbg_inc(&dbg_data.idx); - - write_unlock_irqrestore(&dbg_data.lck, flags); - - if (dbg_data.tty != 0) - pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", - stamp, addr, name, status, extra); -} - -/** - * dbg_done: prints a DONE event - * @addr: endpoint address - * @td: transfer descriptor - * @status: status - */ -static void dbg_done(u8 addr, const u32 token, int status) -{ - char msg[DBG_DATA_MSG]; - - scnprintf(msg, sizeof(msg), "%d %02X", - (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), - (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); - dbg_print(addr, "DONE", status, msg); -} - -/** - * dbg_event: prints a generic event - * @addr: endpoint address - * @name: event name - * @status: status - */ -static void dbg_event(u8 addr, const char *name, int status) -{ - if (name != NULL) - dbg_print(addr, name, status, ""); -} - -/* - * dbg_queue: prints a QUEUE event - * @addr: endpoint address - * @req: USB request - * @status: status - */ -static void dbg_queue(u8 addr, const struct usb_request *req, int status) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%d %d", !req->no_interrupt, req->length); - dbg_print(addr, "QUEUE", status, msg); - } -} - -/** - * dbg_setup: prints a SETUP event - * @addr: endpoint address - * @req: setup request - */ -static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) -{ - char msg[DBG_DATA_MSG]; - - if (req != NULL) { - scnprintf(msg, sizeof(msg), - "%02X %02X %04X %04X %d", req->bRequestType, - req->bRequest, le16_to_cpu(req->wValue), - le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); - dbg_print(addr, "SETUP", 0, msg); - } -} - -/** - * show_events: displays the event buffer - * - * Check "device.h" for details - */ -static ssize_t show_events(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned long flags; - unsigned i, j, n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - read_lock_irqsave(&dbg_data.lck, flags); - - i = dbg_data.idx; - for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { - n += strlen(dbg_data.buf[i]); - if (n >= PAGE_SIZE) { - n -= strlen(dbg_data.buf[i]); - break; - } - } - for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) - j += scnprintf(buf + j, PAGE_SIZE - j, - "%s", dbg_data.buf[i]); - - read_unlock_irqrestore(&dbg_data.lck, flags); - - return n; -} - -/** - * store_events: configure if events are going to be also printed to console - * - * Check "device.h" for details - */ -static ssize_t store_events(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned tty; - - dbg_trace("[%s] %p, %d\n", __func__, buf, count); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { - dev_err(dev, "<1|0>: enable|disable console log\n"); - goto done; - } - - dbg_data.tty = tty; - dev_info(dev, "tty = %u", dbg_data.tty); - - done: - return count; -} -static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); - -/** - * show_inters: interrupt status, enable status and historic - * - * Check "device.h" for details - */ -static ssize_t show_inters(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 intr; - unsigned i, j, n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(udc->lock, flags); - - n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status()); - n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable()); - - n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", - isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", - isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", - isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", - isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", - isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", - isr_statistics.sli); - n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", - isr_statistics.none); - n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", - isr_statistics.hndl.cnt); - - for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { - i &= ISR_MASK; - intr = isr_statistics.hndl.buf[i]; - - if (USBi_UI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); - intr &= ~USBi_UI; - if (USBi_UEI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); - intr &= ~USBi_UEI; - if (USBi_PCI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); - intr &= ~USBi_PCI; - if (USBi_URI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); - intr &= ~USBi_URI; - if (USBi_SLI & intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); - intr &= ~USBi_SLI; - if (intr) - n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); - if (isr_statistics.hndl.buf[i]) - n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); - } - - spin_unlock_irqrestore(udc->lock, flags); - - return n; -} - -/** - * store_inters: enable & force or disable an individual interrutps - * (to be used for test purposes only) - * - * Check "device.h" for details - */ -static ssize_t store_inters(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned en, bit; - - dbg_trace("[%s] %p, %d\n", __func__, buf, count); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(dev, "<1|0> <bit>: enable|disable interrupt"); - goto done; - } - - spin_lock_irqsave(udc->lock, flags); - if (en) { - if (hw_intr_force(bit)) - dev_err(dev, "invalid bit number\n"); - else - isr_statistics.test++; - } else { - if (hw_intr_clear(bit)) - dev_err(dev, "invalid bit number\n"); - } - spin_unlock_irqrestore(udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); - -/** - * show_port_test: reads port test mode - * - * Check "device.h" for details - */ -static ssize_t show_port_test(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned mode; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(udc->lock, flags); - mode = hw_port_test_get(); - spin_unlock_irqrestore(udc->lock, flags); - - return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); -} - -/** - * store_port_test: writes port test mode - * - * Check "device.h" for details - */ -static ssize_t store_port_test(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned mode; - - dbg_trace("[%s] %p, %d\n", __func__, buf, count); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%u", &mode) != 1) { - dev_err(dev, "<mode>: set port test mode"); - goto done; - } - - spin_lock_irqsave(udc->lock, flags); - if (hw_port_test_set(mode)) - dev_err(dev, "invalid mode\n"); - spin_unlock_irqrestore(udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, - show_port_test, store_port_test); - -/** - * show_qheads: DMA contents of all queue heads - * - * Check "device.h" for details - */ -static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - unsigned i, j, n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(udc->lock, flags); - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: RX=%08X TX=%08X\n", - i, (u32)mEp->qh[RX].dma, (u32)mEp->qh[TX].dma); - for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X %08X\n", j, - *((u32 *)mEp->qh[RX].ptr + j), - *((u32 *)mEp->qh[TX].ptr + j)); - } - } - spin_unlock_irqrestore(udc->lock, flags); - - return n; -} -static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); - -/** - * show_registers: dumps all registers - * - * Check "device.h" for details - */ -static ssize_t show_registers(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - u32 dump[512]; - unsigned i, k, n = 0; - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(udc->lock, flags); - k = hw_register_read(dump, sizeof(dump)/sizeof(u32)); - spin_unlock_irqrestore(udc->lock, flags); - - for (i = 0; i < k; i++) { - n += scnprintf(buf + n, PAGE_SIZE - n, - "reg[0x%04X] = 0x%08X\n", - i * (unsigned)sizeof(u32), dump[i]); - } - - return n; -} - -/** - * store_registers: writes value to register address - * - * Check "device.h" for details - */ -static ssize_t store_registers(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long addr, data, flags; - - dbg_trace("[%s] %p, %d\n", __func__, buf, count); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - goto done; - } - - if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(dev, "<addr> <data>: write data to register address"); - goto done; - } - - spin_lock_irqsave(udc->lock, flags); - if (hw_register_write(addr, data)) - dev_err(dev, "invalid address range\n"); - spin_unlock_irqrestore(udc->lock, flags); - - done: - return count; -} -static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, - show_registers, store_registers); - -/** - * show_requests: DMA contents of all requests currently queued (all endpts) - * - * Check "device.h" for details - */ -static ssize_t show_requests(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - unsigned long flags; - struct list_head *ptr = NULL; - struct ci13xxx_req *req = NULL; - unsigned i, j, k, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); - - dbg_trace("[%s] %p\n", __func__, buf); - if (attr == NULL || buf == NULL) { - dev_err(dev, "[%s] EINVAL\n", __func__); - return 0; - } - - spin_lock_irqsave(udc->lock, flags); - for (i = 0; i < hw_ep_max; i++) - for (k = RX; k <= TX; k++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh[k].queue) - { - req = list_entry(ptr, - struct ci13xxx_req, queue); - - n += scnprintf(buf + n, PAGE_SIZE - n, - "EP=%02i: TD=%08X %s\n", - i, (u32)req->dma, - ((k == RX) ? "RX" : "TX")); - - for (j = 0; j < qSize; j++) - n += scnprintf(buf + n, PAGE_SIZE - n, - " %04X: %08X\n", j, - *((u32 *)req->ptr + j)); - } - spin_unlock_irqrestore(udc->lock, flags); - - return n; -} -static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); - -/** - * dbg_create_files: initializes the attribute interface - * @dev: device - * - * This function returns an error code - */ -__maybe_unused static int dbg_create_files(struct device *dev) -{ - int retval = 0; - - if (dev == NULL) - return -EINVAL; - retval = device_create_file(dev, &dev_attr_device); - if (retval) - goto done; - retval = device_create_file(dev, &dev_attr_driver); - if (retval) - goto rm_device; - retval = device_create_file(dev, &dev_attr_events); - if (retval) - goto rm_driver; - retval = device_create_file(dev, &dev_attr_inters); - if (retval) - goto rm_events; - retval = device_create_file(dev, &dev_attr_port_test); - if (retval) - goto rm_inters; - retval = device_create_file(dev, &dev_attr_qheads); - if (retval) - goto rm_port_test; - retval = device_create_file(dev, &dev_attr_registers); - if (retval) - goto rm_qheads; - retval = device_create_file(dev, &dev_attr_requests); - if (retval) - goto rm_registers; - return 0; - - rm_registers: - device_remove_file(dev, &dev_attr_registers); - rm_qheads: - device_remove_file(dev, &dev_attr_qheads); - rm_port_test: - device_remove_file(dev, &dev_attr_port_test); - rm_inters: - device_remove_file(dev, &dev_attr_inters); - rm_events: - device_remove_file(dev, &dev_attr_events); - rm_driver: - device_remove_file(dev, &dev_attr_driver); - rm_device: - device_remove_file(dev, &dev_attr_device); - done: - return retval; -} - -/** - * dbg_remove_files: destroys the attribute interface - * @dev: device - * - * This function returns an error code - */ -__maybe_unused static int dbg_remove_files(struct device *dev) -{ - if (dev == NULL) - return -EINVAL; - device_remove_file(dev, &dev_attr_requests); - device_remove_file(dev, &dev_attr_registers); - device_remove_file(dev, &dev_attr_qheads); - device_remove_file(dev, &dev_attr_port_test); - device_remove_file(dev, &dev_attr_inters); - device_remove_file(dev, &dev_attr_events); - device_remove_file(dev, &dev_attr_driver); - device_remove_file(dev, &dev_attr_device); - return 0; -} - -/****************************************************************************** - * UTIL block - *****************************************************************************/ -/** - * _usb_addr: calculates endpoint address from direction & number - * @ep: endpoint - */ -static inline u8 _usb_addr(struct ci13xxx_ep *ep) -{ - return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num; -} - -/** - * _hardware_queue: configures a request at hardware level - * @gadget: gadget - * @mEp: endpoint - * - * This function returns an error code - */ -static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) -{ - unsigned i; - - trace("%p, %p", mEp, mReq); - - /* don't queue twice */ - if (mReq->req.status == -EALREADY) - return -EALREADY; - - if (hw_ep_is_primed(mEp->num, mEp->dir)) - return -EBUSY; - - mReq->req.status = -EALREADY; - - if (mReq->req.length && !mReq->req.dma) { - mReq->req.dma = \ - dma_map_single(mEp->device, mReq->req.buf, - mReq->req.length, mEp->dir ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (mReq->req.dma == 0) - return -ENOMEM; - - mReq->map = 1; - } - - /* - * TD configuration - * TODO - handle requests which spawns into several TDs - */ - memset(mReq->ptr, 0, sizeof(*mReq->ptr)); - mReq->ptr->next |= TD_TERMINATE; - mReq->ptr->token = mReq->req.length << ffs_nr(TD_TOTAL_BYTES); - mReq->ptr->token &= TD_TOTAL_BYTES; - mReq->ptr->token |= TD_IOC; - mReq->ptr->token |= TD_STATUS_ACTIVE; - mReq->ptr->page[0] = mReq->req.dma; - for (i = 1; i < 5; i++) - mReq->ptr->page[i] = - (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; - - /* - * QH configuration - * At this point it's guaranteed exclusive access to qhead - * (endpt is not primed) so it's no need to use tripwire - */ - mEp->qh[mEp->dir].ptr->td.next = mReq->dma; /* TERMINATE = 0 */ - mEp->qh[mEp->dir].ptr->td.token &= ~TD_STATUS; /* clear status */ - if (mReq->req.zero == 0) - mEp->qh[mEp->dir].ptr->cap |= QH_ZLT; - else - mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; - - wmb(); /* synchronize before ep prime */ - - return hw_ep_prime(mEp->num, mEp->dir, - mEp->type == USB_ENDPOINT_XFER_CONTROL); -} - -/** - * _hardware_dequeue: handles a request at hardware level - * @gadget: gadget - * @mEp: endpoint - * - * This function returns an error code - */ -static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) -{ - trace("%p, %p", mEp, mReq); - - if (mReq->req.status != -EALREADY) - return -EINVAL; - - if (hw_ep_is_primed(mEp->num, mEp->dir)) - hw_ep_flush(mEp->num, mEp->dir); - - mReq->req.status = 0; - - if (mReq->map) { - dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length, - mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - mReq->req.dma = 0; - mReq->map = 0; - } - - mReq->req.status = mReq->ptr->token & TD_STATUS; - if ((TD_STATUS_ACTIVE & mReq->req.status) != 0) - mReq->req.status = -ECONNRESET; - else if ((TD_STATUS_HALTED & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) - mReq->req.status = -1; - - mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; - mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); - mReq->req.actual = mReq->req.length - mReq->req.actual; - mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; - - return mReq->req.actual; -} - -/** - * _ep_nuke: dequeues all endpoint requests - * @mEp: endpoint - * - * This function returns an error code - * Caller must hold lock - */ -static int _ep_nuke(struct ci13xxx_ep *mEp) -__releases(mEp->lock) -__acquires(mEp->lock) -{ - trace("%p", mEp); - - if (mEp == NULL) - return -EINVAL; - - hw_ep_flush(mEp->num, mEp->dir); - - while (!list_empty(&mEp->qh[mEp->dir].queue)) { - - /* pop oldest request */ - struct ci13xxx_req *mReq = \ - list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - list_del_init(&mReq->queue); - mReq->req.status = -ESHUTDOWN; - - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); - spin_lock(mEp->lock); - } - } - return 0; -} - -/** - * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts - * @gadget: gadget - * - * This function returns an error code - * Caller must hold lock - */ -static int _gadget_stop_activity(struct usb_gadget *gadget) -__releases(udc->lock) -__acquires(udc->lock) -{ - struct usb_ep *ep; - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); - struct ci13xxx_ep *mEp = container_of(gadget->ep0, - struct ci13xxx_ep, ep); - - trace("%p", gadget); - - if (gadget == NULL) - return -EINVAL; - - spin_unlock(udc->lock); - - /* flush all endpoints */ - gadget_for_each_ep(ep, gadget) { - usb_ep_fifo_flush(ep); - } - usb_ep_fifo_flush(gadget->ep0); - - udc->driver->disconnect(gadget); - - /* make sure to disable all endpoints */ - gadget_for_each_ep(ep, gadget) { - usb_ep_disable(ep); - } - usb_ep_disable(gadget->ep0); - - if (mEp->status != NULL) { - usb_ep_free_request(gadget->ep0, mEp->status); - mEp->status = NULL; - } - - spin_lock(udc->lock); - - return 0; -} - -/****************************************************************************** - * ISR block - *****************************************************************************/ -/** - * isr_reset_handler: USB reset interrupt handler - * @udc: UDC device - * - * This function resets USB engine after a bus reset occurred - */ -static void isr_reset_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) -{ - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[0]; - int retval; - - trace("%p", udc); - - if (udc == NULL) { - err("EINVAL"); - return; - } - - dbg_event(0xFF, "BUS RST", 0); - - retval = _gadget_stop_activity(&udc->gadget); - if (retval) - goto done; - - retval = hw_usb_reset(); - if (retval) - goto done; - - spin_unlock(udc->lock); - retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc); - if (!retval) { - mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL); - if (mEp->status == NULL) { - usb_ep_disable(&mEp->ep); - retval = -ENOMEM; - } - } - spin_lock(udc->lock); - - done: - if (retval) - err("error: %i", retval); -} - -/** - * isr_get_status_complete: get_status request complete function - * @ep: endpoint - * @req: request handled - * - * Caller must release lock - */ -static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) -{ - trace("%p, %p", ep, req); - - if (ep == NULL || req == NULL) { - err("EINVAL"); - return; - } - - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -/** - * isr_get_status_response: get_status request response - * @ep: endpoint - * @setup: setup request packet - * - * This function returns an error code - */ -static int isr_get_status_response(struct ci13xxx_ep *mEp, - struct usb_ctrlrequest *setup) -__releases(mEp->lock) -__acquires(mEp->lock) -{ - struct usb_request *req = NULL; - gfp_t gfp_flags = GFP_ATOMIC; - int dir, num, retval; - - trace("%p, %p", mEp, setup); - - if (mEp == NULL || setup == NULL) - return -EINVAL; - - spin_unlock(mEp->lock); - req = usb_ep_alloc_request(&mEp->ep, gfp_flags); - spin_lock(mEp->lock); - if (req == NULL) - return -ENOMEM; - - req->complete = isr_get_status_complete; - req->length = 2; - req->buf = kzalloc(req->length, gfp_flags); - if (req->buf == NULL) { - retval = -ENOMEM; - goto err_free_req; - } - - if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { - /* TODO: D1 - Remote Wakeup; D0 - Self Powered */ - retval = 0; - } else if ((setup->bRequestType & USB_RECIP_MASK) \ - == USB_RECIP_ENDPOINT) { - dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? - TX : RX; - num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; - *((u16 *)req->buf) = hw_ep_get_halt(num, dir); - } - /* else do nothing; reserved for future use */ - - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, req, gfp_flags); - spin_lock(mEp->lock); - if (retval) - goto err_free_buf; - - return 0; - - err_free_buf: - kfree(req->buf); - err_free_req: - spin_unlock(mEp->lock); - usb_ep_free_request(&mEp->ep, req); - spin_lock(mEp->lock); - return retval; -} - -/** - * isr_setup_status_phase: queues the status phase of a setup transation - * @mEp: endpoint - * - * This function returns an error code - */ -static int isr_setup_status_phase(struct ci13xxx_ep *mEp) -__releases(mEp->lock) -__acquires(mEp->lock) -{ - int retval; - - trace("%p", mEp); - - /* mEp is always valid & configured */ - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; - - mEp->status->no_interrupt = 1; - - spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, mEp->status, GFP_ATOMIC); - spin_lock(mEp->lock); - - return retval; -} - -/** - * isr_tr_complete_low: transaction complete low level handler - * @mEp: endpoint - * - * This function returns an error code - * Caller must hold lock - */ -static int isr_tr_complete_low(struct ci13xxx_ep *mEp) -__releases(mEp->lock) -__acquires(mEp->lock) -{ - struct ci13xxx_req *mReq; - int retval; - - trace("%p", mEp); - - if (list_empty(&mEp->qh[mEp->dir].queue)) - return -EINVAL; - - /* pop oldest request */ - mReq = list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - list_del_init(&mReq->queue); - - retval = _hardware_dequeue(mEp, mReq); - if (retval < 0) { - dbg_event(_usb_addr(mEp), "DONE", retval); - goto done; - } - - dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); - - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); - spin_lock(mEp->lock); - } - - if (!list_empty(&mEp->qh[mEp->dir].queue)) { - mReq = list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - _hardware_enqueue(mEp, mReq); - } - - done: - return retval; -} - -/** - * isr_tr_complete_handler: transaction complete interrupt handler - * @udc: UDC descriptor - * - * This function handles traffic events - */ -static void isr_tr_complete_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) -{ - unsigned i; - - trace("%p", udc); - - if (udc == NULL) { - err("EINVAL"); - return; - } - - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - int type, num, err = -EINVAL; - struct usb_ctrlrequest req; - - - if (mEp->desc == NULL) - continue; /* not configured */ - - if ((mEp->dir == RX && hw_test_and_clear_complete(i)) || - (mEp->dir == TX && hw_test_and_clear_complete(i + 16))) { - err = isr_tr_complete_low(mEp); - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { - if (err > 0) /* needs status phase */ - err = isr_setup_status_phase(mEp); - if (err < 0) { - dbg_event(_usb_addr(mEp), - "ERROR", err); - spin_unlock(udc->lock); - if (usb_ep_set_halt(&mEp->ep)) - err("error: ep_set_halt"); - spin_lock(udc->lock); - } - } - } - - if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(i)) - continue; - - if (i != 0) { - warn("ctrl traffic received at endpoint"); - continue; - } - - /* read_setup_packet */ - do { - hw_test_and_set_setup_guard(); - memcpy(&req, &mEp->qh[RX].ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard()); - - type = req.bRequestType; - - mEp->dir = (type & USB_DIR_IN) ? TX : RX; - - dbg_setup(_usb_addr(mEp), &req); - - switch (req.bRequest) { - case USB_REQ_CLEAR_FEATURE: - if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) - goto delegate; - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - num &= USB_ENDPOINT_NUMBER_MASK; - if (!udc->ci13xxx_ep[num].wedge) { - spin_unlock(udc->lock); - err = usb_ep_clear_halt( - &udc->ci13xxx_ep[num].ep); - spin_lock(udc->lock); - if (err) - break; - } - err = isr_setup_status_phase(mEp); - break; - case USB_REQ_GET_STATUS: - if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && - type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && - type != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 2 || - le16_to_cpu(req.wValue) != 0) - break; - err = isr_get_status_response(mEp, &req); - break; - case USB_REQ_SET_ADDRESS: - if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) - goto delegate; - if (le16_to_cpu(req.wLength) != 0 || - le16_to_cpu(req.wIndex) != 0) - break; - err = hw_usb_set_address((u8)le16_to_cpu(req.wValue)); - if (err) - break; - err = isr_setup_status_phase(mEp); - break; - case USB_REQ_SET_FEATURE: - if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && - le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) - goto delegate; - if (req.wLength != 0) - break; - num = le16_to_cpu(req.wIndex); - num &= USB_ENDPOINT_NUMBER_MASK; - - spin_unlock(udc->lock); - err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); - spin_lock(udc->lock); - if (err) - break; - err = isr_setup_status_phase(mEp); - break; - default: -delegate: - if (req.wLength == 0) /* no data phase */ - mEp->dir = TX; - - spin_unlock(udc->lock); - err = udc->driver->setup(&udc->gadget, &req); - spin_lock(udc->lock); - break; - } - - if (err < 0) { - dbg_event(_usb_addr(mEp), "ERROR", err); - - spin_unlock(udc->lock); - if (usb_ep_set_halt(&mEp->ep)) - err("error: ep_set_halt"); - spin_lock(udc->lock); - } - } -} - -/****************************************************************************** - * ENDPT block - *****************************************************************************/ -/** - * ep_enable: configure endpoint, making it usable - * - * Check usb_ep_enable() at "usb_gadget.h" for details - */ -static int ep_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *desc) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - int direction, retval = 0; - unsigned long flags; - - trace("%p, %p", ep, desc); - - if (ep == NULL || desc == NULL) - return -EINVAL; - - spin_lock_irqsave(mEp->lock, flags); - - /* only internal SW should enable ctrl endpts */ - - mEp->desc = desc; - - if (!list_empty(&mEp->qh[mEp->dir].queue)) - warn("enabling a non-empty endpoint!"); - - mEp->dir = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX; - mEp->num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - mEp->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - - mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); - - direction = mEp->dir; - do { - dbg_event(_usb_addr(mEp), "ENABLE", 0); - - mEp->qh[mEp->dir].ptr->cap = 0; - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->qh[mEp->dir].ptr->cap |= QH_IOS; - else if (mEp->type == USB_ENDPOINT_XFER_ISOC) - mEp->qh[mEp->dir].ptr->cap &= ~QH_MULT; - else - mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; - - mEp->qh[mEp->dir].ptr->cap |= - (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; - mEp->qh[mEp->dir].ptr->td.next |= TD_TERMINATE; /* needed? */ - - retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; - - } while (mEp->dir != direction); - - spin_unlock_irqrestore(mEp->lock, flags); - return retval; -} - -/** - * ep_disable: endpoint is no longer usable - * - * Check usb_ep_disable() at "usb_gadget.h" for details - */ -static int ep_disable(struct usb_ep *ep) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - int direction, retval = 0; - unsigned long flags; - - trace("%p", ep); - - if (ep == NULL) - return -EINVAL; - else if (mEp->desc == NULL) - return -EBUSY; - - spin_lock_irqsave(mEp->lock, flags); - - /* only internal SW should disable ctrl endpts */ - - direction = mEp->dir; - do { - dbg_event(_usb_addr(mEp), "DISABLE", 0); - - retval |= _ep_nuke(mEp); - retval |= hw_ep_disable(mEp->num, mEp->dir); - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; - - } while (mEp->dir != direction); - - mEp->desc = NULL; - - spin_unlock_irqrestore(mEp->lock, flags); - return retval; -} - -/** - * ep_alloc_request: allocate a request object to use with this endpoint - * - * Check usb_ep_alloc_request() at "usb_gadget.h" for details - */ -static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = NULL; - unsigned long flags; - - trace("%p, %i", ep, gfp_flags); - - if (ep == NULL) { - err("EINVAL"); - return NULL; - } - - spin_lock_irqsave(mEp->lock, flags); - - mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); - if (mReq != NULL) { - INIT_LIST_HEAD(&mReq->queue); - - mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags, - &mReq->dma); - if (mReq->ptr == NULL) { - kfree(mReq); - mReq = NULL; - } - } - - dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); - - spin_unlock_irqrestore(mEp->lock, flags); - - return (mReq == NULL) ? NULL : &mReq->req; -} - -/** - * ep_free_request: frees a request object - * - * Check usb_ep_free_request() at "usb_gadget.h" for details - */ -static void ep_free_request(struct usb_ep *ep, struct usb_request *req) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - unsigned long flags; - - trace("%p, %p", ep, req); - - if (ep == NULL || req == NULL) { - err("EINVAL"); - return; - } else if (!list_empty(&mReq->queue)) { - err("EBUSY"); - return; - } - - spin_lock_irqsave(mEp->lock, flags); - - if (mReq->ptr) - dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma); - kfree(mReq); - - dbg_event(_usb_addr(mEp), "FREE", 0); - - spin_unlock_irqrestore(mEp->lock, flags); -} - -/** - * ep_queue: queues (submits) an I/O request to an endpoint - * - * Check usb_ep_queue()* at usb_gadget.h" for details - */ -static int ep_queue(struct usb_ep *ep, struct usb_request *req, - gfp_t __maybe_unused gfp_flags) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - int retval = 0; - unsigned long flags; - - trace("%p, %p, %X", ep, req, gfp_flags); - - if (ep == NULL || req == NULL || mEp->desc == NULL) - return -EINVAL; - - spin_lock_irqsave(mEp->lock, flags); - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL && - !list_empty(&mEp->qh[mEp->dir].queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - warn("endpoint ctrl %X nuked", _usb_addr(mEp)); - } - - /* first nuke then test link, e.g. previous status has not sent */ - if (!list_empty(&mReq->queue)) { - retval = -EBUSY; - err("request already in queue"); - goto done; - } - - if (req->length > (4 * PAGE_SIZE)) { - req->length = (4 * PAGE_SIZE); - retval = -EMSGSIZE; - warn("request length truncated"); - } - - dbg_queue(_usb_addr(mEp), req, retval); - - /* push request */ - mReq->req.status = -EINPROGRESS; - mReq->req.actual = 0; - list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); - - retval = _hardware_enqueue(mEp, mReq); - if (retval == -EALREADY || retval == -EBUSY) { - dbg_event(_usb_addr(mEp), "QUEUE", retval); - retval = 0; - } - - done: - spin_unlock_irqrestore(mEp->lock, flags); - return retval; -} - -/** - * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint - * - * Check usb_ep_dequeue() at "usb_gadget.h" for details - */ -static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - unsigned long flags; - - trace("%p, %p", ep, req); - - if (ep == NULL || req == NULL || mEp->desc == NULL || - list_empty(&mReq->queue) || list_empty(&mEp->qh[mEp->dir].queue)) - return -EINVAL; - - spin_lock_irqsave(mEp->lock, flags); - - dbg_event(_usb_addr(mEp), "DEQUEUE", 0); - - if (mReq->req.status == -EALREADY) - _hardware_dequeue(mEp, mReq); - - /* pop request */ - list_del_init(&mReq->queue); - req->status = -ECONNRESET; - - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { - spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); - spin_lock(mEp->lock); - } - - spin_unlock_irqrestore(mEp->lock, flags); - return 0; -} - -/** - * ep_set_halt: sets the endpoint halt feature - * - * Check usb_ep_set_halt() at "usb_gadget.h" for details - */ -static int ep_set_halt(struct usb_ep *ep, int value) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - int direction, retval = 0; - unsigned long flags; - - trace("%p, %i", ep, value); - - if (ep == NULL || mEp->desc == NULL) - return -EINVAL; - - spin_lock_irqsave(mEp->lock, flags); - -#ifndef STALL_IN - /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ - if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX && - !list_empty(&mEp->qh[mEp->dir].queue)) { - spin_unlock_irqrestore(mEp->lock, flags); - return -EAGAIN; - } -#endif - - direction = mEp->dir; - do { - dbg_event(_usb_addr(mEp), "HALT", value); - retval |= hw_ep_set_halt(mEp->num, mEp->dir, value); - - if (!value) - mEp->wedge = 0; - - if (mEp->type == USB_ENDPOINT_XFER_CONTROL) - mEp->dir = (mEp->dir == TX) ? RX : TX; - - } while (mEp->dir != direction); - - spin_unlock_irqrestore(mEp->lock, flags); - return retval; -} - -/** - * ep_set_wedge: sets the halt feature and ignores clear requests - * - * Check usb_ep_set_wedge() at "usb_gadget.h" for details - */ -static int ep_set_wedge(struct usb_ep *ep) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - unsigned long flags; - - trace("%p", ep); - - if (ep == NULL || mEp->desc == NULL) - return -EINVAL; - - spin_lock_irqsave(mEp->lock, flags); - - dbg_event(_usb_addr(mEp), "WEDGE", 0); - mEp->wedge = 1; - - spin_unlock_irqrestore(mEp->lock, flags); - - return usb_ep_set_halt(ep); -} - -/** - * ep_fifo_flush: flushes contents of a fifo - * - * Check usb_ep_fifo_flush() at "usb_gadget.h" for details - */ -static void ep_fifo_flush(struct usb_ep *ep) -{ - struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); - unsigned long flags; - - trace("%p", ep); - - if (ep == NULL) { - err("%02X: -EINVAL", _usb_addr(mEp)); - return; - } - - spin_lock_irqsave(mEp->lock, flags); - - dbg_event(_usb_addr(mEp), "FFLUSH", 0); - hw_ep_flush(mEp->num, mEp->dir); - - spin_unlock_irqrestore(mEp->lock, flags); -} - -/** - * Endpoint-specific part of the API to the USB controller hardware - * Check "usb_gadget.h" for details - */ -static const struct usb_ep_ops usb_ep_ops = { - .enable = ep_enable, - .disable = ep_disable, - .alloc_request = ep_alloc_request, - .free_request = ep_free_request, - .queue = ep_queue, - .dequeue = ep_dequeue, - .set_halt = ep_set_halt, - .set_wedge = ep_set_wedge, - .fifo_flush = ep_fifo_flush, -}; - -/****************************************************************************** - * GADGET block - *****************************************************************************/ -/** - * Device operations part of the API to the USB controller hardware, - * which don't involve endpoints (or i/o) - * Check "usb_gadget.h" for details - */ -static const struct usb_gadget_ops usb_gadget_ops; - -/** - * usb_gadget_register_driver: register a gadget driver - * - * Check usb_gadget_register_driver() at "usb_gadget.h" for details - * Interrupts are enabled here - */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) -{ - struct ci13xxx *udc = _udc; - unsigned long i, k, flags; - int retval = -ENOMEM; - - trace("%p", driver); - - if (driver == NULL || - driver->bind == NULL || - driver->unbind == NULL || - driver->setup == NULL || - driver->disconnect == NULL || - driver->suspend == NULL || - driver->resume == NULL) - return -EINVAL; - else if (udc == NULL) - return -ENODEV; - else if (udc->driver != NULL) - return -EBUSY; - - /* alloc resources */ - udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, - sizeof(struct ci13xxx_qh), - 64, PAGE_SIZE); - if (udc->qh_pool == NULL) - return -ENOMEM; - - udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, - sizeof(struct ci13xxx_td), - 64, PAGE_SIZE); - if (udc->td_pool == NULL) { - dma_pool_destroy(udc->qh_pool); - udc->qh_pool = NULL; - return -ENOMEM; - } - - spin_lock_irqsave(udc->lock, flags); - - info("hw_ep_max = %d", hw_ep_max); - - udc->driver = driver; - udc->gadget.ops = NULL; - udc->gadget.dev.driver = NULL; - - retval = 0; - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - - scnprintf(mEp->name, sizeof(mEp->name), "ep%i", (int)i); - - mEp->lock = udc->lock; - mEp->device = &udc->gadget.dev; - mEp->td_pool = udc->td_pool; - - mEp->ep.name = mEp->name; - mEp->ep.ops = &usb_ep_ops; - mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; - - /* this allocation cannot be random */ - for (k = RX; k <= TX; k++) { - INIT_LIST_HEAD(&mEp->qh[k].queue); - mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool, - GFP_KERNEL, - &mEp->qh[k].dma); - if (mEp->qh[k].ptr == NULL) - retval = -ENOMEM; - else - memset(mEp->qh[k].ptr, 0, - sizeof(*mEp->qh[k].ptr)); - } - if (i == 0) - udc->gadget.ep0 = &mEp->ep; - else - list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); - } - if (retval) - goto done; - - /* bind gadget */ - driver->driver.bus = NULL; - udc->gadget.ops = &usb_gadget_ops; - udc->gadget.dev.driver = &driver->driver; - - spin_unlock_irqrestore(udc->lock, flags); - retval = driver->bind(&udc->gadget); /* MAY SLEEP */ - spin_lock_irqsave(udc->lock, flags); - - if (retval) { - udc->gadget.ops = NULL; - udc->gadget.dev.driver = NULL; - goto done; - } - - retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); - - done: - spin_unlock_irqrestore(udc->lock, flags); - if (retval) - usb_gadget_unregister_driver(driver); - return retval; -} -EXPORT_SYMBOL(usb_gadget_register_driver); - -/** - * usb_gadget_unregister_driver: unregister a gadget driver - * - * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details - */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct ci13xxx *udc = _udc; - unsigned long i, k, flags; - - trace("%p", driver); - - if (driver == NULL || - driver->bind == NULL || - driver->unbind == NULL || - driver->setup == NULL || - driver->disconnect == NULL || - driver->suspend == NULL || - driver->resume == NULL || - driver != udc->driver) - return -EINVAL; - - spin_lock_irqsave(udc->lock, flags); - - hw_device_state(0); - - /* unbind gadget */ - if (udc->gadget.ops != NULL) { - _gadget_stop_activity(&udc->gadget); - - spin_unlock_irqrestore(udc->lock, flags); - driver->unbind(&udc->gadget); /* MAY SLEEP */ - spin_lock_irqsave(udc->lock, flags); - - udc->gadget.ops = NULL; - udc->gadget.dev.driver = NULL; - } - - /* free resources */ - for (i = 0; i < hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - - if (i == 0) - udc->gadget.ep0 = NULL; - else if (!list_empty(&mEp->ep.ep_list)) - list_del_init(&mEp->ep.ep_list); - - for (k = RX; k <= TX; k++) - if (mEp->qh[k].ptr != NULL) - dma_pool_free(udc->qh_pool, - mEp->qh[k].ptr, mEp->qh[k].dma); - } - - udc->driver = NULL; - - spin_unlock_irqrestore(udc->lock, flags); - - if (udc->td_pool != NULL) { - dma_pool_destroy(udc->td_pool); - udc->td_pool = NULL; - } - if (udc->qh_pool != NULL) { - dma_pool_destroy(udc->qh_pool); - udc->qh_pool = NULL; - } - - return 0; -} -EXPORT_SYMBOL(usb_gadget_unregister_driver); - -/****************************************************************************** - * BUS block - *****************************************************************************/ -/** - * udc_irq: global interrupt handler - * - * This function returns IRQ_HANDLED if the IRQ has been handled - * It locks access to registers - */ -static irqreturn_t udc_irq(void) -{ - struct ci13xxx *udc = _udc; - irqreturn_t retval; - u32 intr; - - trace(); - - if (udc == NULL) { - err("ENODEV"); - return IRQ_HANDLED; - } - - spin_lock(udc->lock); - intr = hw_test_and_clear_intr_active(); - if (intr) { - isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; - isr_statistics.hndl.idx &= ISR_MASK; - isr_statistics.hndl.cnt++; - - /* order defines priority - do NOT change it */ - if (USBi_URI & intr) { - isr_statistics.uri++; - isr_reset_handler(udc); - } - if (USBi_PCI & intr) { - isr_statistics.pci++; - udc->gadget.speed = hw_port_is_high_speed() ? - USB_SPEED_HIGH : USB_SPEED_FULL; - } - if (USBi_UEI & intr) - isr_statistics.uei++; - if (USBi_UI & intr) { - isr_statistics.ui++; - isr_tr_complete_handler(udc); - } - if (USBi_SLI & intr) - isr_statistics.sli++; - retval = IRQ_HANDLED; - } else { - isr_statistics.none++; - retval = IRQ_NONE; - } - spin_unlock(udc->lock); - - return retval; -} - -/** - * udc_release: driver release function - * @dev: device - * - * Currently does nothing - */ -static void udc_release(struct device *dev) -{ - trace("%p", dev); - - if (dev == NULL) - err("EINVAL"); -} - -/** - * udc_probe: parent probe must call this to initialize UDC - * @dev: parent device - * @regs: registers base address - * @name: driver name - * - * This function returns an error code - * No interrupts active, the IRQ has not been requested yet - * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask - */ -static int udc_probe(struct device *dev, void __iomem *regs, const char *name) -{ - struct ci13xxx *udc; - int retval = 0; - - trace("%p, %p, %p", dev, regs, name); - - if (dev == NULL || regs == NULL || name == NULL) - return -EINVAL; - - udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); - if (udc == NULL) - return -ENOMEM; - - udc->lock = &udc_lock; - - retval = hw_device_reset(regs); - if (retval) - goto done; - - udc->gadget.ops = NULL; - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->gadget.is_dualspeed = 1; - udc->gadget.is_otg = 0; - udc->gadget.name = name; - - INIT_LIST_HEAD(&udc->gadget.ep_list); - udc->gadget.ep0 = NULL; - - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.dev.parent = dev; - udc->gadget.dev.release = udc_release; - - retval = device_register(&udc->gadget.dev); - if (retval) - goto done; - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - retval = dbg_create_files(&udc->gadget.dev); -#endif - if (retval) { - device_unregister(&udc->gadget.dev); - goto done; - } - - _udc = udc; - return retval; - - done: - err("error = %i", retval); - kfree(udc); - _udc = NULL; - return retval; -} - -/** - * udc_remove: parent remove must call this to remove UDC - * - * No interrupts active, the IRQ has been released - */ -static void udc_remove(void) -{ - struct ci13xxx *udc = _udc; - - if (udc == NULL) { - err("EINVAL"); - return; - } - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - dbg_remove_files(&udc->gadget.dev); -#endif - device_unregister(&udc->gadget.dev); - - kfree(udc); - _udc = NULL; -} - -/****************************************************************************** - * PCI block - *****************************************************************************/ -/** - * ci13xxx_pci_irq: interrut handler - * @irq: irq number - * @pdev: USB Device Controller interrupt source - * - * This function returns IRQ_HANDLED if the IRQ has been handled - * This is an ISR don't trace, use attribute interface instead - */ -static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev) -{ - if (irq == 0) { - dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!"); - return IRQ_HANDLED; - } - return udc_irq(); -} - -/** - * ci13xxx_pci_probe: PCI probe - * @pdev: USB device controller being probed - * @id: PCI hotplug ID connecting controller to UDC framework - * - * This function returns an error code - * Allocates basic PCI resources for this USB device controller, and then - * invokes the udc_probe() method to start the UDC associated with it - */ -static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - void __iomem *regs = NULL; - int retval = 0; - - if (id == NULL) - return -EINVAL; - - retval = pci_enable_device(pdev); - if (retval) - goto done; - - if (!pdev->irq) { - dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!"); - retval = -ENODEV; - goto disable_device; - } - - retval = pci_request_regions(pdev, UDC_DRIVER_NAME); - if (retval) - goto disable_device; - - /* BAR 0 holds all the registers */ - regs = pci_iomap(pdev, 0, 0); - if (!regs) { - dev_err(&pdev->dev, "Error mapping memory!"); - retval = -EFAULT; - goto release_regions; - } - pci_set_drvdata(pdev, (__force void *)regs); - - pci_set_master(pdev); - pci_try_set_mwi(pdev); - - retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME); - if (retval) - goto iounmap; - - /* our device does not have MSI capability */ - - retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED, - UDC_DRIVER_NAME, pdev); - if (retval) - goto gadget_remove; - - return 0; - - gadget_remove: - udc_remove(); - iounmap: - pci_iounmap(pdev, regs); - release_regions: - pci_release_regions(pdev); - disable_device: - pci_disable_device(pdev); - done: - return retval; -} - -/** - * ci13xxx_pci_remove: PCI remove - * @pdev: USB Device Controller being removed - * - * Reverses the effect of ci13xxx_pci_probe(), - * first invoking the udc_remove() and then releases - * all PCI resources allocated for this USB device controller - */ -static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) -{ - free_irq(pdev->irq, pdev); - udc_remove(); - pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev)); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -/** - * PCI device table - * PCI device structure - * - * Check "pci.h" for details - */ -static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { - { PCI_DEVICE(0x153F, 0x1004) }, - { PCI_DEVICE(0x153F, 0x1006) }, - { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table); - -static struct pci_driver ci13xxx_pci_driver = { - .name = UDC_DRIVER_NAME, - .id_table = ci13xxx_pci_id_table, - .probe = ci13xxx_pci_probe, - .remove = __devexit_p(ci13xxx_pci_remove), -}; - -/** - * ci13xxx_pci_init: module init - * - * Driver load - */ -static int __init ci13xxx_pci_init(void) -{ - return pci_register_driver(&ci13xxx_pci_driver); -} -module_init(ci13xxx_pci_init); - -/** - * ci13xxx_pci_exit: module exit - * - * Driver unload - */ -static void __exit ci13xxx_pci_exit(void) -{ - pci_unregister_driver(&ci13xxx_pci_driver); -} -module_exit(ci13xxx_pci_exit); - -MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>"); -MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("June 2008"); diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h deleted file mode 100644 index 4026e9cede3..00000000000 --- a/drivers/usb/gadget/ci13xxx_udc.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core - * - * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. - * - * Author: David Lopo - * - * 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. - * - * Description: MIPS USB IP core family device controller - * Structures, registers and logging macros - */ - -#ifndef _CI13XXX_h_ -#define _CI13XXX_h_ - -/****************************************************************************** - * DEFINE - *****************************************************************************/ -#define ENDPT_MAX (16) -#define CTRL_PAYLOAD_MAX (64) -#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */ -#define TX (1) /* similar to USB_DIR_IN but can be used as an index */ - -/****************************************************************************** - * STRUCTURES - *****************************************************************************/ -/* DMA layout of transfer descriptors */ -struct ci13xxx_td { - /* 0 */ - u32 next; -#define TD_TERMINATE BIT(0) - /* 1 */ - u32 token; -#define TD_STATUS (0x00FFUL << 0) -#define TD_STATUS_TR_ERR BIT(3) -#define TD_STATUS_DT_ERR BIT(5) -#define TD_STATUS_HALTED BIT(6) -#define TD_STATUS_ACTIVE BIT(7) -#define TD_MULTO (0x0003UL << 10) -#define TD_IOC BIT(15) -#define TD_TOTAL_BYTES (0x7FFFUL << 16) - /* 2 */ - u32 page[5]; -#define TD_CURR_OFFSET (0x0FFFUL << 0) -#define TD_FRAME_NUM (0x07FFUL << 0) -#define TD_RESERVED_MASK (0x0FFFUL << 0) -} __attribute__ ((packed)); - -/* DMA layout of queue heads */ -struct ci13xxx_qh { - /* 0 */ - u32 cap; -#define QH_IOS BIT(15) -#define QH_MAX_PKT (0x07FFUL << 16) -#define QH_ZLT BIT(29) -#define QH_MULT (0x0003UL << 30) - /* 1 */ - u32 curr; - /* 2 - 8 */ - struct ci13xxx_td td; - /* 9 */ - u32 RESERVED; - struct usb_ctrlrequest setup; -} __attribute__ ((packed)); - -/* Extension of usb_request */ -struct ci13xxx_req { - struct usb_request req; - unsigned map; - struct list_head queue; - struct ci13xxx_td *ptr; - dma_addr_t dma; -}; - -/* Extension of usb_ep */ -struct ci13xxx_ep { - struct usb_ep ep; - const struct usb_endpoint_descriptor *desc; - u8 dir; - u8 num; - u8 type; - char name[16]; - struct { - struct list_head queue; - struct ci13xxx_qh *ptr; - dma_addr_t dma; - } qh[2]; - struct usb_request *status; - int wedge; - - /* global resources */ - spinlock_t *lock; - struct device *device; - struct dma_pool *td_pool; -}; - -/* CI13XXX UDC descriptor & global resources */ -struct ci13xxx { - spinlock_t *lock; /* ctrl register bank access */ - - struct dma_pool *qh_pool; /* DMA pool for queue heads */ - struct dma_pool *td_pool; /* DMA pool for transfer descs */ - - struct usb_gadget gadget; /* USB slave device */ - struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */ - - struct usb_gadget_driver *driver; /* 3rd party gadget driver */ -}; - -/****************************************************************************** - * REGISTERS - *****************************************************************************/ -/* register size */ -#define REG_BITS (32) - -/* HCCPARAMS */ -#define HCCPARAMS_LEN BIT(17) - -/* DCCPARAMS */ -#define DCCPARAMS_DEN (0x1F << 0) -#define DCCPARAMS_DC BIT(7) - -/* TESTMODE */ -#define TESTMODE_FORCE BIT(0) - -/* USBCMD */ -#define USBCMD_RS BIT(0) -#define USBCMD_RST BIT(1) -#define USBCMD_SUTW BIT(13) - -/* USBSTS & USBINTR */ -#define USBi_UI BIT(0) -#define USBi_UEI BIT(1) -#define USBi_PCI BIT(2) -#define USBi_URI BIT(6) -#define USBi_SLI BIT(8) - -/* DEVICEADDR */ -#define DEVICEADDR_USBADRA BIT(24) -#define DEVICEADDR_USBADR (0x7FUL << 25) - -/* PORTSC */ -#define PORTSC_SUSP BIT(7) -#define PORTSC_HSP BIT(9) -#define PORTSC_PTC (0x0FUL << 16) - -/* DEVLC */ -#define DEVLC_PSPD (0x03UL << 25) -#define DEVLC_PSPD_HS (0x02UL << 25) - -/* USBMODE */ -#define USBMODE_CM (0x03UL << 0) -#define USBMODE_CM_IDLE (0x00UL << 0) -#define USBMODE_CM_DEVICE (0x02UL << 0) -#define USBMODE_CM_HOST (0x03UL << 0) -#define USBMODE_SLOM BIT(3) - -/* ENDPTCTRL */ -#define ENDPTCTRL_RXS BIT(0) -#define ENDPTCTRL_RXT (0x03UL << 2) -#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */ -#define ENDPTCTRL_RXE BIT(7) -#define ENDPTCTRL_TXS BIT(16) -#define ENDPTCTRL_TXT (0x03UL << 18) -#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */ -#define ENDPTCTRL_TXE BIT(23) - -/****************************************************************************** - * LOGGING - *****************************************************************************/ -#define ci13xxx_printk(level, format, args...) \ -do { \ - if (_udc == NULL) \ - printk(level "[%s] " format "\n", __func__, ## args); \ - else \ - dev_printk(level, _udc->gadget.dev.parent, \ - "[%s] " format "\n", __func__, ## args); \ -} while (0) - -#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args) -#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args) -#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args) - -#ifdef TRACE -#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args) -#define dbg_trace(format, args...) dev_dbg(dev, format, ##args) -#else -#define trace(format, args...) do {} while (0) -#define dbg_trace(format, args...) do {} while (0) -#endif - -#endif /* _CI13XXX_h_ */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 59e85234fa0..f8015193205 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ @@ -23,10 +14,30 @@ #include <linux/kallsyms.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/device.h> +#include <linux/utsname.h> #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 @@ -35,41 +46,139 @@ * with the relevant device-wide data. */ -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 512 - -static struct usb_composite_driver *composite; +static struct usb_gadget_strings **get_containers_gs( + struct usb_gadget_string_container *uc) +{ + return (struct usb_gadget_strings **)uc->stash; +} -/* Some systems will need runtime overrides for the product identifers - * published in the device descriptor, either numbers or strings or both. - * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). +/** + * next_ep_desc() - advance to the next EP descriptor + * @t: currect pointer within descriptor array + * + * Return: next EP descriptor or NULL + * + * Iterate over @t until either EP descriptor found or + * NULL (that indicates end of list) encountered */ +static struct usb_descriptor_header** +next_ep_desc(struct usb_descriptor_header **t) +{ + for (; *t; t++) { + if ((*t)->bDescriptorType == USB_DT_ENDPOINT) + return t; + } + return NULL; +} -static ushort idVendor; -module_param(idVendor, ushort, 0); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, 0); -MODULE_PARM_DESC(idProduct, "USB Product ID"); +/* + * for_each_ep_desc()- iterate over endpoint descriptors in the + * descriptors list + * @start: pointer within descriptor array. + * @ep_desc: endpoint descriptor to use as the loop cursor + */ +#define for_each_ep_desc(start, ep_desc) \ + for (ep_desc = next_ep_desc(start); \ + ep_desc; ep_desc = next_ep_desc(ep_desc+1)) -static ushort bcdDevice; -module_param(bcdDevice, ushort, 0); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); +/** + * config_ep_by_speed() - configures the given endpoint + * according to gadget speed. + * @g: pointer to the gadget + * @f: usb function + * @_ep: the endpoint to configure + * + * Return: error code, 0 on success + * + * This function chooses the right descriptors for a given + * endpoint according to gadget speed and saves it in the + * endpoint desc field. If the endpoint already has a descriptor + * assigned to it - overwrites it with currently corresponding + * descriptor. The endpoint maxpacket field is updated according + * to the chosen descriptor. + * Note: the supplied function should hold all the descriptors + * for supported speeds + */ +int config_ep_by_speed(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep) +{ + struct usb_composite_dev *cdev = get_gadget_data(g); + struct usb_endpoint_descriptor *chosen_desc = NULL; + struct usb_descriptor_header **speed_desc = NULL; -static char *iManufacturer; -module_param(iManufacturer, charp, 0); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + struct usb_ss_ep_comp_descriptor *comp_desc = NULL; + int want_comp_desc = 0; -static char *iProduct; -module_param(iProduct, charp, 0); -MODULE_PARM_DESC(iProduct, "USB Product string"); + struct usb_descriptor_header **d_spd; /* cursor for speed desc */ -static char *iSerialNumber; -module_param(iSerialNumber, charp, 0); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + if (!g || !f || !_ep) + return -EIO; -/*-------------------------------------------------------------------------*/ + /* select desired speed */ + switch (g->speed) { + case USB_SPEED_SUPER: + if (gadget_is_superspeed(g)) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; + } + /* else: Fall trough */ + case USB_SPEED_HIGH: + if (gadget_is_dualspeed(g)) { + speed_desc = f->hs_descriptors; + break; + } + /* else: fall through */ + default: + speed_desc = f->fs_descriptors; + } + /* find descriptors */ + for_each_ep_desc(speed_desc, d_spd) { + chosen_desc = (struct usb_endpoint_descriptor *)*d_spd; + if (chosen_desc->bEndpointAddress == _ep->address) + goto ep_found; + } + return -EIO; + +ep_found: + /* commit results */ + _ep->maxpacket = usb_endpoint_maxp(chosen_desc); + _ep->desc = chosen_desc; + _ep->comp_desc = NULL; + _ep->maxburst = 0; + _ep->mult = 0; + if (!want_comp_desc) + return 0; + + /* + * Companion descriptor should follow EP descriptor + * USB 3.0 spec, #9.6.7 + */ + comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd); + if (!comp_desc || + (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP)) + return -EIO; + _ep->comp_desc = comp_desc; + if (g->speed == USB_SPEED_SUPER) { + switch (usb_endpoint_type(_ep->desc)) { + case USB_ENDPOINT_XFER_ISOC: + /* mult: bits 1:0 of bmAttributes */ + _ep->mult = comp_desc->bmAttributes & 0x3; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + _ep->maxburst = comp_desc->bMaxBurst + 1; + break; + default: + if (comp_desc->bMaxBurst != 0) + ERROR(cdev, "ep0 bMaxBurst must be 0\n"); + _ep->maxburst = 1; + break; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(config_ep_by_speed); /** * usb_add_function() - add a function to a configuration @@ -85,7 +194,7 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); * This function returns the value of the function's bind(), which is * zero for success else a negative errno value. */ -int __init usb_add_function(struct usb_configuration *config, +int usb_add_function(struct usb_configuration *config, struct usb_function *function) { int value = -EINVAL; @@ -115,10 +224,12 @@ int __init usb_add_function(struct usb_configuration *config, * as full speed ... it's the function drivers that will need * to avoid bulk and ISO transfers. */ - if (!config->fullspeed && function->descriptors) + if (!config->fullspeed && function->fs_descriptors) config->fullspeed = true; if (!config->highspeed && function->hs_descriptors) config->highspeed = true; + if (!config->superspeed && function->ss_descriptors) + config->superspeed = true; done: if (value) @@ -126,6 +237,19 @@ done: function->name, function, value); return value; } +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 @@ -162,6 +286,7 @@ int usb_function_deactivate(struct usb_function *function) spin_unlock_irqrestore(&cdev->lock, flags); return status; } +EXPORT_SYMBOL_GPL(usb_function_deactivate); /** * usb_function_activate - allow function and gadget enumeration @@ -176,9 +301,10 @@ int usb_function_deactivate(struct usb_function *function) int usb_function_activate(struct usb_function *function) { struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; int status = 0; - spin_lock(&cdev->lock); + spin_lock_irqsave(&cdev->lock, flags); if (WARN_ON(cdev->deactivations == 0)) status = -EINVAL; @@ -188,9 +314,10 @@ int usb_function_activate(struct usb_function *function) status = usb_gadget_connect(cdev->gadget); } - spin_unlock(&cdev->lock); + spin_unlock_irqrestore(&cdev->lock, flags); return status; } +EXPORT_SYMBOL_GPL(usb_function_activate); /** * usb_interface_id() - allocate an unused interface ID @@ -201,21 +328,21 @@ int usb_function_activate(struct usb_function *function) * usb_interface_id() is called from usb_function.bind() callbacks to * allocate new interface IDs. The function driver will then store that * ID in interface, association, CDC union, and other descriptors. It - * will also handle any control requests targetted at that interface, + * will also handle any control requests targeted at that interface, * particularly changing its altsetting via set_alt(). There may * also be class-specific or vendor-specific requests to handle. * * All interface identifier should be allocated using this routine, to * ensure that for example different functions don't wrongly assign * different meanings to the same identifier. Note that since interface - * identifers are configuration-specific, functions used in more than + * identifiers are configuration-specific, functions used in more than * one configuration (or more than once in a given configuration) need * multiple versions of the relevant descriptors. * * Returns the interface ID which was allocated; or -ENODEV if no * more interface IDs can be allocated. */ -int __init usb_interface_id(struct usb_configuration *config, +int usb_interface_id(struct usb_configuration *config, struct usb_function *function) { unsigned id = config->next_interface_id; @@ -227,16 +354,37 @@ int __init usb_interface_id(struct usb_configuration *config, } return -ENODEV; } +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) { struct usb_config_descriptor *c = buf; void *next = buf + USB_DT_CONFIG_SIZE; - int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + int len; struct usb_function *f; int status; + len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE; /* write the config descriptor */ c = buf; c->bLength = USB_DT_CONFIG_SIZE; @@ -246,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) { @@ -262,10 +410,17 @@ static int config_buf(struct usb_configuration *config, list_for_each_entry(f, &config->functions, list) { struct usb_descriptor_header **descriptors; - if (speed == USB_SPEED_HIGH) + switch (speed) { + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + break; + case USB_SPEED_HIGH: descriptors = f->hs_descriptors; - else - descriptors = f->descriptors; + break; + default: + descriptors = f->fs_descriptors; + } + if (!descriptors) continue; status = usb_descriptor_fillbuf(next, len, @@ -285,12 +440,14 @@ 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; - if (gadget_is_dualspeed(gadget)) { - int hs = 0; - + if (gadget->speed == USB_SPEED_SUPER) + speed = gadget->speed; + else if (gadget_is_dualspeed(gadget)) { + int hs = 0; if (gadget->speed == USB_SPEED_HIGH) hs = 1; if (type == USB_DT_OTHER_SPEED_CONFIG) @@ -302,15 +459,35 @@ 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 */ - if (speed == USB_SPEED_HIGH) { + switch (speed) { + case USB_SPEED_SUPER: + if (!c->superspeed) + continue; + break; + case USB_SPEED_HIGH: if (!c->highspeed) continue; - } else { + break; + default: if (!c->fullspeed) continue; } + if (w_value == 0) return config_buf(c, speed, cdev->req->buf, type); w_value--; @@ -324,16 +501,22 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) struct usb_configuration *c; unsigned count = 0; int hs = 0; + int ss = 0; if (gadget_is_dualspeed(gadget)) { if (gadget->speed == USB_SPEED_HIGH) hs = 1; + if (gadget->speed == USB_SPEED_SUPER) + ss = 1; if (type == USB_DT_DEVICE_QUALIFIER) hs = !hs; } list_for_each_entry(c, &cdev->configs, list) { /* ignore configs that won't work at this speed */ - if (hs) { + if (ss) { + if (!c->superspeed) + continue; + } else if (hs) { if (!c->highspeed) continue; } else { @@ -345,6 +528,71 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) return count; } +/** + * bos_desc() - prepares the BOS descriptor. + * @cdev: pointer to usb_composite device to generate the bos + * descriptor for + * + * This function generates the BOS (Binary Device Object) + * descriptor and its device capabilities descriptors. The BOS + * descriptor should be supported by a SuperSpeed device. + */ +static int bos_desc(struct usb_composite_dev *cdev) +{ + struct usb_ext_cap_descriptor *usb_ext; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_dcd_config_params dcd_config_params; + struct usb_bos_descriptor *bos = cdev->req->buf; + + bos->bLength = USB_DT_BOS_SIZE; + bos->bDescriptorType = USB_DT_BOS; + + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); + bos->bNumDeviceCaps = 0; + + /* + * A SuperSpeed device shall include the USB2.0 extension descriptor + * and shall support LPM when operating in USB2.0 HS mode. + */ + usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); + usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; + usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT); + + /* + * The Superspeed USB Capability descriptor shall be implemented by all + * SuperSpeed devices. + */ + ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* LTM is not supported yet */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | + USB_FULL_SPEED_OPERATION | + USB_HIGH_SPEED_OPERATION | + USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) + cdev->gadget->ops->get_config_params(&dcd_config_params); + else { + dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + } + ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; + ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; + + return le16_to_cpu(bos->wTotalLength); +} + static void device_qual(struct usb_composite_dev *cdev) { struct usb_qualifier_descriptor *qual = cdev->req->buf; @@ -357,7 +605,7 @@ static void device_qual(struct usb_composite_dev *cdev) qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; /* ASSUME same EP0 fifo size at both speeds */ - qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket; qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); qual->bRESERVED = 0; } @@ -373,8 +621,11 @@ static void reset_config(struct usb_composite_dev *cdev) list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; + cdev->delayed_status = 0; } static int set_config(struct usb_composite_dev *cdev, @@ -386,42 +637,76 @@ static int set_config(struct usb_composite_dev *cdev, unsigned power = gadget_is_otg(gadget) ? 8 : 100; int tmp; - if (cdev->config) - reset_config(cdev); - if (number) { list_for_each_entry(c, &cdev->configs, list) { if (c->bConfigurationValue == number) { + /* + * We disable the FDs of the previous + * configuration only if the new configuration + * is a valid one + */ + if (cdev->config) + reset_config(cdev); result = 0; break; } } if (result < 0) goto done; - } else + } else { /* Zero configuration value - need to reset the config */ + if (cdev->config) + reset_config(cdev); result = 0; + } - INFO(cdev, "%s speed config #%d: %s\n", - ({ char *speed; - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } ; speed; }), number, c ? c->label : "unconfigured"); + INFO(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); cdev->config = c; /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { struct usb_function *f = c->interface[tmp]; + struct usb_descriptor_header **descriptors; if (!f) break; + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + switch (gadget->speed) { + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + break; + case USB_SPEED_HIGH: + descriptors = f->hs_descriptors; + break; + default: + descriptors = f->fs_descriptors; + } + + for (; *descriptors; ++descriptors) { + struct usb_endpoint_descriptor *ep; + int addr; + + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + set_bit(addr, f->endpoints); + } + result = f->set_alt(f, tmp, 0); if (result < 0) { DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", @@ -430,65 +715,106 @@ static int set_config(struct usb_composite_dev *cdev, reset_config(cdev); goto done; } + + if (result == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, tmp, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } } /* 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) + result = USB_GADGET_DELAYED_STATUS; 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 * @config: the configuration, with bConfigurationValue assigned + * @bind: the configuration's bind function * Context: single threaded during gadget setup * - * One of the main tasks of a composite driver's bind() routine is to + * One of the main tasks of a composite @bind() routine is to * add each of the configurations it supports, using this routine. * - * This function returns the value of the configuration's bind(), which + * This function returns the value of the configuration's @bind(), which * is zero for success else a negative errno value. Binding configurations * assigns global resources including string IDs, and per-configuration * resources such as interface IDs and endpoints. */ -int __init usb_add_config(struct usb_composite_dev *cdev, - struct usb_configuration *config) +int usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config, + 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 || !config->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; - - status = config->bind(config); + status = bind(config); if (status < 0) { + while (!list_empty(&config->functions)) { + struct usb_function *f; + + f = list_first_entry(&config->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(config, f); + /* may free memory for "f" */ + } + } list_del(&config->list); config->cdev = NULL; } else { unsigned i; - DBG(cdev, "cfg %d/%p speeds:%s%s\n", + DBG(cdev, "cfg %d/%p speeds:%s%s%s\n", config->bConfigurationValue, config, + config->superspeed ? " super" : "", config->highspeed ? " high" : "", config->fullspeed ? (gadget_is_dualspeed(cdev->gadget) @@ -506,7 +832,7 @@ int __init usb_add_config(struct usb_composite_dev *cdev, } } - /* set_alt(), or next config->bind(), sets up + /* set_alt(), or next bind(), sets up * ep->driver_data as needed. */ usb_ep_autoconfig_reset(cdev->gadget); @@ -517,6 +843,54 @@ done: config->bConfigurationValue, status); return status; } +EXPORT_SYMBOL_GPL(usb_add_config); + +static void remove_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + while (!list_empty(&config->functions)) { + struct usb_function *f; + + f = list_first_entry(&config->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", f->name, f); + f->unbind(config, f); + /* may free memory for "f" */ + } + } + list_del(&config->list); + if (config->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", config->label, config); + config->unbind(config); + /* may free memory for "c" */ + } +} + +/** + * usb_remove_config() - remove a configuration from a device. + * @cdev: wraps the USB gadget + * @config: the configuration + * + * Drivers must call usb_gadget_disconnect before calling this function + * to disconnect the device from the host and make sure the host will not + * try to enumerate the device while we are changing the config list. + */ +void usb_remove_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->config == config) + reset_config(cdev); + + spin_unlock_irqrestore(&cdev->lock, flags); + + remove_config(cdev, config); +} /*-------------------------------------------------------------------------*/ @@ -529,7 +903,7 @@ done: static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) { const struct usb_gadget_strings *s; - u16 language; + __le16 language; __le16 *tmp; while (*sp) { @@ -569,6 +943,8 @@ static int lookup_string( 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; @@ -601,8 +977,14 @@ 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; - for (len = 0; s->wData[len] && len <= 126; len++) + sp = get_containers_gs(uc); + collect_langs(sp, s->wData); + } + + for (len = 0; len <= 126 && s->wData[len]; len++) continue; if (!len) return -EINVAL; @@ -611,9 +993,31 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } - /* Otherwise, look up and return a specified string. String IDs - * are device-scoped, so we look up each string table we're told - * about. These lookups are infrequent; simpler-is-better here. + if (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. */ if (composite->strings) { len = lookup_string(composite->strings, buf, language, id); @@ -646,19 +1050,197 @@ static int get_string(struct usb_composite_dev *cdev, * string IDs. Drivers for functions, configurations, or gadgets will * then store that ID in the appropriate descriptors and string table. * - * All string identifier should be allocated using this routine, to - * ensure that for example different functions don't wrongly assign - * different meanings to the same identifier. + * All string identifier should be allocated using this, + * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure + * that for example different functions don't wrongly assign different + * meanings to the same identifier. */ -int __init usb_string_id(struct usb_composite_dev *cdev) +int usb_string_id(struct usb_composite_dev *cdev) { if (cdev->next_string_id < 254) { - /* string id 0 is reserved */ + /* string id 0 is reserved by USB spec for list of + * supported languages */ + /* 255 reserved as well? -- mina86 */ cdev->next_string_id++; return cdev->next_string_id; } return -ENODEV; } +EXPORT_SYMBOL_GPL(usb_string_id); + +/** + * usb_string_ids() - allocate unused string IDs in batch + * @cdev: the device whose string descriptor IDs are being allocated + * @str: an array of usb_string objects to assign numbers to + * Context: single threaded during gadget setup + * + * @usb_string_ids() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then copy IDs from the string table to the appropriate descriptors + * and string table for other languages. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str) +{ + int next = cdev->next_string_id; + + for (; str->s; ++str) { + if (unlikely(next >= 254)) + return -ENODEV; + str->id = ++next; + } + + cdev->next_string_id = next; + + return 0; +} +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 + * @n: number of string IDs to allocate + * Context: single threaded during gadget setup + * + * Returns the first requested ID. This ID and next @n-1 IDs are now + * valid IDs. At least provided that @n is non-zero because if it + * is, returns last requested ID which is now very useful information. + * + * @usb_string_ids_n() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_n(struct usb_composite_dev *c, unsigned n) +{ + unsigned next = c->next_string_id; + if (unlikely(n > 254 || (unsigned)next + n > 254)) + return -ENODEV; + c->next_string_id += n; + return next + 1; +} +EXPORT_SYMBOL_GPL(usb_string_ids_n); /*-------------------------------------------------------------------------*/ @@ -670,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 @@ -677,17 +1409,19 @@ 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); struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; + int status = 0; u16 w_index = le16_to_cpu(ctrl->wIndex); u8 intf = w_index & 0xFF; u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + u8 endp; /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion @@ -695,7 +1429,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) */ req->zero = 0; req->complete = composite_setup_complete; - req->length = USB_BUFSIZ; + req->length = 0; gadget->ep0->driver_data = cdev; switch (ctrl->bRequest) { @@ -709,18 +1443,31 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE: cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE); + cdev->desc.bMaxPacketSize0 = + cdev->gadget->ep0->maxpacket; + if (gadget_is_superspeed(gadget)) { + if (gadget->speed >= USB_SPEED_SUPER) { + cdev->desc.bcdUSB = cpu_to_le16(0x0300); + cdev->desc.bMaxPacketSize0 = 9; + } else { + cdev->desc.bcdUSB = cpu_to_le16(0x0210); + } + } + value = min(w_length, (u16) sizeof cdev->desc); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) + if (!gadget_is_dualspeed(gadget) || + gadget->speed >= USB_SPEED_SUPER) break; device_qual(cdev); value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor)); break; case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) + if (!gadget_is_dualspeed(gadget) || + gadget->speed >= USB_SPEED_SUPER) break; /* FALLTHROUGH */ case USB_DT_CONFIG: @@ -734,6 +1481,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value >= 0) value = min(w_length, (u16) value); break; + case USB_DT_BOS: + if (gadget_is_superspeed(gadget)) { + value = bos_desc(cdev); + value = min(w_length, (u16) value); + } + break; } break; @@ -769,7 +1522,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; - if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) @@ -777,11 +1530,19 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); + if (value == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, intf, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto unknown; - if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) @@ -793,42 +1554,208 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); break; + + /* + * USB 3.0 additions: + * Function driver should handle get_status request. If such cb + * wasn't supplied we respond with default value = 0 + * Note: function driver should supply such cb only for the first + * interface of the function + */ + case USB_REQ_GET_STATUS: + if (!gadget_is_superspeed(gadget)) + goto unknown; + if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) + goto unknown; + value = 2; /* This is the length of the get_status reply */ + put_unaligned_le16(0, req->buf); + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + status = f->get_status ? f->get_status(f) : 0; + if (status < 0) + break; + put_unaligned_le16(status & 0x0000ffff, req->buf); + break; + /* + * Function drivers should handle SetFeature/ClearFeature + * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied + * only for the first interface of the function + */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (!gadget_is_superspeed(gadget)) + goto unknown; + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) + goto unknown; + switch (w_value) { + case USB_INTRF_FUNC_SUSPEND: + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + value = 0; + if (f->func_suspend) + value = f->func_suspend(f, w_index >> 8); + if (value < 0) { + ERROR(cdev, + "func_suspend() returned error %d\n", + value); + value = 0; + } + break; + } + 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, w_value, w_index, w_length); - /* functions always handle their interfaces ... punt other - * recipients (endpoint, other, WUSB, ...) to the current + /* functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current * configuration code. * * REVISIT it could make sense to let the composite device * take such requests too, if that's ever needed: to work * in config 0, etc. */ - if ((ctrl->bRequestType & USB_RECIP_MASK) - == USB_RECIP_INTERFACE) { + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) + break; f = cdev->config->interface[intf]; - if (f && f->setup) - value = f->setup(f, ctrl); - else + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) f = NULL; + break; } - if (value < 0 && !f) { + + if (f && f->setup) + value = f->setup(f, ctrl); + else { 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; } /* respond with data transfer before status phase? */ - if (value >= 0) { + if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; req->zero = value < w_length; value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); @@ -837,6 +1764,10 @@ unknown: req->status = 0; composite_setup_complete(gadget->ep0, req); } + } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) { + WARN(cdev, + "%s: Delayed status not supported for w_length != 0", + __func__); } done: @@ -844,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; @@ -855,13 +1786,24 @@ static void composite_disconnect(struct usb_gadget *gadget) spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) reset_config(cdev); + if (cdev->driver->disconnect) + cdev->driver->disconnect(cdev); spin_unlock_irqrestore(&cdev->lock, flags); } /*-------------------------------------------------------------------------*/ -static void /* __init_or_exit */ -composite_unbind(struct usb_gadget *gadget) +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 void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) { struct usb_composite_dev *cdev = get_gadget_data(gadget); @@ -874,97 +1816,172 @@ composite_unbind(struct usb_gadget *gadget) while (!list_empty(&cdev->configs)) { struct usb_configuration *c; - c = list_first_entry(&cdev->configs, struct usb_configuration, list); - while (!list_empty(&c->functions)) { - struct usb_function *f; - - f = list_first_entry(&c->functions, - struct usb_function, list); - list_del(&f->list); - if (f->unbind) { - DBG(cdev, "unbind function '%s'/%p\n", - f->name, f); - f->unbind(c, f); - /* may free memory for "f" */ - } - } - list_del(&c->list); - if (c->unbind) { - DBG(cdev, "unbind config '%s'/%p\n", c->label, c); - c->unbind(c); - /* may free memory for "c" */ - } + remove_config(cdev, c); } - if (composite->unbind) - composite->unbind(cdev); + 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); - } + composite_dev_cleanup(cdev); + + kfree(cdev->def_manufacturer); kfree(cdev); set_gadget_data(gadget, NULL); - composite = NULL; } -static void __init -string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +static void composite_unbind(struct usb_gadget *gadget) { - struct usb_string *str = tab->strings; - - for (str = tab->strings; str->s; str++) { - if (str->id == id) { - str->s = s; - return; - } - } + __composite_unbind(gadget, true); } -static void __init -string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +static void update_unchanged_dev_desc(struct usb_device_descriptor *new, + const struct usb_device_descriptor *old) { - while (*tab) { - string_override_one(*tab, id, s); - tab++; - } + __le16 idVendor; + __le16 idProduct; + __le16 bcdDevice; + u8 iSerialNumber; + u8 iManufacturer; + u8 iProduct; + + /* + * these variables may have been set in + * usb_composite_overwrite_options() + */ + idVendor = new->idVendor; + idProduct = new->idProduct; + bcdDevice = new->bcdDevice; + iSerialNumber = new->iSerialNumber; + iManufacturer = new->iManufacturer; + iProduct = new->iProduct; + + *new = *old; + if (idVendor) + new->idVendor = idVendor; + if (idProduct) + new->idProduct = idProduct; + if (bcdDevice) + new->bcdDevice = bcdDevice; + else + new->bcdDevice = cpu_to_le16(get_default_bcdDevice()); + if (iSerialNumber) + new->iSerialNumber = iSerialNumber; + if (iManufacturer) + new->iManufacturer = iManufacturer; + if (iProduct) + new->iProduct = iProduct; } -static int __init composite_bind(struct usb_gadget *gadget) +int composite_dev_prepare(struct usb_composite_driver *composite, + struct usb_composite_dev *cdev) { - struct usb_composite_dev *cdev; - int status = -ENOMEM; - - cdev = kzalloc(sizeof *cdev, GFP_KERNEL); - if (!cdev) - return status; - - spin_lock_init(&cdev->lock); - cdev->gadget = gadget; - set_gadget_data(gadget, cdev); - INIT_LIST_HEAD(&cdev->configs); + 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; - cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + 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; - cdev->bufsiz = USB_BUFSIZ; cdev->driver = composite; - usb_gadget_set_selfpowered(gadget); + /* + * As per USB compliance update, a device that is actively drawing + * more than 100mA from USB must report itself as bus-powered in + * the GetStatus(DEVICE) call. + */ + if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW) + usb_gadget_set_selfpowered(gadget); /* interface and string IDs start at zero via kzalloc. * we force endpoints to start unassigned; few controller * drivers will zero ep->driver_data. */ - usb_ep_autoconfig_reset(cdev->gadget); + 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 @@ -974,35 +1991,23 @@ static int __init composite_bind(struct usb_gadget *gadget) if (status < 0) goto fail; - cdev->desc = *composite->dev; - cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + if (cdev->use_os_string) { + status = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (status) + goto fail; + } - /* standardized runtime overrides for device ID data */ - if (idVendor) - cdev->desc.idVendor = cpu_to_le16(idVendor); - if (idProduct) - cdev->desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) - cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + update_unchanged_dev_desc(&cdev->desc, composite->dev); - /* strings can't be assigned before bind() allocates the - * releavnt identifiers - */ - if (cdev->desc.iManufacturer && iManufacturer) - string_override(composite->strings, - cdev->desc.iManufacturer, iManufacturer); - if (cdev->desc.iProduct && iProduct) - string_override(composite->strings, - cdev->desc.iProduct, iProduct); - if (cdev->desc.iSerialNumber && iSerialNumber) - string_override(composite->strings, - cdev->desc.iSerialNumber, iSerialNumber); + /* has userspace failed to provide a serial number? */ + if (composite->needs_serial && !cdev->desc.iSerialNumber) + WARNING(cdev, "userspace failed to provide iSerialNumber\n"); INFO(cdev, "%s ready\n", composite->name); return 0; fail: - composite_unbind(gadget); + __composite_unbind(gadget, false); return status; } @@ -1024,8 +2029,12 @@ composite_suspend(struct usb_gadget *gadget) f->suspend(f); } } - if (composite->suspend) - composite->suspend(cdev); + if (cdev->driver->suspend) + cdev->driver->suspend(cdev); + + cdev->suspended = 1; + + usb_gadget_vbus_draw(gadget, 2); } static void @@ -1033,28 +2042,34 @@ composite_resume(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_function *f; + u16 maxpower; /* REVISIT: should we have config level * suspend/resume callbacks? */ DBG(cdev, "resume\n"); - if (composite->resume) - composite->resume(cdev); + if (cdev->driver->resume) + cdev->driver->resume(cdev); if (cdev->config) { list_for_each_entry(f, &cdev->config->functions, list) { if (f->resume) f->resume(f); } + + maxpower = cdev->config->MaxPower; + + usb_gadget_vbus_draw(gadget, maxpower ? + maxpower : CONFIG_USB_GADGET_VBUS_DRAW); } + + cdev->suspended = 0; } /*-------------------------------------------------------------------------*/ -static struct usb_gadget_driver composite_driver = { - .speed = USB_SPEED_HIGH, - +static const struct usb_gadget_driver composite_driver_template = { .bind = composite_bind, - .unbind = __exit_p(composite_unbind), + .unbind = composite_unbind, .setup = composite_setup, .disconnect = composite_disconnect, @@ -1068,8 +2083,9 @@ static struct usb_gadget_driver composite_driver = { }; /** - * usb_composite_register() - register a composite driver + * usb_composite_probe() - register a composite driver * @driver: the driver to register + * * Context: single threaded during gadget setup * * This function is used to register drivers using the composite driver @@ -1082,19 +2098,26 @@ static struct usb_gadget_driver composite_driver = { * while it was binding. That would usually be done in order to wait for * some userspace participation. */ -int __init usb_composite_register(struct usb_composite_driver *driver) +int usb_composite_probe(struct usb_composite_driver *driver) { - if (!driver || !driver->dev || !driver->bind || composite) + struct usb_gadget_driver *gadget_driver; + + if (!driver || !driver->dev || !driver->bind) return -EINVAL; if (!driver->name) driver->name = "composite"; - composite_driver.function = (char *) driver->name; - composite_driver.driver.name = driver->name; - composite = driver; - return usb_gadget_register_driver(&composite_driver); + driver->gadget_driver = composite_driver_template; + gadget_driver = &driver->gadget_driver; + + gadget_driver->function = (char *) driver->name; + gadget_driver->driver.name = driver->name; + gadget_driver->max_speed = driver->max_speed; + + return usb_gadget_probe_driver(gadget_driver); } +EXPORT_SYMBOL_GPL(usb_composite_probe); /** * usb_composite_unregister() - unregister a composite driver @@ -1103,9 +2126,101 @@ int __init usb_composite_register(struct usb_composite_driver *driver) * This function is used to unregister drivers using the composite * driver framework. */ -void __exit usb_composite_unregister(struct usb_composite_driver *driver) +void usb_composite_unregister(struct usb_composite_driver *driver) { - if (composite != driver) - return; - usb_gadget_unregister_driver(&composite_driver); + usb_gadget_unregister_driver(&driver->gadget_driver); } +EXPORT_SYMBOL_GPL(usb_composite_unregister); + +/** + * usb_composite_setup_continue() - Continue with the control transfer + * @cdev: the composite device who's control transfer was kept waiting + * + * This function must be called by the USB function driver to continue + * with the control transfer's data/status stage in case it had requested to + * delay the data/status stages. A USB function's setup handler (e.g. set_alt()) + * can request the composite framework to delay the setup request's data/status + * stages by returning USB_GADGET_DELAYED_STATUS. + */ +void usb_composite_setup_continue(struct usb_composite_dev *cdev) +{ + int value; + struct usb_request *req = cdev->req; + unsigned long flags; + + DBG(cdev, "%s\n", __func__); + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->delayed_status == 0) { + WARN(cdev, "%s: Unexpected call\n", __func__); + + } else if (--cdev->delayed_status == 0) { + DBG(cdev, "%s: Completing delayed status\n", __func__); + req->length = 0; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(cdev->gadget->ep0, req); + } + } + + spin_unlock_irqrestore(&cdev->lock, flags); +} +EXPORT_SYMBOL_GPL(usb_composite_setup_continue); + +static char *composite_default_mfr(struct usb_gadget *gadget) +{ + char *mfr; + int len; + + len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname, + init_utsname()->release, gadget->name); + len++; + mfr = kmalloc(len, GFP_KERNEL); + if (!mfr) + return NULL; + snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname, + init_utsname()->release, gadget->name); + return mfr; +} + +void usb_composite_overwrite_options(struct usb_composite_dev *cdev, + struct usb_composite_overwrite *covr) +{ + struct usb_device_descriptor *desc = &cdev->desc; + struct usb_gadget_strings *gstr = cdev->driver->strings[0]; + struct usb_string *dev_str = gstr->strings; + + if (covr->idVendor) + desc->idVendor = cpu_to_le16(covr->idVendor); + + if (covr->idProduct) + desc->idProduct = cpu_to_le16(covr->idProduct); + + if (covr->bcdDevice) + desc->bcdDevice = cpu_to_le16(covr->bcdDevice); + + if (covr->serial_number) { + desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id; + dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number; + } + if (covr->manufacturer) { + desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; + dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer; + + } else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) { + desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id; + cdev->def_manufacturer = composite_default_mfr(cdev->gadget); + dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer; + } + + if (covr->product) { + desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id; + dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product; + } +} +EXPORT_SYMBOL_GPL(usb_composite_overwrite_options); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index e1191b9a316..34e12fc52c2 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -7,26 +7,19 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/errno.h> +#include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/list.h> #include <linux/string.h> #include <linux/device.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> - +#include <linux/usb/composite.h> /** * usb_descriptor_fillbuf - fill buffer with descriptors @@ -61,7 +54,7 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen, } return dest - (u8 *)buf; } - +EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf); /** * usb_gadget_config_buf - builts a complete configuration descriptor @@ -114,6 +107,7 @@ int usb_gadget_config_buf( cp->bmAttributes |= USB_CONFIG_ATT_ONE; return len; } +EXPORT_SYMBOL_GPL(usb_gadget_config_buf); /** * usb_copy_descriptors - copy a vector of USB descriptors @@ -127,7 +121,7 @@ int usb_gadget_config_buf( * with identifiers (for interfaces, strings, endpoints, and more) * as needed by a given function instance. */ -struct usb_descriptor_header **__init +struct usb_descriptor_header ** usb_copy_descriptors(struct usb_descriptor_header **src) { struct usb_descriptor_header **tmp; @@ -163,29 +157,41 @@ usb_copy_descriptors(struct usb_descriptor_header **src) return ret; } +EXPORT_SYMBOL_GPL(usb_copy_descriptors); -/** - * usb_find_endpoint - find a copy of an endpoint descriptor - * @src: original vector of descriptors - * @copy: copy of @src - * @match: endpoint descriptor found in @src - * - * This returns the copy of the @match descriptor made for @copy. Its - * intended use is to help remembering the endpoint descriptor to use - * when enabling a given endpoint. - */ -struct usb_endpoint_descriptor *__init -usb_find_endpoint( - struct usb_descriptor_header **src, - struct usb_descriptor_header **copy, - struct usb_endpoint_descriptor *match -) +int usb_assign_descriptors(struct usb_function *f, + struct usb_descriptor_header **fs, + struct usb_descriptor_header **hs, + struct usb_descriptor_header **ss) { - while (*src) { - if (*src == (void *) match) - return (void *)*copy; - src++; - copy++; + struct usb_gadget *g = f->config->cdev->gadget; + + if (fs) { + f->fs_descriptors = usb_copy_descriptors(fs); + if (!f->fs_descriptors) + goto err; + } + if (hs && gadget_is_dualspeed(g)) { + f->hs_descriptors = usb_copy_descriptors(hs); + if (!f->hs_descriptors) + goto err; } - return NULL; + if (ss && gadget_is_superspeed(g)) { + f->ss_descriptors = usb_copy_descriptors(ss); + if (!f->ss_descriptors) + goto err; + } + return 0; +err: + usb_free_all_descriptors(f); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(usb_assign_descriptors); + +void usb_free_all_descriptors(struct usb_function *f) +{ + usb_free_descriptors(f->fs_descriptors); + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->ss_descriptors); } +EXPORT_SYMBOL_GPL(usb_free_all_descriptors); 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 new file mode 100644 index 00000000000..986fc511a2e --- /dev/null +++ b/drivers/usb/gadget/dbgp.c @@ -0,0 +1,434 @@ +/* + * dbgp.c -- EHCI Debug Port device gadget + * + * Copyright (C) 2010 Stephane Duverger + * + * Released under the GPLv2. + */ + +/* verbose messages */ +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "u_serial.h" + +#define DRIVER_VENDOR_ID 0x0525 /* NetChip */ +#define DRIVER_PRODUCT_ID 0xc0de /* undefined */ + +#define USB_DEBUG_MAX_PACKET_SIZE 8 +#define DBGP_REQ_EP0_LEN 128 +#define DBGP_REQ_LEN 512 + +static struct dbgp { + struct usb_gadget *gadget; + struct usb_request *req; + struct usb_ep *i_ep; + struct usb_ep *o_ep; +#ifdef CONFIG_USB_G_DBGP_SERIAL + struct gserial *serial; +#endif +} dbgp; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), + .bNumConfigurations = 1, +}; + +static struct usb_debug_descriptor dbg_desc = { + .bLength = sizeof dbg_desc, + .bDescriptorType = USB_DT_DEBUG, +}; + +static struct usb_endpoint_descriptor i_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_IN, +}; + +static struct usb_endpoint_descriptor o_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_OUT, +}; + +#ifdef CONFIG_USB_G_DBGP_PRINTK +static int dbgp_consume(char *buf, unsigned len) +{ + char c; + + if (!len) + return 0; + + c = buf[len-1]; + if (c != 0) + buf[len-1] = 0; + + printk(KERN_NOTICE "%s%c", buf, c); + return 0; +} + +static void __disable_ep(struct usb_ep *ep) +{ + if (ep && ep->driver_data == dbgp.gadget) { + usb_ep_disable(ep); + ep->driver_data = NULL; + } +} + +static void dbgp_disable_ep(void) +{ + __disable_ep(dbgp.i_ep); + __disable_ep(dbgp.o_ep); +} + +static void dbgp_complete(struct usb_ep *ep, struct usb_request *req) +{ + int stp; + int err = 0; + int status = req->status; + + if (ep == dbgp.i_ep) { + stp = 1; + goto fail; + } + + if (status != 0) { + stp = 2; + goto release_req; + } + + dbgp_consume(req->buf, req->actual); + + req->length = DBGP_REQ_LEN; + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err < 0) { + stp = 3; + goto release_req; + } + + return; + +release_req: + kfree(req->buf); + usb_ep_free_request(dbgp.o_ep, req); + dbgp_disable_ep(); +fail: + dev_dbg(&dbgp.gadget->dev, + "complete: failure (%d:%d) ==> %d\n", stp, err, status); +} + +static int dbgp_enable_ep_req(struct usb_ep *ep) +{ + int err, stp; + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + stp = 1; + goto fail_1; + } + + req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL); + if (!req->buf) { + err = -ENOMEM; + stp = 2; + goto fail_2; + } + + req->complete = dbgp_complete; + req->length = DBGP_REQ_LEN; + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + kfree(req->buf); +fail_2: + usb_ep_free_request(dbgp.o_ep, req); +fail_1: + dev_dbg(&dbgp.gadget->dev, + "enable ep req: failure (%d:%d)\n", stp, err); + return err; +} + +static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc) +{ + int err; + ep->desc = desc; + err = usb_ep_enable(ep); + ep->driver_data = dbgp.gadget; + return err; +} + +static int dbgp_enable_ep(void) +{ + int err, stp; + + err = __enable_ep(dbgp.i_ep, &i_desc); + if (err < 0) { + stp = 1; + goto fail_1; + } + + err = __enable_ep(dbgp.o_ep, &o_desc); + if (err < 0) { + stp = 2; + goto fail_2; + } + + err = dbgp_enable_ep_req(dbgp.o_ep); + if (err < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + __disable_ep(dbgp.o_ep); +fail_2: + __disable_ep(dbgp.i_ep); +fail_1: + dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err); + return err; +} +#endif + +static void dbgp_disconnect(struct usb_gadget *gadget) +{ +#ifdef CONFIG_USB_G_DBGP_PRINTK + dbgp_disable_ep(); +#else + gserial_disconnect(dbgp.serial); +#endif +} + +static void dbgp_unbind(struct usb_gadget *gadget) +{ +#ifdef CONFIG_USB_G_DBGP_SERIAL + kfree(dbgp.serial); +#endif + if (dbgp.req) { + kfree(dbgp.req->buf); + usb_ep_free_request(gadget->ep0, dbgp.req); + } + + 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; + + usb_ep_autoconfig_reset(gadget); + + dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc); + if (!dbgp.i_ep) { + stp = 1; + goto fail_1; + } + + dbgp.i_ep->driver_data = gadget; + i_desc.wMaxPacketSize = + __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc); + if (!dbgp.o_ep) { + dbgp.i_ep->driver_data = NULL; + stp = 2; + goto fail_2; + } + + dbgp.o_ep->driver_data = gadget; + o_desc.wMaxPacketSize = + __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress; + dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress; + +#ifdef CONFIG_USB_G_DBGP_SERIAL + dbgp.serial->in = dbgp.i_ep; + dbgp.serial->out = dbgp.o_ep; + + dbgp.serial->in->desc = &i_desc; + dbgp.serial->out->desc = &o_desc; + + if (gserial_alloc_line(&tty_line)) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + dbgp.o_ep->driver_data = NULL; +#else + return 0; +#endif +fail_2: + dbgp.i_ep->driver_data = NULL; +fail_1: + dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp); + return -ENODEV; +} + +static int __init dbgp_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + int err, stp; + + dbgp.gadget = gadget; + + dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!dbgp.req) { + err = -ENOMEM; + stp = 1; + goto fail; + } + + dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL); + if (!dbgp.req->buf) { + err = -ENOMEM; + stp = 2; + goto fail; + } + + dbgp.req->length = DBGP_REQ_EP0_LEN; + gadget->ep0->driver_data = gadget; + +#ifdef CONFIG_USB_G_DBGP_SERIAL + dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); + if (!dbgp.serial) { + stp = 3; + err = -ENOMEM; + goto fail; + } +#endif + err = dbgp_configure_endpoints(gadget); + if (err < 0) { + stp = 4; + goto fail; + } + + dev_dbg(&dbgp.gadget->dev, "bind: success\n"); + return 0; + +fail: + dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err); + dbgp_unbind(gadget); + return err; +} + +static void dbgp_setup_complete(struct usb_ep *ep, + struct usb_request *req) +{ + dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int dbgp_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = dbgp.req; + u8 request = ctrl->bRequest; + u16 value = le16_to_cpu(ctrl->wValue); + u16 length = le16_to_cpu(ctrl->wLength); + int err = -EOPNOTSUPP; + void *data = NULL; + u16 len = 0; + + gadget->ep0->driver_data = gadget; + + if (request == USB_REQ_GET_DESCRIPTOR) { + switch (value>>8) { + case USB_DT_DEVICE: + dev_dbg(&dbgp.gadget->dev, "setup: desc device\n"); + len = sizeof device_desc; + data = &device_desc; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + break; + case USB_DT_DEBUG: + dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n"); + len = sizeof dbg_desc; + data = &dbg_desc; + break; + default: + goto fail; + } + err = 0; + } else if (request == USB_REQ_SET_FEATURE && + value == USB_DEVICE_DEBUG_MODE) { + dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n"); +#ifdef CONFIG_USB_G_DBGP_PRINTK + err = dbgp_enable_ep(); +#else + err = gserial_connect(dbgp.serial, tty_line); +#endif + if (err < 0) + goto fail; + } else + goto fail; + + req->length = min(length, len); + req->zero = len < req->length; + if (data && req->length) + memcpy(req->buf, data, req->length); + + req->complete = dbgp_setup_complete; + return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + +fail: + dev_dbg(&dbgp.gadget->dev, + "setup: failure req %x v %x\n", request, value); + return err; +} + +static __refdata struct usb_gadget_driver dbgp_driver = { + .function = "dbgp", + .max_speed = USB_SPEED_HIGH, + .bind = dbgp_bind, + .unbind = dbgp_unbind, + .setup = dbgp_setup, + .disconnect = dbgp_disconnect, + .driver = { + .owner = THIS_MODULE, + .name = "dbgp" + }, +}; + +static int __init dbgp_init(void) +{ + return usb_gadget_probe_driver(&dbgp_driver); +} + +static void __exit dbgp_exit(void) +{ + usb_gadget_unregister_driver(&dbgp_driver); +#ifdef CONFIG_USB_G_DBGP_SERIAL + gserial_free_line(tty_line); +#endif +} + +MODULE_AUTHOR("Stephane Duverger"); +MODULE_LICENSE("GPL"); +module_init(dbgp_init); +module_exit(dbgp_exit); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a56b24d305f..2b54955d316 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -47,31 +38,45 @@ #include <linux/platform_device.h> #include <linux/usb.h> #include <linux/usb/gadget.h> +#include <linux/usb/hcd.h> +#include <linux/scatterlist.h> #include <asm/byteorder.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/unaligned.h> - -#include "../core/hcd.h" - - #define DRIVER_DESC "USB Host+Gadget Emulator" #define DRIVER_VERSION "02 May 2005" #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ -static const char driver_name [] = "dummy_hcd"; -static const char driver_desc [] = "USB Host+Gadget Emulator"; +static const char driver_name[] = "dummy_hcd"; +static const char driver_desc[] = "USB Host+Gadget Emulator"; + +static const char gadget_name[] = "dummy_udc"; -static const char gadget_name [] = "dummy_udc"; +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); -MODULE_LICENSE ("GPL"); +struct dummy_hcd_module_parameters { + bool is_super_speed; + bool is_high_speed; + unsigned int num; +}; +static struct dummy_hcd_module_parameters mod_data = { + .is_super_speed = false, + .is_high_speed = true, + .num = 1, +}; +module_param_named(is_super_speed, mod_data.is_super_speed, bool, S_IRUGO); +MODULE_PARM_DESC(is_super_speed, "true to simulate SuperSpeed connection"); +module_param_named(is_high_speed, mod_data.is_high_speed, bool, S_IRUGO); +MODULE_PARM_DESC(is_high_speed, "true to simulate HighSpeed connection"); +module_param_named(num, mod_data.num, uint, S_IRUGO); +MODULE_PARM_DESC(num, "number of emulated controllers"); /*-------------------------------------------------------------------------*/ /* gadget side driver data structres */ @@ -81,10 +86,11 @@ struct dummy_ep { struct usb_gadget *gadget; const struct usb_endpoint_descriptor *desc; struct usb_ep ep; - unsigned halted : 1; - unsigned wedged : 1; - unsigned already_seen : 1; - unsigned setup_stage : 1; + unsigned halted:1; + unsigned wedged:1; + unsigned already_seen:1; + unsigned setup_stage:1; + unsigned stream_en:1; }; struct dummy_request { @@ -92,15 +98,15 @@ struct dummy_request { struct usb_request req; }; -static inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep) +static inline struct dummy_ep *usb_ep_to_dummy_ep(struct usb_ep *_ep) { - return container_of (_ep, struct dummy_ep, ep); + return container_of(_ep, struct dummy_ep, ep); } static inline struct dummy_request *usb_request_to_dummy_request (struct usb_request *_req) { - return container_of (_req, struct dummy_request, req); + return container_of(_req, struct dummy_request, req); } /*-------------------------------------------------------------------------*/ @@ -119,15 +125,12 @@ static inline struct dummy_request *usb_request_to_dummy_request * configurations, illegal or unsupported packet lengths, and so on. */ -static const char ep0name [] = "ep0"; +static const char ep0name[] = "ep0"; -static const char *const ep_name [] = { +static const char *const ep_name[] = { ep0name, /* everyone has ep0 */ - /* act like a net2280: high speed, six configurable endpoints */ - "ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f", - - /* or like pxa250: fifteen fixed function endpoints */ + /* act like a pxa250: fifteen fixed function endpoints */ "ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int", "ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int", "ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso", @@ -135,6 +138,10 @@ static const char *const ep_name [] = { /* or like sa1100: two fixed function endpoints */ "ep1out-bulk", "ep2in-bulk", + + /* and now some generic EPs so we have enough in multi config */ + "ep3out", "ep4in", "ep5out", "ep6out", "ep7in", "ep8out", "ep9in", + "ep10out", "ep11out", "ep12in", "ep13out", "ep14in", "ep15out", }; #define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name) @@ -145,6 +152,8 @@ static const char *const ep_name [] = { struct urbp { struct urb *urb; struct list_head urbp_list; + struct sg_mapping_iter miter; + u32 miter_started; }; @@ -154,98 +163,108 @@ enum dummy_rh_state { DUMMY_RH_RUNNING }; +struct dummy_hcd { + struct dummy *dum; + enum dummy_rh_state rh_state; + struct timer_list timer; + u32 port_status; + u32 old_status; + unsigned long re_timeout; + + struct usb_device *udev; + struct list_head urbp_list; + u32 stream_en_ep; + u8 num_stream[30 / 2]; + + unsigned active:1; + unsigned old_active:1; + unsigned resuming:1; +}; + struct dummy { spinlock_t lock; /* * SLAVE/GADGET side support */ - struct dummy_ep ep [DUMMY_ENDPOINTS]; + struct dummy_ep ep[DUMMY_ENDPOINTS]; int address; struct usb_gadget gadget; struct usb_gadget_driver *driver; struct dummy_request fifo_req; - u8 fifo_buf [FIFO_SIZE]; + u8 fifo_buf[FIFO_SIZE]; u16 devstatus; unsigned udc_suspended:1; unsigned pullup:1; - unsigned active:1; - unsigned old_active:1; /* * MASTER/HOST side support */ - enum dummy_rh_state rh_state; - struct timer_list timer; - u32 port_status; - u32 old_status; - unsigned resuming:1; - unsigned long re_timeout; - - struct usb_device *udev; - struct list_head urbp_list; + struct dummy_hcd *hs_hcd; + struct dummy_hcd *ss_hcd; }; -static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd) +static inline struct dummy_hcd *hcd_to_dummy_hcd(struct usb_hcd *hcd) { - return (struct dummy *) (hcd->hcd_priv); + return (struct dummy_hcd *) (hcd->hcd_priv); } -static inline struct usb_hcd *dummy_to_hcd (struct dummy *dum) +static inline struct usb_hcd *dummy_hcd_to_hcd(struct dummy_hcd *dum) { return container_of((void *) dum, struct usb_hcd, hcd_priv); } -static inline struct device *dummy_dev (struct dummy *dum) +static inline struct device *dummy_dev(struct dummy_hcd *dum) { - return dummy_to_hcd(dum)->self.controller; + return dummy_hcd_to_hcd(dum)->self.controller; } -static inline struct device *udc_dev (struct dummy *dum) +static inline struct device *udc_dev(struct dummy *dum) { return dum->gadget.dev.parent; } -static inline struct dummy *ep_to_dummy (struct dummy_ep *ep) +static inline struct dummy *ep_to_dummy(struct dummy_ep *ep) { - return container_of (ep->gadget, struct dummy, gadget); + return container_of(ep->gadget, struct dummy, gadget); } -static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget) +static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget) { - return container_of (gadget, struct dummy, gadget); + struct dummy *dum = container_of(gadget, struct dummy, gadget); + if (dum->gadget.speed == USB_SPEED_SUPER) + return dum->ss_hcd; + else + return dum->hs_hcd; } -static inline struct dummy *gadget_dev_to_dummy (struct device *dev) +static inline struct dummy *gadget_dev_to_dummy(struct device *dev) { - return container_of (dev, struct dummy, gadget.dev); + return container_of(dev, struct dummy, gadget.dev); } -static struct dummy *the_controller; - /*-------------------------------------------------------------------------*/ /* SLAVE/GADGET SIDE UTILITY ROUTINES */ /* called with spinlock held */ -static void nuke (struct dummy *dum, struct dummy_ep *ep) +static void nuke(struct dummy *dum, struct dummy_ep *ep) { - while (!list_empty (&ep->queue)) { + while (!list_empty(&ep->queue)) { struct dummy_request *req; - req = list_entry (ep->queue.next, struct dummy_request, queue); - list_del_init (&req->queue); + req = list_entry(ep->queue.next, struct dummy_request, queue); + list_del_init(&req->queue); req->req.status = -ESHUTDOWN; - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); } } /* caller must hold lock */ -static void -stop_activity (struct dummy *dum) +static void stop_activity(struct dummy *dum) { struct dummy_ep *ep; @@ -255,67 +274,128 @@ stop_activity (struct dummy *dum) /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ - list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) - nuke (dum, ep); + list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list) + nuke(dum, ep); /* driver now does any non-usb quiescing necessary */ } -/* caller must hold lock */ -static void -set_link_state (struct dummy *dum) -{ - dum->active = 0; - if ((dum->port_status & USB_PORT_STAT_POWER) == 0) - dum->port_status = 0; - - /* UDC suspend must cause a disconnect */ - else if (!dum->pullup || dum->udc_suspended) { - dum->port_status &= ~(USB_PORT_STAT_CONNECTION | - USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | - USB_PORT_STAT_HIGH_SPEED | - USB_PORT_STAT_SUSPEND); - if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0) - dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); +/** + * set_link_state_by_speed() - Sets the current state of the link according to + * the hcd speed + * @dum_hcd: pointer to the dummy_hcd structure to update the link state for + * + * This function updates the port_status according to the link state and the + * speed of the hcd. + */ +static void set_link_state_by_speed(struct dummy_hcd *dum_hcd) +{ + struct dummy *dum = dum_hcd->dum; + + if (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3) { + if ((dum_hcd->port_status & USB_SS_PORT_STAT_POWER) == 0) { + dum_hcd->port_status = 0; + } else if (!dum->pullup || dum->udc_suspended) { + /* UDC suspend must cause a disconnect */ + dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE); + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) != 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + } else { + /* device is connected and not suspended */ + dum_hcd->port_status |= (USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_SPEED_5GBPS) ; + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) == 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + if ((dum_hcd->port_status & + USB_PORT_STAT_ENABLE) == 1 && + (dum_hcd->port_status & + USB_SS_PORT_LS_U0) == 1 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) + dum_hcd->active = 1; + } } else { - dum->port_status |= USB_PORT_STAT_CONNECTION; - if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0) - dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); - if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) - dum->port_status &= ~USB_PORT_STAT_SUSPEND; - else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && - dum->rh_state != DUMMY_RH_SUSPENDED) - dum->active = 1; + if ((dum_hcd->port_status & USB_PORT_STAT_POWER) == 0) { + dum_hcd->port_status = 0; + } else if (!dum->pullup || dum->udc_suspended) { + /* UDC suspend must cause a disconnect */ + dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_SUSPEND); + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) != 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + } else { + dum_hcd->port_status |= USB_PORT_STAT_CONNECTION; + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) == 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0) + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; + else if ((dum_hcd->port_status & + USB_PORT_STAT_SUSPEND) == 0 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) + dum_hcd->active = 1; + } } +} - if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active) - dum->resuming = 0; - - if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 || - (dum->port_status & USB_PORT_STAT_RESET) != 0) { - if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 && - (dum->old_status & USB_PORT_STAT_RESET) == 0 && - dum->driver) { - stop_activity (dum); - spin_unlock (&dum->lock); - dum->driver->disconnect (&dum->gadget); - spin_lock (&dum->lock); +/* caller must hold lock */ +static void set_link_state(struct dummy_hcd *dum_hcd) +{ + struct dummy *dum = dum_hcd->dum; + + dum_hcd->active = 0; + if (dum->pullup) + if ((dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 && + dum->gadget.speed != USB_SPEED_SUPER) || + (dummy_hcd_to_hcd(dum_hcd)->speed != HCD_USB3 && + dum->gadget.speed == USB_SPEED_SUPER)) + return; + + set_link_state_by_speed(dum_hcd); + + if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 || + dum_hcd->active) + dum_hcd->resuming = 0; + + /* if !connected or reset */ + if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 || + (dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) { + /* + * We're connected and not reset (reset occurred now), + * and driver attached - disconnect! + */ + if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 && + (dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 && + dum->driver) { + stop_activity(dum); + spin_unlock(&dum->lock); + dum->driver->disconnect(&dum->gadget); + spin_lock(&dum->lock); } - } else if (dum->active != dum->old_active) { - if (dum->old_active && dum->driver->suspend) { - spin_unlock (&dum->lock); - dum->driver->suspend (&dum->gadget); - spin_lock (&dum->lock); - } else if (!dum->old_active && dum->driver->resume) { - spin_unlock (&dum->lock); - dum->driver->resume (&dum->gadget); - spin_lock (&dum->lock); + } else if (dum_hcd->active != dum_hcd->old_active) { + if (dum_hcd->old_active && dum->driver->suspend) { + spin_unlock(&dum->lock); + dum->driver->suspend(&dum->gadget); + spin_lock(&dum->lock); + } else if (!dum_hcd->old_active && dum->driver->resume) { + spin_unlock(&dum->lock); + dum->driver->resume(&dum->gadget); + spin_lock(&dum->lock); } } - dum->old_status = dum->port_status; - dum->old_active = dum->active; + dum_hcd->old_status = dum_hcd->port_status; + dum_hcd->old_active = dum_hcd->active; } /*-------------------------------------------------------------------------*/ @@ -330,22 +410,33 @@ set_link_state (struct dummy *dum) #define is_enabled(dum) \ (dum->port_status & USB_PORT_STAT_ENABLE) -static int -dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +static int dummy_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) { struct dummy *dum; + struct dummy_hcd *dum_hcd; struct dummy_ep *ep; unsigned max; int retval; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; - dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + dum = ep_to_dummy(ep); + if (!dum->driver) + return -ESHUTDOWN; + + dum_hcd = gadget_to_dummy_hcd(&dum->gadget); + if (!is_enabled(dum_hcd)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff; + + /* + * For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the + * maximum packet size. + * For SS devices the wMaxPacketSize is limited by 1024. + */ + max = usb_endpoint_maxp(desc) & 0x7ff; /* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints @@ -356,13 +447,17 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * especially for "ep9out" style fixed function ones.) */ retval = -EINVAL; - switch (desc->bmAttributes & 0x03) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: - if (strstr (ep->ep.name, "-iso") - || strstr (ep->ep.name, "-int")) { + if (strstr(ep->ep.name, "-iso") + || strstr(ep->ep.name, "-int")) { goto done; } switch (dum->gadget.speed) { + case USB_SPEED_SUPER: + if (max == 1024) + break; + goto done; case USB_SPEED_HIGH: if (max == 512) break; @@ -377,10 +472,11 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } break; case USB_ENDPOINT_XFER_INT: - if (strstr (ep->ep.name, "-iso")) /* bulk is ok */ + if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; @@ -396,11 +492,12 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } break; case USB_ENDPOINT_XFER_ISOC: - if (strstr (ep->ep.name, "-bulk") - || strstr (ep->ep.name, "-int")) + if (strstr(ep->ep.name, "-bulk") + || strstr(ep->ep.name, "-int")) goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; @@ -419,20 +516,36 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } _ep->maxpacket = max; + if (usb_ss_max_streams(_ep->comp_desc)) { + if (!usb_endpoint_xfer_bulk(desc)) { + dev_err(udc_dev(dum), "Can't enable stream support on " + "non-bulk ep %s\n", _ep->name); + return -EINVAL; + } + ep->stream_en = 1; + } ep->desc = desc; - dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n", + dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n", _ep->name, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", ({ char *val; - switch (desc->bmAttributes & 0x03) { - case USB_ENDPOINT_XFER_BULK: val = "bulk"; break; - case USB_ENDPOINT_XFER_ISOC: val = "iso"; break; - case USB_ENDPOINT_XFER_INT: val = "intr"; break; - default: val = "ctrl"; break; - }; val; }), - max); + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_BULK: + val = "bulk"; + break; + case USB_ENDPOINT_XFER_ISOC: + val = "iso"; + break; + case USB_ENDPOINT_XFER_INT: + val = "intr"; + break; + default: + val = "ctrl"; + break; + } val; }), + max, ep->stream_en ? "enabled" : "disabled"); /* at this point real hardware should be NAKing transfers * to that endpoint, until a buffer is queued to it. @@ -443,116 +556,113 @@ done: return retval; } -static int dummy_disable (struct usb_ep *_ep) +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); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !ep->desc || _ep->name == ep0name) return -EINVAL; - dum = ep_to_dummy (ep); + dum = ep_to_dummy(ep); - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); ep->desc = NULL; - retval = 0; - nuke (dum, ep); - spin_unlock_irqrestore (&dum->lock, flags); + ep->stream_en = 0; + nuke(dum, ep); + spin_unlock_irqrestore(&dum->lock, flags); - dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name); - return retval; + dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name); + return 0; } -static struct usb_request * -dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags) +static struct usb_request *dummy_alloc_request(struct usb_ep *_ep, + gfp_t mem_flags) { struct dummy_ep *ep; struct dummy_request *req; if (!_ep) return NULL; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); req = kzalloc(sizeof(*req), mem_flags); if (!req) return NULL; - INIT_LIST_HEAD (&req->queue); + INIT_LIST_HEAD(&req->queue); return &req->req; } -static void -dummy_free_request (struct usb_ep *_ep, struct usb_request *_req) +static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req) { - struct dummy_ep *ep; struct dummy_request *req; - ep = usb_ep_to_dummy_ep (_ep); - if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) + if (!_ep || !_req) { + WARN_ON(1); return; + } - req = usb_request_to_dummy_request (_req); - WARN_ON (!list_empty (&req->queue)); - kfree (req); + req = usb_request_to_dummy_request(_req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); } -static void -fifo_complete (struct usb_ep *ep, struct usb_request *req) +static void fifo_complete(struct usb_ep *ep, struct usb_request *req) { } -static int -dummy_queue (struct usb_ep *_ep, struct usb_request *_req, +static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t mem_flags) { struct dummy_ep *ep; struct dummy_request *req; struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; - req = usb_request_to_dummy_request (_req); - if (!_req || !list_empty (&req->queue) || !_req->complete) + req = usb_request_to_dummy_request(_req); + if (!_req || !list_empty(&req->queue) || !_req->complete) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || (!ep->desc && _ep->name != ep0name)) return -EINVAL; - dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + dum = ep_to_dummy(ep); + dum_hcd = gadget_to_dummy_hcd(&dum->gadget); + if (!dum->driver || !is_enabled(dum_hcd)) return -ESHUTDOWN; #if 0 - dev_dbg (udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", + dev_dbg(udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", ep, _req, _ep->name, _req->length, _req->buf); #endif - _req->status = -EINPROGRESS; _req->actual = 0; - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - list_empty (&dum->fifo_req.queue) && - list_empty (&ep->queue) && + list_empty(&dum->fifo_req.queue) && + list_empty(&ep->queue) && _req->length <= FIFO_SIZE) { req = &dum->fifo_req; req->req = *_req; req->req.buf = dum->fifo_buf; - memcpy (dum->fifo_buf, _req->buf, _req->length); + memcpy(dum->fifo_buf, _req->buf, _req->length); req->req.context = dum; req->req.complete = fifo_complete; list_add_tail(&req->queue, &ep->queue); - spin_unlock (&dum->lock); + spin_unlock(&dum->lock); _req->actual = _req->length; _req->status = 0; - _req->complete (_ep, _req); - spin_lock (&dum->lock); + _req->complete(_ep, _req); + spin_lock(&dum->lock); } else list_add_tail(&req->queue, &ep->queue); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); /* real hardware would likely enable transfers here, in case * it'd been left NAKing. @@ -560,7 +670,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, return 0; } -static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) +static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct dummy_ep *ep; struct dummy *dum; @@ -570,31 +680,31 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) if (!_ep || !_req) return retval; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); + ep = usb_ep_to_dummy_ep(_ep); + dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN; - local_irq_save (flags); - spin_lock (&dum->lock); - list_for_each_entry (req, &ep->queue, queue) { + local_irq_save(flags); + spin_lock(&dum->lock); + list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) { - list_del_init (&req->queue); + list_del_init(&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } - spin_unlock (&dum->lock); + spin_unlock(&dum->lock); if (retval == 0) { - dev_dbg (udc_dev(dum), + dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - _req->complete (_ep, _req); + _req->complete(_ep, _req); } - local_irq_restore (flags); + local_irq_restore(flags); return retval; } @@ -606,14 +716,14 @@ dummy_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) if (!_ep) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); + ep = usb_ep_to_dummy_ep(_ep); + dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN; if (!value) ep->halted = ep->wedged = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - !list_empty (&ep->queue)) + !list_empty(&ep->queue)) return -EAGAIN; else { ep->halted = 1; @@ -654,42 +764,42 @@ static const struct usb_ep_ops dummy_ep_ops = { /*-------------------------------------------------------------------------*/ /* there are both host and device side versions of this call ... */ -static int dummy_g_get_frame (struct usb_gadget *_gadget) +static int dummy_g_get_frame(struct usb_gadget *_gadget) { struct timeval tv; - do_gettimeofday (&tv); + do_gettimeofday(&tv); return tv.tv_usec / 1000; } -static int dummy_wakeup (struct usb_gadget *_gadget) +static int dummy_wakeup(struct usb_gadget *_gadget) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; - dum = gadget_to_dummy (_gadget); - if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE) + dum_hcd = gadget_to_dummy_hcd(_gadget); + if (!(dum_hcd->dum->devstatus & ((1 << USB_DEVICE_B_HNP_ENABLE) | (1 << USB_DEVICE_REMOTE_WAKEUP)))) return -EINVAL; - if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0) + if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0) return -ENOLINK; - if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && - dum->rh_state != DUMMY_RH_SUSPENDED) + if ((dum_hcd->port_status & USB_PORT_STAT_SUSPEND) == 0 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) return -EIO; /* FIXME: What if the root hub is suspended but the port isn't? */ /* hub notices our request, issues downstream resume, etc */ - dum->resuming = 1; - dum->re_timeout = jiffies + msecs_to_jiffies(20); - mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout); + dum_hcd->resuming = 1; + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(20); + mod_timer(&dummy_hcd_to_hcd(dum_hcd)->rh_timer, dum_hcd->re_timeout); return 0; } -static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) +static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value) { struct dummy *dum; - dum = gadget_to_dummy (_gadget); + dum = gadget_to_dummy_hcd(_gadget)->dum; if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else @@ -697,41 +807,75 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) return 0; } -static int dummy_pullup (struct usb_gadget *_gadget, int value) +static void dummy_udc_update_ep0(struct dummy *dum) { + if (dum->gadget.speed == USB_SPEED_SUPER) + dum->ep[0].ep.maxpacket = 9; + else + dum->ep[0].ep.maxpacket = 64; +} + +static int dummy_pullup(struct usb_gadget *_gadget, int value) +{ + struct dummy_hcd *dum_hcd; struct dummy *dum; unsigned long flags; - dum = gadget_to_dummy (_gadget); - spin_lock_irqsave (&dum->lock, flags); + dum = gadget_dev_to_dummy(&_gadget->dev); + + if (value && dum->driver) { + if (mod_data.is_super_speed) + dum->gadget.speed = dum->driver->max_speed; + else if (mod_data.is_high_speed) + dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, + dum->driver->max_speed); + else + dum->gadget.speed = USB_SPEED_FULL; + dummy_udc_update_ep0(dum); + + if (dum->gadget.speed < dum->driver->max_speed) + dev_dbg(udc_dev(dum), "This device can perform faster" + " if you connect it to a %s port...\n", + usb_speed_string(dum->driver->max_speed)); + } + dum_hcd = gadget_to_dummy_hcd(_gadget); + + spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); - set_link_state (dum); - spin_unlock_irqrestore (&dum->lock, flags); + set_link_state(dum_hcd); + spin_unlock_irqrestore(&dum->lock, flags); - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } +static int dummy_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int dummy_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); + static const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, .wakeup = dummy_wakeup, .set_selfpowered = dummy_set_selfpowered, .pullup = dummy_pullup, + .udc_start = dummy_udc_start, + .udc_stop = dummy_udc_stop, }; /*-------------------------------------------------------------------------*/ /* "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 dummy *dum = gadget_dev_to_dummy (dev); + struct dummy *dum = gadget_dev_to_dummy(dev); if (!dum->driver || !dum->driver->function) return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function); + return scnprintf(buf, PAGE_SIZE, "%s\n", dum->driver->function); } -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR_RO(function); /*-------------------------------------------------------------------------*/ @@ -749,18 +893,13 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); * for each driver that registers: just add to a big root hub. */ -int -usb_gadget_register_driver (struct usb_gadget_driver *driver) +static int dummy_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct dummy *dum = the_controller; - int retval, i; + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); + struct dummy *dum = dum_hcd->dum; - if (!dum) - return -EINVAL; - if (dum->driver) - return -EBUSY; - if (!driver->bind || !driver->setup - || driver->speed == USB_SPEED_UNKNOWN) + if (driver->max_speed == USB_SPEED_UNKNOWN) return -EINVAL; /* @@ -770,170 +909,129 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) dum->devstatus = 0; - INIT_LIST_HEAD (&dum->gadget.ep_list); - for (i = 0; i < DUMMY_ENDPOINTS; i++) { - struct dummy_ep *ep = &dum->ep [i]; - - if (!ep_name [i]) - break; - ep->ep.name = ep_name [i]; - ep->ep.ops = &dummy_ep_ops; - 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; - ep->last_io = jiffies; - ep->gadget = &dum->gadget; - ep->desc = NULL; - INIT_LIST_HEAD (&ep->queue); - } - - dum->gadget.ep0 = &dum->ep [0].ep; - dum->ep [0].ep.maxpacket = 64; - list_del_init (&dum->ep [0].ep.ep_list); - INIT_LIST_HEAD(&dum->fifo_req.queue); - - driver->driver.bus = NULL; dum->driver = driver; - dum->gadget.dev.driver = &driver->driver; - dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", + dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); - retval = driver->bind(&dum->gadget); - if (retval) { - dum->driver = NULL; - dum->gadget.dev.driver = NULL; - return retval; - } - - /* khubd will enumerate this in a while */ - spin_lock_irq (&dum->lock); - dum->pullup = 1; - set_link_state (dum); - spin_unlock_irq (&dum->lock); - - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } -EXPORT_SYMBOL (usb_gadget_register_driver); -int -usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int dummy_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct dummy *dum = the_controller; - unsigned long flags; + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); + struct dummy *dum = dum_hcd->dum; - if (!dum) - return -ENODEV; - if (!driver || driver != dum->driver || !driver->unbind) - return -EINVAL; - - 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); - spin_lock_irqsave (&dum->lock, flags); - dum->pullup = 0; - set_link_state (dum); - spin_unlock_irqrestore (&dum->lock, flags); - - driver->unbind (&dum->gadget); - dum->gadget.dev.driver = NULL; dum->driver = NULL; - spin_lock_irqsave (&dum->lock, flags); - dum->pullup = 0; - set_link_state (dum); - spin_unlock_irqrestore (&dum->lock, flags); - - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); #undef is_enabled -/* just declare this in any driver that really need it */ -extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); - -int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) -{ - return -ENOSYS; -} -EXPORT_SYMBOL (net2280_set_fifo_mode); - - /* The gadget structure is stored inside the hcd structure and will be * released along with it. */ -static void -dummy_gadget_release (struct device *dev) +static void init_dummy_udc_hw(struct dummy *dum) { - struct dummy *dum = gadget_dev_to_dummy (dev); + int i; - usb_put_hcd (dummy_to_hcd (dum)); + INIT_LIST_HEAD(&dum->gadget.ep_list); + for (i = 0; i < DUMMY_ENDPOINTS; i++) { + struct dummy_ep *ep = &dum->ep[i]; + + if (!ep_name[i]) + break; + ep->ep.name = ep_name[i]; + ep->ep.ops = &dummy_ep_ops; + list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list); + ep->halted = ep->wedged = ep->already_seen = + ep->setup_stage = 0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.max_streams = 16; + ep->last_io = jiffies; + ep->gadget = &dum->gadget; + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + } + + dum->gadget.ep0 = &dum->ep[0].ep; + list_del_init(&dum->ep[0].ep.ep_list); + INIT_LIST_HEAD(&dum->fifo_req.queue); + +#ifdef CONFIG_USB_OTG + dum->gadget.is_otg = 1; +#endif } -static int dummy_udc_probe (struct platform_device *pdev) +static int dummy_udc_probe(struct platform_device *pdev) { - struct dummy *dum = the_controller; + struct dummy *dum; int rc; + dum = *((void **)dev_get_platdata(&pdev->dev)); dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; - dum->gadget.is_dualspeed = 1; - - /* maybe claim OTG support, though we won't complete HNP */ - dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); + 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) - return rc; + init_dummy_udc_hw(dum); - usb_get_hcd (dummy_to_hcd (dum)); + rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget); + if (rc < 0) + goto err_udc; - platform_set_drvdata (pdev, dum); - rc = device_create_file (&dum->gadget.dev, &dev_attr_function); + rc = device_create_file(&dum->gadget.dev, &dev_attr_function); if (rc < 0) - device_unregister (&dum->gadget.dev); + goto err_dev; + platform_set_drvdata(pdev, dum); + return rc; + +err_dev: + usb_del_gadget_udc(&dum->gadget); +err_udc: return rc; } -static int dummy_udc_remove (struct platform_device *pdev) +static int dummy_udc_remove(struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata (pdev); + struct dummy *dum = platform_get_drvdata(pdev); - platform_set_drvdata (pdev, NULL); - device_remove_file (&dum->gadget.dev, &dev_attr_function); - device_unregister (&dum->gadget.dev); + device_remove_file(&dum->gadget.dev, &dev_attr_function); + usb_del_gadget_udc(&dum->gadget); return 0; } -static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state) +static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd, + int suspend) { - struct dummy *dum = platform_get_drvdata(pdev); + spin_lock_irq(&dum->lock); + dum->udc_suspended = suspend; + set_link_state(dum_hcd); + spin_unlock_irq(&dum->lock); +} - dev_dbg (&pdev->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->udc_suspended = 1; - set_link_state (dum); - spin_unlock_irq (&dum->lock); +static int dummy_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dummy *dum = platform_get_drvdata(pdev); + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget); - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + dev_dbg(&pdev->dev, "%s\n", __func__); + dummy_udc_pm(dum, dum_hcd, 1); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } -static int dummy_udc_resume (struct platform_device *pdev) +static int dummy_udc_resume(struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata(pdev); - - dev_dbg (&pdev->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->udc_suspended = 0; - set_link_state (dum); - spin_unlock_irq (&dum->lock); + struct dummy *dum = platform_get_drvdata(pdev); + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget); - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + dev_dbg(&pdev->dev, "%s\n", __func__); + dummy_udc_pm(dum, dum_hcd, 0); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } @@ -950,6 +1048,16 @@ static struct platform_driver dummy_udc_driver = { /*-------------------------------------------------------------------------*/ +static unsigned int dummy_get_ep_idx(const struct usb_endpoint_descriptor *desc) +{ + unsigned int index; + + index = usb_endpoint_num(desc) << 1; + if (usb_endpoint_dir_in(desc)) + index |= 1; + return index; +} + /* MASTER/HOST SIDE DRIVER * * this uses the hcd framework to hook up to host side drivers. @@ -962,86 +1070,237 @@ static struct platform_driver dummy_udc_driver = { * usb 2.0 rules. */ -static int dummy_urb_enqueue ( +static int dummy_ep_stream_en(struct dummy_hcd *dum_hcd, struct urb *urb) +{ + const struct usb_endpoint_descriptor *desc = &urb->ep->desc; + u32 index; + + if (!usb_endpoint_xfer_bulk(desc)) + return 0; + + index = dummy_get_ep_idx(desc); + return (1 << index) & dum_hcd->stream_en_ep; +} + +/* + * The max stream number is saved as a nibble so for the 30 possible endpoints + * we only 15 bytes of memory. Therefore we are limited to max 16 streams (0 + * means we use only 1 stream). The maximum according to the spec is 16bit so + * if the 16 stream limit is about to go, the array size should be incremented + * to 30 elements of type u16. + */ +static int get_max_streams_for_pipe(struct dummy_hcd *dum_hcd, + unsigned int pipe) +{ + int max_streams; + + max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)]; + if (usb_pipeout(pipe)) + max_streams >>= 4; + else + max_streams &= 0xf; + max_streams++; + return max_streams; +} + +static void set_max_streams_for_pipe(struct dummy_hcd *dum_hcd, + unsigned int pipe, unsigned int streams) +{ + int max_streams; + + streams--; + max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)]; + if (usb_pipeout(pipe)) { + streams <<= 4; + max_streams &= 0xf; + } else { + max_streams &= 0xf0; + } + max_streams |= streams; + dum_hcd->num_stream[usb_pipeendpoint(pipe)] = max_streams; +} + +static int dummy_validate_stream(struct dummy_hcd *dum_hcd, struct urb *urb) +{ + unsigned int max_streams; + int enabled; + + enabled = dummy_ep_stream_en(dum_hcd, urb); + if (!urb->stream_id) { + if (enabled) + return -EINVAL; + return 0; + } + if (!enabled) + return -EINVAL; + + max_streams = get_max_streams_for_pipe(dum_hcd, + usb_pipeendpoint(urb->pipe)); + if (urb->stream_id > max_streams) { + dev_err(dummy_dev(dum_hcd), "Stream id %d is out of range.\n", + urb->stream_id); + BUG(); + return -EINVAL; + } + return 0; +} + +static int dummy_urb_enqueue( struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags ) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; struct urbp *urbp; unsigned long flags; int rc; - if (!urb->transfer_buffer && urb->transfer_buffer_length) - return -EINVAL; - - urbp = kmalloc (sizeof *urbp, mem_flags); + urbp = kmalloc(sizeof *urbp, mem_flags); if (!urbp) return -ENOMEM; urbp->urb = urb; + urbp->miter_started = 0; + + dum_hcd = hcd_to_dummy_hcd(hcd); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + + rc = dummy_validate_stream(dum_hcd, urb); + if (rc) { + kfree(urbp); + goto done; + } - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); rc = usb_hcd_link_urb_to_ep(hcd, urb); if (rc) { kfree(urbp); goto done; } - if (!dum->udev) { - dum->udev = urb->dev; - usb_get_dev (dum->udev); - } else if (unlikely (dum->udev != urb->dev)) - dev_err (dummy_dev(dum), "usb_device address has changed!\n"); + if (!dum_hcd->udev) { + dum_hcd->udev = urb->dev; + usb_get_dev(dum_hcd->udev); + } else if (unlikely(dum_hcd->udev != urb->dev)) + dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n"); - list_add_tail (&urbp->urbp_list, &dum->urbp_list); + list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); urb->hcpriv = urbp; - if (usb_pipetype (urb->pipe) == PIPE_CONTROL) + if (usb_pipetype(urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ /* kick the scheduler, it'll do the rest */ - if (!timer_pending (&dum->timer)) - mod_timer (&dum->timer, jiffies + 1); + if (!timer_pending(&dum_hcd->timer)) + mod_timer(&dum_hcd->timer, jiffies + 1); done: - spin_unlock_irqrestore(&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; } static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; int rc; /* giveback happens automatically in timer callback, * so make sure the callback happens */ - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); + dum_hcd = hcd_to_dummy_hcd(hcd); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (!rc && dum->rh_state != DUMMY_RH_RUNNING && - !list_empty(&dum->urbp_list)) - mod_timer (&dum->timer, jiffies); + if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING && + !list_empty(&dum_hcd->urbp_list)) + mod_timer(&dum_hcd->timer, jiffies); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; } +static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, + u32 len) +{ + void *ubuf, *rbuf; + struct urbp *urbp = urb->hcpriv; + int to_host; + struct sg_mapping_iter *miter = &urbp->miter; + u32 trans = 0; + u32 this_sg; + bool next_sg; + + to_host = usb_pipein(urb->pipe); + rbuf = req->req.buf + req->req.actual; + + if (!urb->num_sgs) { + ubuf = urb->transfer_buffer + urb->actual_length; + if (to_host) + memcpy(ubuf, rbuf, len); + else + memcpy(rbuf, ubuf, len); + return len; + } + + if (!urbp->miter_started) { + u32 flags = SG_MITER_ATOMIC; + + if (to_host) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(miter, urb->sg, urb->num_sgs, flags); + urbp->miter_started = 1; + } + next_sg = sg_miter_next(miter); + if (next_sg == false) { + WARN_ON_ONCE(1); + return -EINVAL; + } + do { + ubuf = miter->addr; + this_sg = min_t(u32, len, miter->length); + miter->consumed = this_sg; + trans += this_sg; + + if (to_host) + memcpy(ubuf, rbuf, this_sg); + else + memcpy(rbuf, ubuf, this_sg); + len -= this_sg; + + if (!len) + break; + next_sg = sg_miter_next(miter); + if (next_sg == false) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + rbuf += this_sg; + } while (1); + + sg_miter_stop(miter); + return trans; +} + /* transfer up to a frame's worth; caller must own lock */ -static int -transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit, - int *status) +static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, + struct dummy_ep *ep, int limit, int *status) { + struct dummy *dum = dum_hcd->dum; struct dummy_request *req; top: /* if there's no request queued, the device is NAKing; return */ - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { unsigned host_len, dev_len, len; int is_short, to_host; int rescan = 0; + if (dummy_ep_stream_en(dum_hcd, urb)) { + if ((urb->stream_id != req->req.stream_id)) + continue; + } + /* 1..N packets of ep->ep.maxpacket each ... the last one * may be short (including zero length). * @@ -1051,20 +1310,18 @@ top: */ host_len = urb->transfer_buffer_length - urb->actual_length; dev_len = req->req.length - req->req.actual; - len = min (host_len, dev_len); + len = min(host_len, dev_len); /* FIXME update emulated data toggle too */ - to_host = usb_pipein (urb->pipe); - if (unlikely (len == 0)) + to_host = usb_pipein(urb->pipe); + if (unlikely(len == 0)) is_short = 1; else { - char *ubuf, *rbuf; - /* not enough bandwidth left? */ if (limit < ep->ep.maxpacket && limit < len) break; - len = min (len, (unsigned) limit); + len = min_t(unsigned, len, limit); if (len == 0) break; @@ -1075,18 +1332,16 @@ top: } is_short = (len % ep->ep.maxpacket) != 0; - /* else transfer packet(s) */ - ubuf = urb->transfer_buffer + urb->actual_length; - rbuf = req->req.buf + req->req.actual; - if (to_host) - memcpy (ubuf, rbuf, len); - else - memcpy (rbuf, ubuf, len); - ep->last_io = jiffies; + len = dummy_perform_transfer(urb, req, len); - limit -= len; - urb->actual_length += len; - req->req.actual += len; + ep->last_io = jiffies; + if ((int)len < 0) { + req->req.status = len; + } else { + limit -= len; + urb->actual_length += len; + req->req.actual += len; + } } /* short packets terminate, maybe with overflow/underflow. @@ -1127,11 +1382,11 @@ top: /* device side completion --> continuable */ if (req->req.status != -EINPROGRESS) { - list_del_init (&req->queue); + list_del_init(&req->queue); - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); /* requests might have been unlinked... */ rescan = 1; @@ -1148,7 +1403,7 @@ top: return limit; } -static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) +static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep) { int limit = ep->ep.maxpacket; @@ -1156,29 +1411,45 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) int tmp; /* high bandwidth mode */ - tmp = le16_to_cpu(ep->desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(ep->desc); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; } + if (dum->gadget.speed == USB_SPEED_SUPER) { + switch (usb_endpoint_type(ep->desc)) { + case USB_ENDPOINT_XFER_ISOC: + /* Sec. 4.4.8.2 USB3.0 Spec */ + limit = 3 * 16 * 1024 * 8; + break; + case USB_ENDPOINT_XFER_INT: + /* Sec. 4.4.7.2 USB3.0 Spec */ + limit = 3 * 1024 * 8; + break; + case USB_ENDPOINT_XFER_BULK: + default: + break; + } + } return limit; } -#define is_active(dum) ((dum->port_status & \ +#define is_active(dum_hcd) ((dum_hcd->port_status & \ (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \ USB_PORT_STAT_SUSPEND)) \ == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) -static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) +static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address) { int i; - if (!is_active (dum)) + if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ? + dum->ss_hcd : dum->hs_hcd))) return NULL; if ((address & ~USB_DIR_IN) == 0) - return &dum->ep [0]; + return &dum->ep[0]; for (i = 1; i < DUMMY_ENDPOINTS; i++) { - struct dummy_ep *ep = &dum->ep [i]; + struct dummy_ep *ep = &dum->ep[i]; if (!ep->desc) continue; @@ -1197,12 +1468,189 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) #define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) #define Ep_InRequest (Ep_Request | USB_DIR_IN) + +/** + * handle_control_request() - handles all control transfers + * @dum: pointer to dummy (the_controller) + * @urb: the urb request to handle + * @setup: pointer to the setup data for a USB device control + * request + * @status: pointer to request handling status + * + * Return 0 - if the request was handled + * 1 - if the request wasn't handles + * error code on error + */ +static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, + struct usb_ctrlrequest *setup, + int *status) +{ + struct dummy_ep *ep2; + struct dummy *dum = dum_hcd->dum; + int ret_val = 1; + unsigned w_index; + unsigned w_value; + + w_index = le16_to_cpu(setup->wIndex); + w_value = le16_to_cpu(setup->wValue); + switch (setup->bRequest) { + case USB_REQ_SET_ADDRESS: + if (setup->bRequestType != Dev_Request) + break; + dum->address = w_value; + *status = 0; + dev_dbg(udc_dev(dum), "set_address = %d\n", + w_value); + ret_val = 0; + break; + case USB_REQ_SET_FEATURE: + if (setup->bRequestType == Dev_Request) { + ret_val = 0; + switch (w_value) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + case USB_DEVICE_B_HNP_ENABLE: + dum->gadget.b_hnp_enable = 1; + break; + case USB_DEVICE_A_HNP_SUPPORT: + dum->gadget.a_hnp_support = 1; + break; + case USB_DEVICE_A_ALT_HNP_SUPPORT: + dum->gadget.a_alt_hnp_support = 1; + break; + case USB_DEVICE_U1_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U1_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_U2_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U2_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_LTM_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_LTM_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + default: + ret_val = -EOPNOTSUPP; + } + if (ret_val == 0) { + dum->devstatus |= (1 << w_value); + *status = 0; + } + } else if (setup->bRequestType == Ep_Request) { + /* endpoint halt */ + ep2 = find_endpoint(dum, w_index); + if (!ep2 || ep2->ep.name == ep0name) { + ret_val = -EOPNOTSUPP; + break; + } + ep2->halted = 1; + ret_val = 0; + *status = 0; + } + break; + case USB_REQ_CLEAR_FEATURE: + if (setup->bRequestType == Dev_Request) { + ret_val = 0; + switch (w_value) { + case USB_DEVICE_REMOTE_WAKEUP: + w_value = USB_DEVICE_REMOTE_WAKEUP; + break; + case USB_DEVICE_U1_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U1_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_U2_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U2_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_LTM_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_LTM_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + default: + ret_val = -EOPNOTSUPP; + break; + } + if (ret_val == 0) { + dum->devstatus &= ~(1 << w_value); + *status = 0; + } + } else if (setup->bRequestType == Ep_Request) { + /* endpoint halt */ + ep2 = find_endpoint(dum, w_index); + if (!ep2) { + ret_val = -EOPNOTSUPP; + break; + } + if (!ep2->wedged) + ep2->halted = 0; + ret_val = 0; + *status = 0; + } + break; + case USB_REQ_GET_STATUS: + if (setup->bRequestType == Dev_InRequest + || setup->bRequestType == Intf_InRequest + || setup->bRequestType == Ep_InRequest) { + char *buf; + /* + * device: remote wakeup, selfpowered + * interface: nothing + * endpoint: halt + */ + buf = (char *)urb->transfer_buffer; + if (urb->transfer_buffer_length > 0) { + if (setup->bRequestType == Ep_InRequest) { + ep2 = find_endpoint(dum, w_index); + if (!ep2) { + ret_val = -EOPNOTSUPP; + break; + } + buf[0] = ep2->halted; + } else if (setup->bRequestType == + Dev_InRequest) { + buf[0] = (u8)dum->devstatus; + } else + buf[0] = 0; + } + if (urb->transfer_buffer_length > 1) + buf[1] = 0; + urb->actual_length = min_t(u32, 2, + urb->transfer_buffer_length); + ret_val = 0; + *status = 0; + } + break; + } + return ret_val; +} + /* drive both sides of the transfers; looks like irq handlers to * both drivers except the callbacks aren't in_irq(). */ -static void dummy_timer (unsigned long _dum) +static void dummy_timer(unsigned long _dum_hcd) { - struct dummy *dum = (struct dummy *) _dum; + struct dummy_hcd *dum_hcd = (struct dummy_hcd *) _dum_hcd; + struct dummy *dum = dum_hcd->dum; struct urbp *urbp, *tmp; unsigned long flags; int limit, total; @@ -1219,31 +1667,35 @@ static void dummy_timer (unsigned long _dum) case USB_SPEED_HIGH: total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/; break; + case USB_SPEED_SUPER: + /* Bus speed is 500000 bytes/ms, so use a little less */ + total = 490000; + break; default: - dev_err (dummy_dev(dum), "bogus device speed\n"); + dev_err(dummy_dev(dum_hcd), "bogus device speed\n"); return; } /* FIXME if HZ != 1000 this will probably misbehave ... */ /* look at each urb queued by the host side driver */ - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); - if (!dum->udev) { - dev_err (dummy_dev(dum), + if (!dum_hcd->udev) { + dev_err(dummy_dev(dum_hcd), "timer fired with no URBs pending?\n"); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); return; } for (i = 0; i < DUMMY_ENDPOINTS; i++) { - if (!ep_name [i]) + if (!ep_name[i]) break; - dum->ep [i].already_seen = 0; + dum->ep[i].already_seen = 0; } restart: - list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) { + list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) { struct urb *urb; struct dummy_request *req; u8 address; @@ -1254,9 +1706,9 @@ restart: urb = urbp->urb; if (urb->unlinked) goto return_urb; - else if (dum->rh_state != DUMMY_RH_RUNNING) + else if (dum_hcd->rh_state != DUMMY_RH_RUNNING) continue; - type = usb_pipetype (urb->pipe); + type = usb_pipetype(urb->pipe); /* used up this frame's non-periodic bandwidth? * FIXME there's infinite bandwidth for control and @@ -1267,12 +1719,12 @@ restart: /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) + if (usb_pipein(urb->pipe)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ - dev_dbg (dummy_dev(dum), + dev_dbg(dummy_dev(dum_hcd), "no ep configured for urb %p\n", urb); status = -EPROTO; @@ -1282,13 +1734,13 @@ restart: if (ep->already_seen) continue; ep->already_seen = 1; - if (ep == &dum->ep [0] && urb->error_count) { + if (ep == &dum->ep[0] && urb->error_count) { ep->setup_stage = 1; /* a new urb */ urb->error_count = 0; } if (ep->halted && !ep->setup_stage) { /* NOTE: must not be iso! */ - dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n", + dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n", ep->ep.name, urb); status = -EPIPE; goto return_urb; @@ -1296,32 +1748,21 @@ restart: /* FIXME make sure both ends agree on maxpacket */ /* handle control requests */ - if (ep == &dum->ep [0] && ep->setup_stage) { + if (ep == &dum->ep[0] && ep->setup_stage) { struct usb_ctrlrequest setup; int value = 1; - struct dummy_ep *ep2; - unsigned w_index; - unsigned w_value; - - setup = *(struct usb_ctrlrequest*) urb->setup_packet; - w_index = le16_to_cpu(setup.wIndex); - w_value = le16_to_cpu(setup.wValue); - if (le16_to_cpu(setup.wLength) != - urb->transfer_buffer_length) { - status = -EOVERFLOW; - goto return_urb; - } + setup = *(struct usb_ctrlrequest *) urb->setup_packet; /* paranoia, in case of stale queued data */ - list_for_each_entry (req, &ep->queue, queue) { - list_del_init (&req->queue); + list_for_each_entry(req, &ep->queue, queue) { + list_del_init(&req->queue); req->req.status = -EOVERFLOW; - dev_dbg (udc_dev(dum), "stale req = %p\n", + dev_dbg(udc_dev(dum), "stale req = %p\n", req); - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); ep->already_seen = 0; goto restart; } @@ -1333,126 +1774,18 @@ restart: ep->last_io = jiffies; ep->setup_stage = 0; ep->halted = 0; - switch (setup.bRequest) { - case USB_REQ_SET_ADDRESS: - if (setup.bRequestType != Dev_Request) - break; - dum->address = w_value; - status = 0; - dev_dbg (udc_dev(dum), "set_address = %d\n", - w_value); - value = 0; - break; - case USB_REQ_SET_FEATURE: - if (setup.bRequestType == Dev_Request) { - value = 0; - switch (w_value) { - case USB_DEVICE_REMOTE_WAKEUP: - break; - case USB_DEVICE_B_HNP_ENABLE: - dum->gadget.b_hnp_enable = 1; - break; - case USB_DEVICE_A_HNP_SUPPORT: - dum->gadget.a_hnp_support = 1; - break; - case USB_DEVICE_A_ALT_HNP_SUPPORT: - dum->gadget.a_alt_hnp_support - = 1; - break; - default: - value = -EOPNOTSUPP; - } - if (value == 0) { - dum->devstatus |= - (1 << w_value); - status = 0; - } - } else if (setup.bRequestType == Ep_Request) { - // endpoint halt - ep2 = find_endpoint (dum, w_index); - if (!ep2 || ep2->ep.name == ep0name) { - value = -EOPNOTSUPP; - break; - } - ep2->halted = 1; - value = 0; - status = 0; - } - break; - case USB_REQ_CLEAR_FEATURE: - if (setup.bRequestType == Dev_Request) { - switch (w_value) { - case USB_DEVICE_REMOTE_WAKEUP: - dum->devstatus &= ~(1 << - USB_DEVICE_REMOTE_WAKEUP); - value = 0; - status = 0; - break; - default: - value = -EOPNOTSUPP; - break; - } - } else if (setup.bRequestType == Ep_Request) { - // endpoint halt - ep2 = find_endpoint (dum, w_index); - if (!ep2) { - value = -EOPNOTSUPP; - break; - } - if (!ep2->wedged) - ep2->halted = 0; - value = 0; - status = 0; - } - break; - case USB_REQ_GET_STATUS: - if (setup.bRequestType == Dev_InRequest - || setup.bRequestType - == Intf_InRequest - || setup.bRequestType - == Ep_InRequest - ) { - char *buf; - - // device: remote wakeup, selfpowered - // interface: nothing - // endpoint: halt - buf = (char *)urb->transfer_buffer; - if (urb->transfer_buffer_length > 0) { - if (setup.bRequestType == - Ep_InRequest) { - ep2 = find_endpoint (dum, w_index); - if (!ep2) { - value = -EOPNOTSUPP; - break; - } - buf [0] = ep2->halted; - } else if (setup.bRequestType == - Dev_InRequest) { - buf [0] = (u8) - dum->devstatus; - } else - buf [0] = 0; - } - if (urb->transfer_buffer_length > 1) - buf [1] = 0; - urb->actual_length = min_t(u32, 2, - urb->transfer_buffer_length); - value = 0; - status = 0; - } - break; - } + value = handle_control_request(dum_hcd, urb, &setup, + &status); /* gadget driver handles all other requests. block * until setup() returns; no reentrancy issues etc. */ if (value > 0) { - spin_unlock (&dum->lock); - value = dum->driver->setup (&dum->gadget, + spin_unlock(&dum->lock); + value = dum->driver->setup(&dum->gadget, &setup); - spin_lock (&dum->lock); + spin_lock(&dum->lock); if (value >= 0) { /* no delays (max 64KB data stage) */ @@ -1464,7 +1797,7 @@ restart: if (value < 0) { if (value != -EOPNOTSUPP) - dev_dbg (udc_dev(dum), + dev_dbg(udc_dev(dum), "setup --> %d\n", value); status = -EPIPE; @@ -1476,14 +1809,14 @@ restart: /* non-control requests */ limit = total; - switch (usb_pipetype (urb->pipe)) { + switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: /* FIXME is it urb->interval since the last xfer? * use urb->iso_frame_desc[i]. * complete whether or not ep has requests queued. * report random errors, to debug drivers. */ - limit = max (limit, periodic_bytes (dum, ep)); + limit = max(limit, periodic_bytes(dum, ep)); status = -ENOSYS; break; @@ -1491,14 +1824,13 @@ restart: /* FIXME is it urb->interval since the last xfer? * this almost certainly polls too fast. */ - limit = max (limit, periodic_bytes (dum, ep)); + limit = max(limit, periodic_bytes(dum, ep)); /* FALLTHROUGH */ - // case PIPE_BULK: case PIPE_CONTROL: default: - treat_control_like_bulk: +treat_control_like_bulk: ep->last_io = jiffies; - total = transfer(dum, urb, ep, limit, &status); + total = transfer(dum_hcd, urb, ep, limit, &status); break; } @@ -1507,28 +1839,28 @@ restart: continue; return_urb: - list_del (&urbp->urbp_list); - kfree (urbp); + list_del(&urbp->urbp_list); + kfree(urbp); if (ep) ep->already_seen = ep->setup_stage = 0; - usb_hcd_unlink_urb_from_ep(dummy_to_hcd(dum), urb); - spin_unlock (&dum->lock); - usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status); - spin_lock (&dum->lock); + usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb); + spin_unlock(&dum->lock); + usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status); + spin_lock(&dum->lock); goto restart; } - if (list_empty (&dum->urbp_list)) { - usb_put_dev (dum->udev); - dum->udev = NULL; - } else if (dum->rh_state == DUMMY_RH_RUNNING) { + if (list_empty(&dum_hcd->urbp_list)) { + usb_put_dev(dum_hcd->udev); + dum_hcd->udev = NULL; + } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { /* want a 1 msec delay here */ - mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); + mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1)); } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); } /*-------------------------------------------------------------------------*/ @@ -1540,50 +1872,82 @@ return_urb: | USB_PORT_STAT_C_OVERCURRENT \ | USB_PORT_STAT_C_RESET) << 16) -static int dummy_hub_status (struct usb_hcd *hcd, char *buf) +static int dummy_hub_status(struct usb_hcd *hcd, char *buf) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; int retval = 0; - dum = hcd_to_dummy (hcd); + dum_hcd = hcd_to_dummy_hcd(hcd); - spin_lock_irqsave (&dum->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + if (!HCD_HW_ACCESSIBLE(hcd)) goto done; - if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); - dum->port_status &= ~USB_PORT_STAT_SUSPEND; - set_link_state (dum); + if (dum_hcd->resuming && time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; + set_link_state(dum_hcd); } - if ((dum->port_status & PORT_C_MASK) != 0) { + if ((dum_hcd->port_status & PORT_C_MASK) != 0) { *buf = (1 << 1); - dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", - dum->port_status); + dev_dbg(dummy_dev(dum_hcd), "port status 0x%08x has changes\n", + dum_hcd->port_status); retval = 1; - if (dum->rh_state == DUMMY_RH_SUSPENDED) - usb_hcd_resume_root_hub (hcd); + if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED) + usb_hcd_resume_root_hub(hcd); } done: - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return retval; } +/* usb 3.0 root hub device descriptor */ +static struct { + struct usb_bos_descriptor bos; + struct usb_ss_cap_descriptor ss_cap; +} __packed usb3_bos_desc = { + + .bos = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + .wTotalLength = cpu_to_le16(sizeof(usb3_bos_desc)), + .bNumDeviceCaps = 1, + }, + .ss_cap = { + .bLength = USB_DT_USB_SS_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_SS_CAP_TYPE, + .wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION), + .bFunctionalitySupport = ilog2(USB_5GBPS_OPERATION), + }, +}; + static inline void -hub_descriptor (struct usb_hub_descriptor *desc) +ss_hub_descriptor(struct usb_hub_descriptor *desc) { - memset (desc, 0, sizeof *desc); + memset(desc, 0, sizeof *desc); + desc->bDescriptorType = 0x2a; + desc->bDescLength = 12; + desc->wHubCharacteristics = cpu_to_le16(0x0001); + desc->bNbrPorts = 1; + desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/ + desc->u.ss.DeviceRemovable = 0xffff; +} + +static inline void hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof *desc); desc->bDescriptorType = 0x29; desc->bDescLength = 9; desc->wHubCharacteristics = cpu_to_le16(0x0001); desc->bNbrPorts = 1; - desc->bitmap [0] = 0xff; - desc->bitmap [1] = 0xff; + desc->u.hs.DeviceRemovable[0] = 0xff; + desc->u.hs.DeviceRemovable[1] = 0xff; } -static int dummy_hub_control ( +static int dummy_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -1591,42 +1955,79 @@ static int dummy_hub_control ( char *buf, u16 wLength ) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; int retval = 0; unsigned long flags; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); + dum_hcd = hcd_to_dummy_hcd(hcd); + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); switch (typeReq) { case ClearHubFeature: break; case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if (dum->port_status & USB_PORT_STAT_SUSPEND) { + if (hcd->speed == HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_SUSPEND req not " + "supported for USB 3.0 roothub\n"); + goto error; + } + if (dum_hcd->port_status & USB_PORT_STAT_SUSPEND) { /* 20msec resume signaling */ - dum->resuming = 1; - dum->re_timeout = jiffies + + dum_hcd->resuming = 1; + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(20); } break; case USB_PORT_FEAT_POWER: - if (dum->port_status & USB_PORT_STAT_POWER) - dev_dbg (dummy_dev(dum), "power-off\n"); + if (hcd->speed == HCD_USB3) { + if (dum_hcd->port_status & USB_PORT_STAT_POWER) + dev_dbg(dummy_dev(dum_hcd), + "power-off\n"); + } else + if (dum_hcd->port_status & + USB_SS_PORT_STAT_POWER) + dev_dbg(dummy_dev(dum_hcd), + "power-off\n"); /* FALLS THROUGH */ default: - dum->port_status &= ~(1 << wValue); - set_link_state (dum); + dum_hcd->port_status &= ~(1 << wValue); + set_link_state(dum_hcd); } break; case GetHubDescriptor: - hub_descriptor ((struct usb_hub_descriptor *) buf); + if (hcd->speed == HCD_USB3 && + (wLength < USB_DT_SS_HUB_SIZE || + wValue != (USB_DT_SS_HUB << 8))) { + dev_dbg(dummy_dev(dum_hcd), + "Wrong hub descriptor type for " + "USB 3.0 roothub.\n"); + goto error; + } + if (hcd->speed == HCD_USB3) + ss_hub_descriptor((struct usb_hub_descriptor *) buf); + else + hub_descriptor((struct usb_hub_descriptor *) buf); break; + + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + if (hcd->speed != HCD_USB3) + goto error; + + if ((wValue >> 8) != USB_DT_BOS) + goto error; + + memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc)); + retval = sizeof(usb3_bos_desc); + break; + case GetHubStatus: - *(__le32 *) buf = cpu_to_le32 (0); + *(__le32 *) buf = cpu_to_le32(0); break; case GetPortStatus: if (wIndex != 1) @@ -1635,199 +2036,321 @@ static int dummy_hub_control ( /* whoever resets or resumes must GetPortStatus to * complete it!! */ - if (dum->resuming && - time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); - dum->port_status &= ~USB_PORT_STAT_SUSPEND; + if (dum_hcd->resuming && + time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; } - if ((dum->port_status & USB_PORT_STAT_RESET) != 0 && - time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_RESET << 16); - dum->port_status &= ~USB_PORT_STAT_RESET; - if (dum->pullup) { - dum->port_status |= USB_PORT_STAT_ENABLE; - /* give it the best speed we agree on */ - dum->gadget.speed = dum->driver->speed; - dum->gadget.ep0->maxpacket = 64; - switch (dum->gadget.speed) { - case USB_SPEED_HIGH: - dum->port_status |= - USB_PORT_STAT_HIGH_SPEED; - break; - case USB_SPEED_LOW: - dum->gadget.ep0->maxpacket = 8; - dum->port_status |= - USB_PORT_STAT_LOW_SPEED; - break; - default: - dum->gadget.speed = USB_SPEED_FULL; - break; + if ((dum_hcd->port_status & USB_PORT_STAT_RESET) != 0 && + time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_RESET << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_RESET; + if (dum_hcd->dum->pullup) { + dum_hcd->port_status |= USB_PORT_STAT_ENABLE; + + if (hcd->speed < HCD_USB3) { + switch (dum_hcd->dum->gadget.speed) { + case USB_SPEED_HIGH: + dum_hcd->port_status |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum_hcd->dum->gadget.ep0-> + maxpacket = 8; + dum_hcd->port_status |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + dum_hcd->dum->gadget.speed = + USB_SPEED_FULL; + break; + } } } } - set_link_state (dum); - ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status); - ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); + set_link_state(dum_hcd); + ((__le16 *) buf)[0] = cpu_to_le16(dum_hcd->port_status); + ((__le16 *) buf)[1] = cpu_to_le16(dum_hcd->port_status >> 16); break; case SetHubFeature: retval = -EPIPE; break; case SetPortFeature: switch (wValue) { + case USB_PORT_FEAT_LINK_STATE: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_LINK_STATE req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* + * Since this is dummy we don't have an actual link so + * there is nothing to do for the SET_LINK_STATE cmd + */ + break; + case USB_PORT_FEAT_U1_TIMEOUT: + case USB_PORT_FEAT_U2_TIMEOUT: + /* TODO: add suspend/resume support! */ + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_U1/2_TIMEOUT req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + break; case USB_PORT_FEAT_SUSPEND: - if (dum->active) { - dum->port_status |= USB_PORT_STAT_SUSPEND; + /* Applicable only for USB2.0 hub */ + if (hcd->speed == HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_SUSPEND req not " + "supported for USB 3.0 roothub\n"); + goto error; + } + if (dum_hcd->active) { + dum_hcd->port_status |= USB_PORT_STAT_SUSPEND; /* HNP would happen here; for now we * assume b_bus_req is always true. */ - set_link_state (dum); + set_link_state(dum_hcd); if (((1 << USB_DEVICE_B_HNP_ENABLE) - & dum->devstatus) != 0) - dev_dbg (dummy_dev(dum), + & dum_hcd->dum->devstatus) != 0) + dev_dbg(dummy_dev(dum_hcd), "no HNP yet!\n"); } break; case USB_PORT_FEAT_POWER: - dum->port_status |= USB_PORT_STAT_POWER; - set_link_state (dum); + if (hcd->speed == HCD_USB3) + dum_hcd->port_status |= USB_SS_PORT_STAT_POWER; + else + dum_hcd->port_status |= USB_PORT_STAT_POWER; + set_link_state(dum_hcd); break; + case USB_PORT_FEAT_BH_PORT_RESET: + /* Applicable only for USB3.0 hub */ + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_BH_PORT_RESET req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* FALLS THROUGH */ case USB_PORT_FEAT_RESET: /* if it's already enabled, disable */ - dum->port_status &= ~(USB_PORT_STAT_ENABLE + if (hcd->speed == HCD_USB3) { + dum_hcd->port_status = 0; + dum_hcd->port_status = + (USB_SS_PORT_STAT_POWER | + USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_RESET); + } else + dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); - dum->devstatus = 0; - /* 50msec reset signaling */ - dum->re_timeout = jiffies + msecs_to_jiffies(50); + /* + * We want to reset device status. All but the + * Self powered feature + */ + dum_hcd->dum->devstatus &= + (1 << USB_DEVICE_SELF_POWERED); + /* + * FIXME USB3.0: what is the correct reset signaling + * interval? Is it still 50msec as for HS? + */ + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLS THROUGH */ default: - if ((dum->port_status & USB_PORT_STAT_POWER) != 0) { - dum->port_status |= (1 << wValue); - set_link_state (dum); - } + if (hcd->speed == HCD_USB3) { + if ((dum_hcd->port_status & + USB_SS_PORT_STAT_POWER) != 0) { + dum_hcd->port_status |= (1 << wValue); + set_link_state(dum_hcd); + } + } else + if ((dum_hcd->port_status & + USB_PORT_STAT_POWER) != 0) { + dum_hcd->port_status |= (1 << wValue); + set_link_state(dum_hcd); + } + } + break; + case GetPortErrorCount: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "GetPortErrorCount req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* We'll always return 0 since this is a dummy hub */ + *(__le32 *) buf = cpu_to_le32(0); + break; + case SetHubDepth: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "SetHubDepth req not supported for " + "USB 2.0 roothub\n"); + goto error; } break; - default: - dev_dbg (dummy_dev(dum), + dev_dbg(dummy_dev(dum_hcd), "hub control req%04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); - +error: /* "protocol stall" on error */ retval = -EPIPE; } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); - if ((dum->port_status & PORT_C_MASK) != 0) - usb_hcd_poll_rh_status (hcd); + if ((dum_hcd->port_status & PORT_C_MASK) != 0) + usb_hcd_poll_rh_status(hcd); return retval; } -static int dummy_bus_suspend (struct usb_hcd *hcd) +static int dummy_bus_suspend(struct usb_hcd *hcd) { - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); - dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->rh_state = DUMMY_RH_SUSPENDED; - set_link_state (dum); + spin_lock_irq(&dum_hcd->dum->lock); + dum_hcd->rh_state = DUMMY_RH_SUSPENDED; + set_link_state(dum_hcd); hcd->state = HC_STATE_SUSPENDED; - spin_unlock_irq (&dum->lock); + spin_unlock_irq(&dum_hcd->dum->lock); return 0; } -static int dummy_bus_resume (struct usb_hcd *hcd) +static int dummy_bus_resume(struct usb_hcd *hcd) { - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); int rc = 0; - dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + spin_lock_irq(&dum_hcd->dum->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { - dum->rh_state = DUMMY_RH_RUNNING; - set_link_state (dum); - if (!list_empty(&dum->urbp_list)) - mod_timer (&dum->timer, jiffies); + dum_hcd->rh_state = DUMMY_RH_RUNNING; + set_link_state(dum_hcd); + if (!list_empty(&dum_hcd->urbp_list)) + mod_timer(&dum_hcd->timer, jiffies); hcd->state = HC_STATE_RUNNING; } - spin_unlock_irq (&dum->lock); + spin_unlock_irq(&dum_hcd->dum->lock); return rc; } /*-------------------------------------------------------------------------*/ -static inline ssize_t -show_urb (char *buf, size_t size, struct urb *urb) +static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) { - int ep = usb_pipeendpoint (urb->pipe); + int ep = usb_pipeendpoint(urb->pipe); - return snprintf (buf, size, + return snprintf(buf, size, "urb/%p %s ep%d%s%s len %d/%d\n", urb, ({ char *s; - switch (urb->dev->speed) { - case USB_SPEED_LOW: s = "ls"; break; - case USB_SPEED_FULL: s = "fs"; break; - case USB_SPEED_HIGH: s = "hs"; break; - default: s = "?"; break; - }; s; }), - ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "", + switch (urb->dev->speed) { + case USB_SPEED_LOW: + s = "ls"; + break; + case USB_SPEED_FULL: + s = "fs"; + break; + case USB_SPEED_HIGH: + s = "hs"; + break; + case USB_SPEED_SUPER: + s = "ss"; + break; + default: + s = "?"; + break; + } s; }), + ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", ({ char *s; \ - switch (usb_pipetype (urb->pipe)) { \ - case PIPE_CONTROL: s = ""; break; \ - case PIPE_BULK: s = "-bulk"; break; \ - case PIPE_INTERRUPT: s = "-int"; break; \ - default: s = "-iso"; break; \ - }; s;}), + switch (usb_pipetype(urb->pipe)) { \ + case PIPE_CONTROL: \ + s = ""; \ + break; \ + case PIPE_BULK: \ + s = "-bulk"; \ + break; \ + case PIPE_INTERRUPT: \ + s = "-int"; \ + break; \ + default: \ + s = "-iso"; \ + break; \ + } s; }), urb->actual_length, urb->transfer_buffer_length); } -static ssize_t -show_urbs (struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t urbs_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct usb_hcd *hcd = dev_get_drvdata (dev); - struct dummy *dum = hcd_to_dummy (hcd); + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); struct urbp *urbp; size_t size = 0; unsigned long flags; - spin_lock_irqsave (&dum->lock, flags); - list_for_each_entry (urbp, &dum->urbp_list, urbp_list) { + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + list_for_each_entry(urbp, &dum_hcd->urbp_list, urbp_list) { size_t temp; - temp = show_urb (buf, PAGE_SIZE - size, urbp->urb); + temp = show_urb(buf, PAGE_SIZE - size, urbp->urb); buf += temp; size += temp; } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return size; } -static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL); +static DEVICE_ATTR_RO(urbs); -static int dummy_start (struct usb_hcd *hcd) +static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - struct dummy *dum; + init_timer(&dum_hcd->timer); + dum_hcd->timer.function = dummy_timer; + dum_hcd->timer.data = (unsigned long)dum_hcd; + dum_hcd->rh_state = DUMMY_RH_RUNNING; + dum_hcd->stream_en_ep = 0; + INIT_LIST_HEAD(&dum_hcd->urbp_list); + dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; + dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; + dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1; +#ifdef CONFIG_USB_OTG + dummy_hcd_to_hcd(dum_hcd)->self.otg_port = 1; +#endif + return 0; - dum = hcd_to_dummy (hcd); + /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ + return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs); +} + +static int dummy_start(struct usb_hcd *hcd) +{ + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); /* * MASTER side init ... we emulate a root hub that'll only ever * talk to one device (the slave side). Also appears in sysfs, * just like more familiar pci-based HCDs. */ - spin_lock_init (&dum->lock); - init_timer (&dum->timer); - dum->timer.function = dummy_timer; - dum->timer.data = (unsigned long) dum; - dum->rh_state = DUMMY_RH_RUNNING; + if (!usb_hcd_is_primary_hcd(hcd)) + return dummy_start_ss(dum_hcd); + + spin_lock_init(&dum_hcd->dum->lock); + init_timer(&dum_hcd->timer); + dum_hcd->timer.function = dummy_timer; + dum_hcd->timer.data = (unsigned long)dum_hcd; + dum_hcd->rh_state = DUMMY_RH_RUNNING; - INIT_LIST_HEAD (&dum->urbp_list); + INIT_LIST_HEAD(&dum_hcd->urbp_list); hcd->power_budget = POWER_BUDGET; hcd->state = HC_STATE_RUNNING; @@ -1838,90 +2361,231 @@ static int dummy_start (struct usb_hcd *hcd) #endif /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ - return device_create_file (dummy_dev(dum), &dev_attr_urbs); + return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs); } -static void dummy_stop (struct usb_hcd *hcd) +static void dummy_stop(struct usb_hcd *hcd) { struct dummy *dum; - dum = hcd_to_dummy (hcd); - - device_remove_file (dummy_dev(dum), &dev_attr_urbs); - usb_gadget_unregister_driver (dum->driver); - dev_info (dummy_dev(dum), "stopped\n"); + dum = hcd_to_dummy_hcd(hcd)->dum; + device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs); + usb_gadget_unregister_driver(dum->driver); + dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n"); } /*-------------------------------------------------------------------------*/ -static int dummy_h_get_frame (struct usb_hcd *hcd) +static int dummy_h_get_frame(struct usb_hcd *hcd) +{ + return dummy_g_get_frame(NULL); +} + +static int dummy_setup(struct usb_hcd *hcd) +{ + struct dummy *dum; + + dum = *((void **)dev_get_platdata(hcd->self.controller)); + hcd->self.sg_tablesize = ~0; + if (usb_hcd_is_primary_hcd(hcd)) { + dum->hs_hcd = hcd_to_dummy_hcd(hcd); + dum->hs_hcd->dum = dum; + /* + * Mark the first roothub as being USB 2.0. + * The USB 3.0 roothub will be registered later by + * dummy_hcd_probe() + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + } else { + dum->ss_hcd = hcd_to_dummy_hcd(hcd); + dum->ss_hcd->dum = dum; + hcd->speed = HCD_USB3; + hcd->self.root_hub->speed = USB_SPEED_SUPER; + } + return 0; +} + +/* Change a group of bulk endpoints to support multiple stream IDs */ +static int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint **eps, unsigned int num_eps, + unsigned int num_streams, gfp_t mem_flags) { - return dummy_g_get_frame (NULL); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + unsigned long flags; + int max_stream; + int ret_streams = num_streams; + unsigned int index; + unsigned int i; + + if (!num_eps) + return -EINVAL; + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + if ((1 << index) & dum_hcd->stream_en_ep) { + ret_streams = -EINVAL; + goto out; + } + max_stream = usb_ss_max_streams(&eps[i]->ss_ep_comp); + if (!max_stream) { + ret_streams = -EINVAL; + goto out; + } + if (max_stream < ret_streams) { + dev_dbg(dummy_dev(dum_hcd), "Ep 0x%x only supports %u " + "stream IDs.\n", + eps[i]->desc.bEndpointAddress, + max_stream); + ret_streams = max_stream; + } + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep |= 1 << index; + set_max_streams_for_pipe(dum_hcd, + usb_endpoint_num(&eps[i]->desc), ret_streams); + } +out: + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); + return ret_streams; } -static const struct hc_driver dummy_hcd = { +/* Reverts a group of bulk endpoints back to not using stream IDs. */ +static int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev, + struct usb_host_endpoint **eps, unsigned int num_eps, + gfp_t mem_flags) +{ + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + unsigned long flags; + int ret; + unsigned int index; + unsigned int i; + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + if (!((1 << index) & dum_hcd->stream_en_ep)) { + ret = -EINVAL; + goto out; + } + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep &= ~(1 << index); + set_max_streams_for_pipe(dum_hcd, + usb_endpoint_num(&eps[i]->desc), 0); + } + ret = 0; +out: + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); + return ret; +} + +static struct hc_driver dummy_hcd = { .description = (char *) driver_name, .product_desc = "Dummy host controller", - .hcd_priv_size = sizeof(struct dummy), + .hcd_priv_size = sizeof(struct dummy_hcd), - .flags = HCD_USB2, + .flags = HCD_USB3 | HCD_SHARED, + .reset = dummy_setup, .start = dummy_start, .stop = dummy_stop, - .urb_enqueue = dummy_urb_enqueue, - .urb_dequeue = dummy_urb_dequeue, + .urb_enqueue = dummy_urb_enqueue, + .urb_dequeue = dummy_urb_dequeue, - .get_frame_number = dummy_h_get_frame, + .get_frame_number = dummy_h_get_frame, - .hub_status_data = dummy_hub_status, - .hub_control = dummy_hub_control, + .hub_status_data = dummy_hub_status, + .hub_control = dummy_hub_control, .bus_suspend = dummy_bus_suspend, .bus_resume = dummy_bus_resume, + + .alloc_streams = dummy_alloc_streams, + .free_streams = dummy_free_streams, }; static int dummy_hcd_probe(struct platform_device *pdev) { - struct usb_hcd *hcd; + struct dummy *dum; + struct usb_hcd *hs_hcd; + struct usb_hcd *ss_hcd; int retval; dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); + dum = *((void **)dev_get_platdata(&pdev->dev)); - hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) + if (!mod_data.is_super_speed) + dummy_hcd.flags = HCD_USB2; + hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); + if (!hs_hcd) return -ENOMEM; - the_controller = hcd_to_dummy (hcd); + hs_hcd->has_tt = 1; + + retval = usb_add_hcd(hs_hcd, 0, 0); + if (retval) + goto put_usb2_hcd; + + if (mod_data.is_super_speed) { + ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev, + dev_name(&pdev->dev), hs_hcd); + if (!ss_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + } - retval = usb_add_hcd(hcd, 0, 0); - if (retval != 0) { - usb_put_hcd (hcd); - the_controller = NULL; + retval = usb_add_hcd(ss_hcd, 0, 0); + if (retval) + goto put_usb3_hcd; } + return 0; + +put_usb3_hcd: + usb_put_hcd(ss_hcd); +dealloc_usb2_hcd: + usb_remove_hcd(hs_hcd); +put_usb2_hcd: + usb_put_hcd(hs_hcd); + dum->hs_hcd = dum->ss_hcd = NULL; return retval; } -static int dummy_hcd_remove (struct platform_device *pdev) +static int dummy_hcd_remove(struct platform_device *pdev) { - struct usb_hcd *hcd; + struct dummy *dum; + + dum = hcd_to_dummy_hcd(platform_get_drvdata(pdev))->dum; + + if (dum->ss_hcd) { + usb_remove_hcd(dummy_hcd_to_hcd(dum->ss_hcd)); + usb_put_hcd(dummy_hcd_to_hcd(dum->ss_hcd)); + } + + usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); + usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); + + dum->hs_hcd = NULL; + dum->ss_hcd = NULL; - hcd = platform_get_drvdata (pdev); - usb_remove_hcd (hcd); - usb_put_hcd (hcd); - the_controller = NULL; return 0; } -static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) +static int dummy_hcd_suspend(struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd; - struct dummy *dum; + struct dummy_hcd *dum_hcd; int rc = 0; - dev_dbg (&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); - hcd = platform_get_drvdata (pdev); - dum = hcd_to_dummy (hcd); - if (dum->rh_state == DUMMY_RH_RUNNING) { + hcd = platform_get_drvdata(pdev); + dum_hcd = hcd_to_dummy_hcd(hcd); + if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { dev_warn(&pdev->dev, "Root hub isn't suspended!\n"); rc = -EBUSY; } else @@ -1929,15 +2593,15 @@ static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) return rc; } -static int dummy_hcd_resume (struct platform_device *pdev) +static int dummy_hcd_resume(struct platform_device *pdev) { struct usb_hcd *hcd; - dev_dbg (&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); - hcd = platform_get_drvdata (pdev); + hcd = platform_get_drvdata(pdev); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - usb_hcd_poll_rh_status (hcd); + usb_hcd_poll_rh_status(hcd); return 0; } @@ -1953,58 +2617,148 @@ static struct platform_driver dummy_hcd_driver = { }; /*-------------------------------------------------------------------------*/ +#define MAX_NUM_UDC 2 +static struct platform_device *the_udc_pdev[MAX_NUM_UDC]; +static struct platform_device *the_hcd_pdev[MAX_NUM_UDC]; -static struct platform_device *the_udc_pdev; -static struct platform_device *the_hcd_pdev; - -static int __init init (void) +static int __init init(void) { int retval = -ENOMEM; + int i; + struct dummy *dum[MAX_NUM_UDC]; - if (usb_disabled ()) + if (usb_disabled()) return -ENODEV; - the_hcd_pdev = platform_device_alloc(driver_name, -1); - if (!the_hcd_pdev) - return retval; - the_udc_pdev = platform_device_alloc(gadget_name, -1); - if (!the_udc_pdev) - goto err_alloc_udc; + if (!mod_data.is_high_speed && mod_data.is_super_speed) + return -EINVAL; + + if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { + pr_err("Number of emulated UDC must be in range of 1…%d\n", + MAX_NUM_UDC); + return -EINVAL; + } + + for (i = 0; i < mod_data.num; i++) { + the_hcd_pdev[i] = platform_device_alloc(driver_name, i); + if (!the_hcd_pdev[i]) { + i--; + while (i >= 0) + platform_device_put(the_hcd_pdev[i--]); + return retval; + } + } + for (i = 0; i < mod_data.num; i++) { + the_udc_pdev[i] = platform_device_alloc(gadget_name, i); + if (!the_udc_pdev[i]) { + i--; + while (i >= 0) + platform_device_put(the_udc_pdev[i--]); + goto err_alloc_udc; + } + } + for (i = 0; i < mod_data.num; i++) { + dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL); + if (!dum[i]) { + retval = -ENOMEM; + goto err_add_pdata; + } + retval = platform_device_add_data(the_hcd_pdev[i], &dum[i], + sizeof(void *)); + if (retval) + goto err_add_pdata; + retval = platform_device_add_data(the_udc_pdev[i], &dum[i], + sizeof(void *)); + if (retval) + goto err_add_pdata; + } retval = platform_driver_register(&dummy_hcd_driver); if (retval < 0) - goto err_register_hcd_driver; + goto err_add_pdata; retval = platform_driver_register(&dummy_udc_driver); if (retval < 0) goto err_register_udc_driver; - retval = platform_device_add(the_hcd_pdev); - if (retval < 0) - goto err_add_hcd; - retval = platform_device_add(the_udc_pdev); - if (retval < 0) - goto err_add_udc; + for (i = 0; i < mod_data.num; i++) { + retval = platform_device_add(the_hcd_pdev[i]); + if (retval < 0) { + i--; + while (i >= 0) + platform_device_del(the_hcd_pdev[i--]); + goto err_add_hcd; + } + } + for (i = 0; i < mod_data.num; i++) { + if (!dum[i]->hs_hcd || + (!dum[i]->ss_hcd && mod_data.is_super_speed)) { + /* + * The hcd was added successfully but its probe + * function failed for some reason. + */ + retval = -EINVAL; + goto err_add_udc; + } + } + + for (i = 0; i < mod_data.num; i++) { + retval = platform_device_add(the_udc_pdev[i]); + if (retval < 0) { + i--; + while (i >= 0) + platform_device_del(the_udc_pdev[i]); + goto err_add_udc; + } + } + + for (i = 0; i < mod_data.num; i++) { + if (!platform_get_drvdata(the_udc_pdev[i])) { + /* + * The udc was added successfully but its probe + * function failed for some reason. + */ + retval = -EINVAL; + goto err_probe_udc; + } + } return retval; +err_probe_udc: + for (i = 0; i < mod_data.num; i++) + platform_device_del(the_udc_pdev[i]); err_add_udc: - platform_device_del(the_hcd_pdev); + for (i = 0; i < mod_data.num; i++) + platform_device_del(the_hcd_pdev[i]); err_add_hcd: platform_driver_unregister(&dummy_udc_driver); err_register_udc_driver: platform_driver_unregister(&dummy_hcd_driver); -err_register_hcd_driver: - platform_device_put(the_udc_pdev); +err_add_pdata: + for (i = 0; i < mod_data.num; i++) + kfree(dum[i]); + for (i = 0; i < mod_data.num; i++) + platform_device_put(the_udc_pdev[i]); err_alloc_udc: - platform_device_put(the_hcd_pdev); + for (i = 0; i < mod_data.num; i++) + platform_device_put(the_hcd_pdev[i]); return retval; } -module_init (init); +module_init(init); -static void __exit cleanup (void) +static void __exit cleanup(void) { - platform_device_unregister(the_udc_pdev); - platform_device_unregister(the_hcd_pdev); + int i; + + for (i = 0; i < mod_data.num; i++) { + struct dummy *dum; + + dum = *((void **)dev_get_platdata(&the_udc_pdev[i]->dev)); + + platform_device_unregister(the_udc_pdev[i]); + platform_device_unregister(the_hcd_pdev[i]); + kfree(dum); + } platform_driver_unregister(&dummy_udc_driver); platform_driver_unregister(&dummy_hcd_driver); } -module_exit (cleanup); +module_exit(cleanup); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index cd0914ec898..0567cca1465 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -7,20 +7,10 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/kernel.h> -#include <linux/init.h> +#include <linux/module.h> #include <linux/types.h> #include <linux/device.h> @@ -32,17 +22,6 @@ #include "gadget_chips.h" - -/* we must assign addresses for configurable endpoints (like net2280) */ -static __initdata unsigned epnum; - -// #define MANY_ENDPOINTS -#ifdef MANY_ENDPOINTS -/* more than 15 configurable endpoints */ -static __initdata unsigned in_epnum; -#endif - - /* * This should work with endpoints from controller drivers sharing the * same endpoint naming convention. By example: @@ -59,23 +38,26 @@ static __initdata unsigned in_epnum; * NOTE: each endpoint is unidirectional, as specified by its USB * descriptor; and isn't specific to a configuration or altsetting. */ -static int __init +static int ep_matches ( struct usb_gadget *gadget, struct usb_ep *ep, - struct usb_endpoint_descriptor *desc + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp ) { u8 type; const char *tmp; u16 max; + int num_req_streams = 0; + /* endpoint already claimed? */ if (NULL != ep->driver_data) 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; @@ -128,28 +110,48 @@ ep_matches ( } } + /* + * Get the number of required streams from the EP companion + * descriptor and see if the EP matches it + */ + if (usb_endpoint_xfer_bulk(desc)) { + if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) { + num_req_streams = ep_comp->bmAttributes & 0x1f; + if (num_req_streams > ep->max_streams) + return 0; + } + + } + + /* + * If the protocol driver hasn't yet decided on wMaxPacketSize + * and wants to know the maximum possible, provide the info. + */ + if (desc->wMaxPacketSize == 0) + 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. * the usb spec fixes high speed bulk maxpacket at 512 bytes. */ - max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); + max = 0x7ff & usb_endpoint_maxp(desc); switch (type) { case USB_ENDPOINT_XFER_INT: - /* INT: limit 64 bytes full speed, 1024 high speed */ - if (!gadget->is_dualspeed && max > 64) + /* INT: limit 64 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 64) return 0; /* FALLTHROUGH */ case USB_ENDPOINT_XFER_ISOC: - /* ISO: limit 1023 bytes full speed, 1024 high speed */ - if (ep->maxpacket < max) + /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ + if (ep->maxpacket_limit < max) return 0; - if (!gadget->is_dualspeed && max > 1023) + if (!gadget_is_dualspeed(gadget) && max > 1023) return 0; /* BOTH: "high bandwidth" works only at high speed */ if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) { - if (!gadget->is_dualspeed) + if (!gadget_is_dualspeed(gadget)) return 0; /* configure your hardware with enough buffering!! */ } @@ -163,31 +165,30 @@ ep_matches ( if (isdigit (ep->name [2])) { u8 num = simple_strtoul (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; -#ifdef MANY_ENDPOINTS } else if (desc->bEndpointAddress & USB_DIR_IN) { - if (++in_epnum > 15) + if (++gadget->in_epnum > 15) return 0; - desc->bEndpointAddress = USB_DIR_IN | in_epnum; -#endif + desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum; } else { - if (++epnum > 15) + if (++gadget->out_epnum > 15) return 0; - desc->bEndpointAddress |= epnum; + desc->bEndpointAddress |= gadget->out_epnum; } /* report (variable) full speed bulk maxpacket */ - if (USB_ENDPOINT_XFER_BULK == type) { - int size = ep->maxpacket; + if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) { + int size = ep->maxpacket_limit; /* min() doesn't work on bitfields with gcc-3.5 */ if (size > 64) size = 64; desc->wMaxPacketSize = cpu_to_le16(size); } + ep->address = desc->bEndpointAddress; return 1; } -static struct usb_ep * __init +static struct usb_ep * find_ep (struct usb_gadget *gadget, const char *name) { struct usb_ep *ep; @@ -200,38 +201,53 @@ find_ep (struct usb_gadget *gadget, const char *name) } /** - * usb_ep_autoconfig - choose an endpoint matching the descriptor + * usb_ep_autoconfig_ss() - choose an endpoint matching the ep + * descriptor and ep companion descriptor * @gadget: The device to which the endpoint must belong. * @desc: Endpoint descriptor, with endpoint direction and transfer mode - * initialized. For periodic transfers, the maximum packet - * size must also be initialized. This is modified on success. + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on + * success. + * @ep_comp: Endpoint companion descriptor, with the required + * number of streams. Will be modified when the chosen EP + * supports a different number of streams. * - * By choosing an endpoint to use with the specified descriptor, this - * routine simplifies writing gadget drivers that work with multiple - * USB device controllers. The endpoint would be passed later to - * usb_ep_enable(), along with some descriptor. + * This routine replaces the usb_ep_autoconfig when needed + * superspeed enhancments. If such enhancemnets are required, + * the FD should call usb_ep_autoconfig_ss directly and provide + * the additional ep_comp parameter. + * + * By choosing an endpoint to use with the specified descriptor, + * this routine simplifies writing gadget drivers that work with + * multiple USB device controllers. The endpoint would be + * passed later to usb_ep_enable(), along with some descriptor. * * That second descriptor won't always be the same as the first one. * For example, isochronous endpoints can be autoconfigured for high * bandwidth, and then used in several lower bandwidth altsettings. * Also, high and full speed descriptors will be different. * - * Be sure to examine and test the results of autoconfiguration on your - * hardware. This code may not make the best choices about how to use the - * USB controller, and it can't know all the restrictions that may apply. - * Some combinations of driver and hardware won't be able to autoconfigure. + * Be sure to examine and test the results of autoconfiguration + * on your hardware. This code may not make the best choices + * about how to use the USB controller, and it can't know all + * the restrictions that may apply. Some combinations of driver + * and hardware won't be able to autoconfigure. * * On success, this returns an un-claimed usb_ep, and modifies the endpoint * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value - * is initialized as if the endpoint were used at full speed. To prevent - * the endpoint from being returned by a later autoconfig call, claim it - * by assigning ep->driver_data to some non-null value. + * is initialized as if the endpoint were used at full speed and + * the bmAttribute field in the ep companion descriptor is + * updated with the assigned number of streams if it is + * different from the original value. To prevent the endpoint + * from being returned by a later autoconfig call, claim it by + * assigning ep->driver_data to some non-null value. * * On failure, this returns a null endpoint descriptor. */ -struct usb_ep * __init usb_ep_autoconfig ( +struct usb_ep *usb_ep_autoconfig_ss( struct usb_gadget *gadget, - struct usb_endpoint_descriptor *desc + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp ) { struct usb_ep *ep; @@ -245,47 +261,101 @@ struct usb_ep * __init usb_ep_autoconfig ( if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { /* ep-e, ep-f are PIO with only 64 byte fifos */ ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; ep = find_ep (gadget, "ep-f"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ - ep = find_ep (gadget, "ep3-bulk"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; + ep = find_ep(gadget, "ep3-bulk"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; } else if (USB_ENDPOINT_XFER_BULK == type && (USB_DIR_IN & desc->bEndpointAddress)) { /* DMA may be available */ - ep = find_ep (gadget, "ep2-bulk"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; + ep = find_ep(gadget, "ep2-bulk"); + if (ep && ep_matches(gadget, ep, desc, + ep_comp)) + goto found_ep; } - } else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) { - /* single buffering is enough; maybe 8 byte fifo is too */ - ep = find_ep (gadget, "ep3in-bulk"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; - - } else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) { - ep = find_ep (gadget, "ep1-bulk"); - if (ep && ep_matches (gadget, ep, desc)) - return ep; +#ifdef CONFIG_BLACKFIN + } else if (gadget_is_musbhdrc(gadget)) { + if ((USB_ENDPOINT_XFER_BULK == type) || + (USB_ENDPOINT_XFER_ISOC == type)) { + if (USB_DIR_IN & desc->bEndpointAddress) + ep = find_ep (gadget, "ep5in"); + else + ep = find_ep (gadget, "ep6out"); + } else if (USB_ENDPOINT_XFER_INT == type) { + if (USB_DIR_IN & desc->bEndpointAddress) + ep = find_ep(gadget, "ep1in"); + else + ep = find_ep(gadget, "ep2out"); + } else + ep = NULL; + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; +#endif } /* Second, look at endpoints until an unclaimed one looks usable */ list_for_each_entry (ep, &gadget->ep_list, ep_list) { - if (ep_matches (gadget, ep, desc)) - return ep; + if (ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; } /* Fail */ return NULL; +found_ep: + ep->desc = NULL; + ep->comp_desc = NULL; + return ep; } +EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss); + +/** + * usb_ep_autoconfig() - choose an endpoint matching the + * descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Endpoint descriptor, with endpoint direction and transfer mode + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on success. + * + * By choosing an endpoint to use with the specified descriptor, this + * routine simplifies writing gadget drivers that work with multiple + * USB device controllers. The endpoint would be passed later to + * usb_ep_enable(), along with some descriptor. + * + * That second descriptor won't always be the same as the first one. + * For example, isochronous endpoints can be autoconfigured for high + * bandwidth, and then used in several lower bandwidth altsettings. + * Also, high and full speed descriptors will be different. + * + * Be sure to examine and test the results of autoconfiguration on your + * hardware. This code may not make the best choices about how to use the + * USB controller, and it can't know all the restrictions that may apply. + * Some combinations of driver and hardware won't be able to autoconfigure. + * + * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value + * is initialized as if the endpoint were used at full speed. To prevent + * the endpoint from being returned by a later autoconfig call, claim it + * by assigning ep->driver_data to some non-null value. + * + * On failure, this returns a null endpoint descriptor. + */ +struct usb_ep *usb_ep_autoconfig( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc +) +{ + return usb_ep_autoconfig_ss(gadget, desc, NULL); +} +EXPORT_SYMBOL_GPL(usb_ep_autoconfig); /** * usb_ep_autoconfig_reset - reset endpoint autoconfig state @@ -296,16 +366,14 @@ struct usb_ep * __init usb_ep_autoconfig ( * state such as ep->driver_data and the record of assigned endpoints * used by usb_ep_autoconfig(). */ -void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget) +void usb_ep_autoconfig_reset (struct usb_gadget *gadget) { struct usb_ep *ep; list_for_each_entry (ep, &gadget->ep_list, ep_list) { ep->driver_data = NULL; } -#ifdef MANY_ENDPOINTS - in_epnum = 0; -#endif - epnum = 0; + gadget->in_epnum = 0; + gadget->out_epnum = 0; } - +EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d006dc652e0..c1c113ef950 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -9,21 +9,19 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> +#include <linux/netdevice.h> + +#if defined USB_ETH_RNDIS +# undef USB_ETH_RNDIS +#endif +#ifdef CONFIG_USB_ETH_RNDIS +# define USB_ETH_RNDIS y +#endif #include "u_ether.h" @@ -66,7 +64,7 @@ #define DRIVER_DESC "Ethernet Gadget" #define DRIVER_VERSION "Memorial Day 2008" -#ifdef CONFIG_USB_ETH_RNDIS +#ifdef USB_ETH_RNDIS #define PREFIX "RNDIS/" #else #define PREFIX "" @@ -87,36 +85,29 @@ static inline bool has_rndis(void) { -#ifdef CONFIG_USB_ETH_RNDIS +#ifdef USB_ETH_RNDIS return true; #else return false; #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 "composite.c" -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" - -#include "f_ecm.c" -#include "f_subset.c" -#ifdef CONFIG_USB_ETH_RNDIS -#include "f_rndis.c" -#include "rndis.c" +#include "u_ecm.h" +#include "u_gether.h" +#ifdef USB_ETH_RNDIS +#include "u_rndis.h" +#include "rndis.h" +#else +#define rndis_borrow_net(...) do {} while (0) #endif -#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. @@ -150,6 +141,10 @@ static inline bool has_rndis(void) #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ +/* For EEM gadgets */ +#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */ + /*-------------------------------------------------------------------------*/ static struct usb_device_descriptor device_desc = { @@ -191,17 +186,10 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; - -/* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - -static char manufacturer[50]; - static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", { } /* end of list */ }; @@ -215,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; /*-------------------------------------------------------------------------*/ @@ -226,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)) { @@ -233,12 +233,19 @@ 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 = { .label = "RNDIS", - .bind = rndis_do_config, .bConfigurationValue = 2, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -246,11 +253,21 @@ static struct usb_configuration rndis_config_driver = { /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_ETH_EEM +static bool use_eem = 1; +#else +static bool use_eem; +#endif +module_param(use_eem, bool, 0); +MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); + /* - * We _always_ have an ECM or CDC Subset configuration. + * We _always_ have an ECM, CDC Subset, or EEM configuration. */ 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)) { @@ -258,15 +275,42 @@ static int __init eth_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - 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 = { /* .label = f(hardware) */ - .bind = eth_do_config, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, @@ -276,103 +320,157 @@ static struct usb_configuration eth_config_driver = { static int __init eth_bind(struct usb_composite_dev *cdev) { - int gcnum; 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 (can_support_ecm(cdev->gadget)) { + 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(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), - device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), - device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM); + if (!has_rndis()) + 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 */ - device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), - device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), - device_desc.bNumConfigurations = 2; - } + gether_set_gadget(net, cdev->gadget); + status = gether_register_netdev(net); + if (status) + goto fail; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - dev_warn(&gadget->dev, - "controller '%s' not recognized; trying %s\n", - gadget->name, - eth_config_driver.label); - device_desc.bcdDevice = - cpu_to_le16(0x0300 | 0x0099); - } + 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; + } /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - /* device descriptor strings: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - device_desc.iManufacturer = status; - - status = usb_string_id(cdev); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) - goto fail; - strings_dev[STRING_PRODUCT_IDX].id = status; - device_desc.iProduct = status; + goto fail1; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; /* register our configuration(s); RNDIS first, if it's used */ if (has_rndis()) { - status = usb_add_config(cdev, &rndis_config_driver); + 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); + 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", DRIVER_DESC); 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; } -static struct usb_composite_driver eth_driver = { +static __refdata struct usb_composite_driver eth_driver = { .name = "g_ether", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, .bind = eth_bind, .unbind = __exit_p(eth_unbind), }; @@ -383,7 +481,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(ð_driver); + return usb_composite_probe(ð_driver); } module_init(init); diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 7953948bfe4..ab1065afbbd 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -4,6 +4,8 @@ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation + * Copyright (C) 2009 by Samsung Electronics + * Author: Michal Nazarewicz (mina86@mina86.com) * * This software is distributed under the terms of the GNU General * Public License ("GPL") as published by the Free Software Foundation, @@ -12,8 +14,11 @@ /* #define VERBOSE_DEBUG */ +#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" @@ -36,12 +41,6 @@ * descriptors (roughly equivalent to CDC Unions) may sometimes help. */ -struct acm_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; - struct f_acm { struct gserial port; u8 ctrl_id, data_id; @@ -55,11 +54,7 @@ struct f_acm { */ spinlock_t lock; - struct acm_ep_descs fs; - struct acm_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ @@ -94,12 +89,26 @@ static inline struct f_acm *port_to_acm(struct gserial *p) /* notification endpoint uses smallish and infrequent fixed-size messages */ -#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +#define GS_NOTIFY_INTERVAL_MS 32 #define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ /* interface and class descriptors: */ -static struct usb_interface_descriptor acm_control_interface_desc __initdata = { +static struct usb_interface_assoc_descriptor +acm_iad_descriptor = { + .bLength = sizeof acm_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + /* .bFirstInterface = DYNAMIC, */ + .bInterfaceCount = 2, // control + data + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .iFunction = DYNAMIC */ +}; + + +static struct usb_interface_descriptor acm_control_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -110,7 +119,7 @@ static struct usb_interface_descriptor acm_control_interface_desc __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_interface_descriptor acm_data_interface_desc __initdata = { +static struct usb_interface_descriptor acm_data_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -121,7 +130,7 @@ static struct usb_interface_descriptor acm_data_interface_desc __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc acm_header_desc __initdata = { +static struct usb_cdc_header_desc acm_header_desc = { .bLength = sizeof(acm_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -129,7 +138,7 @@ static struct usb_cdc_header_desc acm_header_desc __initdata = { }; static struct usb_cdc_call_mgmt_descriptor -acm_call_mgmt_descriptor __initdata = { +acm_call_mgmt_descriptor = { .bLength = sizeof(acm_call_mgmt_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, @@ -137,14 +146,14 @@ acm_call_mgmt_descriptor __initdata = { /* .bDataInterface = DYNAMIC */ }; -static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { +static struct usb_cdc_acm_descriptor acm_descriptor = { .bLength = sizeof(acm_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, .bmCapabilities = USB_CDC_CAP_LINE, }; -static struct usb_cdc_union_desc acm_union_desc __initdata = { +static struct usb_cdc_union_desc acm_union_desc = { .bLength = sizeof(acm_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -154,30 +163,31 @@ static struct usb_cdc_union_desc acm_union_desc __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = { +static struct usb_endpoint_descriptor acm_fs_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, + .bInterval = GS_NOTIFY_INTERVAL_MS, }; -static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = { +static struct usb_endpoint_descriptor acm_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 acm_fs_out_desc __initdata = { +static struct usb_endpoint_descriptor acm_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 *acm_fs_function[] __initdata = { +static struct usb_descriptor_header *acm_fs_function[] = { + (struct usb_descriptor_header *) &acm_iad_descriptor, (struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, @@ -191,31 +201,31 @@ static struct usb_descriptor_header *acm_fs_function[] __initdata = { }; /* high speed support: */ - -static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = { +static struct usb_endpoint_descriptor acm_hs_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, + .bInterval = USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS), }; -static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = { +static struct usb_endpoint_descriptor acm_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 acm_hs_out_desc __initdata = { +static struct usb_endpoint_descriptor acm_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 *acm_hs_function[] __initdata = { +static struct usb_descriptor_header *acm_hs_function[] = { + (struct usb_descriptor_header *) &acm_iad_descriptor, (struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, @@ -228,16 +238,54 @@ static struct usb_descriptor_header *acm_hs_function[] __initdata = { NULL, }; +static struct usb_endpoint_descriptor acm_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 acm_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 acm_ss_bulk_comp_desc = { + .bLength = sizeof acm_ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *acm_ss_function[] = { + (struct usb_descriptor_header *) &acm_iad_descriptor, + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_hs_notify_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_ss_in_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &acm_ss_out_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ #define ACM_CTRL_IDX 0 #define ACM_DATA_IDX 1 +#define ACM_IAD_IDX 2 /* static strings, in UTF-8 */ static struct usb_string acm_string_defs[] = { [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_DATA_IDX].s = "CDC ACM Data", - { /* ZEROES END LIST */ }, + [ACM_IAD_IDX ].s = "CDC Serial", + { } /* end of list */ }; static struct usb_gadget_strings acm_string_table = { @@ -384,23 +432,27 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) usb_ep_disable(acm->notify); } else { VDBG(cdev, "init acm ctrl interface %d\n", intf); - acm->notify_desc = ep_choose(cdev->gadget, - acm->hs.notify, - acm->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, acm->notify)) + return -EINVAL; } - usb_ep_enable(acm->notify, acm->notify_desc); + usb_ep_enable(acm->notify); acm->notify->driver_data = acm; } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); - } else { + } + if (!acm->port.in->desc || !acm->port.out->desc) { DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); - acm->port.in_desc = ep_choose(cdev->gadget, - acm->hs.in, acm->fs.in); - acm->port.out_desc = ep_choose(cdev->gadget, - acm->hs.out, acm->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + acm->port.in) || + config_ep_by_speed(cdev->gadget, f, + acm->port.out)) { + acm->port.in->desc = NULL; + acm->port.out->desc = NULL; + return -EINVAL; + } } gserial_connect(&acm->port, acm->port_num); @@ -432,7 +484,7 @@ static void acm_disable(struct usb_function *f) * @length: size of data * Context: irqs blocked, acm->lock held, acm_notify_req non-null * - * Returns zero on sucess or a negative errno. + * Returns zero on success or a negative errno. * * See section 6.3.5 of the CDC 1.1 specification for information * about the only notification we issue: SerialState change. @@ -550,19 +602,34 @@ static int acm_send_break(struct gserial *port, int duration) /*-------------------------------------------------------------------------*/ /* ACM function driver setup/binding */ -static int __init +static int 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) goto fail; acm->ctrl_id = status; + acm_iad_descriptor.bFirstInterface = status; acm_control_interface_desc.bInterfaceNumber = status; acm_union_desc .bMasterInterface0 = status; @@ -607,43 +674,26 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm->notify_req->complete = acm_cdc_notify_complete; acm->notify_req->context = acm; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(acm_fs_function); - if (!f->descriptors) - goto fail; - - acm->fs.in = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_in_desc); - acm->fs.out = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_out_desc); - acm->fs.notify = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - acm_hs_in_desc.bEndpointAddress = - acm_fs_in_desc.bEndpointAddress; - acm_hs_out_desc.bEndpointAddress = - acm_fs_out_desc.bEndpointAddress; - acm_hs_notify_desc.bEndpointAddress = - acm_fs_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(acm_hs_function); - - acm->hs.in = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_in_desc); - acm->hs.out = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_out_desc); - acm->hs.notify = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_notify_desc); - } + acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; + acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; + acm_hs_notify_desc.bEndpointAddress = + acm_fs_notify_desc.bEndpointAddress; + + acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; + acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, + acm_ss_function); + if (status) + goto fail; DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify->name); @@ -666,83 +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); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - gs_free_req(acm->notify, acm->notify_req); - kfree(acm); + acm_string_defs[0].id = 0; + usb_free_all_descriptors(f); + 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) { - /* SH3 doesn't support multiple interfaces */ - if (gadget_is_sh(c->cdev->gadget)) - return false; - - /* sa1100 doesn't have a third interrupt endpoint */ - if (gadget_is_sa1100(c->cdev->gadget)) - return false; + struct f_acm *acm = func_to_acm(f); - /* everything else is *probably* fine ... */ - return true; + 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 __init 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; - - if (!can_support_cdc(c)) - return -EINVAL; - - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ - - /* maybe allocate device-global string IDs, and patch descriptors */ - if (acm_string_defs[ACM_CTRL_IDX].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - acm_string_defs[ACM_CTRL_IDX].id = status; - - acm_control_interface_desc.iInterface = status; - - status = usb_string_id(c->cdev); - if (status < 0) - return status; - acm_string_defs[ACM_DATA_IDX].id = status; + struct f_serial_opts *opts; + struct f_acm *acm; - acm_data_interface_desc.iInterface = status; - } - - /* 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; @@ -751,13 +752,97 @@ int __init 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 ecf5bdd0ae0..798760fa7e7 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -8,24 +8,19 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ +#include <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" /* @@ -45,11 +40,6 @@ * and also means that a get_alt() method is required. */ -struct ecm_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; enum ecm_notify_state { ECM_NOTIFY_NONE, /* don't notify */ @@ -63,11 +53,7 @@ struct f_ecm { char ethaddr[14]; - struct ecm_ep_descs fs; - struct ecm_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; u8 notify_state; bool is_open; @@ -85,10 +71,12 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static inline unsigned ecm_bitrate(struct usb_gadget *g) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 13 * 1024 * 8 * 1000 * 8; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else - return 19 * 64 * 1 * 1000 * 8; + return 19 * 64 * 1 * 1000 * 8; } /*-------------------------------------------------------------------------*/ @@ -106,13 +94,27 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g) * encapsulated commands (vendor-specific, using control-OUT). */ -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define ECM_STATUS_INTERVAL_MS 32 #define ECM_STATUS_BYTECOUNT 16 /* 8 byte header + data */ /* interface descriptor: */ -static struct usb_interface_descriptor ecm_control_intf __initdata = { +static struct usb_interface_assoc_descriptor +ecm_iad_descriptor = { + .bLength = sizeof ecm_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + /* .bFirstInterface = DYNAMIC, */ + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + + +static struct usb_interface_descriptor ecm_control_intf = { .bLength = sizeof ecm_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -125,7 +127,7 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc ecm_header_desc __initdata = { +static struct usb_cdc_header_desc ecm_header_desc = { .bLength = sizeof ecm_header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -133,7 +135,7 @@ static struct usb_cdc_header_desc ecm_header_desc __initdata = { .bcdCDC = cpu_to_le16(0x0110), }; -static struct usb_cdc_union_desc ecm_union_desc __initdata = { +static struct usb_cdc_union_desc ecm_union_desc = { .bLength = sizeof(ecm_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -141,7 +143,7 @@ static struct usb_cdc_union_desc ecm_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, @@ -156,7 +158,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = { /* the default data interface has no endpoints ... */ -static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { +static struct usb_interface_descriptor ecm_data_nop_intf = { .bLength = sizeof ecm_data_nop_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -171,7 +173,7 @@ static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { /* ... but the "real" data interface has two bulk endpoints */ -static struct usb_interface_descriptor ecm_data_intf __initdata = { +static struct usb_interface_descriptor ecm_data_intf = { .bLength = sizeof ecm_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -186,17 +188,17 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, + .bInterval = ECM_STATUS_INTERVAL_MS, }; -static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -204,7 +206,7 @@ static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -212,14 +214,17 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *ecm_fs_function[] __initdata = { +static struct usb_descriptor_header *ecm_fs_function[] = { /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_iad_descriptor, (struct usb_descriptor_header *) &ecm_control_intf, (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, (struct usb_descriptor_header *) &ecm_desc, + /* NOTE: status endpoint might need to be removed */ (struct usb_descriptor_header *) &fs_ecm_notify_desc, + /* data interface, altsettings 0 and 1 */ (struct usb_descriptor_header *) &ecm_data_nop_intf, (struct usb_descriptor_header *) &ecm_data_intf, @@ -230,16 +235,17 @@ static struct usb_descriptor_header *ecm_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = { +static struct usb_endpoint_descriptor hs_ecm_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), - .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, + .bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS), }; -static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = { + +static struct usb_endpoint_descriptor hs_ecm_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -248,7 +254,7 @@ static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_ecm_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -257,14 +263,17 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *ecm_hs_function[] __initdata = { +static struct usb_descriptor_header *ecm_hs_function[] = { /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_iad_descriptor, (struct usb_descriptor_header *) &ecm_control_intf, (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, (struct usb_descriptor_header *) &ecm_desc, + /* NOTE: status endpoint might need to be removed */ (struct usb_descriptor_header *) &hs_ecm_notify_desc, + /* data interface, altsettings 0 and 1 */ (struct usb_descriptor_header *) &ecm_data_nop_intf, (struct usb_descriptor_header *) &ecm_data_intf, @@ -273,12 +282,84 @@ static struct usb_descriptor_header *ecm_hs_function[] __initdata = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_ecm_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), + .bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS), +}; + +static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = { + .bLength = sizeof ss_ecm_intr_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ss_ecm_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ecm_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = { + .bLength = sizeof ss_ecm_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ecm_ss_function[] = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_iad_descriptor, + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &ecm_header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) &ecm_desc, + + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &ss_ecm_notify_desc, + (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc, + + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &ss_ecm_in_desc, + (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_ecm_out_desc, + (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string ecm_string_defs[] = { [0].s = "CDC Ethernet Control Model (ECM)", - [1].s = NULL /* DYNAMIC */, + [1].s = "", [2].s = "CDC Ethernet Data", + [3].s = "CDC ECM", { } /* end of list */ }; @@ -463,13 +544,13 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ecm->notify->driver_data) { VDBG(cdev, "reset ecm control %d\n", intf); usb_ep_disable(ecm->notify); - } else { + } + if (!(ecm->notify->desc)) { VDBG(cdev, "init ecm ctrl %d\n", intf); - ecm->notify_desc = ep_choose(cdev->gadget, - ecm->hs.notify, - ecm->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, ecm->notify)) + goto fail; } - usb_ep_enable(ecm->notify, ecm->notify_desc); + usb_ep_enable(ecm->notify); ecm->notify->driver_data = ecm; /* Data interface has two altsettings, 0 and 1 */ @@ -482,12 +563,17 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gether_disconnect(&ecm->port); } - if (!ecm->port.in) { + if (!ecm->port.in_ep->desc || + !ecm->port.out_ep->desc) { DBG(cdev, "init ecm\n"); - ecm->port.in = ep_choose(cdev->gadget, - ecm->hs.in, ecm->fs.in); - ecm->port.out = ep_choose(cdev->gadget, - ecm->hs.out, ecm->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + ecm->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + ecm->port.out_ep)) { + ecm->port.in_ep->desc = NULL; + ecm->port.out_ep->desc = NULL; + goto fail; + } } /* CDC Ethernet only sends data in non-default altsettings. @@ -497,12 +583,9 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct net_device *net; /* Enable zlps by default for ECM conformance; - * override for musb_hdrc (avoids txdma ovhead) - * and sa1100 (can't). + * override for musb_hdrc (avoids txdma ovhead). */ - ecm->port.is_zlp_ok = !( - gadget_is_sa1100(cdev->gadget) - || gadget_is_musbhdrc(cdev->gadget) + ecm->port.is_zlp_ok = !(gadget_is_musbhdrc(cdev->gadget) ); ecm->port.cdc_filter = DEFAULT_FILTER; DBG(cdev, "activate ecm\n"); @@ -551,7 +634,7 @@ static void ecm_disable(struct usb_function *f) if (ecm->notify->driver_data) { usb_ep_disable(ecm->notify); ecm->notify->driver_data = NULL; - ecm->notify_desc = NULL; + ecm->notify->desc = NULL; } } @@ -599,19 +682,54 @@ static void ecm_close(struct gether *geth) /* ethernet function driver setup/binding */ -static int __init +static int 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) goto fail; ecm->ctrl_id = status; + ecm_iad_descriptor.bFirstInterface = status; ecm_control_intf.bInterfaceNumber = status; ecm_union_desc.bMasterInterface0 = status; @@ -662,42 +780,24 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->notify_req->context = ecm; ecm->notify_req->complete = ecm_notify_complete; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(ecm_fs_function); - if (!f->descriptors) - goto fail; - - ecm->fs.in = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_in_desc); - ecm->fs.out = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_out_desc); - ecm->fs.notify = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_ecm_in_desc.bEndpointAddress = - fs_ecm_in_desc.bEndpointAddress; - hs_ecm_out_desc.bEndpointAddress = - fs_ecm_out_desc.bEndpointAddress; - hs_ecm_notify_desc.bEndpointAddress = - fs_ecm_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(ecm_hs_function); - if (!f->hs_descriptors) - goto fail; - - ecm->hs.in = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_in_desc); - ecm->hs.out = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_out_desc); - ecm->hs.notify = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_notify_desc); - } + hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; + hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; + hs_ecm_notify_desc.bEndpointAddress = + fs_ecm_notify_desc.bEndpointAddress; + + ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; + ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; + ss_ecm_notify_desc.bEndpointAddress = + fs_ecm_notify_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function, + ecm_ss_function); + if (status) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -708,15 +808,13 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->port.close = ecm_close; DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); - if (ecm->notify_req) { kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); @@ -725,9 +823,9 @@ fail: /* we might as well release our claims on endpoints */ if (ecm->notify) ecm->notify->driver_data = NULL; - if (ecm->port.out) + if (ecm->port.out_ep) ecm->port.out_ep->driver_data = NULL; - if (ecm->port.in) + if (ecm->port.in_ep) ecm->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -735,85 +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) { - struct f_ecm *ecm = func_to_ecm(f); + return container_of(to_config_group(item), struct f_ecm_opts, + func_inst.group); +} - DBG(c->cdev, "ecm unbind\n"); +/* f_ecm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ecm); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); +/* f_ecm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm); - kfree(ecm->notify_req->buf); - usb_ep_free_request(ecm->notify, ecm->notify_req); +/* f_ecm_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm); - ecm_string_defs[1].s = NULL; - kfree(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); } -/** - * ecm_bind_config - add CDC Ethernet network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +static struct usb_function_instance *ecm_alloc_inst(void) { - struct f_ecm *ecm; - int status; + 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); + } - if (!can_support_ecm(c->cdev->gadget) || !ethaddr) - return -EINVAL; + config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type); - /* maybe allocate device-global string IDs */ - if (ecm_string_defs[0].id == 0) { + return &opts->func_inst; +} - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - ecm_string_defs[0].id = status; - ecm_control_intf.iInterface = status; +static void ecm_free(struct usb_function *f) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; - /* data interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - ecm_string_defs[2].id = status; - ecm_data_intf.iInterface = status; + 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); +} - /* MAC address */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - ecm_string_defs[1].id = status; - ecm_desc.iMACAddress = status; - } +static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + + DBG(c->cdev, "ecm unbind\n"); + + usb_free_all_descriptors(f); + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); +} + +static struct usb_function *ecm_alloc(struct usb_function_instance *fi) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; + int status; /* allocate and initialize one new instance */ - ecm = kzalloc(sizeof *ecm, GFP_KERNEL); + 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, - "%02X%02X%02X%02X%02X%02X", - ethaddr[0], ethaddr[1], ethaddr[2], - ethaddr[3], ethaddr[4], ethaddr[5]); + 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; @@ -821,11 +963,11 @@ int __init 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) { - ecm_string_defs[1].s = NULL; - 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 new file mode 100644 index 00000000000..d61c11d765d --- /dev/null +++ b/drivers/usb/gadget/f_eem.c @@ -0,0 +1,662 @@ +/* + * f_eem.c -- USB CDC Ethernet (EEM) link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 EF Johnson Technologies + * + * 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> +#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 + +/* + * This function is a "CDC Ethernet Emulation Model" (CDC EEM) + * Ethernet link. + */ + +struct f_eem { + struct gether port; + u8 ctrl_id; +}; + +static inline struct f_eem *func_to_eem(struct usb_function *f) +{ + return container_of(f, struct f_eem, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* interface descriptor: */ + +static struct usb_interface_descriptor eem_intf = { + .bLength = sizeof eem_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_EEM, + .bInterfaceProtocol = USB_CDC_PROTO_EEM, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor eem_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 eem_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 *eem_fs_function[] = { + /* CDC EEM control descriptors */ + (struct usb_descriptor_header *) &eem_intf, + (struct usb_descriptor_header *) &eem_fs_in_desc, + (struct usb_descriptor_header *) &eem_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor eem_hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor eem_hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +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, + (struct usb_descriptor_header *) &eem_hs_out_desc, + NULL, +}; + +/* super speed support: */ + +static struct usb_endpoint_descriptor eem_ss_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor eem_ss_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +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, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +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, + (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &eem_ss_out_desc, + (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string eem_string_defs[] = { + [0].s = "CDC Ethernet Emulation Model (EEM)", + { } /* end of list */ +}; + +static struct usb_gadget_strings eem_string_table = { + .language = 0x0409, /* en-us */ + .strings = eem_string_defs, +}; + +static struct usb_gadget_strings *eem_strings[] = { + &eem_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_eem *eem = func_to_eem(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct net_device *net; + + /* we know alt == 0, so this is an activation or a reset */ + if (alt != 0) + goto fail; + + if (intf == eem->ctrl_id) { + + if (eem->port.in_ep->driver_data) { + DBG(cdev, "reset eem\n"); + gether_disconnect(&eem->port); + } + + if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) { + DBG(cdev, "init eem\n"); + if (config_ep_by_speed(cdev->gadget, f, + eem->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + eem->port.out_ep)) { + eem->port.in_ep->desc = NULL; + eem->port.out_ep->desc = NULL; + goto fail; + } + } + + /* zlps should not occur because zero-length EEM packets + * will be inserted in those cases where they would occur + */ + eem->port.is_zlp_ok = 1; + eem->port.cdc_filter = DEFAULT_FILTER; + DBG(cdev, "activate eem\n"); + net = gether_connect(&eem->port); + if (IS_ERR(net)) + return PTR_ERR(net); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +static void eem_disable(struct usb_function *f) +{ + struct f_eem *eem = func_to_eem(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "eem deactivated\n"); + + if (eem->port.in_ep->driver_data) + gether_disconnect(&eem->port); +} + +/*-------------------------------------------------------------------------*/ + +/* EEM function driver setup/binding */ + +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) + goto fail; + eem->ctrl_id = status; + eem_intf.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); + if (!ep) + goto fail; + eem->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); + if (!ep) + goto fail; + eem->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; + eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; + + eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; + eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, + eem_ss_function); + if (status) + goto fail; + + DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + eem->port.in_ep->name, eem->port.out_ep->name); + return 0; + +fail: + usb_free_all_descriptors(f); + if (eem->port.out_ep) + eem->port.out_ep->driver_data = NULL; + if (eem->port.in_ep) + eem->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = (struct sk_buff *)req->context; + + dev_kfree_skb_any(skb); +} + +/* + * Add the EEM header and ethernet checksum. + * We currently do not attempt to put multiple ethernet frames + * into a single USB transfer + */ +static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) +{ + struct sk_buff *skb2 = NULL; + struct usb_ep *in = port->in_ep; + int padlen = 0; + u16 len = skb->len; + + if (!skb_cloned(skb)) { + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); + + /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, + * stick two bytes of zero-length EEM packet on the end. + */ + if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) + padlen += 2; + + if ((tailroom >= (ETH_FCS_LEN + padlen)) && + (headroom >= EEM_HLEN)) + goto done; + } + + skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return skb; + +done: + /* use the "no CRC" option */ + put_unaligned_be32(0xdeadbeef, skb_put(skb, 4)); + + /* EEM packet header format: + * b0..13: length of ethernet frame + * b14: bmCRC (0 == sentinel CRC) + * b15: bmType (0 == data) + */ + len = skb->len; + put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2)); + + /* add a zero-length EEM packet, if needed */ + if (padlen) + put_unaligned_le16(0, skb_put(skb, 2)); + + return skb; +} + +/* + * Remove the EEM header. Note that there can be many EEM packets in a single + * USB transfer, so we need to break them out and handle them independently. + */ +static int eem_unwrap(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) +{ + struct usb_composite_dev *cdev = port->func.config->cdev; + int status = 0; + + do { + struct sk_buff *skb2; + u16 header; + u16 len = 0; + + if (skb->len < EEM_HLEN) { + status = -EINVAL; + DBG(cdev, "invalid EEM header\n"); + goto error; + } + + /* remove the EEM header */ + header = get_unaligned_le16(skb->data); + skb_pull(skb, EEM_HLEN); + + /* EEM packet header format: + * b0..14: EEM type dependent (data or command) + * b15: bmType (0 == data, 1 == command) + */ + if (header & BIT(15)) { + struct usb_request *req = cdev->req; + u16 bmEEMCmd; + + /* EEM command packet format: + * b0..10: bmEEMCmdParam + * b11..13: bmEEMCmd + * b14: reserved (must be zero) + * b15: bmType (1 == command) + */ + if (header & BIT(14)) + continue; + + bmEEMCmd = (header >> 11) & 0x7; + switch (bmEEMCmd) { + case 0: /* echo */ + len = header & 0x7FF; + if (skb->len < len) { + status = -EOVERFLOW; + goto error; + } + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!skb2)) { + DBG(cdev, "EEM echo response error\n"); + goto next; + } + skb_trim(skb2, len); + put_unaligned_le16(BIT(15) | BIT(11) | len, + skb_push(skb2, 2)); + skb_copy_bits(skb2, 0, req->buf, skb2->len); + req->length = skb2->len; + req->complete = eem_cmd_complete; + req->zero = 1; + req->context = skb2; + if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) + DBG(cdev, "echo response queue fail\n"); + break; + + case 1: /* echo response */ + case 2: /* suspend hint */ + case 3: /* response hint */ + case 4: /* response complete hint */ + case 5: /* tickle */ + default: /* reserved */ + continue; + } + } else { + u32 crc, crc2; + struct sk_buff *skb3; + + /* check for zero-length EEM packet */ + if (header == 0) + continue; + + /* EEM data packet format: + * b0..13: length of ethernet frame + * b14: bmCRC (0 == sentinel, 1 == calculated) + * b15: bmType (0 == data) + */ + len = header & 0x3FFF; + if ((skb->len < len) + || (len < (ETH_HLEN + ETH_FCS_LEN))) { + status = -EINVAL; + goto error; + } + + /* validate CRC */ + if (header & BIT(14)) { + crc = get_unaligned_le32(skb->data + len + - ETH_FCS_LEN); + crc2 = ~crc32_le(~0, + skb->data, len - ETH_FCS_LEN); + } else { + crc = get_unaligned_be32(skb->data + len + - ETH_FCS_LEN); + crc2 = 0xdeadbeef; + } + if (crc != crc2) { + DBG(cdev, "invalid EEM CRC\n"); + goto next; + } + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!skb2)) { + DBG(cdev, "unable to unframe EEM packet\n"); + continue; + } + skb_trim(skb2, len - ETH_FCS_LEN); + + skb3 = skb_copy_expand(skb2, + NET_IP_ALIGN, + 0, + GFP_ATOMIC); + if (unlikely(!skb3)) { + DBG(cdev, "unable to realign EEM packet\n"); + dev_kfree_skb_any(skb2); + continue; + } + dev_kfree_skb_any(skb2); + skb_queue_tail(list, skb3); + } +next: + skb_pull(skb, len); + } while (skb->len); + +error: + dev_kfree_skb_any(skb); + return status; +} + +static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_eem_opts, + func_inst.group); +} + +/* f_eem_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(eem); + +/* 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); + if (!eem) + 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"; + /* 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; + + 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 new file mode 100644 index 00000000000..8598c27c7d4 --- /dev/null +++ b/drivers/usb/gadget/f_fs.c @@ -0,0 +1,3023 @@ +/* + * f_fs.c -- user mode file system API for USB composite function controllers + * + * Copyright (C) 2010 Samsung Electronics + * Author: Michal Nazarewicz <mina86@mina86.com> + * + * Based on inode.c (GadgetFS) which was: + * Copyright (C) 2003-2004 David Brownell + * Copyright (C) 2003 Agilent Technologies + * + * 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. + */ + + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/blkdev.h> +#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> + +#include "u_fs.h" +#include "u_f.h" +#include "configfs.h" + +#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ + +/* Reference counter handling */ +static void ffs_data_get(struct ffs_data *ffs); +static void ffs_data_put(struct ffs_data *ffs); +/* Creates new ffs_data object. */ +static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc)); + +/* Opened counter handling. */ +static void ffs_data_opened(struct ffs_data *ffs); +static void ffs_data_closed(struct ffs_data *ffs); + +/* Called with ffs->mutex held; take over ownership of data. */ +static int __must_check +__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len); +static int __must_check +__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len); + + +/* The function structure ***************************************************/ + +struct ffs_ep; + +struct ffs_function { + struct usb_configuration *conf; + struct usb_gadget *gadget; + struct ffs_data *ffs; + + struct ffs_ep *eps; + u8 eps_revmap[16]; + short *interfaces_nums; + + struct usb_function function; +}; + + +static struct ffs_function *ffs_func_from_usb(struct usb_function *f) +{ + return container_of(f, struct ffs_function, function); +} + + +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 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 *, + const struct usb_ctrlrequest *); +static void ffs_func_suspend(struct usb_function *); +static void ffs_func_resume(struct usb_function *); + + +static int ffs_func_revmap_ep(struct ffs_function *func, u8 num); +static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf); + + +/* The endpoints structures *************************************************/ + +struct ffs_ep { + struct usb_ep *ep; /* P: ffs->eps_lock */ + struct usb_request *req; /* P: epfile->mutex */ + + /* [0]: full speed, [1]: high speed, [2]: super speed */ + struct usb_endpoint_descriptor *descs[3]; + + u8 num; + + int status; /* P: epfile->mutex */ +}; + +struct ffs_epfile { + /* Protects ep->ep and ep->req. */ + struct mutex mutex; + wait_queue_head_t wait; + + struct ffs_data *ffs; + struct ffs_ep *ep; /* P: ffs->eps_lock */ + + struct dentry *dentry; + + char name[5]; + + unsigned char in; /* P: ffs->eps_lock */ + unsigned char isoc; /* P: ffs->eps_lock */ + + 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); + +static struct inode *__must_check +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 ****************************************************/ + +static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) + __attribute__((warn_unused_result, nonnull)); +static char *ffs_prepare_buffer(const char __user *buf, size_t len) + __attribute__((warn_unused_result, nonnull)); + + +/* Control file aka ep0 *****************************************************/ + +static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct ffs_data *ffs = req->context; + + complete_all(&ffs->ep0req_completion); +} + +static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) +{ + struct usb_request *req = ffs->ep0req; + int ret; + + req->zero = len < le16_to_cpu(ffs->ev.setup.wLength); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + req->buf = data; + req->length = len; + + /* + * UDC layer requires to provide a buffer even for ZLP, but should + * not use it at all. Let's provide some poisoned pointer to catch + * possible bug in the driver. + */ + if (req->buf == NULL) + req->buf = (void *)0xDEADBABE; + + reinit_completion(&ffs->ep0req_completion); + + ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC); + if (unlikely(ret < 0)) + return ret; + + ret = wait_for_completion_interruptible(&ffs->ep0req_completion); + if (unlikely(ret)) { + usb_ep_dequeue(ffs->gadget->ep0, req); + return -EINTR; + } + + ffs->setup_state = FFS_NO_SETUP; + return req->status ? req->status : req->actual; +} + +static int __ffs_ep0_stall(struct ffs_data *ffs) +{ + if (ffs->ev.can_stall) { + pr_vdebug("ep0 stall\n"); + usb_ep_set_halt(ffs->gadget->ep0); + ffs->setup_state = FFS_NO_SETUP; + return -EL2HLT; + } else { + pr_debug("bogus ep0 stall!\n"); + return -ESRCH; + } +} + +static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, + size_t len, loff_t *ptr) +{ + struct ffs_data *ffs = file->private_data; + ssize_t ret; + char *data; + + ENTER(); + + /* Fast check if setup was canceled */ + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) + return -EIDRM; + + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return ret; + + /* Check state */ + switch (ffs->state) { + case FFS_READ_DESCRIPTORS: + case FFS_READ_STRINGS: + /* Copy data */ + if (unlikely(len < 16)) { + ret = -EINVAL; + break; + } + + data = ffs_prepare_buffer(buf, len); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + break; + } + + /* Handle data */ + if (ffs->state == FFS_READ_DESCRIPTORS) { + pr_info("read descriptors\n"); + ret = __ffs_data_got_descs(ffs, data, len); + if (unlikely(ret < 0)) + break; + + ffs->state = FFS_READ_STRINGS; + ret = len; + } else { + pr_info("read strings\n"); + ret = __ffs_data_got_strings(ffs, data, len); + if (unlikely(ret < 0)) + break; + + ret = ffs_epfiles_create(ffs); + if (unlikely(ret)) { + ffs->state = FFS_CLOSING; + break; + } + + ffs->state = FFS_ACTIVE; + mutex_unlock(&ffs->mutex); + + ret = ffs_ready(ffs); + if (unlikely(ret < 0)) { + ffs->state = FFS_CLOSING; + return ret; + } + + set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags); + return len; + } + break; + + case FFS_ACTIVE: + data = NULL; + /* + * We're called from user space, we can use _irq + * rather then _irqsave + */ + spin_lock_irq(&ffs->ev.waitq.lock); + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: + ret = -EIDRM; + goto done_spin; + + case FFS_NO_SETUP: + ret = -ESRCH; + goto done_spin; + + case FFS_SETUP_PENDING: + break; + } + + /* FFS_SETUP_PENDING */ + if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) { + spin_unlock_irq(&ffs->ev.waitq.lock); + ret = __ffs_ep0_stall(ffs); + break; + } + + /* FFS_SETUP_PENDING and not stall */ + len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + data = ffs_prepare_buffer(buf, len); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + break; + } + + spin_lock_irq(&ffs->ev.waitq.lock); + + /* + * 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_CANCELLED so we need + * to check for that. If that happened we copied data + * from user space in vain but it's unlikely. + * + * For sure we are not in FFS_NO_SETUP since this is + * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP + * transition can be performed and it's protected by + * mutex. + */ + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { + ret = -EIDRM; +done_spin: + spin_unlock_irq(&ffs->ev.waitq.lock); + } else { + /* unlocks spinlock */ + ret = __ffs_ep0_queue_wait(ffs, data, len); + } + kfree(data); + break; + + default: + ret = -EBADFD; + break; + } + + mutex_unlock(&ffs->mutex); + return ret; +} + +static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, + size_t n) +{ + /* + * We are holding ffs->ev.waitq.lock and ffs->mutex and we need + * to release them. + */ + struct usb_functionfs_event events[n]; + unsigned i = 0; + + memset(events, 0, sizeof events); + + do { + events[i].type = ffs->ev.types[i]; + if (events[i].type == FUNCTIONFS_SETUP) { + events[i].u.setup = ffs->ev.setup; + ffs->setup_state = FFS_SETUP_PENDING; + } + } while (++i < n); + + if (n < ffs->ev.count) { + ffs->ev.count -= n; + memmove(ffs->ev.types, ffs->ev.types + n, + ffs->ev.count * sizeof *ffs->ev.types); + } else { + ffs->ev.count = 0; + } + + spin_unlock_irq(&ffs->ev.waitq.lock); + mutex_unlock(&ffs->mutex); + + return unlikely(__copy_to_user(buf, events, sizeof events)) + ? -EFAULT : sizeof events; +} + +static ssize_t ffs_ep0_read(struct file *file, char __user *buf, + size_t len, loff_t *ptr) +{ + struct ffs_data *ffs = file->private_data; + char *data = NULL; + size_t n; + int ret; + + ENTER(); + + /* Fast check if setup was canceled */ + if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) + return -EIDRM; + + /* Acquire mutex */ + ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret < 0)) + return ret; + + /* Check state */ + if (ffs->state != FFS_ACTIVE) { + ret = -EBADFD; + goto done_mutex; + } + + /* + * We're called from user space, we can use _irq rather then + * _irqsave + */ + spin_lock_irq(&ffs->ev.waitq.lock); + + switch (ffs_setup_state_clear_cancelled(ffs)) { + case FFS_SETUP_CANCELLED: + ret = -EIDRM; + break; + + case FFS_NO_SETUP: + n = len / sizeof(struct usb_functionfs_event); + if (unlikely(!n)) { + ret = -EINVAL; + break; + } + + if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) { + ret = -EAGAIN; + break; + } + + if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, + ffs->ev.count)) { + ret = -EINTR; + break; + } + + return __ffs_ep0_read_events(ffs, buf, + min(n, (size_t)ffs->ev.count)); + + case FFS_SETUP_PENDING: + if (ffs->ev.setup.bRequestType & USB_DIR_IN) { + spin_unlock_irq(&ffs->ev.waitq.lock); + ret = __ffs_ep0_stall(ffs); + goto done_mutex; + } + + len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + + spin_unlock_irq(&ffs->ev.waitq.lock); + + if (likely(len)) { + data = kmalloc(len, GFP_KERNEL); + if (unlikely(!data)) { + ret = -ENOMEM; + goto done_mutex; + } + } + + spin_lock_irq(&ffs->ev.waitq.lock); + + /* See ffs_ep0_write() */ + if (ffs_setup_state_clear_cancelled(ffs) == + FFS_SETUP_CANCELLED) { + ret = -EIDRM; + break; + } + + /* unlocks spinlock */ + ret = __ffs_ep0_queue_wait(ffs, data, len); + if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len))) + ret = -EFAULT; + goto done_mutex; + + default: + ret = -EBADFD; + break; + } + + spin_unlock_irq(&ffs->ev.waitq.lock); +done_mutex: + mutex_unlock(&ffs->mutex); + kfree(data); + return ret; +} + +static int ffs_ep0_open(struct inode *inode, struct file *file) +{ + struct ffs_data *ffs = inode->i_private; + + ENTER(); + + if (unlikely(ffs->state == FFS_CLOSING)) + return -EBUSY; + + file->private_data = ffs; + ffs_data_opened(ffs); + + return 0; +} + +static int ffs_ep0_release(struct inode *inode, struct file *file) +{ + struct ffs_data *ffs = file->private_data; + + ENTER(); + + ffs_data_closed(ffs); + + return 0; +} + +static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) +{ + struct ffs_data *ffs = file->private_data; + struct usb_gadget *gadget = ffs->gadget; + long ret; + + ENTER(); + + if (code == FUNCTIONFS_INTERFACE_REVMAP) { + struct ffs_function *func = ffs->func; + ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV; + } else if (gadget && gadget->ops->ioctl) { + ret = gadget->ops->ioctl(gadget, code, value); + } else { + ret = -ENOTTY; + } + + 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 = { + .llseek = no_llseek, + + .open = ffs_ep0_open, + .write = ffs_ep0_write, + .read = ffs_ep0_read, + .release = ffs_ep0_release, + .unlocked_ioctl = ffs_ep0_ioctl, + .poll = ffs_ep0_poll, +}; + + +/* "Normal" endpoints operations ********************************************/ + +static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) +{ + ENTER(); + if (likely(req->context)) { + struct ffs_ep *ep = _ep->driver_data; + ep->status = req->status ? req->status : req->actual; + complete(req->context); + } +} + +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, data_len; + int halt; + + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { + ret = -ENODEV; + goto error; + } + + /* Wait for endpoint to be enabled */ + ep = epfile->ep; + if (!ep) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto error; + } + + ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep)); + if (ret) { + ret = -EINTR; + goto error; + } + } + + /* 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; + + 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; + + spin_lock_irq(&epfile->ffs->eps_lock); + + 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 */ + struct usb_request *req; + + if (io_data->aio) { + req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); + if (unlikely(!req)) + goto error_lock; + + req->buf = data; + req->length = io_data->len; + + io_data->buf = data; + io_data->ep = ep->ep; + io_data->req = 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 { + 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; +} + +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(); + + 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(); + + 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 +ffs_epfile_open(struct inode *inode, struct file *file) +{ + struct ffs_epfile *epfile = inode->i_private; + + ENTER(); + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + + file->private_data = epfile; + ffs_data_opened(epfile->ffs); + + 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) +{ + struct ffs_epfile *epfile = inode->i_private; + + ENTER(); + + ffs_data_closed(epfile->ffs); + + return 0; +} + +static long ffs_epfile_ioctl(struct file *file, unsigned code, + unsigned long value) +{ + struct ffs_epfile *epfile = file->private_data; + int ret; + + ENTER(); + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + + spin_lock_irq(&epfile->ffs->eps_lock); + if (likely(epfile->ep)) { + switch (code) { + case FUNCTIONFS_FIFO_STATUS: + ret = usb_ep_fifo_status(epfile->ep->ep); + break; + case FUNCTIONFS_FIFO_FLUSH: + usb_ep_fifo_flush(epfile->ep->ep); + ret = 0; + break; + case FUNCTIONFS_CLEAR_HALT: + ret = usb_ep_clear_halt(epfile->ep->ep); + break; + case FUNCTIONFS_ENDPOINT_REVMAP: + ret = epfile->ep->num; + break; + default: + ret = -ENOTTY; + } + } else { + ret = -ENODEV; + } + spin_unlock_irq(&epfile->ffs->eps_lock); + + return ret; +} + +static const struct file_operations ffs_epfile_operations = { + .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, +}; + + +/* File system and super block operations ***********************************/ + +/* + * Mounting the file system creates a controller file, used first for + * function configuration then later for event monitoring. + */ + +static struct inode *__must_check +ffs_sb_make_inode(struct super_block *sb, void *data, + const struct file_operations *fops, + const struct inode_operations *iops, + struct ffs_file_perms *perms) +{ + struct inode *inode; + + ENTER(); + + inode = new_inode(sb); + + if (likely(inode)) { + struct timespec current_time = CURRENT_TIME; + + inode->i_ino = get_next_ino(); + inode->i_mode = perms->mode; + inode->i_uid = perms->uid; + inode->i_gid = perms->gid; + inode->i_atime = current_time; + inode->i_mtime = current_time; + inode->i_ctime = current_time; + inode->i_private = data; + if (fops) + inode->i_fop = fops; + if (iops) + inode->i_op = iops; + } + + return inode; +} + +/* Create "regular" file */ +static struct inode *ffs_sb_create_file(struct super_block *sb, + const char *name, void *data, + const struct file_operations *fops, + struct dentry **dentry_p) +{ + struct ffs_data *ffs = sb->s_fs_info; + struct dentry *dentry; + struct inode *inode; + + ENTER(); + + dentry = d_alloc_name(sb->s_root, name); + if (unlikely(!dentry)) + return NULL; + + inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); + if (unlikely(!inode)) { + dput(dentry); + return NULL; + } + + d_add(dentry, inode); + if (dentry_p) + *dentry_p = dentry; + + return inode; +} + +/* Super block */ +static const struct super_operations ffs_sb_operations = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +struct ffs_sb_fill_data { + struct ffs_file_perms perms; + umode_t root_mode; + const char *dev_name; + 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 = data->ffs_data; + + ENTER(); + + ffs->sb = sb; + data->ffs_data = NULL; + sb->s_fs_info = ffs; + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = FUNCTIONFS_MAGIC; + sb->s_op = &ffs_sb_operations; + sb->s_time_gran = 1; + + /* Root inode */ + data->perms.mode = data->root_mode; + inode = ffs_sb_make_inode(sb, NULL, + &simple_dir_operations, + &simple_dir_inode_operations, + &data->perms); + sb->s_root = d_make_root(inode); + if (unlikely(!sb->s_root)) + return -ENOMEM; + + /* EP0 file */ + if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, + &ffs_ep0_operations, NULL))) + return -ENOMEM; + + return 0; +} + +static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) +{ + ENTER(); + + if (!opts || !*opts) + return 0; + + for (;;) { + unsigned long value; + char *eq, *comma; + + /* Option limit */ + comma = strchr(opts, ','); + if (comma) + *comma = 0; + + /* Value limit */ + eq = strchr(opts, '='); + if (unlikely(!eq)) { + pr_err("'=' missing in %s\n", opts); + return -EINVAL; + } + *eq = 0; + + /* Parse value */ + if (kstrtoul(eq + 1, 0, &value)) { + pr_err("%s: invalid value: %s\n", opts, eq + 1); + return -EINVAL; + } + + /* Interpret option */ + switch (eq - opts) { + case 5: + if (!memcmp(opts, "rmode", 5)) + data->root_mode = (value & 0555) | S_IFDIR; + else if (!memcmp(opts, "fmode", 5)) + data->perms.mode = (value & 0666) | S_IFREG; + else + goto invalid; + break; + + case 4: + if (!memcmp(opts, "mode", 4)) { + data->root_mode = (value & 0555) | S_IFDIR; + data->perms.mode = (value & 0666) | S_IFREG; + } else { + goto invalid; + } + break; + + case 3: + if (!memcmp(opts, "uid", 3)) { + data->perms.uid = make_kuid(current_user_ns(), value); + if (!uid_valid(data->perms.uid)) { + pr_err("%s: unmapped value: %lu\n", opts, value); + return -EINVAL; + } + } else if (!memcmp(opts, "gid", 3)) { + data->perms.gid = make_kgid(current_user_ns(), value); + if (!gid_valid(data->perms.gid)) { + pr_err("%s: unmapped value: %lu\n", opts, value); + return -EINVAL; + } + } else { + goto invalid; + } + break; + + default: +invalid: + pr_err("%s: invalid option\n", opts); + return -EINVAL; + } + + /* Next iteration */ + if (!comma) + break; + opts = comma + 1; + } + + return 0; +} + +/* "mount -t functionfs dev_name /dev/function" ends up here */ + +static struct dentry * +ffs_fs_mount(struct file_system_type *t, int flags, + const char *dev_name, void *opts) +{ + struct ffs_sb_fill_data data = { + .perms = { + .mode = S_IFREG | 0600, + .uid = GLOBAL_ROOT_UID, + .gid = GLOBAL_ROOT_GID, + }, + .root_mode = S_IFDIR | 0500, + }; + struct dentry *rv; + int ret; + void *ffs_dev; + struct ffs_data *ffs; + + ENTER(); + + ret = ffs_fs_parse_opts(&data, opts); + if (unlikely(ret < 0)) + return ERR_PTR(ret); + + ffs = ffs_data_new(); + if (unlikely(!ffs)) + return ERR_PTR(-ENOMEM); + ffs->file_perms = data.perms; + + ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); + if (unlikely(!ffs->dev_name)) { + ffs_data_put(ffs); + return ERR_PTR(-ENOMEM); + } + + 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; +} + +static void +ffs_fs_kill_sb(struct super_block *sb) +{ + ENTER(); + + kill_litter_super(sb); + if (sb->s_fs_info) { + ffs_release_dev(sb->s_fs_info); + ffs_data_put(sb->s_fs_info); + } +} + +static struct file_system_type ffs_fs_type = { + .owner = THIS_MODULE, + .name = "functionfs", + .mount = ffs_fs_mount, + .kill_sb = ffs_fs_kill_sb, +}; +MODULE_ALIAS_FS("functionfs"); + + +/* Driver's main init/cleanup functions *************************************/ + +static int functionfs_init(void) +{ + int ret; + + ENTER(); + + ret = register_filesystem(&ffs_fs_type); + if (likely(!ret)) + pr_info("file system registered\n"); + else + pr_err("failed registering file system (%d)\n", ret); + + return ret; +} + +static void functionfs_cleanup(void) +{ + ENTER(); + + pr_info("unloading\n"); + unregister_filesystem(&ffs_fs_type); +} + + +/* ffs_data and ffs_function construction and destruction code **************/ + +static void ffs_data_clear(struct ffs_data *ffs); +static void ffs_data_reset(struct ffs_data *ffs); + +static void ffs_data_get(struct ffs_data *ffs) +{ + ENTER(); + + atomic_inc(&ffs->ref); +} + +static void ffs_data_opened(struct ffs_data *ffs) +{ + ENTER(); + + atomic_inc(&ffs->ref); + atomic_inc(&ffs->opened); +} + +static void ffs_data_put(struct ffs_data *ffs) +{ + ENTER(); + + if (unlikely(atomic_dec_and_test(&ffs->ref))) { + pr_info("%s(): freeing\n", __func__); + ffs_data_clear(ffs); + BUG_ON(waitqueue_active(&ffs->ev.waitq) || + waitqueue_active(&ffs->ep0req_completion.wait)); + kfree(ffs->dev_name); + kfree(ffs); + } +} + +static void ffs_data_closed(struct ffs_data *ffs) +{ + ENTER(); + + if (atomic_dec_and_test(&ffs->opened)) { + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + } + + ffs_data_put(ffs); +} + +static struct ffs_data *ffs_data_new(void) +{ + struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); + if (unlikely(!ffs)) + return NULL; + + ENTER(); + + atomic_set(&ffs->ref, 1); + atomic_set(&ffs->opened, 0); + ffs->state = FFS_READ_DESCRIPTORS; + mutex_init(&ffs->mutex); + spin_lock_init(&ffs->eps_lock); + init_waitqueue_head(&ffs->ev.waitq); + init_completion(&ffs->ep0req_completion); + + /* XXX REVISIT need to update it in some places, or do we? */ + ffs->ev.can_stall = 1; + + return ffs; +} + +static void ffs_data_clear(struct ffs_data *ffs) +{ + ENTER(); + + if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) + ffs_closed(ffs); + + BUG_ON(ffs->gadget); + + if (ffs->epfiles) + ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); + + kfree(ffs->raw_descs_data); + kfree(ffs->raw_strings); + kfree(ffs->stringtabs); +} + +static void ffs_data_reset(struct ffs_data *ffs) +{ + ENTER(); + + 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->fs_descs_count = 0; + ffs->hs_descs_count = 0; + ffs->ss_descs_count = 0; + + ffs->strings_count = 0; + ffs->interfaces_count = 0; + ffs->eps_count = 0; + + ffs->ev.count = 0; + + ffs->state = FFS_READ_DESCRIPTORS; + ffs->setup_state = FFS_NO_SETUP; + ffs->flags = 0; +} + + +static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) +{ + struct usb_gadget_strings **lang; + int first_id; + + ENTER(); + + if (WARN_ON(ffs->state != FFS_ACTIVE + || test_and_set_bit(FFS_FL_BOUND, &ffs->flags))) + return -EBADFD; + + first_id = usb_string_ids_n(cdev, ffs->strings_count); + if (unlikely(first_id < 0)) + return first_id; + + ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); + if (unlikely(!ffs->ep0req)) + return -ENOMEM; + ffs->ep0req->complete = ffs_ep0_complete; + ffs->ep0req->context = ffs; + + lang = ffs->stringtabs; + 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; + ffs_data_get(ffs); + return 0; +} + +static void functionfs_unbind(struct ffs_data *ffs) +{ + ENTER(); + + if (!WARN_ON(!ffs->gadget)) { + usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req); + ffs->ep0req = NULL; + ffs->gadget = NULL; + clear_bit(FFS_FL_BOUND, &ffs->flags); + ffs_data_put(ffs); + } +} + +static int ffs_epfiles_create(struct ffs_data *ffs) +{ + struct ffs_epfile *epfile, *epfiles; + unsigned i, count; + + ENTER(); + + count = ffs->eps_count; + epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL); + if (!epfiles) + return -ENOMEM; + + epfile = epfiles; + for (i = 1; i <= count; ++i, ++epfile) { + epfile->ffs = ffs; + mutex_init(&epfile->mutex); + init_waitqueue_head(&epfile->wait); + sprintf(epfiles->name, "ep%u", i); + if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile, + &ffs_epfile_operations, + &epfile->dentry))) { + ffs_epfiles_destroy(epfiles, i - 1); + return -ENOMEM; + } + } + + ffs->epfiles = epfiles; + return 0; +} + +static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) +{ + struct ffs_epfile *epfile = epfiles; + + ENTER(); + + for (; count; --count, ++epfile) { + BUG_ON(mutex_is_locked(&epfile->mutex) || + waitqueue_active(&epfile->wait)); + if (epfile->dentry) { + d_delete(epfile->dentry); + dput(epfile->dentry); + epfile->dentry = NULL; + } + } + + kfree(epfiles); +} + + +static void ffs_func_eps_disable(struct ffs_function *func) +{ + struct ffs_ep *ep = func->eps; + struct ffs_epfile *epfile = func->ffs->epfiles; + unsigned count = func->ffs->eps_count; + unsigned long flags; + + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + /* pending requests get nuked */ + if (likely(ep->ep)) + usb_ep_disable(ep->ep); + epfile->ep = NULL; + + ++ep; + ++epfile; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); +} + +static int ffs_func_eps_enable(struct ffs_function *func) +{ + struct ffs_data *ffs = func->ffs; + struct ffs_ep *ep = func->eps; + struct ffs_epfile *epfile = ffs->epfiles; + unsigned count = ffs->eps_count; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + struct usb_endpoint_descriptor *ds; + 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; + ret = usb_ep_enable(ep->ep); + if (likely(!ret)) { + epfile->ep = ep; + epfile->in = usb_endpoint_dir_in(ds); + epfile->isoc = usb_endpoint_xfer_isoc(ds); + } else { + break; + } + + wake_up(&epfile->wait); + + ++ep; + ++epfile; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); + + return ret; +} + + +/* Parsing and building descriptors and strings *****************************/ + +/* + * This validates if data pointed by data is a valid USB descriptor as + * well as record how many interfaces, endpoints and strings are + * required by given configuration. Returns address after the + * descriptor or NULL if data is invalid. + */ + +enum ffs_entity_type { + FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT +}; + +typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, + u8 *valuep, + struct usb_descriptor_header *desc, + void *priv); + +static int __must_check ffs_do_desc(char *data, unsigned len, + ffs_entity_callback entity, void *priv) +{ + struct usb_descriptor_header *_ds = (void *)data; + u8 length; + int ret; + + ENTER(); + + /* At least two bytes are required: length and type */ + if (len < 2) { + pr_vdebug("descriptor too short\n"); + return -EINVAL; + } + + /* If we have at least as many bytes as the descriptor takes? */ + length = _ds->bLength; + if (len < length) { + pr_vdebug("descriptor longer then available data\n"); + return -EINVAL; + } + +#define __entity_check_INTERFACE(val) 1 +#define __entity_check_STRING(val) (val) +#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK) +#define __entity(type, val) do { \ + pr_vdebug("entity " #type "(%02x)\n", (val)); \ + if (unlikely(!__entity_check_ ##type(val))) { \ + pr_vdebug("invalid entity's value\n"); \ + return -EINVAL; \ + } \ + ret = entity(FFS_ ##type, &val, _ds, priv); \ + if (unlikely(ret < 0)) { \ + pr_debug("entity " #type "(%02x); ret = %d\n", \ + (val), ret); \ + return ret; \ + } \ + } while (0) + + /* Parse descriptor depending on type. */ + switch (_ds->bDescriptorType) { + case USB_DT_DEVICE: + case USB_DT_CONFIG: + case USB_DT_STRING: + case USB_DT_DEVICE_QUALIFIER: + /* function can't have any of those */ + pr_vdebug("descriptor reserved for gadget: %d\n", + _ds->bDescriptorType); + return -EINVAL; + + case USB_DT_INTERFACE: { + struct usb_interface_descriptor *ds = (void *)_ds; + pr_vdebug("interface descriptor\n"); + if (length != sizeof *ds) + goto inv_length; + + __entity(INTERFACE, ds->bInterfaceNumber); + if (ds->iInterface) + __entity(STRING, ds->iInterface); + } + break; + + case USB_DT_ENDPOINT: { + struct usb_endpoint_descriptor *ds = (void *)_ds; + pr_vdebug("endpoint descriptor\n"); + if (length != USB_DT_ENDPOINT_SIZE && + length != USB_DT_ENDPOINT_AUDIO_SIZE) + goto inv_length; + __entity(ENDPOINT, ds->bEndpointAddress); + } + break; + + case HID_DT_HID: + pr_vdebug("hid descriptor\n"); + if (length != sizeof(struct hid_descriptor)) + goto inv_length; + break; + + case USB_DT_OTG: + if (length != sizeof(struct usb_otg_descriptor)) + goto inv_length; + break; + + case USB_DT_INTERFACE_ASSOCIATION: { + struct usb_interface_assoc_descriptor *ds = (void *)_ds; + pr_vdebug("interface association descriptor\n"); + if (length != sizeof *ds) + goto inv_length; + if (ds->iFunction) + __entity(STRING, ds->iFunction); + } + 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: + case USB_DT_SECURITY: + case USB_DT_CS_RADIO_CONTROL: + /* TODO */ + pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType); + return -EINVAL; + + default: + /* We should never be here */ + pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType); + return -EINVAL; + +inv_length: + pr_vdebug("invalid length: %d (descriptor %d)\n", + _ds->bLength, _ds->bDescriptorType); + return -EINVAL; + } + +#undef __entity +#undef __entity_check_DESCRIPTOR +#undef __entity_check_INTERFACE +#undef __entity_check_STRING +#undef __entity_check_ENDPOINT + + return length; +} + +static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, + ffs_entity_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (;;) { + int ret; + + if (num == count) + data = NULL; + + /* Record "descriptor" entity */ + ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv); + if (unlikely(ret < 0)) { + pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n", + num, ret); + return ret; + } + + if (!data) + return _len - len; + + ret = ffs_do_desc(data, len, entity, priv); + if (unlikely(ret < 0)) { + pr_debug("%s returns %d\n", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + ++num; + } +} + +static int __ffs_data_do_entity(enum ffs_entity_type type, + u8 *valuep, struct usb_descriptor_header *desc, + void *priv) +{ + struct ffs_data *ffs = priv; + + ENTER(); + + switch (type) { + case FFS_DESCRIPTOR: + break; + + case FFS_INTERFACE: + /* + * Interfaces are indexed from zero so if we + * encountered interface "n" then there are at least + * "n+1" interfaces. + */ + if (*valuep >= ffs->interfaces_count) + ffs->interfaces_count = *valuep + 1; + break; + + case FFS_STRING: + /* + * Strings are indexed from 1 (0 is magic ;) reserved + * for languages list or some such) + */ + if (*valuep > ffs->strings_count) + ffs->strings_count = *valuep; + break; + + case FFS_ENDPOINT: + /* Endpoints are indexed from 1 as well. */ + if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count) + ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK); + break; + } + + return 0; +} + +static int __ffs_data_got_descs(struct ffs_data *ffs, + char *const _data, size_t len) +{ + char *data = _data, *raw_descs; + unsigned counts[3], flags; + int ret = -EINVAL, i; + + ENTER(); + + if (get_unaligned_le32(data + 4) != len) + goto error; + + 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; + } + + /* 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; + } + } + + /* 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 (ret < 0) + goto error; + data += ret; + len -= ret; + } + + if (raw_descs == data || len) { + ret = -EINVAL; + goto error; + } + + 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; + +error: + kfree(_data); + return ret; +} + +static int __ffs_data_got_strings(struct ffs_data *ffs, + char *const _data, size_t len) +{ + u32 str_count, needed_count, lang_count; + struct usb_gadget_strings **stringtabs, *t; + struct usb_string *strings, *s; + const char *data = _data; + + ENTER(); + + if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC || + get_unaligned_le32(data + 4) != len)) + goto error; + str_count = get_unaligned_le32(data + 8); + lang_count = get_unaligned_le32(data + 12); + + /* if one is zero the other must be zero */ + if (unlikely(!str_count != !lang_count)) + goto error; + + /* Do we have at least as many strings as descriptors need? */ + needed_count = ffs->strings_count; + if (unlikely(str_count < needed_count)) + goto error; + + /* + * If we don't need any strings just return and free all + * memory. + */ + if (!needed_count) { + kfree(_data); + return 0; + } + + /* Allocate everything in one chunk so there's less maintenance. */ + { + 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)); + + char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + + if (unlikely(!vlabuf)) { + kfree(_data); + return -ENOMEM; + } + + /* 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 = 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; + } + + /* For each language */ + data += 16; + len -= 16; + + do { /* lang_count > 0 so we can use do-while */ + unsigned needed = needed_count; + + if (unlikely(len < 3)) + goto error_free; + t->language = get_unaligned_le16(data); + t->strings = s; + ++t; + + data += 2; + len -= 2; + + /* For each string */ + do { /* str_count > 0 so we can use do-while */ + size_t length = strnlen(data, len); + + if (unlikely(length == len)) + goto error_free; + + /* + * User may provide more strings then we need, + * if that's the case we simply ignore the + * rest + */ + if (likely(needed)) { + /* + * s->id will be set while adding + * function to configuration so for + * now just leave garbage here. + */ + s->s = data; + --needed; + ++s; + } + + data += length + 1; + len -= length + 1; + } while (--str_count); + + s->id = 0; /* terminator */ + s->s = NULL; + ++s; + + } while (--lang_count); + + /* Some garbage left? */ + if (unlikely(len)) + goto error_free; + + /* Done! */ + ffs->stringtabs = stringtabs; + ffs->raw_strings = _data; + + return 0; + +error_free: + kfree(stringtabs); +error: + kfree(_data); + return -EINVAL; +} + + +/* Events handling and management *******************************************/ + +static void __ffs_event_add(struct ffs_data *ffs, + enum usb_functionfs_event_type type) +{ + enum usb_functionfs_event_type rem_type1, rem_type2 = type; + int neg = 0; + + /* + * Abort any unhandled setup + * + * We do not need to worry about some cmpxchg() changing value + * of ffs->setup_state without holding the lock because when + * state is FFS_SETUP_PENDING cmpxchg() in several places in + * the source does nothing. + */ + if (ffs->setup_state == FFS_SETUP_PENDING) + ffs->setup_state = FFS_SETUP_CANCELLED; + + switch (type) { + case FUNCTIONFS_RESUME: + rem_type2 = FUNCTIONFS_SUSPEND; + /* FALL THROUGH */ + case FUNCTIONFS_SUSPEND: + case FUNCTIONFS_SETUP: + rem_type1 = type; + /* Discard all similar events */ + break; + + case FUNCTIONFS_BIND: + case FUNCTIONFS_UNBIND: + case FUNCTIONFS_DISABLE: + case FUNCTIONFS_ENABLE: + /* Discard everything other then power management. */ + rem_type1 = FUNCTIONFS_SUSPEND; + rem_type2 = FUNCTIONFS_RESUME; + neg = 1; + break; + + default: + BUG(); + } + + { + u8 *ev = ffs->ev.types, *out = ev; + unsigned n = ffs->ev.count; + for (; n; --n, ++ev) + if ((*ev == rem_type1 || *ev == rem_type2) == neg) + *out++ = *ev; + else + pr_vdebug("purging event %d\n", *ev); + ffs->ev.count = out - ffs->ev.types; + } + + pr_vdebug("adding event %d\n", type); + ffs->ev.types[ffs->ev.count++] = type; + wake_up_locked(&ffs->ev.waitq); +} + +static void ffs_event_add(struct ffs_data *ffs, + enum usb_functionfs_event_type type) +{ + unsigned long flags; + spin_lock_irqsave(&ffs->ev.waitq.lock, flags); + __ffs_event_add(ffs, type); + spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); +} + + +/* Bind/unbind USB function hooks *******************************************/ + +static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, + struct usb_descriptor_header *desc, + void *priv) +{ + struct usb_endpoint_descriptor *ds = (void *)desc; + struct ffs_function *func = priv; + struct ffs_ep *ffs_ep; + unsigned ep_desc_id, idx; + static const char *speed_names[] = { "full", "high", "super" }; + + if (type != FFS_DESCRIPTOR) + return 0; + + /* + * 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 { + ep_desc_id = 0; + func->function.fs_descriptors[(long)valuep] = desc; + } + + if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return 0; + + idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; + ffs_ep = func->eps + idx; + + 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[ep_desc_id] = ds; + + ffs_dump_mem(": Original ep desc", ds, ds->bLength); + if (ffs_ep->ep) { + ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress; + if (!ds->wMaxPacketSize) + ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize; + } else { + struct usb_request *req; + struct usb_ep *ep; + + pr_vdebug("autoconfig\n"); + ep = usb_ep_autoconfig(func->gadget, ds); + if (unlikely(!ep)) + return -ENOTSUPP; + ep->driver_data = func->eps + idx; + + req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (unlikely(!req)) + return -ENOMEM; + + ffs_ep->ep = ep; + ffs_ep->req = req; + func->eps_revmap[ds->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK] = idx + 1; + } + ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength); + + return 0; +} + +static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, + struct usb_descriptor_header *desc, + void *priv) +{ + struct ffs_function *func = priv; + unsigned idx; + u8 newValue; + + switch (type) { + default: + case FFS_DESCRIPTOR: + /* Handled in previous pass by __ffs_func_bind_do_descs() */ + return 0; + + case FFS_INTERFACE: + idx = *valuep; + if (func->interfaces_nums[idx] < 0) { + int id = usb_interface_id(func->conf, &func->function); + if (unlikely(id < 0)) + return id; + func->interfaces_nums[idx] = id; + } + newValue = func->interfaces_nums[idx]; + break; + + case FFS_STRING: + /* String' IDs are allocated when fsf_data is bound to cdev */ + newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id; + break; + + case FFS_ENDPOINT: + /* + * USB_DT_ENDPOINT are handled in + * __ffs_func_bind_do_descs(). + */ + if (desc->bDescriptorType == USB_DT_ENDPOINT) + return 0; + + idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1; + if (unlikely(!func->eps[idx].ep)) + return -EINVAL; + + { + struct usb_endpoint_descriptor **descs; + descs = func->eps[idx].descs; + newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress; + } + break; + } + + pr_vdebug("%02x -> %02x\n", *valuep, newValue); + *valuep = newValue; + return 0; +} + +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; + + 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 fs_len, hs_len, ret; + + /* Make it a single chunk, less management later on */ + 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(); + + /* Has descriptors only for speeds gadget does not support */ + if (unlikely(!(full | high | super))) + return -ENOTSUPP; + + /* Allocate a single chunk, less management later on */ + vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + if (unlikely(!vlabuf)) + return -ENOMEM; + + /* Zero */ + 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); + + 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 + * endpoints first, so that later we can rewrite the endpoint + * numbers without worrying that it may be described later on. + */ + if (likely(full)) { + 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 { + fs_len = 0; + } + + if (likely(high)) { + 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; + } + + /* + * Now handle interface numbers allocation and interface and + * endpoint numbers rewriting. We can do that in one go + * now. + */ + ret = ffs_do_descs(ffs->fs_descs_count + + (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; + + /* And we're done */ + ffs_event_add(ffs, FUNCTIONFS_BIND); + return 0; + +error: + /* XXX Do we need to release all claimed endpoints here? */ + return ret; +} + +static int ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); + + if (IS_ERR(ffs_opts)) + return PTR_ERR(ffs_opts); + + return _ffs_func_bind(c, f); +} + + +/* Other USB function hooks *************************************************/ + +static int ffs_func_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + int ret = 0, intf; + + if (alt != (unsigned)-1) { + intf = ffs_func_revmap_intf(func, interface); + if (unlikely(intf < 0)) + return intf; + } + + if (ffs->func) + ffs_func_eps_disable(ffs->func); + + if (ffs->state != FFS_ACTIVE) + return -ENODEV; + + if (alt == (unsigned)-1) { + ffs->func = NULL; + ffs_event_add(ffs, FUNCTIONFS_DISABLE); + return 0; + } + + ffs->func = func; + ret = ffs_func_eps_enable(func); + if (likely(ret >= 0)) + ffs_event_add(ffs, FUNCTIONFS_ENABLE); + return ret; +} + +static void ffs_func_disable(struct usb_function *f) +{ + ffs_func_set_alt(f, 0, (unsigned)-1); +} + +static int ffs_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *creq) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + unsigned long flags; + int ret; + + ENTER(); + + pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType); + pr_vdebug("creq->bRequest = %02x\n", creq->bRequest); + pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue)); + pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex)); + pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength)); + + /* + * Most requests directed to interface go through here + * (notable exceptions are set/get interface) so we need to + * handle them. All other either handled by composite or + * passed to usb_configuration->setup() (if one is set). No + * matter, we will handle requests directed to endpoint here + * as well (as it's straightforward) but what to do with any + * other request? + */ + if (ffs->state != FFS_ACTIVE) + return -ENODEV; + + switch (creq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex)); + if (unlikely(ret < 0)) + return ret; + break; + + case USB_RECIP_ENDPOINT: + ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex)); + if (unlikely(ret < 0)) + return ret; + break; + + default: + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&ffs->ev.waitq.lock, flags); + ffs->ev.setup = *creq; + ffs->ev.setup.wIndex = cpu_to_le16(ret); + __ffs_event_add(ffs, FUNCTIONFS_SETUP); + spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); + + return 0; +} + +static void ffs_func_suspend(struct usb_function *f) +{ + ENTER(); + ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND); +} + +static void ffs_func_resume(struct usb_function *f) +{ + ENTER(); + ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME); +} + + +/* Endpoint and interface numbers reverse mapping ***************************/ + +static int ffs_func_revmap_ep(struct ffs_function *func, u8 num) +{ + num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK]; + return num ? num : -EDOM; +} + +static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) +{ + short *nums = func->interfaces_nums; + unsigned count = func->ffs->interfaces_count; + + for (; count; --count, ++nums) { + if (*nums >= 0 && *nums == intf) + return nums - func->interfaces_nums; + } + + return -EDOM; +} + + +/* 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) +{ + return nonblock + ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN + : mutex_lock_interruptible(mutex); +} + +static char *ffs_prepare_buffer(const char __user *buf, size_t len) +{ + char *data; + + if (unlikely(!len)) + return NULL; + + data = kmalloc(len, GFP_KERNEL); + if (unlikely(!data)) + return ERR_PTR(-ENOMEM); + + if (unlikely(__copy_from_user(data, buf, len))) { + kfree(data); + return ERR_PTR(-EFAULT); + } + + pr_vdebug("Buffer from user space:\n"); + ffs_dump_mem("", data, 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 new file mode 100644 index 00000000000..a95290a1289 --- /dev/null +++ b/drivers/usb/gadget/f_hid.c @@ -0,0 +1,763 @@ +/* + * f_hid.c -- USB HID function driver + * + * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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> +#include <linux/module.h> +#include <linux/hid.h> +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/uaccess.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/usb/g_hid.h> + +#include "u_f.h" + +static int major, minors; +static struct class *hidg_class; + +/*-------------------------------------------------------------------------*/ +/* HID gadget struct */ + +struct f_hidg_req_list { + struct usb_request *req; + unsigned int pos; + struct list_head list; +}; + +struct f_hidg { + /* configuration */ + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned short report_desc_length; + char *report_desc; + unsigned short report_length; + + /* recv report */ + struct list_head completed_out_req; + spinlock_t spinlock; + wait_queue_head_t read_queue; + unsigned int qlen; + + /* send report */ + struct mutex lock; + bool write_pending; + wait_queue_head_t write_queue; + struct usb_request *req; + + int minor; + struct cdev cdev; + struct usb_function func; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; +}; + +static inline struct f_hidg *func_to_hidg(struct usb_function *f) +{ + return container_of(f, struct f_hidg, func); +} + +/*-------------------------------------------------------------------------*/ +/* Static descriptors */ + +static struct usb_interface_descriptor hidg_interface_desc = { + .bLength = sizeof hidg_interface_desc, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + /* .bInterfaceSubClass = DYNAMIC */ + /* .bInterfaceProtocol = DYNAMIC */ + /* .iInterface = DYNAMIC */ +}; + +static struct hid_descriptor hidg_desc = { + .bLength = sizeof hidg_desc, + .bDescriptorType = HID_DT_HID, + .bcdHID = 0x0101, + .bCountryCode = 0x00, + .bNumDescriptors = 0x1, + /*.desc[0].bDescriptorType = DYNAMIC */ + /*.desc[0].wDescriptorLenght = DYNAMIC */ +}; + +/* High-Speed Support */ + +static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 4, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + +static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 4, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + +static struct usb_descriptor_header *hidg_hs_descriptors[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, + (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, + NULL, +}; + +/* Full-Speed Support */ + +static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 10, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + +static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + /*.wMaxPacketSize = DYNAMIC */ + .bInterval = 10, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; + +static struct usb_descriptor_header *hidg_fs_descriptors[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, + (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +/* Char Device */ + +static ssize_t f_hidg_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + struct f_hidg *hidg = file->private_data; + struct f_hidg_req_list *list; + struct usb_request *req; + unsigned long flags; + int ret; + + if (!count) + return 0; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&hidg->spinlock, flags); + +#define READ_COND (!list_empty(&hidg->completed_out_req)) + + /* wait for at least one buffer to complete */ + while (!READ_COND) { + spin_unlock_irqrestore(&hidg->spinlock, flags); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(hidg->read_queue, READ_COND)) + return -ERESTARTSYS; + + spin_lock_irqsave(&hidg->spinlock, flags); + } + + /* pick the first one */ + list = list_first_entry(&hidg->completed_out_req, + struct f_hidg_req_list, list); + req = list->req; + count = min_t(unsigned int, count, req->actual - list->pos); + spin_unlock_irqrestore(&hidg->spinlock, flags); + + /* copy to user outside spinlock */ + count -= copy_to_user(buffer, req->buf + list->pos, count); + list->pos += count; + + /* + * if this request is completely handled and transfered to + * userspace, remove its entry from the list and requeue it + * again. Otherwise, we will revisit it again upon the next + * call, taking into account its current read position. + */ + if (list->pos == req->actual) { + spin_lock_irqsave(&hidg->spinlock, flags); + list_del(&list->list); + kfree(list); + spin_unlock_irqrestore(&hidg->spinlock, flags); + + req->length = hidg->report_length; + ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); + if (ret < 0) + return ret; + } + + return count; +} + +static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; + + if (req->status != 0) { + ERROR(hidg->func.config->cdev, + "End Point Request ERROR: %d\n", req->status); + } + + hidg->write_pending = 0; + wake_up(&hidg->write_queue); +} + +static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + size_t count, loff_t *offp) +{ + struct f_hidg *hidg = file->private_data; + ssize_t status = -ENOMEM; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + mutex_lock(&hidg->lock); + +#define WRITE_COND (!hidg->write_pending) + + /* write queue */ + while (!WRITE_COND) { + mutex_unlock(&hidg->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible_exclusive( + hidg->write_queue, WRITE_COND)) + return -ERESTARTSYS; + + mutex_lock(&hidg->lock); + } + + count = min_t(unsigned, count, hidg->report_length); + status = copy_from_user(hidg->req->buf, buffer, count); + + if (status != 0) { + ERROR(hidg->func.config->cdev, + "copy_from_user error\n"); + mutex_unlock(&hidg->lock); + return -EINVAL; + } + + hidg->req->status = 0; + hidg->req->zero = 0; + hidg->req->length = count; + hidg->req->complete = f_hidg_req_complete; + hidg->req->context = hidg; + hidg->write_pending = 1; + + status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); + if (status < 0) { + ERROR(hidg->func.config->cdev, + "usb_ep_queue error on int endpoint %zd\n", status); + hidg->write_pending = 0; + wake_up(&hidg->write_queue); + } else { + status = count; + } + + mutex_unlock(&hidg->lock); + + return status; +} + +static unsigned int f_hidg_poll(struct file *file, poll_table *wait) +{ + struct f_hidg *hidg = file->private_data; + unsigned int ret = 0; + + poll_wait(file, &hidg->read_queue, wait); + poll_wait(file, &hidg->write_queue, wait); + + if (WRITE_COND) + ret |= POLLOUT | POLLWRNORM; + + if (READ_COND) + ret |= POLLIN | POLLRDNORM; + + return ret; +} + +#undef WRITE_COND +#undef READ_COND + +static int f_hidg_release(struct inode *inode, struct file *fd) +{ + fd->private_data = NULL; + return 0; +} + +static int f_hidg_open(struct inode *inode, struct file *fd) +{ + struct f_hidg *hidg = + container_of(inode->i_cdev, struct f_hidg, cdev); + + fd->private_data = hidg; + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* usb_function */ + +static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, + unsigned length) +{ + return alloc_ep_req(ep, length, length); +} + +static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *) req->context; + struct f_hidg_req_list *req_list; + unsigned long flags; + + req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); + if (!req_list) + return; + + req_list->req = req; + + spin_lock_irqsave(&hidg->spinlock, flags); + list_add_tail(&req_list->list, &hidg->completed_out_req); + spin_unlock_irqrestore(&hidg->spinlock, flags); + + wake_up(&hidg->read_queue); +} + +static int hidg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_hidg *hidg = func_to_hidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int status = 0; + __u16 value, length; + + value = __le16_to_cpu(ctrl->wValue); + length = __le16_to_cpu(ctrl->wLength); + + VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " + "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_REPORT): + VDBG(cdev, "get_report\n"); + + /* send an empty report */ + length = min_t(unsigned, length, hidg->report_length); + memset(req->buf, 0x0, length); + + goto respond; + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_PROTOCOL): + VDBG(cdev, "get_protocol\n"); + goto stall; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_REPORT): + VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); + goto stall; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_PROTOCOL): + VDBG(cdev, "set_protocol\n"); + goto stall; + break; + + case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 + | USB_REQ_GET_DESCRIPTOR): + switch (value >> 8) { + case HID_DT_HID: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); + length = min_t(unsigned short, length, + hidg_desc.bLength); + memcpy(req->buf, &hidg_desc, length); + goto respond; + break; + case HID_DT_REPORT: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); + length = min_t(unsigned short, length, + hidg->report_desc_length); + memcpy(req->buf, hidg->report_desc, length); + goto respond; + break; + + default: + VDBG(cdev, "Unknown descriptor request 0x%x\n", + value >> 8); + goto stall; + break; + } + break; + + default: + VDBG(cdev, "Unknown request 0x%x\n", + ctrl->bRequest); + goto stall; + break; + } + +stall: + return -EOPNOTSUPP; + +respond: + req->zero = 0; + req->length = length; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (status < 0) + ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); + return status; +} + +static void hidg_disable(struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + struct f_hidg_req_list *list, *next; + + usb_ep_disable(hidg->in_ep); + hidg->in_ep->driver_data = NULL; + + usb_ep_disable(hidg->out_ep); + hidg->out_ep->driver_data = NULL; + + list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { + list_del(&list->list); + kfree(list); + } +} + +static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_hidg *hidg = func_to_hidg(f); + int i, status = 0; + + VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); + + if (hidg->in_ep != NULL) { + /* restart endpoint */ + if (hidg->in_ep->driver_data != NULL) + usb_ep_disable(hidg->in_ep); + + status = config_ep_by_speed(f->config->cdev->gadget, f, + hidg->in_ep); + if (status) { + ERROR(cdev, "config_ep_by_speed FAILED!\n"); + goto fail; + } + status = usb_ep_enable(hidg->in_ep); + if (status < 0) { + ERROR(cdev, "Enable IN endpoint FAILED!\n"); + goto fail; + } + hidg->in_ep->driver_data = hidg; + } + + + if (hidg->out_ep != NULL) { + /* restart endpoint */ + if (hidg->out_ep->driver_data != NULL) + usb_ep_disable(hidg->out_ep); + + status = config_ep_by_speed(f->config->cdev->gadget, f, + hidg->out_ep); + if (status) { + ERROR(cdev, "config_ep_by_speed FAILED!\n"); + goto fail; + } + status = usb_ep_enable(hidg->out_ep); + if (status < 0) { + ERROR(cdev, "Enable IN endpoint FAILED!\n"); + goto fail; + } + hidg->out_ep->driver_data = hidg; + + /* + * allocate a bunch of read buffers and queue them all at once. + */ + for (i = 0; i < hidg->qlen && status == 0; i++) { + struct usb_request *req = + hidg_alloc_ep_req(hidg->out_ep, + hidg->report_length); + if (req) { + req->complete = hidg_set_report_complete; + req->context = hidg; + status = usb_ep_queue(hidg->out_ep, req, + GFP_ATOMIC); + if (status) + ERROR(cdev, "%s queue req --> %d\n", + hidg->out_ep->name, status); + } else { + usb_ep_disable(hidg->out_ep); + hidg->out_ep->driver_data = NULL; + status = -ENOMEM; + goto fail; + } + } + } + +fail: + return status; +} + +const struct file_operations f_hidg_fops = { + .owner = THIS_MODULE, + .open = f_hidg_open, + .release = f_hidg_release, + .write = f_hidg_write, + .read = f_hidg_read, + .poll = f_hidg_poll, + .llseek = noop_llseek, +}; + +static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_ep *ep; + struct f_hidg *hidg = func_to_hidg(f); + int status; + dev_t dev; + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + hidg_interface_desc.bInterfaceNumber = status; + + /* allocate instance-specific endpoints */ + status = -ENODEV; + ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); + if (!ep) + goto fail; + ep->driver_data = c->cdev; /* claim */ + hidg->in_ep = ep; + + ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); + if (!ep) + goto fail; + ep->driver_data = c->cdev; /* claim */ + hidg->out_ep = ep; + + /* preallocate request and buffer */ + status = -ENOMEM; + hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); + if (!hidg->req) + goto fail; + + hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); + if (!hidg->req->buf) + goto fail; + + /* set descriptor dynamic values */ + hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; + hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; + hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; + hidg_desc.desc[0].wDescriptorLength = + cpu_to_le16(hidg->report_desc_length); + + hidg_hs_in_ep_desc.bEndpointAddress = + hidg_fs_in_ep_desc.bEndpointAddress; + hidg_hs_out_ep_desc.bEndpointAddress = + hidg_fs_out_ep_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, hidg_fs_descriptors, + hidg_hs_descriptors, NULL); + if (status) + goto fail; + + mutex_init(&hidg->lock); + spin_lock_init(&hidg->spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + + /* create char device */ + cdev_init(&hidg->cdev, &f_hidg_fops); + dev = MKDEV(major, hidg->minor); + status = cdev_add(&hidg->cdev, dev, 1); + if (status) + goto fail; + + device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor); + + return 0; + +fail: + ERROR(f->config->cdev, "hidg_bind FAILED\n"); + if (hidg->req != NULL) { + kfree(hidg->req->buf); + if (hidg->in_ep != NULL) + usb_ep_free_request(hidg->in_ep, hidg->req); + } + + usb_free_all_descriptors(f); + return status; +} + +static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + + device_destroy(hidg_class, MKDEV(major, hidg->minor)); + cdev_del(&hidg->cdev); + + /* disable/free request and end point */ + usb_ep_disable(hidg->in_ep); + usb_ep_dequeue(hidg->in_ep, hidg->req); + kfree(hidg->req->buf); + usb_ep_free_request(hidg->in_ep, hidg->req); + + usb_free_all_descriptors(f); + + kfree(hidg->report_desc); + kfree(hidg); +} + +/*-------------------------------------------------------------------------*/ +/* Strings */ + +#define CT_FUNC_HID_IDX 0 + +static struct usb_string ct_func_string_defs[] = { + [CT_FUNC_HID_IDX].s = "HID Interface", + {}, /* end of list */ +}; + +static struct usb_gadget_strings ct_func_string_table = { + .language = 0x0409, /* en-US */ + .strings = ct_func_string_defs, +}; + +static struct usb_gadget_strings *ct_func_strings[] = { + &ct_func_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +/* usb_configuration */ + +int __init hidg_bind_config(struct usb_configuration *c, + struct hidg_func_descriptor *fdesc, int index) +{ + struct f_hidg *hidg; + int status; + + if (index >= minors) + return -ENOENT; + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ct_func_string_defs[CT_FUNC_HID_IDX].id = status; + hidg_interface_desc.iInterface = status; + } + + /* allocate and initialize one new instance */ + hidg = kzalloc(sizeof *hidg, GFP_KERNEL); + if (!hidg) + return -ENOMEM; + + hidg->minor = index; + hidg->bInterfaceSubClass = fdesc->subclass; + hidg->bInterfaceProtocol = fdesc->protocol; + hidg->report_length = fdesc->report_length; + hidg->report_desc_length = fdesc->report_desc_length; + hidg->report_desc = kmemdup(fdesc->report_desc, + fdesc->report_desc_length, + GFP_KERNEL); + if (!hidg->report_desc) { + kfree(hidg); + return -ENOMEM; + } + + hidg->func.name = "hid"; + hidg->func.strings = ct_func_strings; + hidg->func.bind = hidg_bind; + hidg->func.unbind = hidg_unbind; + hidg->func.set_alt = hidg_set_alt; + hidg->func.disable = hidg_disable; + hidg->func.setup = hidg_setup; + + /* this could me made configurable at some point */ + hidg->qlen = 4; + + status = usb_add_function(c, &hidg->func); + if (status) + kfree(hidg); + + return status; +} + +int __init ghid_setup(struct usb_gadget *g, int count) +{ + int status; + dev_t dev; + + hidg_class = class_create(THIS_MODULE, "hidg"); + + status = alloc_chrdev_region(&dev, 0, count, "hidg"); + if (!status) { + major = MAJOR(dev); + minors = count; + } + + return status; +} + +void ghid_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), minors); + major = minors = 0; + } + + class_destroy(hidg_class); + hidg_class = NULL; +} diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index eb6ddfc2085..4557cd03f0b 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -8,26 +8,19 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ +#include <linux/slab.h> #include <linux/kernel.h> -#include <linux/utsname.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, @@ -53,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; /*-------------------------------------------------------------------------*/ @@ -118,6 +110,49 @@ static struct usb_descriptor_header *hs_loopback_descs[] = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_loop_source_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 ss_loop_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_loop_sink_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 ss_loop_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_descriptor_header *ss_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &ss_loop_source_desc, + (struct usb_descriptor_header *) &ss_loop_source_comp_desc, + (struct usb_descriptor_header *) &ss_loop_sink_desc, + (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, + NULL, +}; + /* function-specific strings: */ static struct usb_string strings_loopback[] = { @@ -137,12 +172,12 @@ 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); int id; + int ret; /* allocate interface ID(s) */ id = usb_interface_id(c, f); @@ -150,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); @@ -167,23 +208,38 @@ autoconf_fail: loop->out_ep->driver_data = cdev; /* claim */ /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_loop_source_desc.bEndpointAddress = - fs_loop_source_desc.bEndpointAddress; - hs_loop_sink_desc.bEndpointAddress = - fs_loop_sink_desc.bEndpointAddress; - f->hs_descriptors = hs_loopback_descs; - } + hs_loop_source_desc.bEndpointAddress = + fs_loop_source_desc.bEndpointAddress; + hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; + + /* support super speed hardware */ + ss_loop_source_desc.bEndpointAddress = + fs_loop_source_desc.bEndpointAddress; + ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, + ss_loopback_descs); + if (ret) + return ret; DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + (gadget_is_superspeed(c->cdev->gadget) ? "super" : + (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, loop->in_ep->name, loop->out_ep->name); return 0; } -static void -loopback_unbind(struct usb_configuration *c, struct usb_function *f) +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)); } @@ -242,34 +298,40 @@ static void disable_loopback(struct f_loopback *loop) struct usb_composite_dev *cdev; cdev = loop->function.config->cdev; - disable_endpoints(cdev, loop->in_ep, loop->out_ep); + disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); 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) { int result = 0; - const struct usb_endpoint_descriptor *src, *sink; struct usb_ep *ep; struct usb_request *req; unsigned i; - src = ep_choose(cdev->gadget, - &hs_loop_source_desc, &fs_loop_source_desc); - sink = ep_choose(cdev->gadget, - &hs_loop_sink_desc, &fs_loop_sink_desc); - /* one endpoint writes data back IN to the host */ ep = loop->in_ep; - result = usb_ep_enable(ep, src); + result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); + if (result) + return result; + result = usb_ep_enable(ep); if (result < 0) return result; ep->driver_data = loop; /* one endpoint just reads OUT packets */ ep = loop->out_ep; - result = usb_ep_enable(ep, sink); + result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); + if (result) + goto fail0; + + result = usb_ep_enable(ep); if (result < 0) { fail0: ep = loop->in_ep; @@ -284,7 +346,7 @@ fail0: * than 'buflen' bytes each. */ for (i = 0; i < qlen && result == 0; i++) { - req = alloc_ep_req(ep); + req = lb_alloc_ep_req(ep, 0); if (req) { req->complete = loopback_complete; result = usb_ep_queue(ep, req, GFP_ATOMIC); @@ -322,65 +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.descriptors = fs_loopback_descs; loop->function.bind = loopback_bind; - loop->function.unbind = loopback_unbind; loop->function.set_alt = loopback_set_alt; loop->function.disable = loopback_disable; + loop->function.strings = loopback_strings; + + loop->function.free_func = lb_free_func; + + return &loop->function; +} - status = usb_add_function(c, &loop->function); - if (status) - kfree(loop); - return status; +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); } -static struct usb_configuration loopback_driver = { - .label = "loopback", - .strings = loopback_strings, - .bind = loopback_bind_config, - .bConfigurationValue = 2, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, - /* .iConfiguration = DYNAMIC */ +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) - sourcesink_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); + 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 new file mode 100644 index 00000000000..b9639390886 --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.c @@ -0,0 +1,3668 @@ +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <mina86@mina86.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * For more information about MSF and in particular its module + * parameters and sysfs interface read the + * <Documentation/usb/mass-storage.txt> file. + */ + +/* + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12) + * commands for this LUN shall be ignored. + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. As of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#include <linux/blkdev.h> +#include <linux/completion.h> +#include <linux/dcache.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fcntl.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kref.h> +#include <linux/kthread.h> +#include <linux/limits.h> +#include <linux/rwsem.h> +#include <linux/slab.h> +#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" + + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "2009/09/11" + +static const char fsg_string_interface[] = "Mass Storage"; + +#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; + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct usb_composite_dev *cdev; + struct fsg_dev *fsg, *new_fsg; + wait_queue_head_t fsg_wait; + + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + /* lock protects: state, all the req_busy's */ + spinlock_t lock; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + + 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 *curlun; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + 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; + struct task_struct *thread_task; + + /* Callback functions. */ + const struct fsg_operations *ops; + /* Gadget's private data. */ + void *private_data; + + /* + * Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte + */ + char inquiry_string[8 + 16 + 4 + 1]; + + struct kref ref; +}; + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* 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) + wake_up_process(common->thread_task); +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + unsigned long flags; + + /* + * Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. + */ + spin_lock_irqsave(&common->lock, flags); + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + if (common->thread_task) + send_sig_info(SIGUSR1, SEND_SIG_FORCED, + common->thread_task); + } + spin_unlock_irqrestore(&common->lock, flags); +} + + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +/* Completion handlers. These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&common->lock); + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(common); + spin_unlock(&common->lock); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&common->lock); + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(common); + spin_unlock(&common->lock); +} + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; + + ++fsg->common->ep0_req_tag; /* Record arrival of a new request */ + req->context = NULL; + req->length = 0; + dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); + + switch (ctrl->bRequest) { + + case US_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0 || + w_length != 0) + return -EDOM; + + /* + * Raise an exception to stop the current operation + * and reinitialize our state. + */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return USB_GADGET_DELAYED_STATUS; + + case US_BULK_GET_MAX_LUN: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0 || + w_length != 1) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *)req->buf = fsg->common->nluns - 1; + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + return ep0_queue(fsg->common); + } + + VDBG(fsg, + "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + spin_lock_irq(&fsg->common->lock); + *pbusy = 1; + *state = BUF_STATE_BUSY; + spin_unlock_irq(&fsg->common->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* + * Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. + */ + if (rc != -ESHUTDOWN && + !(rc == -EOPNOTSUPP && req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + +static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) +{ + if (!fsg_is_set(common)) + return false; + start_transfer(common->fsg, common->fsg->bulk_in, + bh->inreq, &bh->inreq_busy, &bh->state); + return true; +} + +static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh) +{ + if (!fsg_is_set(common)) + return false; + start_transfer(common->fsg, common->fsg->bulk_out, + bh->outreq, &bh->outreq_busy, &bh->state); + return true; +} + +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 (;;) { + if (can_freeze) + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + if (common->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + common->thread_wakeup_needed = 0; + smp_rmb(); /* ensure the latest bh->state is visible */ + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset, file_offset_tmp; + unsigned int amount; + ssize_t nread; + + /* + * Get the starting Logical Block Address and check that it's + * not too big. + */ + if (common->cmnd[0] == READ_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* + * We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. + */ + if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << curlun->blkbits; + + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + /* + * Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + */ + amount = min(amount_left, FSG_BUFLEN); + amount = min((loff_t)amount, + curlun->file_length - file_offset); + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common, false); + if (rc) + return rc; + } + + /* + * If we were asked to read past the end of file, + * end with an empty buffer. + */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *)bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long)file_offset, (int)nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", (int)nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int)nread, amount); + nread = round_down(nread, curlun->blksize); + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + + /* + * Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + if (!start_in_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset, file_offset_tmp; + unsigned int amount; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */ + spin_unlock(&curlun->filp->f_lock); + + /* + * Get the starting Logical Block Address and check that it's + * not too big + */ + if (common->cmnd[0] == WRITE_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* + * We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. + */ + if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */ + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* + * Figure out how much we want to get: + * Try to get the remaining amount, + * but not more than the buffer size. + */ + amount = min(amount_left_to_req, FSG_BUFLEN); + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = + usb_offset >> curlun->blkbits; + curlun->info_valid = 1; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(common, bh, amount); + if (!start_out_transfer(common, bh)) + /* Dunno what to do if common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + if (curlun->file_length - file_offset < amount) { + LERROR(curlun, + "write %u @ %llu beyond end %llu\n", + amount, (unsigned long long)file_offset, + (unsigned long long)curlun->file_length); + amount = curlun->file_length - file_offset; + } + + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + + /* Perform the write */ + file_offset_tmp = file_offset; + nwritten = vfs_write(curlun->filp, + (char __user *)bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long)file_offset, (int)nwritten); + if (signal_pending(current)) + return -EINTR; /* Interrupted! */ + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", + (int)nwritten); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int)nwritten, amount); + nwritten = round_down(nwritten, curlun->blksize); + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + curlun->sense_data = SS_WRITE_ERROR; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + break; + } + + empty_write: + /* Did the host decide to stop early? */ + if (bh->outreq->actual < bh->bulk_out_intended_length) { + common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(common, false); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int rc; + + /* We ignore the requested LBA and write out all file's + * dirty data buffers. */ + rc = fsg_lun_fsync_sub(curlun); + if (rc) + curlun->sense_data = SS_WRITE_ERROR; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static void invalidate_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + struct inode *inode = file_inode(filp); + unsigned long rc; + + rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); + VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc); +} + +static int do_verify(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset, file_offset_tmp; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* + * Get the starting Logical Block Address and check that it's + * not too big. + */ + lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* + * We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. + */ + if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; + + /* Write out all the dirty buffers before invalidating them */ + fsg_lun_fsync_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + invalidate_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + /* + * Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + */ + amount = min(amount_left, FSG_BUFLEN); + amount = min((loff_t)amount, + curlun->file_length - file_offset); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file verify: %d\n", (int)nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int)nread, amount); + nread = round_down(nread, curlun->blksize); + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = + file_offset >> curlun->blkbits; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; + memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); + return 36; +} + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; + u8 *buf = (u8 *)bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ + return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *)bh->buf; + + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *)bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* + * Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. + */ + memset(buf, 0, 8); + if (mscmnd == MODE_SENSE) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* MODE_SENSE_10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* + * The mode pages, in numerical order. The only page we support + * is the Caching page. + */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* + * Check that a valid page was requested and the mode data length + * isn't too long. + */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == MODE_SENSE) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + +static int do_start_stop(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int loej, start; + + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ + (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + loej = common->cmnd[4] & 0x02; + start = common->cmnd[4] & 0x01; + + /* + * Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. + */ + if (start) { + if (!fsg_lun_is_open(curlun)) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + return 0; + } + + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + + if (!loej) + return 0; + + up_read(&common->filesem); + down_write(&common->filesem); + fsg_lun_close(curlun); + up_write(&common->filesem); + down_read(&common->filesem); + + return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int prevent; + + if (!common->curlun) { + return -EINVAL; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + +static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = usb_ep_set_wedge(fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_wedge(fsg->bulk_in); + } + return rc; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual < bh->bulk_out_intended_length || + bh->outreq->status != 0) { + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(common, bh, amount); + if (!start_out_transfer(common, bh)) + /* Dunno what to do if common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(common, true); + if (rc) + return rc; + } + return 0; +} + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* + * If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. + */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* Don't know what to do if common->fsg is NULL */ + } else if (!fsg_is_set(common)) { + rc = -EIO; + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + if (!start_in_transfer(common, bh)) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* + * For Bulk-only, mark the end of the data with a short + * packet. If we are allowed to stall, halt the bulk-in + * endpoint. (Note: This violates the Bulk-Only Transport + * specification, which requires us to pad the data if we + * don't halt the endpoint. Presumably nobody will mind.) + */ + } else { + bh->inreq->zero = 1; + if (!start_in_transfer(common, bh)) + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->can_stall) + rc = halt_bulk_in_endpoint(common->fsg); + } + break; + + /* + * We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. + */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* + * We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. + */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +#endif + + /* + * We can't stall. Read in the excess data and throw it + * all away. + */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + +static int send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = US_BULK_STAT_OK; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common, true); + if (rc) + return rc; + } + + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + } else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = US_BULK_STAT_PHASE; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = US_BULK_STAT_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = US_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + if (!start_in_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. + */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + unsigned int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* + * We can't reply at all until we know the correct data direction + * and size. + */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* + * Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. + */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* + * Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n", + common->lun, lun); + + /* Check the LUN */ + curlun = common->curlun; + if (curlun) { + if (common->cmnd[0] != REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { + common->bad_lun_okay = 0; + + /* + * INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. + */ + if (common->cmnd[0] != INQUIRY && + common->cmnd[0] != REQUEST_SENSE) { + DBG(common, "unsupported LUN %u\n", common->lun); + return -EINVAL; + } + } + + /* + * If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. + */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != INQUIRY && + common->cmnd[0] != REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + /* If the medium isn't mounted and the command needs to access + * it, return an error. */ + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + +/* wrapper of check_command for data size in blocks handling */ +static int check_command_size_in_blocks(struct fsg_common *common, + int cmnd_size, enum data_direction data_dir, + unsigned int mask, int needs_medium, const char *name) +{ + if (common->curlun) + common->data_size_from_cmnd <<= common->curlun->blkbits; + return check_command(common, cmnd_size, data_dir, + mask, needs_medium, name); +} + +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common, true); + if (rc) + return rc; + } + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case MODE_SELECT: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case MODE_SELECT_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case MODE_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case MODE_SENSE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case ALLOW_MEDIUM_REMOVAL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case READ_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0) ? 256 : i; + reply = check_command_size_in_blocks(common, 6, + DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); + break; + + case READ_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command_size_in_blocks(common, 10, + DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); + break; + + case READ_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]); + reply = check_command_size_in_blocks(common, 12, + DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); + break; + + case READ_CAPACITY: + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); + break; + + case READ_HEADER: + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); + break; + + case READ_TOC: + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); + break; + + case READ_FORMAT_CAPACITIES: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); + break; + + case REQUEST_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case START_STOP: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SYNCHRONIZE_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case TEST_UNIT_READY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* + * Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. + */ + case VERIFY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); + break; + + case WRITE_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0) ? 256 : i; + reply = check_command_size_in_blocks(common, 6, + DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); + break; + + case WRITE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command_size_in_blocks(common, 10, + DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); + break; + + case WRITE_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]); + reply = check_command_size_in_blocks(common, 12, + DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); + break; + + /* + * Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. + */ + case FORMAT_UNIT: + case RELEASE: + case RESERVE: + case SEND_DIAGNOSTIC: + /* Fall through */ + + default: +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, ~0, 0, unknown); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32)reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != US_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + US_BULK_CB_SIGN)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* + * The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. + */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* + * We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. + */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & US_BULK_FLAG_IN) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + if (common->lun < common->nluns) + common->curlun = common->luns[common->lun]; + else + common->curlun = NULL; + common->tag = cbw->Tag; + return 0; +} + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common, true); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN); + if (!start_out_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* + * We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. + */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(common, true); + if (rc) + return rc; + } + smp_rmb(); + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < common->fsg_num_buffers; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* 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; + } + + common->fsg = NULL; + wake_up(&common->fsg_wait); + } + + common->running = 0; + if (!new_fsg || rc) + return rc; + + common->fsg = new_fsg; + fsg = common->fsg; + + /* Enable the endpoints */ + rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_in); + if (rc) + goto reset; + fsg->bulk_in->driver_data = common; + fsg->bulk_in_enabled = 1; + + rc = config_ep_by_speed(common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_out); + if (rc) + goto reset; + fsg->bulk_out->driver_data = common; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + 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); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + for (i = 0; i < common->nluns; ++i) + if (common->luns[i]) + common->luns[i]->unit_attention_data = + SS_RESET_OCCURRED; + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = fsg; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return USB_GADGET_DELAYED_STATUS; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + siginfo_t info; + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + + /* + * Clear the existing signals. Anything but SIGUSR1 is converted + * into a high-priority EXIT exception. + */ + for (;;) { + int sig = + dequeue_signal_lock(current, ¤t->blocked, &info); + if (!sig) + break; + if (sig != SIGUSR1) { + if (common->state < FSG_STATE_EXIT) + DBG(common, "Main thread exiting on signal\n"); + raise_exception(common, FSG_STATE_EXIT); + } + } + + /* Cancel all the pending transfers */ + if (likely(common->fsg)) { + 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); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + 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, true)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* + * Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. + */ + spin_lock_irq(&common->lock); + + for (i = 0; i < common->fsg_num_buffers; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + old_state = common->state; + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + common->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < common->nluns; ++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; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + common->state = FSG_STATE_IDLE; + } + spin_unlock_irq(&common->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + spin_lock_irq(&common->lock); + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_RESET: + /* + * In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) + */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + /* + * Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. + */ + /* for (i = 0; i < common->nluns; ++i) */ + /* if (common->luns[i]) */ + /* common->luns[i]->unit_attention_data = */ + /* SS_RESET_OCCURRED; */ + break; + + case FSG_STATE_CONFIG_CHANGE: + do_set_interface(common, common->new_fsg); + if (common->new_fsg) + usb_composite_setup_continue(common->cdev); + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_interface(common, NULL); /* Free resources */ + spin_lock_irq(&common->lock); + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; + } +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_main_thread(void *common_) +{ + struct fsg_common *common = common_; + + /* + * Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. + */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* + * Arrange for userspace references to be interpreted as kernel + * pointers. That way we can pass a kernel pointer to a routine + * that expects a __user pointer and it will work okay. + */ + set_fs(get_ds()); + + /* The main loop */ + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common) || signal_pending(current)) { + handle_exception(common); + continue; + } + + if (!common->running) { + sleep_thread(common, true); + continue; + } + + if (get_next_command(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + spin_unlock_irq(&common->lock); + + if (do_scsi_command(common) || finish_reply(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + spin_unlock_irq(&common->lock); + + if (send_status(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); + } + + spin_lock_irq(&common->lock); + common->thread_task = NULL; + spin_unlock_irq(&common->lock); + + if (!common->ops || !common->ops->thread_exits + || common->ops->thread_exits(common) < 0) { + struct fsg_lun **curlun_it = common->luns; + unsigned i = common->nluns; + + down_write(&common->filesem); + for (; i--; ++curlun_it) { + struct fsg_lun *curlun = *curlun_it; + if (!curlun || !fsg_lun_is_open(curlun)) + continue; + + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + up_write(&common->filesem); + } + + /* Let fsg_unbind() know the thread has exited */ + complete_and_exit(&common->thread_notifier, 0); +} + + +/*************************** DEVICE ATTRIBUTES ***************************/ + +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 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 ******************************/ + +static void fsg_common_release(struct kref *ref); + +static void fsg_lun_release(struct device *dev) +{ + /* Nothing needs to be done */ +} + +void fsg_common_get(struct fsg_common *common) +{ + kref_get(&common->ref); +} +EXPORT_SYMBOL_GPL(fsg_common_get); + +void fsg_common_put(struct fsg_common *common) +{ + kref_put(&common->ref, fsg_common_release); +} +EXPORT_SYMBOL_GPL(fsg_common_put); + +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers) +{ + 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; +} + +static struct fsg_common *fsg_common_setup(struct fsg_common *common) +{ + if (!common) { + common = kzalloc(sizeof(*common), GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + 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; + + 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); + } +} + +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) +{ + struct fsg_buffhd *bh, *buffhds; + int i, rc; + + rc = fsg_num_buffers_validate(n); + if (rc != 0) + return rc; + + buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); + if (!buffhds) + return -ENOMEM; + + /* 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; + } 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: + /* + * "buf"s pointed to by heads after n - i are NULL + * so releasing them won't hurt + */ + _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); + } + fsg_lun_close(lun); + kfree(lun); +} +EXPORT_SYMBOL_GPL(fsg_common_remove_lun); + +static void _fsg_common_remove_luns(struct fsg_common *common, int n) +{ + int i; + + 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); + +void fsg_common_remove_luns(struct fsg_common *common) +{ + _fsg_common_remove_luns(common, common->nluns); +} + +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; + + pr_info("Number of LUNs=%d\n", common->nluns); + + 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 = can_stall && !(gadget_is_at91(common->gadget)); + + return 0; +} +EXPORT_SYMBOL_GPL(fsg_common_set_cdev); + +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; + } + + 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); + 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)"; + } + } + 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 0; +} +EXPORT_SYMBOL_GPL(fsg_common_run_thread); + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = container_of(ref, struct fsg_common, ref); + + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + } + + if (likely(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_it) { + struct fsg_lun *lun = *lun_it; + if (!lun) + continue; + if (common->sysfs) + fsg_common_remove_sysfs(lun); + fsg_lun_close(lun); + if (common->sysfs) + device_unregister(&lun->dev); + kfree(lun); + } + + kfree(common->luns); + } + + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int i; + 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; + + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_out = ep; + + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function, + fsg_ss_function); + if (ret) + goto autoconf_fail; + + return 0; + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + return -ENOTSUPP; +} + +/****************************** ALLOCATE FUNCTION *************************/ + +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 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_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; + + 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 ERR_PTR(-ENOMEM); + + 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; + + return &fsg->function; +} + +DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); + +/************************* Module parameters *************************/ + + +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; + + /* Configure LUNs */ + cfg->nluns = + min(params->luns ?: (params->file_count ?: 1u), + (unsigned)FSG_MAX_LUNS); + for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { + lun->ro = !!params->ro[i]; + lun->cdrom = !!params->cdrom[i]; + lun->removable = !!params->removable[i]; + lun->filename = + params->file_count > i && params->file[i][0] + ? params->file[i] + : NULL; + } + + /* Let MSF use defaults */ + 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); + 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 new file mode 100644 index 00000000000..807b31c0edc --- /dev/null +++ b/drivers/usb/gadget/f_midi.c @@ -0,0 +1,986 @@ +/* + * f_midi.c -- USB MIDI class function driver + * + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Developed for Thumtronics by Grey Innovation + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Rewritten for the composite framework + * Copyright (C) 2011 Daniel Mack <zonque@gmail.com> + * + * Based on drivers/usb/gadget/f_audio.c, + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * and drivers/usb/gadget/midi.c, + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/audio.h> +#include <linux/usb/midi.h> + +#include "u_f.h" + +MODULE_AUTHOR("Ben Williamson"); +MODULE_LICENSE("GPL v2"); + +static const char f_midi_shortname[] = "f_midi"; +static const char f_midi_longname[] = "MIDI Gadget"; + +/* + * We can only handle 16 cables on one single endpoint, as cable numbers are + * stored in 4-bit fields. And as the interface currently only holds one + * single endpoint, this is the maximum number of ports we can allow. + */ +#define MAX_PORTS 16 + +/* + * This is a gadget, and the IN/OUT naming is from the host's perspective. + * USB -> OUT endpoint -> rawmidi + * USB <- IN endpoint <- rawmidi + */ +struct gmidi_in_port { + struct f_midi *midi; + int active; + uint8_t cable; + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; +}; + +struct f_midi { + struct usb_function func; + struct usb_gadget *gadget; + struct usb_ep *in_ep, *out_ep; + struct snd_card *card; + struct snd_rawmidi *rmidi; + + struct snd_rawmidi_substream *in_substream[MAX_PORTS]; + struct snd_rawmidi_substream *out_substream[MAX_PORTS]; + struct gmidi_in_port *in_port[MAX_PORTS]; + + unsigned long out_triggered; + struct tasklet_struct tasklet; + unsigned int in_ports; + unsigned int out_ports; + int index; + char *id; + unsigned int buflen, qlen; +}; + +static inline struct f_midi *func_to_midi(struct usb_function *f) +{ + return container_of(f, struct f_midi, func); +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + /* .bNumEndpoints = DYNAMIC */ + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + /* .iInterface = DYNAMIC */ +}; + +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdADC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), + .bInCollection = 1, + /* .baInterfaceNr = DYNAMIC */ +}; + +/* B.4.1 Standard MS Interface Descriptor */ +static struct usb_interface_descriptor ms_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + /* .iInterface = DYNAMIC */ +}; + +/* B.4.2 Class-Specific MS Interface Descriptor */ +static struct usb_ms_header_descriptor ms_header_desc __initdata = { + .bLength = USB_DT_MS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = cpu_to_le16(0x0100), + /* .wTotalLength = DYNAMIC */ +}; + +/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* B.6.1 Standard Bulk IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* string IDs are assigned dynamically */ + +#define STRING_FUNC_IDX 0 + +static struct usb_string midi_string_defs[] = { + [STRING_FUNC_IDX].s = "MIDI function", + { } /* end of list */ +}; + +static struct usb_gadget_strings midi_stringtab = { + .language = 0x0409, /* en-us */ + .strings = midi_string_defs, +}; + +static struct usb_gadget_strings *midi_strings[] = { + &midi_stringtab, + NULL, +}; + +static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, + unsigned length) +{ + return alloc_ep_req(ep, length, length); +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static const uint8_t f_midi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Receives a chunk of MIDI data. + */ +static void f_midi_read_data(struct usb_ep *ep, int cable, + uint8_t *data, int length) +{ + struct f_midi *midi = ep->driver_data; + struct snd_rawmidi_substream *substream = midi->out_substream[cable]; + + if (!substream) + /* Nobody is listening - throw it on the floor. */ + return; + + if (!test_bit(cable, &midi->out_triggered)) + return; + + snd_rawmidi_receive(substream, data, length); +} + +static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int i; + u8 *buf = req->buf; + + for (i = 0; i + 3 < req->actual; i += 4) + if (buf[i] != 0) { + int cable = buf[i] >> 4; + int length = f_midi_cin_length[buf[i] & 0x0f]; + f_midi_read_data(ep, cable, &buf[i + 1], length); + } +} + +static void +f_midi_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_midi *midi = ep->driver_data; + struct usb_composite_dev *cdev = midi->func.config->cdev; + int status = req->status; + + switch (status) { + case 0: /* normal completion */ + if (ep == midi->out_ep) { + /* We received stuff. req is queued again, below */ + f_midi_handle_out_data(ep, req); + } else if (ep == midi->in_ep) { + /* Our transmit completed. See if there's more to go. + * f_midi_transmit eats req, don't queue it again. */ + f_midi_transmit(midi, req); + return; + } + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == midi->out_ep) + f_midi_handle_out_data(ep, req); + + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough buffer. + */ + default: + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + break; + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int f_midi_start_ep(struct f_midi *midi, + struct usb_function *f, + struct usb_ep *ep) +{ + int err; + struct usb_composite_dev *cdev = f->config->cdev; + + if (ep->driver_data) + usb_ep_disable(ep); + + err = config_ep_by_speed(midi->gadget, f, ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", ep->name, err); + return err; + } + + err = usb_ep_enable(ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", ep->name, err); + return err; + } + + ep->driver_data = midi; + + return 0; +} + +static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + unsigned i; + int err; + + err = f_midi_start_ep(midi, f, midi->in_ep); + if (err) + return err; + + err = f_midi_start_ep(midi, f, midi->out_ep); + if (err) + return err; + + if (midi->out_ep->driver_data) + usb_ep_disable(midi->out_ep); + + err = config_ep_by_speed(midi->gadget, f, midi->out_ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", + midi->out_ep->name, err); + return err; + } + + err = usb_ep_enable(midi->out_ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", + midi->out_ep->name, err); + return err; + } + + midi->out_ep->driver_data = midi; + + /* 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 = + midi_alloc_ep_req(midi->out_ep, midi->buflen); + if (req == NULL) + return -ENOMEM; + + req->complete = f_midi_complete; + err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); + if (err) { + ERROR(midi, "%s queue req: %d\n", + midi->out_ep->name, err); + } + } + + return 0; +} + +static void f_midi_disable(struct usb_function *f) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "disable\n"); + + /* + * just disable endpoints, forcing completion of pending i/o. + * all our completion handlers free their requests in this case. + */ + usb_ep_disable(midi->in_ep); + usb_ep_disable(midi->out_ep); +} + +static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_midi *midi = func_to_midi(f); + struct snd_card *card; + + DBG(cdev, "unbind\n"); + + /* just to be sure */ + f_midi_disable(f); + + card = midi->card; + midi->card = NULL; + if (card) + snd_card_free(card); + + kfree(midi->id); + midi->id = NULL; + + usb_free_all_descriptors(f); + kfree(midi); +} + +static int f_midi_snd_free(struct snd_device *device) +{ + return 0; +} + +static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + unsigned length = req->length; + u8 *buf = (u8 *)req->buf + length; + + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + req->length = length + 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void f_midi_transmit_byte(struct usb_request *req, + struct gmidi_in_port *port, uint8_t b) +{ + uint8_t p0 = port->cable << 4; + + if (b >= 0xf8) { + f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + f_midi_transmit_packet(req, + p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + f_midi_transmit_packet(req, + p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x07, port->data[0], + port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, + p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) +{ + struct usb_ep *ep = midi->in_ep; + int i; + + if (!ep) + return; + + if (!req) + req = midi_alloc_ep_req(ep, midi->buflen); + + if (!req) { + ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + return; + } + req->length = 0; + req->complete = f_midi_complete; + + for (i = 0; i < MAX_PORTS; i++) { + struct gmidi_in_port *port = midi->in_port[i]; + struct snd_rawmidi_substream *substream = midi->in_substream[i]; + + if (!port || !port->active || !substream) + continue; + + while (req->length + 3 < midi->buflen) { + uint8_t b; + if (snd_rawmidi_transmit(substream, &b, 1) != 1) { + port->active = 0; + break; + } + f_midi_transmit_byte(req, port, b); + } + } + + if (req->length > 0) + usb_ep_queue(ep, req, GFP_ATOMIC); + else + free_ep_req(ep, req); +} + +static void f_midi_in_tasklet(unsigned long data) +{ + struct f_midi *midi = (struct f_midi *) data; + f_midi_transmit(midi, NULL); +} + +static int f_midi_in_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->in_substream[substream->number] = substream; + midi->in_port[substream->number]->state = STATE_UNKNOWN; + return 0; +} + +static int f_midi_in_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return; + + VDBG(midi, "%s() %d\n", __func__, up); + midi->in_port[substream->number]->active = up; + if (up) + tasklet_hi_schedule(&midi->tasklet); +} + +static int f_midi_out_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (substream->number >= MAX_PORTS) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->out_substream[substream->number] = substream; + return 0; +} + +static int f_midi_out_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + + if (up) + set_bit(substream->number, &midi->out_triggered); + else + clear_bit(substream->number, &midi->out_triggered); +} + +static struct snd_rawmidi_ops gmidi_in_ops = { + .open = f_midi_in_open, + .close = f_midi_in_close, + .trigger = f_midi_in_trigger, +}; + +static struct snd_rawmidi_ops gmidi_out_ops = { + .open = f_midi_out_open, + .close = f_midi_out_close, + .trigger = f_midi_out_trigger +}; + +/* register as a sound "card" */ +static int f_midi_register_card(struct f_midi *midi) +{ + struct snd_card *card; + struct snd_rawmidi *rmidi; + int err; + static struct snd_device_ops ops = { + .dev_free = f_midi_snd_free, + }; + + err = snd_card_new(&midi->gadget->dev, midi->index, midi->id, + THIS_MODULE, 0, &card); + if (err < 0) { + ERROR(midi, "snd_card_new() failed\n"); + goto fail; + } + midi->card = card; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops); + if (err < 0) { + ERROR(midi, "snd_device_new() failed: error %d\n", err); + goto fail; + } + + strcpy(card->driver, f_midi_longname); + strcpy(card->longname, f_midi_longname); + strcpy(card->shortname, f_midi_shortname); + + /* Set up rawmidi */ + snd_component_add(card, "MIDI"); + err = snd_rawmidi_new(card, card->longname, 0, + midi->out_ports, midi->in_ports, &rmidi); + if (err < 0) { + ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err); + goto fail; + } + midi->rmidi = rmidi; + strcpy(rmidi->name, card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + + /* + * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. + * It's an upside-down world being a gadget. + */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); + + /* register it - we're ready to go */ + err = snd_card_register(card); + if (err < 0) { + ERROR(midi, "snd_card_register() failed\n"); + goto fail; + } + + VDBG(midi, "%s() finished ok\n", __func__); + return 0; + +fail: + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } + return err; +} + +/* MIDI function driver setup/binding */ + +static int __init +f_midi_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_descriptor_header **midi_function; + struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; + struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS]; + struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; + struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS]; + struct usb_composite_dev *cdev = c->cdev; + struct f_midi *midi = func_to_midi(f); + int status, n, jack = 1, i = 0; + + /* maybe allocate device-global string ID */ + if (midi_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + goto fail; + midi_string_defs[0].id = status; + } + + /* We have two interfaces, AudioControl and MIDIStreaming */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ms_interface_desc.bInterfaceNumber = status; + ac_header_desc.baInterfaceNr[0] = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); + if (!midi->in_ep) + goto fail; + midi->in_ep->driver_data = cdev; /* claim */ + + midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc); + if (!midi->out_ep) + goto fail; + midi->out_ep->driver_data = cdev; /* claim */ + + /* allocate temporary function list */ + midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function), + GFP_KERNEL); + if (!midi_function) { + status = -ENOMEM; + goto fail; + } + + /* + * construct the function's descriptor set. As the number of + * input and output MIDI ports is configurable, we have to do + * it that way. + */ + + /* add the headers - these are always the same */ + midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc; + + /* calculate the header's wTotalLength */ + n = USB_DT_MS_HEADER_SIZE + + (midi->in_ports + midi->out_ports) * + (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); + ms_header_desc.wTotalLength = cpu_to_le16(n); + + midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; + + /* configure the external IN jacks, each linked to an embedded OUT jack */ + for (n = 0; n < midi->in_ports; n++) { + struct usb_midi_in_jack_descriptor *in_ext = &jack_in_ext_desc[n]; + struct usb_midi_out_jack_descriptor_1 *out_emb = &jack_out_emb_desc[n]; + + in_ext->bLength = USB_DT_MIDI_IN_SIZE; + in_ext->bDescriptorType = USB_DT_CS_INTERFACE; + in_ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + in_ext->bJackType = USB_MS_EXTERNAL; + in_ext->bJackID = jack++; + in_ext->iJack = 0; + midi_function[i++] = (struct usb_descriptor_header *) in_ext; + + out_emb->bLength = USB_DT_MIDI_OUT_SIZE(1); + out_emb->bDescriptorType = USB_DT_CS_INTERFACE; + out_emb->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + out_emb->bJackType = USB_MS_EMBEDDED; + out_emb->bJackID = jack++; + out_emb->bNrInputPins = 1; + out_emb->pins[0].baSourcePin = 1; + out_emb->pins[0].baSourceID = in_ext->bJackID; + out_emb->iJack = 0; + midi_function[i++] = (struct usb_descriptor_header *) out_emb; + + /* link it to the endpoint */ + ms_in_desc.baAssocJackID[n] = out_emb->bJackID; + } + + /* configure the external OUT jacks, each linked to an embedded IN jack */ + for (n = 0; n < midi->out_ports; n++) { + struct usb_midi_in_jack_descriptor *in_emb = &jack_in_emb_desc[n]; + struct usb_midi_out_jack_descriptor_1 *out_ext = &jack_out_ext_desc[n]; + + in_emb->bLength = USB_DT_MIDI_IN_SIZE; + in_emb->bDescriptorType = USB_DT_CS_INTERFACE; + in_emb->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + in_emb->bJackType = USB_MS_EMBEDDED; + in_emb->bJackID = jack++; + in_emb->iJack = 0; + midi_function[i++] = (struct usb_descriptor_header *) in_emb; + + out_ext->bLength = USB_DT_MIDI_OUT_SIZE(1); + out_ext->bDescriptorType = USB_DT_CS_INTERFACE; + out_ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + out_ext->bJackType = USB_MS_EXTERNAL; + out_ext->bJackID = jack++; + out_ext->bNrInputPins = 1; + out_ext->iJack = 0; + out_ext->pins[0].baSourceID = in_emb->bJackID; + out_ext->pins[0].baSourcePin = 1; + midi_function[i++] = (struct usb_descriptor_header *) out_ext; + + /* link it to the endpoint */ + ms_out_desc.baAssocJackID[n] = in_emb->bJackID; + } + + /* configure the endpoint descriptors ... */ + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + + /* ... and add them to the list */ + midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc; + midi_function[i++] = NULL; + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + /* copy descriptors, and track endpoint copies */ + f->fs_descriptors = usb_copy_descriptors(midi_function); + if (!f->fs_descriptors) + goto fail_f_midi; + + if (gadget_is_dualspeed(c->cdev->gadget)) { + bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); + f->hs_descriptors = usb_copy_descriptors(midi_function); + if (!f->hs_descriptors) + goto fail_f_midi; + } + + kfree(midi_function); + + return 0; + +fail_f_midi: + kfree(midi_function); + usb_free_descriptors(f->hs_descriptors); +fail: + /* we might as well release our claims on endpoints */ + if (midi->out_ep) + midi->out_ep->driver_data = NULL; + if (midi->in_ep) + midi->in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +/** + * f_midi_bind_config - add USB MIDI function to a configuration + * @c: the configuration to supcard the USB audio function + * @index: the soundcard index to use for the ALSA device creation + * @id: the soundcard id to use for the ALSA device creation + * @buflen: the buffer length to use + * @qlen the number of read requests to pre-allocate + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +int __init f_midi_bind_config(struct usb_configuration *c, + int index, char *id, + unsigned int in_ports, + unsigned int out_ports, + unsigned int buflen, + unsigned int qlen) +{ + struct f_midi *midi; + int status, i; + + /* sanity check */ + if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) + return -EINVAL; + + /* allocate and initialize one new instance */ + midi = kzalloc(sizeof *midi, GFP_KERNEL); + if (!midi) { + status = -ENOMEM; + goto fail; + } + + for (i = 0; i < in_ports; i++) { + struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + status = -ENOMEM; + goto setup_fail; + } + + port->midi = midi; + port->active = 0; + port->cable = i; + midi->in_port[i] = port; + } + + midi->gadget = c->cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + + /* set up ALSA midi devices */ + midi->in_ports = in_ports; + midi->out_ports = out_ports; + status = f_midi_register_card(midi); + if (status < 0) + goto setup_fail; + + midi->func.name = "gmidi function"; + midi->func.strings = midi_strings; + midi->func.bind = f_midi_bind; + midi->func.unbind = f_midi_unbind; + midi->func.set_alt = f_midi_set_alt; + midi->func.disable = f_midi_disable; + + midi->id = kstrdup(id, GFP_KERNEL); + midi->index = index; + midi->buflen = buflen; + midi->qlen = qlen; + + status = usb_add_function(c, &midi->func); + if (status) + goto setup_fail; + + return 0; + +setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); + kfree(midi); +fail: + return status; +} + diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c new file mode 100644 index 00000000000..a9499fd3079 --- /dev/null +++ b/drivers/usb/gadget/f_ncm.c @@ -0,0 +1,1438 @@ +/* + * f_ncm.c -- USB CDC Network (NCM) link function driver + * + * Copyright (C) 2010 Nokia Corporation + * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com> + * + * The driver borrows from f_ecm.c which is: + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/crc32.h> + +#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. + * NCM is intended to be used with high-speed network attachments. + * + * Note that NCM requires the use of "alternate settings" for its data + * interface. This means that the set_alt() method has real work to do, + * and also means that a get_alt() method is required. + */ + +/* to trigger crc/non-crc ndp signature */ + +#define NCM_NDP_HDR_CRC_MASK 0x01000000 +#define NCM_NDP_HDR_CRC 0x01000000 +#define NCM_NDP_HDR_NOCRC 0x00000000 + +enum ncm_notify_state { + NCM_NOTIFY_NONE, /* don't notify */ + NCM_NOTIFY_CONNECT, /* issue CONNECT next */ + NCM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ +}; + +struct f_ncm { + struct gether port; + u8 ctrl_id, data_id; + + char ethaddr[14]; + + struct usb_ep *notify; + struct usb_request *notify_req; + u8 notify_state; + bool is_open; + + const struct ndp_parser_opts *parser_opts; + bool is_crc; + u32 ndp_sign; + + /* + * for notification, it is accessed from both + * callback and ethernet open/close + */ + spinlock_t lock; +}; + +static inline struct f_ncm *func_to_ncm(struct usb_function *f) +{ + return container_of(f, struct f_ncm, port.func); +} + +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static inline unsigned ncm_bitrate(struct usb_gadget *g) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + +/*-------------------------------------------------------------------------*/ + +/* + * We cannot group frames so use just the minimal size which ok to put + * one max-size ethernet frame. + * If the host can group frames, allow it to do that, 16K is selected, + * because it's used by default by the current linux host driver + */ +#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE +#define NTB_OUT_SIZE 16384 + +/* + * skbs of size less than that will not be aligned + * to NCM's dwNtbInMaxSize to save bus bandwidth + */ + +#define MAX_TX_NONFIXED (512 * 3) + +#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ + USB_CDC_NCM_NTB32_SUPPORTED) + +static struct usb_cdc_ncm_ntb_parameters ntb_parameters = { + .wLength = cpu_to_le16(sizeof(ntb_parameters)), + .bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED), + .dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE), + .wNdpInDivisor = cpu_to_le16(4), + .wNdpInPayloadRemainder = cpu_to_le16(0), + .wNdpInAlignment = cpu_to_le16(4), + + .dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE), + .wNdpOutDivisor = cpu_to_le16(4), + .wNdpOutPayloadRemainder = cpu_to_le16(0), + .wNdpOutAlignment = cpu_to_le16(4), +}; + +/* + * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one + * packet, to simplify cancellation; and a big transfer interval, to + * waste less bandwidth. + */ + +#define NCM_STATUS_INTERVAL_MS 32 +#define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */ + +static struct usb_interface_assoc_descriptor ncm_iad_desc = { + .bLength = sizeof ncm_iad_desc, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + /* .bFirstInterface = DYNAMIC, */ + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_NCM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + +/* interface descriptor: */ + +static struct usb_interface_descriptor ncm_control_intf = { + .bLength = sizeof ncm_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc ncm_header_desc = { + .bLength = sizeof ncm_header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = cpu_to_le16(0x0110), +}; + +static struct usb_cdc_union_desc ncm_union_desc = { + .bLength = sizeof(ncm_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +static struct usb_cdc_ether_desc ecm_desc = { + .bLength = sizeof ecm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + /* .iMACAddress = DYNAMIC */ + .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +#define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE) + +static struct usb_cdc_ncm_desc ncm_desc = { + .bLength = sizeof ncm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_NCM_TYPE, + + .bcdNcmVersion = cpu_to_le16(0x0100), + /* can process SetEthernetPacketFilter */ + .bmNetworkCapabilities = NCAPS, +}; + +/* the default data interface has no endpoints ... */ + +static struct usb_interface_descriptor ncm_data_nop_intf = { + .bLength = sizeof ncm_data_nop_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = USB_CDC_NCM_PROTO_NTB, + /* .iInterface = DYNAMIC */ +}; + +/* ... but the "real" data interface has two bulk endpoints */ + +static struct usb_interface_descriptor ncm_data_intf = { + .bLength = sizeof ncm_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = USB_CDC_NCM_PROTO_NTB, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_ncm_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT), + .bInterval = NCM_STATUS_INTERVAL_MS, +}; + +static struct usb_endpoint_descriptor fs_ncm_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 fs_ncm_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 *ncm_fs_function[] = { + (struct usb_descriptor_header *) &ncm_iad_desc, + /* CDC NCM control descriptors */ + (struct usb_descriptor_header *) &ncm_control_intf, + (struct usb_descriptor_header *) &ncm_header_desc, + (struct usb_descriptor_header *) &ncm_union_desc, + (struct usb_descriptor_header *) &ecm_desc, + (struct usb_descriptor_header *) &ncm_desc, + (struct usb_descriptor_header *) &fs_ncm_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ncm_data_nop_intf, + (struct usb_descriptor_header *) &ncm_data_intf, + (struct usb_descriptor_header *) &fs_ncm_in_desc, + (struct usb_descriptor_header *) &fs_ncm_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_ncm_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .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 = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_ncm_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +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, + (struct usb_descriptor_header *) &ncm_header_desc, + (struct usb_descriptor_header *) &ncm_union_desc, + (struct usb_descriptor_header *) &ecm_desc, + (struct usb_descriptor_header *) &ncm_desc, + (struct usb_descriptor_header *) &hs_ncm_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ncm_data_nop_intf, + (struct usb_descriptor_header *) &ncm_data_intf, + (struct usb_descriptor_header *) &hs_ncm_in_desc, + (struct usb_descriptor_header *) &hs_ncm_out_desc, + NULL, +}; + +/* string descriptors: */ + +#define STRING_CTRL_IDX 0 +#define STRING_MAC_IDX 1 +#define STRING_DATA_IDX 2 +#define STRING_IAD_IDX 3 + +static struct usb_string ncm_string_defs[] = { + [STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)", + [STRING_MAC_IDX].s = "", + [STRING_DATA_IDX].s = "CDC Network Data", + [STRING_IAD_IDX].s = "CDC NCM", + { } /* end of list */ +}; + +static struct usb_gadget_strings ncm_string_table = { + .language = 0x0409, /* en-us */ + .strings = ncm_string_defs, +}; + +static struct usb_gadget_strings *ncm_strings[] = { + &ncm_string_table, + NULL, +}; + +/* + * Here are options for NCM Datagram Pointer table (NDP) parser. + * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3), + * in NDP16 offsets and sizes fields are 1 16bit word wide, + * in NDP32 -- 2 16bit words wide. Also signatures are different. + * To make the parser code the same, put the differences in the structure, + * and switch pointers to the structures when the format is changed. + */ + +struct ndp_parser_opts { + u32 nth_sign; + u32 ndp_sign; + unsigned nth_size; + unsigned ndp_size; + unsigned ndplen_align; + /* sizes in u16 units */ + unsigned dgram_item_len; /* index or length */ + unsigned block_length; + unsigned fp_index; + unsigned reserved1; + unsigned reserved2; + unsigned next_fp_index; +}; + +#define INIT_NDP16_OPTS { \ + .nth_sign = USB_CDC_NCM_NTH16_SIGN, \ + .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ + .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ + .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ + .ndplen_align = 4, \ + .dgram_item_len = 1, \ + .block_length = 1, \ + .fp_index = 1, \ + .reserved1 = 0, \ + .reserved2 = 0, \ + .next_fp_index = 1, \ + } + + +#define INIT_NDP32_OPTS { \ + .nth_sign = USB_CDC_NCM_NTH32_SIGN, \ + .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ + .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ + .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ + .ndplen_align = 8, \ + .dgram_item_len = 2, \ + .block_length = 2, \ + .fp_index = 2, \ + .reserved1 = 1, \ + .reserved2 = 2, \ + .next_fp_index = 2, \ + } + +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) +{ + switch (size) { + case 1: + put_unaligned_le16((u16)val, *p); + break; + case 2: + put_unaligned_le32((u32)val, *p); + + break; + default: + BUG(); + } + + *p += size; +} + +static inline unsigned get_ncm(__le16 **p, unsigned size) +{ + unsigned tmp; + + switch (size) { + case 1: + tmp = get_unaligned_le16(*p); + break; + case 2: + tmp = get_unaligned_le32(*p); + break; + default: + BUG(); + } + + *p += size; + return tmp; +} + +/*-------------------------------------------------------------------------*/ + +static inline void ncm_reset_values(struct f_ncm *ncm) +{ + ncm->parser_opts = &ndp16_opts; + ncm->is_crc = false; + ncm->port.cdc_filter = DEFAULT_FILTER; + + /* doesn't make sense for ncm, fixed size used */ + ncm->port.header_len = 0; + + ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize); + ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE; +} + +/* + * Context: ncm->lock held + */ +static void ncm_do_notify(struct f_ncm *ncm) +{ + struct usb_request *req = ncm->notify_req; + struct usb_cdc_notification *event; + struct usb_composite_dev *cdev = ncm->port.func.config->cdev; + __le32 *data; + int status; + + /* notification already in flight? */ + if (!req) + return; + + event = req->buf; + switch (ncm->notify_state) { + case NCM_NOTIFY_NONE: + return; + + case NCM_NOTIFY_CONNECT: + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + if (ncm->is_open) + event->wValue = cpu_to_le16(1); + else + event->wValue = cpu_to_le16(0); + event->wLength = 0; + req->length = sizeof *event; + + DBG(cdev, "notify connect %s\n", + ncm->is_open ? "true" : "false"); + ncm->notify_state = NCM_NOTIFY_NONE; + break; + + case NCM_NOTIFY_SPEED: + event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; + event->wValue = cpu_to_le16(0); + event->wLength = cpu_to_le16(8); + req->length = NCM_STATUS_BYTECOUNT; + + /* SPEED_CHANGE data is up/down speeds in bits/sec */ + data = req->buf + sizeof *event; + data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget)); + data[1] = data[0]; + + DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget)); + ncm->notify_state = NCM_NOTIFY_CONNECT; + break; + } + event->bmRequestType = 0xA1; + event->wIndex = cpu_to_le16(ncm->ctrl_id); + + ncm->notify_req = NULL; + /* + * In double buffering if there is a space in FIFO, + * completion callback can be called right after the call, + * so unlocking + */ + spin_unlock(&ncm->lock); + status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC); + spin_lock(&ncm->lock); + if (status < 0) { + ncm->notify_req = req; + DBG(cdev, "notify --> %d\n", status); + } +} + +/* + * Context: ncm->lock held + */ +static void ncm_notify(struct f_ncm *ncm) +{ + /* + * NOTE on most versions of Linux, host side cdc-ethernet + * won't listen for notifications until its netdevice opens. + * The first notification then sits in the FIFO for a long + * time, and the second one is queued. + * + * If ncm_notify() is called before the second (CONNECT) + * notification is sent, then it will reset to send the SPEED + * notificaion again (and again, and again), but it's not a problem + */ + ncm->notify_state = NCM_NOTIFY_SPEED; + ncm_do_notify(ncm); +} + +static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ncm *ncm = req->context; + struct usb_composite_dev *cdev = ncm->port.func.config->cdev; + struct usb_cdc_notification *event = req->buf; + + spin_lock(&ncm->lock); + switch (req->status) { + case 0: + VDBG(cdev, "Notification %02x sent\n", + event->bNotificationType); + break; + case -ECONNRESET: + case -ESHUTDOWN: + ncm->notify_state = NCM_NOTIFY_NONE; + break; + default: + DBG(cdev, "event %02x --> %d\n", + event->bNotificationType, req->status); + break; + } + ncm->notify_req = req; + ncm_do_notify(ncm); + spin_unlock(&ncm->lock); +} + +static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* now for SET_NTB_INPUT_SIZE only */ + unsigned in_size; + struct usb_function *f = req->context; + struct f_ncm *ncm = func_to_ncm(f); + struct usb_composite_dev *cdev = ep->driver_data; + + req->context = NULL; + if (req->status || req->actual != req->length) { + DBG(cdev, "Bad control-OUT transfer\n"); + goto invalid; + } + + in_size = get_unaligned_le32(req->buf); + if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE || + in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) { + DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size); + goto invalid; + } + + ncm->port.fixed_in_len = in_size; + VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size); + return; + +invalid: + usb_ep_set_halt(ep); + return; +} + +static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_ncm *ncm = func_to_ncm(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* + * composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_ETHERNET_PACKET_FILTER: + /* + * see 6.2.30: no data, wIndex = interface, + * wValue = packet filter bitmap + */ + if (w_length != 0 || w_index != ncm->ctrl_id) + goto invalid; + DBG(cdev, "packet filter %02x\n", w_value); + /* + * REVISIT locking of cdc_filter. This assumes the UDC + * driver won't have a concurrent packet TX irq running on + * another CPU; or that if it does, this write is atomic... + */ + ncm->port.cdc_filter = w_value; + value = 0; + break; + /* + * and optionally: + * case USB_CDC_SEND_ENCAPSULATED_COMMAND: + * case USB_CDC_GET_ENCAPSULATED_RESPONSE: + * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_STATISTIC: + */ + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_NTB_PARAMETERS: + + if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id) + goto invalid; + value = w_length > sizeof ntb_parameters ? + sizeof ntb_parameters : w_length; + memcpy(req->buf, &ntb_parameters, value); + VDBG(cdev, "Host asked NTB parameters\n"); + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_NTB_INPUT_SIZE: + + if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id) + goto invalid; + put_unaligned_le32(ncm->port.fixed_in_len, req->buf); + value = 4; + VDBG(cdev, "Host asked INPUT SIZE, sending %d\n", + ncm->port.fixed_in_len); + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_NTB_INPUT_SIZE: + { + if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id) + goto invalid; + req->complete = ncm_ep0out_complete; + req->length = w_length; + req->context = f; + + value = req->length; + break; + } + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_NTB_FORMAT: + { + uint16_t format; + + if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id) + goto invalid; + format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001; + put_unaligned_le16(format, req->buf); + value = 2; + VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format); + break; + } + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_NTB_FORMAT: + { + if (w_length != 0 || w_index != ncm->ctrl_id) + goto invalid; + switch (w_value) { + case 0x0000: + ncm->parser_opts = &ndp16_opts; + DBG(cdev, "NCM16 selected\n"); + break; + case 0x0001: + ncm->parser_opts = &ndp32_opts; + DBG(cdev, "NCM32 selected\n"); + break; + default: + goto invalid; + } + value = 0; + break; + } + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_CRC_MODE: + { + uint16_t is_crc; + + if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id) + goto invalid; + is_crc = ncm->is_crc ? 0x0001 : 0x0000; + put_unaligned_le16(is_crc, req->buf); + value = 2; + VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc); + break; + } + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_CRC_MODE: + { + int ndp_hdr_crc = 0; + + if (w_length != 0 || w_index != ncm->ctrl_id) + goto invalid; + switch (w_value) { + case 0x0000: + ncm->is_crc = false; + ndp_hdr_crc = NCM_NDP_HDR_NOCRC; + DBG(cdev, "non-CRC mode selected\n"); + break; + case 0x0001: + ncm->is_crc = true; + ndp_hdr_crc = NCM_NDP_HDR_CRC; + DBG(cdev, "CRC mode selected\n"); + break; + default: + goto invalid; + } + ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; + value = 0; + break; + } + + /* and disabled in ncm descriptor: */ + /* case USB_CDC_GET_NET_ADDRESS: */ + /* case USB_CDC_SET_NET_ADDRESS: */ + /* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */ + /* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */ + + default: +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "ncm req %02x.%02x response err %d\n", + ctrl->bRequestType, ctrl->bRequest, + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_ncm *ncm = func_to_ncm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* Control interface has only altsetting 0 */ + if (intf == ncm->ctrl_id) { + if (alt != 0) + goto fail; + + if (ncm->notify->driver_data) { + DBG(cdev, "reset ncm control %d\n", intf); + usb_ep_disable(ncm->notify); + } + + if (!(ncm->notify->desc)) { + DBG(cdev, "init ncm ctrl %d\n", intf); + if (config_ep_by_speed(cdev->gadget, f, ncm->notify)) + goto fail; + } + usb_ep_enable(ncm->notify); + ncm->notify->driver_data = ncm; + + /* Data interface has two altsettings, 0 and 1 */ + } else if (intf == ncm->data_id) { + if (alt > 1) + goto fail; + + if (ncm->port.in_ep->driver_data) { + DBG(cdev, "reset ncm\n"); + gether_disconnect(&ncm->port); + ncm_reset_values(ncm); + } + + /* + * CDC Network only sends data in non-default altsettings. + * Changing altsettings resets filters, statistics, etc. + */ + if (alt == 1) { + struct net_device *net; + + if (!ncm->port.in_ep->desc || + !ncm->port.out_ep->desc) { + DBG(cdev, "init ncm\n"); + if (config_ep_by_speed(cdev->gadget, f, + ncm->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + ncm->port.out_ep)) { + ncm->port.in_ep->desc = NULL; + ncm->port.out_ep->desc = NULL; + goto fail; + } + } + + /* TODO */ + /* Enable zlps by default for NCM conformance; + * override for musb_hdrc (avoids txdma ovhead) + */ + ncm->port.is_zlp_ok = !( + gadget_is_musbhdrc(cdev->gadget) + ); + ncm->port.cdc_filter = DEFAULT_FILTER; + DBG(cdev, "activate ncm\n"); + net = gether_connect(&ncm->port); + if (IS_ERR(net)) + return PTR_ERR(net); + } + + spin_lock(&ncm->lock); + ncm_notify(ncm); + spin_unlock(&ncm->lock); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +/* + * Because the data interface supports multiple altsettings, + * this NCM function *MUST* implement a get_alt() method. + */ +static int ncm_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_ncm *ncm = func_to_ncm(f); + + if (intf == ncm->ctrl_id) + return 0; + return ncm->port.in_ep->driver_data ? 1 : 0; +} + +static struct sk_buff *ncm_wrap_ntb(struct gether *port, + struct sk_buff *skb) +{ + struct f_ncm *ncm = func_to_ncm(&port->func); + struct sk_buff *skb2; + int ncb_len = 0; + __le16 *tmp; + int div; + int rem; + int pad; + int ndp_align; + int ndp_pad; + unsigned max_size = ncm->port.fixed_in_len; + 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); + rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); + ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + + ncb_len += opts->nth_size; + ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len; + ncb_len += ndp_pad; + ncb_len += opts->ndp_size; + ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */ + ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */ + pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += pad; + + if (ncb_len + skb->len + crc_len > max_size) { + dev_kfree_skb_any(skb); + return NULL; + } + + skb2 = skb_copy_expand(skb, ncb_len, + max_size - skb->len - ncb_len - crc_len, + GFP_ATOMIC); + dev_kfree_skb_any(skb); + if (!skb2) + return NULL; + + skb = skb2; + + tmp = (void *) skb_push(skb, ncb_len); + memset(tmp, 0, ncb_len); + + put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */ + tmp += 2; + /* wHeaderLength */ + put_unaligned_le16(opts->nth_size, tmp++); + tmp++; /* skip wSequence */ + put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */ + /* (d)wFpIndex */ + /* the first pointer is right after the NTH + align */ + put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad); + + tmp = (void *)tmp + ndp_pad; + + /* NDP */ + put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ + tmp += 2; + /* wLength */ + put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); + + tmp += opts->reserved1; + tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ + tmp += opts->reserved2; + + if (ncm->is_crc) { + uint32_t crc; + + crc = ~crc32_le(~0, + skb->data + ncb_len, + skb->len - ncb_len); + put_unaligned_le32(crc, skb->data + skb->len); + skb_put(skb, crc_len); + } + + /* (d)wDatagramIndex[0] */ + put_ncm(&tmp, opts->dgram_item_len, ncb_len); + /* (d)wDatagramLength[0] */ + put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len); + /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */ + + if (skb->len > MAX_TX_NONFIXED) + memset(skb_put(skb, max_size - skb->len), + 0, max_size - skb->len); + + return skb; +} + +static int ncm_unwrap_ntb(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) +{ + struct f_ncm *ncm = func_to_ncm(&port->func); + __le16 *tmp = (void *) skb->data; + unsigned index, index2; + unsigned dg_len, dg_len2; + unsigned ndp_len; + struct sk_buff *skb2; + int ret = -EINVAL; + unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize); + const struct ndp_parser_opts *opts = ncm->parser_opts; + unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; + int dgram_counter; + + /* dwSignature */ + if (get_unaligned_le32(tmp) != opts->nth_sign) { + INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n", + skb->len); + print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1, + skb->data, 32, false); + + goto err; + } + tmp += 2; + /* wHeaderLength */ + if (get_unaligned_le16(tmp++) != opts->nth_size) { + INFO(port->func.config->cdev, "Wrong NTB headersize\n"); + goto err; + } + tmp++; /* skip wSequence */ + + /* (d)wBlockLength */ + if (get_ncm(&tmp, opts->block_length) > max_size) { + INFO(port->func.config->cdev, "OUT size exceeded\n"); + goto err; + } + + index = get_ncm(&tmp, opts->fp_index); + /* NCM 3.2 */ + if (((index % 4) != 0) && (index < opts->nth_size)) { + INFO(port->func.config->cdev, "Bad index: %x\n", + index); + goto err; + } + + /* walk through NDP */ + tmp = ((void *)skb->data) + index; + if (get_unaligned_le32(tmp) != ncm->ndp_sign) { + INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); + goto err; + } + tmp += 2; + + ndp_len = get_unaligned_le16(tmp++); + /* + * NCM 3.3.1 + * entry is 2 items + * item size is 16/32 bits, opts->dgram_item_len * 2 bytes + * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry + */ + if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2)) + || (ndp_len % opts->ndplen_align != 0)) { + INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len); + goto err; + } + tmp += opts->reserved1; + tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ + tmp += opts->reserved2; + + ndp_len -= opts->ndp_size; + index2 = get_ncm(&tmp, opts->dgram_item_len); + dg_len2 = get_ncm(&tmp, opts->dgram_item_len); + dgram_counter = 0; + + do { + index = index2; + dg_len = dg_len2; + if (dg_len < 14 + crc_len) { /* ethernet header + crc */ + INFO(port->func.config->cdev, "Bad dgram length: %x\n", + dg_len); + goto err; + } + if (ncm->is_crc) { + uint32_t crc, crc2; + + crc = get_unaligned_le32(skb->data + + index + dg_len - crc_len); + crc2 = ~crc32_le(~0, + skb->data + index, + dg_len - crc_len); + if (crc != crc2) { + INFO(port->func.config->cdev, "Bad CRC\n"); + goto err; + } + } + + index2 = get_ncm(&tmp, opts->dgram_item_len); + dg_len2 = get_ncm(&tmp, opts->dgram_item_len); + + if (index2 == 0 || dg_len2 == 0) { + skb2 = skb; + } else { + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + goto err; + } + + if (!skb_pull(skb2, index)) { + ret = -EOVERFLOW; + goto err; + } + + skb_trim(skb2, dg_len - crc_len); + skb_queue_tail(list, skb2); + + ndp_len -= 2 * (opts->dgram_item_len * 2); + + dgram_counter++; + + if (index2 == 0 || dg_len2 == 0) + break; + } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */ + + VDBG(port->func.config->cdev, + "Parsed NTB with %d frames\n", dgram_counter); + return 0; +err: + skb_queue_purge(list); + dev_kfree_skb_any(skb); + return ret; +} + +static void ncm_disable(struct usb_function *f) +{ + struct f_ncm *ncm = func_to_ncm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "ncm deactivated\n"); + + if (ncm->port.in_ep->driver_data) + gether_disconnect(&ncm->port); + + if (ncm->notify->driver_data) { + usb_ep_disable(ncm->notify); + ncm->notify->driver_data = NULL; + ncm->notify->desc = NULL; + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * Callbacks let us notify the host about connect/disconnect when the + * net device is opened or closed. + * + * For testing, note that link states on this side include both opened + * and closed variants of: + * + * - disconnected/unconfigured + * - configured but inactive (data alt 0) + * - configured and active (data alt 1) + * + * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and + * SET_INTERFACE (altsetting). Remember also that "configured" doesn't + * imply the host is actually polling the notification endpoint, and + * likewise that "active" doesn't imply it's actually using the data + * endpoints for traffic. + */ + +static void ncm_open(struct gether *geth) +{ + struct f_ncm *ncm = func_to_ncm(&geth->func); + + DBG(ncm->port.func.config->cdev, "%s\n", __func__); + + spin_lock(&ncm->lock); + ncm->is_open = true; + ncm_notify(ncm); + spin_unlock(&ncm->lock); +} + +static void ncm_close(struct gether *geth) +{ + struct f_ncm *ncm = func_to_ncm(&geth->func); + + DBG(ncm->port.func.config->cdev, "%s\n", __func__); + + spin_lock(&ncm->lock); + ncm->is_open = false; + ncm_notify(ncm); + spin_unlock(&ncm->lock); +} + +/*-------------------------------------------------------------------------*/ + +/* ethernet function driver setup/binding */ + +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); + if (status < 0) + goto fail; + ncm->ctrl_id = status; + ncm_iad_desc.bFirstInterface = status; + + ncm_control_intf.bInterfaceNumber = status; + ncm_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ncm->data_id = status; + + ncm_data_nop_intf.bInterfaceNumber = status; + ncm_data_intf.bInterfaceNumber = status; + ncm_union_desc.bSlaveInterface0 = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc); + if (!ep) + goto fail; + ncm->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc); + if (!ep) + goto fail; + ncm->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc); + if (!ep) + goto fail; + ncm->notify = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* allocate notification request and buffer */ + ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!ncm->notify_req) + goto fail; + ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); + if (!ncm->notify_req->buf) + goto fail; + ncm->notify_req->context = ncm; + ncm->notify_req->complete = ncm_notify_complete; + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress; + hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress; + hs_ncm_notify_desc.bEndpointAddress = + fs_ncm_notify_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, + NULL); + /* + * NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + ncm->port.open = ncm_open; + ncm->port.close = ncm_close; + + DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + ncm->port.in_ep->name, ncm->port.out_ep->name, + ncm->notify->name); + return 0; + +fail: + usb_free_all_descriptors(f); + if (ncm->notify_req) { + kfree(ncm->notify_req->buf); + usb_ep_free_request(ncm->notify, ncm->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (ncm->notify) + ncm->notify->driver_data = NULL; + if (ncm->port.out_ep) + ncm->port.out_ep->driver_data = NULL; + if (ncm->port.in_ep) + ncm->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ncm_opts, + func_inst.group); +} + +/* f_ncm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ncm); + +/* f_ncm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm); + +/* 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); +} + +static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_ncm *ncm = func_to_ncm(f); + + DBG(c->cdev, "ncm unbind\n"); + + usb_free_all_descriptors(f); + + kfree(ncm->notify_req->buf); + usb_ep_free_request(ncm->notify, ncm->notify_req); +} + +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); + if (!ncm) + 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 */ + 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"; + /* descriptors are per-instance copies */ + ncm->port.func.bind = ncm_bind; + ncm->port.func.unbind = ncm_unbind; + ncm->port.func.set_alt = ncm_set_alt; + 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; + + 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 46d6266f30e..aebae1853bc 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -10,22 +10,14 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ +#include <linux/slab.h> #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> +#include <linux/module.h> #include "u_serial.h" #include "gadget_chips.h" @@ -39,20 +31,12 @@ * ready to handle the commands. */ -struct obex_ep_descs { - struct usb_endpoint_descriptor *obex_in; - struct usb_endpoint_descriptor *obex_out; -}; - struct f_obex { struct gserial port; u8 ctrl_id; u8 data_id; u8 port_num; u8 can_activate; - - struct obex_ep_descs fs; - struct obex_ep_descs hs; }; static inline struct f_obex *func_to_obex(struct usb_function *f) @@ -88,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, @@ -99,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, @@ -109,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, @@ -119,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, @@ -134,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, @@ -143,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, @@ -152,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, @@ -161,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, @@ -176,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, @@ -184,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, @@ -192,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, @@ -227,12 +211,16 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gserial_disconnect(&obex->port); } - if (!obex->port.in_desc) { + if (!obex->port.in->desc || !obex->port.out->desc) { DBG(cdev, "init obex ttyGS%d\n", obex->port_num); - obex->port.in_desc = ep_choose(cdev->gadget, - obex->hs.obex_in, obex->fs.obex_in); - obex->port.out_desc = ep_choose(cdev->gadget, - obex->hs.obex_out, obex->fs.obex_out); + if (config_ep_by_speed(cdev->gadget, f, + obex->port.in) || + config_ep_by_speed(cdev->gadget, f, + obex->port.out)) { + obex->port.out->desc = NULL; + obex->port.in->desc = NULL; + goto fail; + } } if (alt == 1) { @@ -302,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); @@ -331,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; @@ -343,33 +358,19 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) obex->port.out = ep; ep->driver_data = cdev; /* claim */ - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(fs_function); - - obex->fs.obex_in = usb_find_endpoint(fs_function, - f->descriptors, &obex_fs_ep_in_desc); - obex->fs.obex_out = usb_find_endpoint(fs_function, - f->descriptors, &obex_fs_ep_out_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - - obex_hs_ep_in_desc.bEndpointAddress = - obex_fs_ep_in_desc.bEndpointAddress; - obex_hs_ep_out_desc.bEndpointAddress = - obex_fs_ep_out_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(hs_function); + obex_hs_ep_in_desc.bEndpointAddress = + obex_fs_ep_in_desc.bEndpointAddress; + obex_hs_ep_out_desc.bEndpointAddress = + obex_fs_ep_out_desc.bEndpointAddress; - obex->hs.obex_in = usb_find_endpoint(hs_function, - f->hs_descriptors, &obex_hs_ep_in_desc); - obex->hs.obex_out = usb_find_endpoint(hs_function, - f->hs_descriptors, &obex_hs_ep_out_desc); - } + status = usb_assign_descriptors(f, fs_function, hs_function, NULL); + if (status) + goto fail; /* Avoid letting this gadget enumerate until the userspace * OBEX server is active. @@ -390,6 +391,7 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ if (obex->port.out) obex->port.out->driver_data = NULL; @@ -401,93 +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) { - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - 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); +} - /* maybe allocate device-global string IDs, and patch descriptors */ - if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { - status = usb_string_id(c->cdev); - if (status < 0) - return status; - obex_string_defs[OBEX_CTRL_IDX].id = status; +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); - obex_control_intf.iInterface = status; +static struct configfs_attribute *acm_attrs[] = { + &f_obex_port_num.attr, + NULL, +}; - status = usb_string_id(c->cdev); - if (status < 0) - return status; - obex_string_defs[OBEX_DATA_IDX].id = 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; - obex_data_nop_intf.iInterface = - obex_data_intf.iInterface = status; + 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 c1abeb89b41..f2b781773ee 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -8,19 +8,12 @@ * 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. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ +#include <linux/mm.h> +#include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/netdevice.h> @@ -33,8 +26,13 @@ #include <linux/usb/composite.h> #include "u_phonet.h" +#include "u_ether.h" #define PN_MEDIA_USB 0x1B +#define MAXPACKET 512 +#if (PAGE_SIZE % MAXPACKET) +#error MAXPACKET must divide PAGE_SIZE! +#endif /*-------------------------------------------------------------------------*/ @@ -45,6 +43,10 @@ struct phonet_port { struct f_phonet { struct usb_function function; + struct { + struct sk_buff *skb; + spinlock_t lock; + } rx; struct net_device *dev; struct usb_ep *in_ep, *out_ep; @@ -52,7 +54,7 @@ struct f_phonet { struct usb_request *out_reqv[0]; }; -static int phonet_rxq_size = 2; +static int phonet_rxq_size = 17; static inline struct f_phonet *func_to_pn(struct usb_function *f) { @@ -138,7 +140,7 @@ pn_hs_sink_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), + .wMaxPacketSize = cpu_to_le16(MAXPACKET), }; static struct usb_endpoint_descriptor @@ -188,8 +190,7 @@ static struct usb_descriptor_header *hs_pn_function[] = { static int pn_net_open(struct net_device *dev) { - if (netif_carrier_ok(dev)) - netif_wake_queue(dev); + netif_wake_queue(dev); return 0; } @@ -219,8 +220,7 @@ static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req) } dev_kfree_skb_any(skb); - if (netif_carrier_ok(dev)) - netif_wake_queue(dev); + netif_wake_queue(dev); } static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev) @@ -255,28 +255,18 @@ out_unlock: spin_unlock_irqrestore(&port->lock, flags); out: if (unlikely(skb)) { - dev_kfree_skb_any(skb); + dev_kfree_skb(skb); dev->stats.tx_dropped++; } - return 0; + return NETDEV_TX_OK; } static int pn_net_mtu(struct net_device *dev, int new_mtu) { - struct phonet_port *port = netdev_priv(dev); - unsigned long flags; - int err = -EBUSY; - if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) return -EINVAL; - - spin_lock_irqsave(&port->lock, flags); - if (!netif_carrier_ok(dev)) { - dev->mtu = new_mtu; - err = 0; - } - spin_unlock_irqrestore(&port->lock, flags); - return err; + dev->mtu = new_mtu; + return 0; } static const struct net_device_ops pn_netdev_ops = { @@ -310,21 +300,20 @@ static void pn_net_setup(struct net_device *dev) static int pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) { - struct sk_buff *skb; - const size_t size = fp->dev->mtu; + struct page *page; int err; - skb = alloc_skb(size, gfp_flags); - if (!skb) + page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); + if (!page) return -ENOMEM; - req->buf = skb->data; - req->length = size; - req->context = skb; + req->buf = page_address(page); + req->length = PAGE_SIZE; + req->context = page; err = usb_ep_queue(fp->out_ep, req, gfp_flags); if (unlikely(err)) - dev_kfree_skb_any(skb); + put_page(page); return err; } @@ -332,25 +321,42 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) { struct f_phonet *fp = ep->driver_data; struct net_device *dev = fp->dev; - struct sk_buff *skb = req->context; + struct page *page = req->context; + struct sk_buff *skb; + unsigned long flags; int status = req->status; switch (status) { case 0: - if (unlikely(!netif_running(dev))) - break; - if (unlikely(req->actual < 1)) + spin_lock_irqsave(&fp->rx.lock, flags); + skb = fp->rx.skb; + if (!skb) + skb = fp->rx.skb = netdev_alloc_skb(dev, 12); + if (req->actual < req->length) /* Last fragment */ + fp->rx.skb = NULL; + spin_unlock_irqrestore(&fp->rx.lock, flags); + + if (unlikely(!skb)) break; - skb_put(skb, req->actual); - skb->protocol = htons(ETH_P_PHONET); - skb_reset_mac_header(skb); - __skb_pull(skb, 1); - skb->dev = dev; - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - netif_rx(skb); - skb = NULL; + + if (skb->len == 0) { /* First fragment */ + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + /* Can't use pskb_pull() on page in IRQ */ + memcpy(skb_put(skb, 1), page_address(page), 1); + } + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + skb->len <= 1, req->actual, PAGE_SIZE); + page = NULL; + + if (req->actual < req->length) { /* Last fragment */ + skb->dev = dev; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + netif_rx(skb); + } break; /* Do not resubmit in these cases: */ @@ -368,10 +374,10 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) break; } - if (skb) - dev_kfree_skb_any(skb); + if (page) + put_page(page); if (req) - pn_rx_submit(fp, req, GFP_ATOMIC); + pn_rx_submit(fp, req, GFP_ATOMIC | __GFP_COLD); } /*-------------------------------------------------------------------------*/ @@ -383,11 +389,14 @@ static void __pn_reset(struct usb_function *f) struct phonet_port *port = netdev_priv(dev); netif_carrier_off(dev); - netif_stop_queue(dev); port->usb = NULL; usb_ep_disable(fp->out_ep); usb_ep_disable(fp->in_ep); + if (fp->rx.skb) { + dev_kfree_skb_irq(fp->rx.skb); + fp->rx.skb = NULL; + } } static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) @@ -410,27 +419,25 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) spin_lock(&port->lock); __pn_reset(f); if (alt == 1) { - struct usb_endpoint_descriptor *out, *in; int i; - out = ep_choose(gadget, - &pn_hs_sink_desc, - &pn_fs_sink_desc); - in = ep_choose(gadget, - &pn_hs_source_desc, - &pn_fs_source_desc); - usb_ep_enable(fp->out_ep, out); - usb_ep_enable(fp->in_ep, in); + if (config_ep_by_speed(gadget, f, fp->in_ep) || + config_ep_by_speed(gadget, f, fp->out_ep)) { + fp->in_ep->desc = NULL; + fp->out_ep->desc = NULL; + spin_unlock(&port->lock); + return -EINVAL; + } + usb_ep_enable(fp->out_ep); + usb_ep_enable(fp->in_ep); port->usb = fp; fp->out_ep->driver_data = fp; fp->in_ep->driver_data = fp; netif_carrier_on(dev); - if (netif_running(dev)) - netif_wake_queue(dev); for (i = 0; i < phonet_rxq_size; i++) - pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC); + pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC | __GFP_COLD); } spin_unlock(&port->lock); return 0; @@ -473,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; @@ -482,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) @@ -510,14 +535,14 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) fp->in_ep = ep; ep->driver_data = fp; /* Claim */ - pn_hs_sink_desc.bEndpointAddress = - pn_fs_sink_desc.bEndpointAddress; - pn_hs_source_desc.bEndpointAddress = - pn_fs_source_desc.bEndpointAddress; + pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress; + pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress; /* Do not try to bind Phonet twice... */ - fp->function.descriptors = fs_pn_function; - fp->function.hs_descriptors = hs_pn_function; + status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function, + NULL); + if (status) + goto err; /* Incoming USB requests */ status = -ENOMEM; @@ -526,7 +551,7 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL); if (!req) - goto err; + goto err_req; req->complete = pn_rx_complete; fp->out_reqv[i] = req; @@ -535,14 +560,18 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) /* Outgoing USB requests */ fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL); if (!fp->in_req) - goto err; + goto err_req; INFO(cdev, "USB CDC Phonet function\n"); INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name, fp->out_ep->name, fp->in_ep->name); return 0; +err_req: + for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++) + usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); err: + usb_free_all_descriptors(f); if (fp->out_ep) fp->out_ep->driver_data = NULL; if (fp->in_ep) @@ -551,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; @@ -564,62 +686,73 @@ pn_unbind(struct usb_configuration *c, struct usb_function *f) if (fp->out_reqv[i]) usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); - kfree(fp); + usb_free_all_descriptors(f); } -/*-------------------------------------------------------------------------*/ - -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; + struct f_phonet_opts *opts; + int size; - fp = kzalloc(sizeof(*fp), GFP_KERNEL); + 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) - + (phonet_rxq_size * sizeof(struct usb_request *)), - "upnlink%d", pn_net_setup); + 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); - netif_stop_queue(dev); - SET_NETDEV_DEV(dev, &gadget->dev); - err = register_netdev(dev); - if (err) - free_netdev(dev); - return err; + return dev; } -void gphonet_cleanup(void) +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(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 3279a472604..9c41e9515b8 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -4,33 +4,30 @@ * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (mina86@mina86.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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ +#include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> -#include <asm/atomic.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 @@ -73,23 +70,15 @@ * - MS-Windows drivers sometimes emit undocumented requests. */ -struct rndis_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; - struct f_rndis { struct gether port; u8 ctrl_id, data_id; u8 ethaddr[ETH_ALEN]; + u32 vendorID; + const char *manufacturer; int config; - struct rndis_ep_descs fs; - struct rndis_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; atomic_t notify_count; }; @@ -102,10 +91,12 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static unsigned int bitrate(struct usb_gadget *g) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 13 * 1024 * 8 * 1000 * 8; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else - return 19 * 64 * 1 * 1000 * 8; + return 19 * 64 * 1 * 1000 * 8; } /*-------------------------------------------------------------------------*/ @@ -113,13 +104,13 @@ static unsigned int bitrate(struct usb_gadget *g) /* */ -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define RNDIS_STATUS_INTERVAL_MS 32 #define STATUS_BYTECOUNT 8 /* 8 bytes data */ /* interface descriptor: */ -static struct usb_interface_descriptor rndis_control_intf __initdata = { +static struct usb_interface_descriptor rndis_control_intf = { .bLength = sizeof rndis_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -132,7 +123,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc header_desc __initdata = { +static struct usb_cdc_header_desc header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -140,7 +131,7 @@ static struct usb_cdc_header_desc header_desc __initdata = { .bcdCDC = cpu_to_le16(0x0110), }; -static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { +static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { .bLength = sizeof call_mgmt_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, @@ -149,15 +140,15 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { .bDataInterface = 0x01, }; -static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { - .bLength = sizeof acm_descriptor, +static struct usb_cdc_acm_descriptor rndis_acm_descriptor = { + .bLength = sizeof rndis_acm_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, .bmCapabilities = 0x00, }; -static struct usb_cdc_union_desc rndis_union_desc __initdata = { +static struct usb_cdc_union_desc rndis_union_desc = { .bLength = sizeof(rndis_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -167,7 +158,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = { /* the data interface has two bulk endpoints */ -static struct usb_interface_descriptor rndis_data_intf __initdata = { +static struct usb_interface_descriptor rndis_data_intf = { .bLength = sizeof rndis_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -179,19 +170,33 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = { /* .iInterface = DYNAMIC */ }; + +static struct usb_interface_assoc_descriptor +rndis_iad_descriptor = { + .bLength = sizeof rndis_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, /* XXX, hardcoded */ + .bInterfaceCount = 2, // control + data + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + /* full speed support: */ -static struct usb_endpoint_descriptor fs_notify_desc __initdata = { +static struct usb_endpoint_descriptor fs_notify_desc = { .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 = 1 << LOG2_STATUS_INTERVAL_MSEC, + .bInterval = RNDIS_STATUS_INTERVAL_MS, }; -static struct usb_endpoint_descriptor fs_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -199,7 +204,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor fs_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -207,14 +212,17 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *eth_fs_function[] __initdata = { +static struct usb_descriptor_header *eth_fs_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, + /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface has no altsetting */ (struct usb_descriptor_header *) &rndis_data_intf, (struct usb_descriptor_header *) &fs_in_desc, @@ -224,16 +232,17 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor hs_notify_desc __initdata = { +static struct usb_endpoint_descriptor hs_notify_desc = { .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 = LOG2_STATUS_INTERVAL_MSEC + 4, + .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) }; -static struct usb_endpoint_descriptor hs_in_desc __initdata = { + +static struct usb_endpoint_descriptor hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -242,7 +251,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor hs_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -251,14 +260,17 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *eth_hs_function[] __initdata = { +static struct usb_descriptor_header *eth_hs_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, + /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface has no altsetting */ (struct usb_descriptor_header *) &rndis_data_intf, (struct usb_descriptor_header *) &hs_in_desc, @@ -266,11 +278,82 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_notify_desc = { + .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 = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) +}; + +static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { + .bLength = sizeof ss_intr_comp_desc, + .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), +}; + +static struct usb_endpoint_descriptor ss_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { + .bLength = sizeof ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *eth_ss_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, + + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &ss_notify_desc, + (struct usb_descriptor_header *) &ss_intr_comp_desc, + + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &ss_in_desc, + (struct usb_descriptor_header *) &ss_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_out_desc, + (struct usb_descriptor_header *) &ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string rndis_string_defs[] = { [0].s = "RNDIS Communications Control", [1].s = "RNDIS Ethernet Data", + [2].s = "RNDIS", { } /* end of list */ }; @@ -286,12 +369,17 @@ static struct usb_gadget_strings *rndis_strings[] = { /*-------------------------------------------------------------------------*/ -static struct sk_buff *rndis_add_header(struct sk_buff *skb) +static struct sk_buff *rndis_add_header(struct gether *port, + struct sk_buff *skb) { - skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); - if (skb) - rndis_add_hdr(skb); - return skb; + struct sk_buff *skb2; + + skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); + if (skb2) + rndis_add_hdr(skb2); + + dev_kfree_skb(skb); + return skb2; } static void rndis_response_available(void *_rndis) @@ -362,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); } @@ -395,8 +482,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (w_length > req->length || w_value - || w_index != rndis->ctrl_id) + if (w_value || w_index != rndis->ctrl_id) goto invalid; /* read the request; process it later */ value = w_length; @@ -418,6 +504,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; + req->context = rndis; rndis_free_response(rndis->config, buf); value = n; } @@ -460,13 +547,13 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (rndis->notify->driver_data) { VDBG(cdev, "reset rndis control %d\n", intf); usb_ep_disable(rndis->notify); - } else { + } + if (!rndis->notify->desc) { VDBG(cdev, "init rndis ctrl %d\n", intf); - rndis->notify_desc = ep_choose(cdev->gadget, - rndis->hs.notify, - rndis->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) + goto fail; } - usb_ep_enable(rndis->notify, rndis->notify_desc); + usb_ep_enable(rndis->notify); rndis->notify->driver_data = rndis; } else if (intf == rndis->data_id) { @@ -475,12 +562,18 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (rndis->port.in_ep->driver_data) { DBG(cdev, "reset rndis\n"); gether_disconnect(&rndis->port); - } else { + } + + if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { DBG(cdev, "init rndis\n"); - rndis->port.in = ep_choose(cdev->gadget, - rndis->hs.in, rndis->fs.in); - rndis->port.out = ep_choose(cdev->gadget, - rndis->hs.out, rndis->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + rndis->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + rndis->port.out_ep)) { + rndis->port.in_ep->desc = NULL; + rndis->port.out_ep->desc = NULL; + goto fail; + } } /* Avoid ZLPs; they can be troublesome. */ @@ -548,7 +641,7 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, bitrate(cdev->gadget) / 100); rndis_signal_connect(rndis->config); } @@ -559,27 +652,77 @@ static void rndis_close(struct gether *geth) DBG(geth->func.config->cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_signal_disconnect(rndis->config); } /*-------------------------------------------------------------------------*/ +/* 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 __init +static int 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) goto fail; rndis->ctrl_id = status; + rndis_iad_descriptor.bFirstInterface = status; rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; @@ -630,61 +773,33 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->notify_req->context = rndis; rndis->notify_req->complete = rndis_response_complete; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(eth_fs_function); - if (!f->descriptors) - goto fail; - - rndis->fs.in = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_in_desc); - rndis->fs.out = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_out_desc); - rndis->fs.notify = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_in_desc.bEndpointAddress = - fs_in_desc.bEndpointAddress; - hs_out_desc.bEndpointAddress = - fs_out_desc.bEndpointAddress; - hs_notify_desc.bEndpointAddress = - fs_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(eth_hs_function); - - if (!f->hs_descriptors) - goto fail; + hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; + hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; - rndis->hs.in = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_in_desc); - rndis->hs.out = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_out_desc); - rndis->hs.notify = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_notify_desc); - } + ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; + ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; + ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, + eth_ss_function); + if (status) + goto fail; rndis->port.open = rndis_open; rndis->port.close = rndis_close; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) - goto fail; - rndis->config = status; - - rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); -#if 0 -// FIXME - if (rndis_set_param_vendor(rndis->config, vendorID, - manufacturer)) - goto fail0; -#endif + if (rndis->manufacturer && rndis->vendorID && + rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis->manufacturer)) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -692,16 +807,16 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) */ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", rndis->port.in_ep->name, rndis->port.out_ep->name, rndis->notify->name); return 0; fail: - if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + kfree(f->os_desc_table); + f->os_desc_n = 0; + usb_free_all_descriptors(f); if (rndis->notify_req) { kfree(rndis->notify_req->buf); @@ -711,9 +826,9 @@ fail: /* we might as well release our claims on endpoints */ if (rndis->notify) rndis->notify->driver_data = NULL; - if (rndis->port.out) + if (rndis->port.out_ep) rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in) + if (rndis->port.in_ep) rndis->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -721,86 +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); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); +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) { - /* only two endpoints on sa1100 */ - if (gadget_is_sa1100(c->cdev->gadget)) - return false; + 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); - /* everything else is *presumably* fine */ - return true; + 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; } -/** - * rndis_bind_config - add RNDIS network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +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); +} - /* maybe allocate device-global string IDs */ - if (rndis_string_defs[0].id == 0) { - - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_string_defs[0].id = status; - rndis_control_intf.iInterface = status; - - /* data interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_string_defs[1].id = status; - rndis_data_intf.iInterface = status; - } +static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + kfree(f->os_desc_table); + f->os_desc_n = 0; + usb_free_all_descriptors(f); + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); +} + +static struct usb_function *rndis_alloc(struct usb_function_instance *fi) +{ + struct f_rndis *rndis; + struct f_rndis_opts *opts; + int status; /* allocate and initialize one new instance */ - 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); + 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; @@ -810,19 +988,44 @@ int __init rndis_bind_config(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 db0aa93606e..9ecbcbf36a4 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -10,7 +10,9 @@ * either version 2 of that License or (at your option) any later version. */ +#include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include "u_serial.h" @@ -26,18 +28,10 @@ * if you can arrange appropriate host side drivers. */ -struct gser_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; -}; - struct f_gser { struct gserial port; u8 data_id; u8 port_num; - - struct gser_descs fs; - struct gser_descs hs; }; static inline struct f_gser *func_to_gser(struct usb_function *f) @@ -49,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 */ @@ -62,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, @@ -85,27 +79,55 @@ 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 = { + .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 = { + .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 = { + .bLength = sizeof gser_ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +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, + (struct usb_descriptor_header *) &gser_ss_out_desc, + (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string gser_string_defs[] = { @@ -135,12 +157,15 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (gser->port.in->driver_data) { DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); - } else { + } + if (!gser->port.in->desc || !gser->port.out->desc) { DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); - gser->port.in_desc = ep_choose(cdev->gadget, - gser->hs.in, gser->fs.in); - gser->port.out_desc = ep_choose(cdev->gadget, - gser->hs.out, gser->fs.out); + if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || + config_ep_by_speed(cdev->gadget, f, gser->port.out)) { + gser->port.in->desc = NULL; + gser->port.out->desc = NULL; + return -EINVAL; + } } gserial_connect(&gser->port, gser->port_num); return 0; @@ -159,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) @@ -189,36 +225,23 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) gser->port.out = ep; ep->driver_data = cdev; /* claim */ - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(gser_fs_function); - - gser->fs.in = usb_find_endpoint(gser_fs_function, - f->descriptors, &gser_fs_in_desc); - gser->fs.out = usb_find_endpoint(gser_fs_function, - f->descriptors, &gser_fs_out_desc); - - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - gser_hs_in_desc.bEndpointAddress = - gser_fs_in_desc.bEndpointAddress; - gser_hs_out_desc.bEndpointAddress = - gser_fs_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(gser_hs_function); - - gser->hs.in = usb_find_endpoint(gser_hs_function, - f->hs_descriptors, &gser_hs_in_desc); - gser->hs.out = usb_find_endpoint(gser_hs_function, - f->hs_descriptors, &gser_hs_out_desc); - } + gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; + gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; + + gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; + gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; + status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function, + gser_ss_function); + if (status) + goto fail; DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", gser->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", gser->port.in->name, gser->port.out->name); return 0; @@ -235,50 +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) { - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - 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); + + opts = container_of(fi, struct f_serial_opts, func_inst); - gser->port_num = port_num; + gser->port_num = opts->port_num; gser->port.func.name = "gser"; gser->port.func.strings = gser_strings; @@ -286,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 bffe91d525f..d3cd52db78f 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -8,26 +8,20 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ +#include <linux/slab.h> #include <linux/kernel.h> -#include <linux/utsname.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 @@ -59,6 +53,9 @@ struct f_sourcesink { struct usb_ep *in_ep; struct usb_ep *out_ep; + struct usb_ep *iso_in_ep; + struct usb_ep *iso_out_ep; + int cur_alt; }; static inline struct f_sourcesink *func_to_ss(struct usb_function *f) @@ -67,18 +64,32 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f) } static unsigned pattern; -module_param(pattern, uint, 0); -MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 "); +static unsigned isoc_interval; +static unsigned isoc_maxpacket; +static unsigned isoc_mult; +static unsigned isoc_maxburst; +static unsigned buflen; /*-------------------------------------------------------------------------*/ -static struct usb_interface_descriptor source_sink_intf = { - .bLength = sizeof source_sink_intf, +static struct usb_interface_descriptor source_sink_intf_alt0 = { + .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - /* .iInterface = DYNAMIC */ + /* .iInterface = DYNAMIC */ +}; + +static struct usb_interface_descriptor source_sink_intf_alt1 = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 1, + .bNumEndpoints = 4, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ }; /* full speed support: */ @@ -99,10 +110,36 @@ static struct usb_endpoint_descriptor fs_sink_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; +static struct usb_endpoint_descriptor fs_iso_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1023), + .bInterval = 4, +}; + +static struct usb_endpoint_descriptor fs_iso_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1023), + .bInterval = 4, +}; + static struct usb_descriptor_header *fs_source_sink_descs[] = { - (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &fs_sink_desc, (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt1, +#define FS_ALT_IFC_1_OFFSET 3 + (struct usb_descriptor_header *) &fs_sink_desc, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_iso_sink_desc, + (struct usb_descriptor_header *) &fs_iso_source_desc, NULL, }; @@ -124,10 +161,125 @@ static struct usb_endpoint_descriptor hs_sink_desc = { .wMaxPacketSize = cpu_to_le16(512), }; +static struct usb_endpoint_descriptor hs_iso_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, +}; + +static struct usb_endpoint_descriptor hs_iso_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, +}; + static struct usb_descriptor_header *hs_source_sink_descs[] = { - (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &hs_source_desc, (struct usb_descriptor_header *) &hs_sink_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt1, +#define HS_ALT_IFC_1_OFFSET 3 + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + (struct usb_descriptor_header *) &hs_iso_source_desc, + (struct usb_descriptor_header *) &hs_iso_sink_desc, + NULL, +}; + +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_source_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 ss_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_sink_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 ss_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_iso_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, +}; + +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, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_iso_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, +}; + +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, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + +static struct usb_descriptor_header *ss_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf_alt0, + (struct usb_descriptor_header *) &ss_source_desc, + (struct usb_descriptor_header *) &ss_source_comp_desc, + (struct usb_descriptor_header *) &ss_sink_desc, + (struct usb_descriptor_header *) &ss_sink_comp_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt1, +#define SS_ALT_IFC_1_OFFSET 5 + (struct usb_descriptor_header *) &ss_source_desc, + (struct usb_descriptor_header *) &ss_source_comp_desc, + (struct usb_descriptor_header *) &ss_sink_desc, + (struct usb_descriptor_header *) &ss_sink_comp_desc, + (struct usb_descriptor_header *) &ss_iso_source_desc, + (struct usb_descriptor_header *) &ss_iso_source_comp_desc, + (struct usb_descriptor_header *) &ss_iso_sink_desc, + (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, NULL, }; @@ -150,20 +302,58 @@ 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; struct f_sourcesink *ss = func_to_ss(f); int id; + int ret; /* allocate interface ID(s) */ id = usb_interface_id(c, f); if (id < 0) return id; - source_sink_intf.bInterfaceNumber = id; + source_sink_intf_alt0.bInterfaceNumber = id; + source_sink_intf_alt1.bInterfaceNumber = id; - /* allocate endpoints */ + /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); if (!ss->in_ep) { autoconf_fail: @@ -178,24 +368,124 @@ autoconf_fail: goto autoconf_fail; ss->out_ep->driver_data = cdev; /* claim */ - /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; - f->hs_descriptors = hs_source_sink_descs; + /* sanity check the isoc module parameters */ + if (isoc_interval < 1) + isoc_interval = 1; + if (isoc_interval > 16) + isoc_interval = 16; + if (isoc_mult > 2) + isoc_mult = 2; + if (isoc_maxburst > 15) + isoc_maxburst = 15; + + /* fill in the FS isoc descriptors from the module parameters */ + fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? + 1023 : isoc_maxpacket; + fs_iso_source_desc.bInterval = isoc_interval; + fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? + 1023 : isoc_maxpacket; + fs_iso_sink_desc.bInterval = isoc_interval; + + /* allocate iso endpoints */ + ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc); + if (!ss->iso_in_ep) + goto no_iso; + ss->iso_in_ep->driver_data = cdev; /* claim */ + + ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc); + if (ss->iso_out_ep) { + ss->iso_out_ep->driver_data = cdev; /* claim */ + } else { + ss->iso_in_ep->driver_data = NULL; + ss->iso_in_ep = NULL; +no_iso: + /* + * We still want to work even if the UDC doesn't have isoc + * endpoints, so null out the alt interface that contains + * them and continue. + */ + fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL; + hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL; + ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL; } - DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - f->name, ss->in_ep->name, ss->out_ep->name); + if (isoc_maxpacket > 1024) + isoc_maxpacket = 1024; + + /* support high speed hardware */ + hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; + + /* + * Fill in the HS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; + hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; + hs_iso_source_desc.bInterval = isoc_interval; + hs_iso_source_desc.bEndpointAddress = + fs_iso_source_desc.bEndpointAddress; + + hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; + hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11; + hs_iso_sink_desc.bInterval = isoc_interval; + hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + + /* support super speed hardware */ + ss_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + ss_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + + /* + * Fill in the SS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; + ss_iso_source_desc.bInterval = isoc_interval; + ss_iso_source_comp_desc.bmAttributes = isoc_mult; + ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst; + ss_iso_source_comp_desc.wBytesPerInterval = + isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_source_desc.bEndpointAddress = + fs_iso_source_desc.bEndpointAddress; + + ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; + ss_iso_sink_desc.bInterval = isoc_interval; + ss_iso_sink_comp_desc.bmAttributes = isoc_mult; + ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst; + ss_iso_sink_comp_desc.wBytesPerInterval = + isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_source_sink_descs, + hs_source_sink_descs, ss_source_sink_descs); + if (ret) + return ret; + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", + (gadget_is_superspeed(c->cdev->gadget) ? "super" : + (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), + f->name, ss->in_ep->name, ss->out_ep->name, + ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", + ss->iso_out_ep ? ss->iso_out_ep->name : "<none>"); return 0; } 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)); } @@ -206,6 +496,9 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) u8 *buf = req->buf; struct usb_composite_dev *cdev = ss->function.config->cdev; + if (pattern == 2) + return 0; + for (i = 0; i < req->actual; i++, buf++) { switch (pattern) { @@ -220,7 +513,7 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) * each usb transfer request should be. Resync is done * with set_interface or set_config. (We *WANT* it to * get quickly out of sync if controllers or their drivers - * stutter for any reason, including buffer duplcation...) + * stutter for any reason, including buffer duplication...) */ case 1: if (*buf == (u8)(i % 63)) @@ -247,23 +540,31 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) for (i = 0; i < req->length; i++) *buf++ = (u8) (i % 63); break; + case 2: + break; } } static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) { - struct f_sourcesink *ss = ep->driver_data; - struct usb_composite_dev *cdev = ss->function.config->cdev; - int status = req->status; + struct usb_composite_dev *cdev; + struct f_sourcesink *ss = ep->driver_data; + int status = req->status; + + /* driver_data will be null if ep has been disabled */ + if (!ss) + return; + + cdev = ss->function.config->cdev; switch (status) { case 0: /* normal completion? */ if (ep == ss->out_ep) { check_read_data(ss, req); - memset(req->buf, 0x55, req->length); - } else - reinit_write_data(ep, req); + if (pattern != 2) + memset(req->buf, 0x55, req->length); + } break; /* this endpoint is normally active while we're configured */ @@ -299,32 +600,57 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) } } -static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) +static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, + bool is_iso, int speed) { struct usb_ep *ep; struct usb_request *req; - int status; + int i, size, status; + + for (i = 0; i < 8; i++) { + if (is_iso) { + switch (speed) { + case USB_SPEED_SUPER: + size = isoc_maxpacket * (isoc_mult + 1) * + (isoc_maxburst + 1); + break; + case USB_SPEED_HIGH: + size = isoc_maxpacket * (isoc_mult + 1); + break; + default: + size = isoc_maxpacket > 1023 ? + 1023 : isoc_maxpacket; + break; + } + ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; + req = ss_alloc_ep_req(ep, size); + } else { + ep = is_in ? ss->in_ep : ss->out_ep; + req = ss_alloc_ep_req(ep, 0); + } - ep = is_in ? ss->in_ep : ss->out_ep; - req = alloc_ep_req(ep); - if (!req) - return -ENOMEM; + if (!req) + return -ENOMEM; - req->complete = source_sink_complete; - if (is_in) - reinit_write_data(ep, req); - else - memset(req->buf, 0x55, req->length); + req->complete = source_sink_complete; + if (is_in) + reinit_write_data(ep, req); + else if (pattern != 2) + memset(req->buf, 0x55, req->length); - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - struct usb_composite_dev *cdev; + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + struct usb_composite_dev *cdev; - cdev = ss->function.config->cdev; - ERROR(cdev, "start %s %s --> %d\n", - is_in ? "IN" : "OUT", - ep->name, status); - free_ep_req(ep, req); + cdev = ss->function.config->cdev; + ERROR(cdev, "start %s%s %s --> %d\n", + is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", + ep->name, status); + free_ep_req(ep, req); + } + + if (!is_iso) + break; } return status; @@ -335,28 +661,30 @@ static void disable_source_sink(struct f_sourcesink *ss) struct usb_composite_dev *cdev; cdev = ss->function.config->cdev; - disable_endpoints(cdev, ss->in_ep, ss->out_ep); + disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, + ss->iso_out_ep); VDBG(cdev, "%s disabled\n", ss->function.name); } static int -enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) +enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, + int alt) { int result = 0; - const struct usb_endpoint_descriptor *src, *sink; + int speed = cdev->gadget->speed; struct usb_ep *ep; - src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); - sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); - - /* one endpoint writes (sources) zeroes IN (to the host) */ + /* one bulk endpoint writes (sources) zeroes IN (to the host) */ ep = ss->in_ep; - result = usb_ep_enable(ep, src); + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + return result; + result = usb_ep_enable(ep); if (result < 0) return result; ep->driver_data = ss; - result = source_sink_start_ep(ss, true); + result = source_sink_start_ep(ss, true, false, speed); if (result < 0) { fail: ep = ss->in_ep; @@ -365,34 +693,92 @@ fail: return result; } - /* one endpoint reads (sinks) anything OUT (from the host) */ + /* one bulk endpoint reads (sinks) anything OUT (from the host) */ ep = ss->out_ep; - result = usb_ep_enable(ep, sink); + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + goto fail; + result = usb_ep_enable(ep); if (result < 0) goto fail; ep->driver_data = ss; - result = source_sink_start_ep(ss, false); + result = source_sink_start_ep(ss, false, false, speed); if (result < 0) { +fail2: + ep = ss->out_ep; usb_ep_disable(ep); ep->driver_data = NULL; goto fail; } - DBG(cdev, "%s enabled\n", ss->function.name); + if (alt == 0) + goto out; + + /* one iso endpoint writes (sources) zeroes IN (to the host) */ + ep = ss->iso_in_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + goto fail2; + result = usb_ep_enable(ep); + if (result < 0) + goto fail2; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, true, true, speed); + if (result < 0) { +fail3: + ep = ss->iso_in_ep; + if (ep) { + usb_ep_disable(ep); + ep->driver_data = NULL; + } + goto fail2; + } + } + + /* one iso endpoint reads (sinks) anything OUT (from the host) */ + ep = ss->iso_out_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + goto fail3; + result = usb_ep_enable(ep); + if (result < 0) + goto fail3; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, false, true, speed); + if (result < 0) { + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail3; + } + } +out: + ss->cur_alt = alt; + + DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt); return result; } static int sourcesink_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - struct f_sourcesink *ss = func_to_ss(f); - struct usb_composite_dev *cdev = f->config->cdev; + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; - /* we know alt is zero */ if (ss->in_ep->driver_data) disable_source_sink(ss); - return enable_source_sink(cdev, ss); + return enable_source_sink(cdev, ss, alt); +} + +static int sourcesink_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_sourcesink *ss = func_to_ss(f); + + return ss->cur_alt; } static void sourcesink_disable(struct usb_function *f) @@ -404,37 +790,18 @@ 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.descriptors = fs_source_sink_descs; - ss->function.bind = sourcesink_bind; - ss->function.unbind = sourcesink_unbind; - ss->function.set_alt = sourcesink_set_alt; - ss->function.disable = sourcesink_disable; - - 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); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + req->length = USB_COMP_EP0_BUFSIZ; + /* composite driver infrastructure handles everything except * the two control test requests. */ @@ -487,7 +854,7 @@ unknown: req->length = value; value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); if (value < 0) - ERROR(c->cdev, "source/sinkc response, err %d\n", + ERROR(c->cdev, "source/sink response, err %d\n", value); } @@ -495,42 +862,386 @@ unknown: return value; } -static struct usb_configuration sourcesink_driver = { - .label = "source/sink", - .strings = sourcesink_strings, - .bind = sourcesink_bind_config, - .setup = sourcesink_setup, - .bConfigurationValue = 3, - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, - /* .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); + + return result; +} + +static ssize_t f_ss_opts_pattern_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 != 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; +} - source_sink_intf.iInterface = id; - sourcesink_driver.iConfiguration = id; +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; + } - /* support autoresume for remote wakeup testing */ - if (autoresume) - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; - /* support OTG systems */ - if (gadget_is_otg(cdev->gadget)) { - sourcesink_driver.descriptors = otg_desc; - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + if (num > 15) { + ret = -EINVAL; + goto end; } - return usb_add_config(cdev, &sourcesink_driver); + 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 a9c98fdb626..1ea8baf3333 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -8,23 +8,17 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <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 @@ -56,18 +50,10 @@ * caring about specific product and vendor IDs. */ -struct geth_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; -}; - struct f_gether { struct gether port; char ethaddr[14]; - - struct geth_descs fs; - struct geth_descs hs; }; static inline struct f_gether *func_to_geth(struct usb_function *f) @@ -90,7 +76,7 @@ static inline struct f_gether *func_to_geth(struct usb_function *f) /* interface descriptor: */ -static struct usb_interface_descriptor subset_data_intf __initdata = { +static struct usb_interface_descriptor subset_data_intf = { .bLength = sizeof subset_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -103,7 +89,7 @@ static struct usb_interface_descriptor subset_data_intf __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc mdlm_header_desc __initdata = { +static struct usb_cdc_header_desc mdlm_header_desc = { .bLength = sizeof mdlm_header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -111,7 +97,7 @@ static struct usb_cdc_header_desc mdlm_header_desc __initdata = { .bcdCDC = cpu_to_le16(0x0110), }; -static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { +static struct usb_cdc_mdlm_desc mdlm_desc = { .bLength = sizeof mdlm_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_MDLM_TYPE, @@ -127,7 +113,7 @@ static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { * can't really use its struct. All we do here is say that we're using * the submode of "SAFE" which directly matches the CDC Subset. */ -static u8 mdlm_detail_desc[] __initdata = { +static u8 mdlm_detail_desc[] = { 6, USB_DT_CS_INTERFACE, USB_CDC_MDLM_DETAIL_TYPE, @@ -137,7 +123,7 @@ static u8 mdlm_detail_desc[] __initdata = { 0, /* network data capabilities ("raw" encapsulation) */ }; -static struct usb_cdc_ether_desc ether_desc __initdata = { +static struct usb_cdc_ether_desc ether_desc = { .bLength = sizeof ether_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, @@ -152,7 +138,7 @@ static struct usb_cdc_ether_desc ether_desc __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor fs_subset_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_subset_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -160,7 +146,7 @@ static struct usb_endpoint_descriptor fs_subset_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor fs_subset_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_subset_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -168,7 +154,7 @@ static struct usb_endpoint_descriptor fs_subset_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *fs_eth_function[] __initdata = { +static struct usb_descriptor_header *fs_eth_function[] = { (struct usb_descriptor_header *) &subset_data_intf, (struct usb_descriptor_header *) &mdlm_header_desc, (struct usb_descriptor_header *) &mdlm_desc, @@ -181,7 +167,7 @@ static struct usb_descriptor_header *fs_eth_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor hs_subset_in_desc __initdata = { +static struct usb_endpoint_descriptor hs_subset_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -189,7 +175,7 @@ static struct usb_endpoint_descriptor hs_subset_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor hs_subset_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_subset_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -197,7 +183,7 @@ static struct usb_endpoint_descriptor hs_subset_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *hs_eth_function[] __initdata = { +static struct usb_descriptor_header *hs_eth_function[] = { (struct usb_descriptor_header *) &subset_data_intf, (struct usb_descriptor_header *) &mdlm_header_desc, (struct usb_descriptor_header *) &mdlm_desc, @@ -208,11 +194,51 @@ static struct usb_descriptor_header *hs_eth_function[] __initdata = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_subset_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 ss_subset_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 ss_subset_bulk_comp_desc = { + .bLength = sizeof ss_subset_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ss_eth_function[] = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &mdlm_header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &ss_subset_in_desc, + (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_subset_out_desc, + (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string geth_string_defs[] = { [0].s = "CDC Ethernet Subset/SAFE", - [1].s = NULL /* DYNAMIC */, + [1].s = "", { } /* end of list */ }; @@ -242,13 +268,15 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } DBG(cdev, "init + activate cdc subset\n"); - geth->port.in = ep_choose(cdev->gadget, - geth->hs.in, geth->fs.in); - geth->port.out = ep_choose(cdev->gadget, - geth->hs.out, geth->fs.out); + if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) { + geth->port.in_ep->desc = NULL; + geth->port.out_ep->desc = NULL; + return -EINVAL; + } 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) @@ -264,14 +292,44 @@ static void geth_disable(struct usb_function *f) /* serial function driver setup/binding */ -static int __init +static int 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) @@ -293,33 +351,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) geth->port.out_ep = ep; ep->driver_data = cdev; /* claim */ - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(fs_eth_function); - - geth->fs.in = usb_find_endpoint(fs_eth_function, - f->descriptors, &fs_subset_in_desc); - geth->fs.out = usb_find_endpoint(fs_eth_function, - f->descriptors, &fs_subset_out_desc); - - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_subset_in_desc.bEndpointAddress = - fs_subset_in_desc.bEndpointAddress; - hs_subset_out_desc.bEndpointAddress = - fs_subset_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(hs_eth_function); - - geth->hs.in = usb_find_endpoint(hs_eth_function, - f->hs_descriptors, &hs_subset_in_desc); - geth->hs.out = usb_find_endpoint(hs_eth_function, - f->hs_descriptors, &hs_subset_out_desc); - } + hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; + hs_subset_out_desc.bEndpointAddress = + fs_subset_out_desc.bEndpointAddress; + + ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; + ss_subset_out_desc.bEndpointAddress = + fs_subset_out_desc.bEndpointAddress; + + status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, + ss_eth_function); + if (status) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -327,15 +374,17 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) */ DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", geth->port.in_ep->name, geth->port.out_ep->name); return 0; fail: + usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ - if (geth->port.out) + if (geth->port.out_ep) geth->port.out_ep->driver_data = NULL; - if (geth->port.in) + if (geth->port.in_ep) geth->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -343,79 +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) { - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - geth_string_defs[1].s = NULL; - kfree(func_to_geth(f)); + return container_of(to_config_group(item), struct f_gether_opts, + func_inst.group); } -/** - * geth_bind_config - add CDC Subset network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) -{ - struct f_gether *geth; - int status; +/* f_gether_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(gether); - if (!ethaddr) - return -EINVAL; +/* f_gether_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether); - /* maybe allocate device-global string IDs */ - if (geth_string_defs[0].id == 0) { +/* f_gether_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether); - /* interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - geth_string_defs[0].id = status; - subset_data_intf.iInterface = status; +/* f_gether_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether); - /* MAC address */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - geth_string_defs[1].id = status; - ether_desc.iMACAddress = status; +/* 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); +} + +static struct usb_function *geth_alloc(struct usb_function_instance *fi) +{ + struct f_gether *geth; + struct f_gether_opts *opts; + int status; + /* allocate and initialize one new instance */ - geth = kzalloc(sizeof *geth, GFP_KERNEL); + 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, - "%02X%02X%02X%02X%02X%02X", - ethaddr[0], ethaddr[1], ethaddr[2], - ethaddr[3], ethaddr[4], ethaddr[5]); + 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) { - geth_string_defs[1].s = NULL; - 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 new file mode 100644 index 00000000000..2b4c82d84bf --- /dev/null +++ b/drivers/usb/gadget/f_uac1.c @@ -0,0 +1,768 @@ +/* + * f_audio.c -- USB Audio class function driver + * + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/atomic.h> + +#include "u_uac1.h" + +#define OUT_EP_MAX_PACKET_SIZE 200 +static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; +module_param(req_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); + +static int req_count = 256; +module_param(req_count, int, S_IRUGO); +MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); + +static int audio_buf_size = 48000; +module_param(audio_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); + +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); + +/* + * DESCRIPTORS ... most are static, but strings and full + * configuration descriptors are built on demand. + */ + +/* + * We have two interfaces- AudioControl and AudioStreaming + * TODO: only supcard playback currently + */ +#define F_AUDIO_AC_INTERFACE 0 +#define F_AUDIO_AS_INTERFACE 1 +#define F_AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ + + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = F_AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = F_AUDIO_AC_INTERFACE, + [1] = F_AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, + .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), +}; + +static struct usb_audio_control mute_control = { + .list = LIST_HEAD_INIT(mute_control.list), + .name = "Mute Control", + .type = UAC_FU_MUTE, + /* Todo: add real Mute control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control volume_control = { + .list = LIST_HEAD_INIT(volume_control.list), + .name = "Volume Control", + .type = UAC_FU_VOLUME, + /* Todo: add real Volume control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control_selector feature_unit = { + .list = LIST_HEAD_INIT(feature_unit.list), + .id = FEATURE_UNIT_ID, + .name = "Mute & Volume Control", + .type = UAC_FEATURE_UNIT, + .desc = (struct usb_descriptor_header *)&feature_unit_desc, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor as_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *f_audio_desc[] __initdata = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&as_out_ep_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + NULL, +}; + +/* + * This function is an ALSA sound card following USB Audio Class Spec 1.0. + */ + +/*-------------------------------------------------------------------------*/ +struct f_audio_buf { + u8 *buf; + int actual; + struct list_head list; +}; + +static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) +{ + struct f_audio_buf *copy_buf; + + copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); + if (!copy_buf) + return ERR_PTR(-ENOMEM); + + copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); + if (!copy_buf->buf) { + kfree(copy_buf); + return ERR_PTR(-ENOMEM); + } + + return copy_buf; +} + +static void f_audio_buffer_free(struct f_audio_buf *audio_buf) +{ + kfree(audio_buf->buf); + kfree(audio_buf); +} +/*-------------------------------------------------------------------------*/ + +struct f_audio { + struct gaudio card; + + /* endpoints handle full and/or high speeds */ + struct usb_ep *out_ep; + + spinlock_t lock; + struct f_audio_buf *copy_buf; + struct work_struct playback_work; + struct list_head play_queue; + + /* Control Set command */ + struct list_head cs; + u8 set_cmd; + struct usb_audio_control *set_con; +}; + +static inline struct f_audio *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct f_audio, card.func); +} + +/*-------------------------------------------------------------------------*/ + +static void f_audio_playback_work(struct work_struct *data) +{ + struct f_audio *audio = container_of(data, struct f_audio, + playback_work); + struct f_audio_buf *play_buf; + + spin_lock_irq(&audio->lock); + if (list_empty(&audio->play_queue)) { + spin_unlock_irq(&audio->lock); + return; + } + play_buf = list_first_entry(&audio->play_queue, + struct f_audio_buf, list); + list_del(&play_buf->list); + spin_unlock_irq(&audio->lock); + + u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); + f_audio_buffer_free(play_buf); +} + +static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_audio *audio = req->context; + struct usb_composite_dev *cdev = audio->card.func.config->cdev; + struct f_audio_buf *copy_buf = audio->copy_buf; + int err; + + if (!copy_buf) + return -EINVAL; + + /* Copy buffer is full, add it to the play_queue */ + if (audio_buf_size - copy_buf->actual < req->actual) { + list_add_tail(©_buf->list, &audio->play_queue); + schedule_work(&audio->playback_work); + copy_buf = f_audio_buffer_alloc(audio_buf_size); + if (IS_ERR(copy_buf)) + return -ENOMEM; + } + + memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); + copy_buf->actual += req->actual; + audio->copy_buf = copy_buf; + + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err) + ERROR(cdev, "%s queue req: %d\n", ep->name, err); + + return 0; + +} + +static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_audio *audio = req->context; + int status = req->status; + u32 data = 0; + struct usb_ep *out_ep = audio->out_ep; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == out_ep) + f_audio_out_ep_complete(ep, req); + else if (audio->set_con) { + memcpy(&data, req->buf, req->length); + audio->set_con->set(audio->set_con, audio->set_cmd, + le16_to_cpu(data)); + audio->set_con = NULL; + } + break; + default: + break; + } +} + +static int audio_set_intf_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", + ctrl->bRequest, w_value, len, id); + + list_for_each_entry(cs, &audio->cs, list) { + if (cs->id == id) { + list_for_each_entry(con, &cs->control, list) { + if (con->type == con_sel) { + audio->set_con = con; + break; + } + } + break; + } + } + + audio->set_cmd = cmd; + req->context = audio; + req->complete = f_audio_complete; + + return len; +} + +static int audio_get_intf_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", + ctrl->bRequest, w_value, len, id); + + list_for_each_entry(cs, &audio->cs, list) { + if (cs->id == id) { + list_for_each_entry(con, &cs->control, list) { + if (con->type == con_sel && con->get) { + value = con->get(con, cmd); + break; + } + } + break; + } + } + + req->context = audio; + req->complete = f_audio_complete; + len = min_t(size_t, sizeof(value), len); + memcpy(req->buf, &value, len); + + return len; +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + value = len; + break; + + case UAC_SET_MIN: + break; + + case UAC_SET_MAX: + break; + + case UAC_SET_RES: + break; + + case UAC_SET_MEM: + break; + + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + value = len; + break; + case UAC_GET_MEM: + break; + default: + break; + } + + return value; +} + +static int +f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + value = audio_set_intf_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + value = audio_get_intf_req(f, ctrl); + break; + + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + + default: + ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_ep *out_ep = audio->out_ep; + struct usb_request *req; + int i = 0, err = 0; + + DBG(cdev, "intf %d, alt %d\n", intf, alt); + + if (intf == 1) { + if (alt == 1) { + usb_ep_enable(out_ep); + out_ep->driver_data = audio; + audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); + if (IS_ERR(audio->copy_buf)) + return -ENOMEM; + + /* + * allocate a bunch of read buffers + * and queue them all at once. + */ + for (i = 0; i < req_count && err == 0; i++) { + req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); + if (req) { + req->buf = kzalloc(req_buf_size, + GFP_ATOMIC); + if (req->buf) { + req->length = req_buf_size; + req->context = audio; + req->complete = + f_audio_complete; + err = usb_ep_queue(out_ep, + req, GFP_ATOMIC); + if (err) + ERROR(cdev, + "%s queue req: %d\n", + out_ep->name, err); + } else + err = -ENOMEM; + } else + err = -ENOMEM; + } + + } else { + struct f_audio_buf *copy_buf = audio->copy_buf; + if (copy_buf) { + list_add_tail(©_buf->list, + &audio->play_queue); + schedule_work(&audio->playback_work); + } + } + } + + return err; +} + +static void f_audio_disable(struct usb_function *f) +{ + return; +} + +/*-------------------------------------------------------------------------*/ + +static void f_audio_build_desc(struct f_audio *audio) +{ + struct gaudio *card = &audio->card; + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); + as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); + + /* Set sample rates */ + rate = u_audio_get_playback_rate(card); + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); + + /* Todo: Set Sample bits and other parameters */ + + return; +} + +/* audio function driver setup/binding */ +static int __init +f_audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_audio *audio = func_to_audio(f); + int status; + struct usb_ep *ep = NULL; + + f_audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); + if (!ep) + goto fail; + audio->out_ep = ep; + audio->out_ep->desc = &as_out_ep_desc; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* copy descriptors, and track endpoint copies */ + status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL); + if (status) + goto fail; + return 0; + +fail: + if (ep) + ep->driver_data = NULL; + return status; +} + +static void +f_audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_audio *audio = func_to_audio(f); + + usb_free_all_descriptors(f); + kfree(audio); +} + +/*-------------------------------------------------------------------------*/ + +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +{ + con->data[cmd] = value; + + return 0; +} + +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +{ + return con->data[cmd]; +} + +/* Todo: add more control selecotor dynamically */ +static int __init control_selector_init(struct f_audio *audio) +{ + INIT_LIST_HEAD(&audio->cs); + list_add(&feature_unit.list, &audio->cs); + + INIT_LIST_HEAD(&feature_unit.control); + list_add(&mute_control.list, &feature_unit.control); + list_add(&volume_control.list, &feature_unit.control); + + volume_control.data[UAC__CUR] = 0xffc0; + volume_control.data[UAC__MIN] = 0xe3a0; + volume_control.data[UAC__MAX] = 0xfff0; + volume_control.data[UAC__RES] = 0x0030; + + return 0; +} + +/** + * audio_bind_config - add USB audio function to a configuration + * @c: the configuration to supcard the USB audio function + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +static int __init audio_bind_config(struct usb_configuration *c) +{ + struct f_audio *audio; + int status; + + /* allocate and initialize one new instance */ + audio = kzalloc(sizeof *audio, GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->card.func.name = "g_audio"; + audio->card.gadget = c->cdev->gadget; + + INIT_LIST_HEAD(&audio->play_queue); + spin_lock_init(&audio->lock); + + /* set up ASLA audio devices */ + status = gaudio_setup(&audio->card); + if (status < 0) + goto setup_fail; + + audio->card.func.strings = audio_strings; + audio->card.func.bind = f_audio_bind; + audio->card.func.unbind = f_audio_unbind; + audio->card.func.set_alt = f_audio_set_alt; + audio->card.func.setup = f_audio_setup; + audio->card.func.disable = f_audio_disable; + + control_selector_init(audio); + + INIT_WORK(&audio->playback_work, f_audio_playback_work); + + status = usb_add_function(c, &audio->card.func); + if (status) + goto add_fail; + + INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", + audio_buf_size, req_buf_size, req_count); + + return status; + +add_fail: + gaudio_cleanup(); +setup_fail: + kfree(audio); + return status; +} diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c new file mode 100644 index 00000000000..6261db4a991 --- /dev/null +++ b/drivers/usb/gadget/f_uac2.c @@ -0,0 +1,1354 @@ +/* + * f_uac2.c -- USB Audio Class 2.0 Function + * + * Copyright (C) 2011 + * Yadwinder Singh (yadi.brar01@gmail.com) + * Jaswinder Singh (jaswinder.singh@linaro.org) + * + * 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/usb/audio.h> +#include <linux/usb/audio-v2.h> +#include <linux/platform_device.h> +#include <linux/module.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +/* Playback(USB-IN) Default Stereo - Fl/Fr */ +static int p_chmask = 0x3; +module_param(p_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); + +/* Playback Default 48 KHz */ +static int p_srate = 48000; +module_param(p_srate, uint, S_IRUGO); +MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); + +/* Playback Default 16bits/sample */ +static int p_ssize = 2; +module_param(p_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); + +/* Capture(USB-OUT) Default Stereo - Fl/Fr */ +static int c_chmask = 0x3; +module_param(c_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); + +/* Capture Default 64 KHz */ +static int c_srate = 64000; +module_param(c_srate, uint, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); + +/* Capture Default 16bits/sample */ +static int c_ssize = 2; +module_param(c_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); + +/* Keep everyone on toes */ +#define USB_XFERS 2 + +/* + * The driver implements a simple UAC_2 topology. + * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture + * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN + * Capture and Playback sampling rates are independently + * controlled by two clock sources : + * CLK_5 := c_srate, and CLK_6 := p_srate + */ +#define USB_OUT_IT_ID 1 +#define IO_IN_IT_ID 2 +#define IO_OUT_OT_ID 3 +#define USB_IN_OT_ID 4 +#define USB_OUT_CLK_ID 5 +#define USB_IN_CLK_ID 6 + +#define CONTROL_ABSENT 0 +#define CONTROL_RDONLY 1 +#define CONTROL_RDWR 3 + +#define CLK_FREQ_CTRL 0 +#define CLK_VLD_CTRL 2 + +#define COPY_CTRL 0 +#define CONN_CTRL 2 +#define OVRLD_CTRL 4 +#define CLSTR_CTRL 6 +#define UNFLW_CTRL 8 +#define OVFLW_CTRL 10 + +const char *uac2_name = "snd_uac2"; + +struct uac2_req { + struct uac2_rtd_params *pp; /* parent param */ + struct usb_request *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; + unsigned char *dma_area; + + struct snd_pcm_substream *ss; + + /* Ring buffer */ + ssize_t hw_ptr; + + void *rbuf; + + size_t period_size; + + unsigned max_psize; + struct uac2_req ureq[USB_XFERS]; + + spinlock_t lock; +}; + +struct snd_uac2_chip { + struct platform_device pdev; + struct platform_driver pdrv; + + struct uac2_rtd_params p_prm; + struct uac2_rtd_params c_prm; + + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define BUFF_SIZE_MAX (PAGE_SIZE * 16) +#define PRD_SIZE_MAX PAGE_SIZE +#define MIN_PERIODS 4 + +static struct snd_pcm_hardware uac2_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER + | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, + .buffer_bytes_max = BUFF_SIZE_MAX, + .period_bytes_max = PRD_SIZE_MAX, + .periods_min = MIN_PERIODS, +}; + +struct audio_dev { + u8 ac_intf, ac_alt; + u8 as_out_intf, as_out_alt; + u8 as_in_intf, as_in_alt; + + struct usb_ep *in_ep, *out_ep; + struct usb_function func; + + /* The ALSA Sound Card it represents on the USB-Client side */ + struct snd_uac2_chip uac2; +}; + +static struct audio_dev *agdev_g; + +static inline +struct audio_dev *func_to_agdev(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +static inline +struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u) +{ + return container_of(u, struct audio_dev, uac2); +} + +static inline +struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p) +{ + return container_of(p, struct snd_uac2_chip, pdev); +} + +static inline +uint num_channels(uint chanmask) +{ + uint num = 0; + + while (chanmask) { + num += (chanmask & 1); + chanmask >>= 1; + } + + return num; +} + +static void +agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) +{ + unsigned pending; + unsigned long flags; + bool update_alsa = false; + unsigned char *src, *dst; + int status = req->status; + struct uac2_req *ur = req->context; + struct snd_pcm_substream *substream; + struct uac2_rtd_params *prm = ur->pp; + struct snd_uac2_chip *uac2 = prm->uac2; + + /* i/f shutting down */ + if (!prm->ep_enabled || req->status == -ESHUTDOWN) + return; + + /* + * We can't really do much about bad xfers. + * Afterall, the ISOCH xfers could fail legitimately. + */ + if (status) + pr_debug("%s: iso_complete status(%d) %d/%d\n", + __func__, status, req->actual, req->length); + + substream = prm->ss; + + /* Do nothing if ALSA isn't active */ + if (!substream) + goto exit; + + spin_lock_irqsave(&prm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = prm->dma_area + prm->hw_ptr; + req->actual = req->length; + dst = req->buf; + } else { + dst = prm->dma_area + prm->hw_ptr; + src = req->buf; + } + + pending = prm->hw_ptr % prm->period_size; + pending += req->actual; + if (pending >= prm->period_size) + update_alsa = true; + + prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes; + + spin_unlock_irqrestore(&prm->lock, flags); + + /* Pack USB load in ALSA ring buffer */ + memcpy(dst, src, req->actual); +exit: + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + + if (update_alsa) + snd_pcm_period_elapsed(substream); + + return; +} + +static int +uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + unsigned long flags; + int err = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + + /* Reset */ + prm->hw_ptr = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + prm->ss = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + prm->ss = NULL; + break; + default: + err = -EINVAL; + } + + spin_unlock_irqrestore(&prm->lock, flags); + + /* Clear buffer after Play stops */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) + memset(prm->rbuf, 0, prm->max_psize * USB_XFERS); + + return err; +} + +static snd_pcm_uframes_t uac2_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + return bytes_to_frames(substream->runtime, prm->hw_ptr); +} + +static int uac2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + int err; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err >= 0) { + prm->dma_bytes = substream->runtime->dma_bytes; + prm->dma_area = substream->runtime->dma_area; + prm->period_size = params_period_bytes(hw_params); + } + + return err; +} + +static int uac2_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + prm->dma_area = NULL; + prm->dma_bytes = 0; + prm->period_size = 0; + + return snd_pcm_lib_free_pages(substream); +} + +static int uac2_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = uac2_pcm_hardware; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + spin_lock_init(&uac2->p_prm.lock); + runtime->hw.rate_min = p_srate; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */ + runtime->hw.channels_min = num_channels(p_chmask); + runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize + / runtime->hw.periods_min; + } else { + spin_lock_init(&uac2->c_prm.lock); + runtime->hw.rate_min = c_srate; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */ + runtime->hw.channels_min = num_channels(c_chmask); + runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize + / runtime->hw.periods_min; + } + + runtime->hw.rate_max = runtime->hw.rate_min; + runtime->hw.channels_max = runtime->hw.channels_min; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + return 0; +} + +/* ALSA cries without these function pointers */ +static int uac2_pcm_null(struct snd_pcm_substream *substream) +{ + return 0; +} + +static struct snd_pcm_ops uac2_pcm_ops = { + .open = uac2_pcm_open, + .close = uac2_pcm_null, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = uac2_pcm_hw_params, + .hw_free = uac2_pcm_hw_free, + .trigger = uac2_pcm_trigger, + .pointer = uac2_pcm_pointer, + .prepare = uac2_pcm_null, +}; + +static int snd_uac2_probe(struct platform_device *pdev) +{ + struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev); + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + /* Choose any slot, with no id */ + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); + if (err < 0) + return err; + + uac2->card = card; + + /* + * Create first PCM device + * Create a substream only for non-zero channel streams + */ + err = snd_pcm_new(uac2->card, "UAC2 PCM", 0, + p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); + if (err < 0) + goto snd_fail; + + strcpy(pcm->name, "UAC2 PCM"); + pcm->private_data = uac2; + + uac2->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac2_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac2_pcm_ops); + + strcpy(card->driver, "UAC2_Gadget"); + strcpy(card->shortname, "UAC2_Gadget"); + sprintf(card->longname, "UAC2_Gadget %i", pdev->id); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); + + err = snd_card_register(card); + if (!err) { + platform_set_drvdata(pdev, card); + return 0; + } + +snd_fail: + snd_card_free(card); + + uac2->pcm = NULL; + uac2->card = NULL; + + return err; +} + +static int snd_uac2_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + if (card) + return snd_card_free(card); + + return 0; +} + +static int alsa_uac2_init(struct audio_dev *agdev) +{ + struct snd_uac2_chip *uac2 = &agdev->uac2; + int err; + + uac2->pdrv.probe = snd_uac2_probe; + uac2->pdrv.remove = snd_uac2_remove; + uac2->pdrv.driver.name = uac2_name; + + uac2->pdev.id = 0; + uac2->pdev.name = uac2_name; + + /* Register snd_uac2 driver */ + err = platform_driver_register(&uac2->pdrv); + if (err) + return err; + + /* Register snd_uac2 device */ + err = platform_device_register(&uac2->pdev); + if (err) + platform_driver_unregister(&uac2->pdrv); + + return err; +} + +static void alsa_uac2_exit(struct audio_dev *agdev) +{ + struct snd_uac2_chip *uac2 = &agdev->uac2; + + platform_driver_unregister(&uac2->pdrv); + platform_device_unregister(&uac2->pdev); +} + + +/* --------- USB Function Interface ------------- */ + +enum { + STR_ASSOC, + STR_IF_CTRL, + STR_CLKSRC_IN, + STR_CLKSRC_OUT, + STR_USB_IT, + STR_IO_IT, + STR_USB_OT, + STR_IO_OT, + STR_AS_OUT_ALT0, + STR_AS_OUT_ALT1, + STR_AS_IN_ALT0, + STR_AS_IN_ALT1, +}; + +static char clksrc_in[8]; +static char clksrc_out[8]; + +static struct usb_string strings_fn[] = { + [STR_ASSOC].s = "Source/Sink", + [STR_IF_CTRL].s = "Topology Control", + [STR_CLKSRC_IN].s = clksrc_in, + [STR_CLKSRC_OUT].s = clksrc_out, + [STR_USB_IT].s = "USBH Out", + [STR_IO_IT].s = "USBD Out", + [STR_USB_OT].s = "USBH In", + [STR_IO_OT].s = "USBD In", + [STR_AS_OUT_ALT0].s = "Playback Inactive", + [STR_AS_OUT_ALT1].s = "Playback Active", + [STR_AS_IN_ALT0].s = "Capture Inactive", + [STR_AS_IN_ALT1].s = "Capture Active", + { }, +}; + +static struct usb_gadget_strings str_fn = { + .language = 0x0409, /* en-us */ + .strings = strings_fn, +}; + +static struct usb_gadget_strings *fn_strings[] = { + &str_fn, + NULL, +}; + +static struct usb_qualifier_descriptor devqual_desc = { + .bLength = sizeof devqual_desc, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = cpu_to_le16(0x200), + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, + .bNumConfigurations = 1, + .bRESERVED = 0, +}; + +static struct usb_interface_assoc_descriptor iad_desc = { + .bLength = sizeof iad_desc, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 3, + .bFunctionClass = USB_CLASS_AUDIO, + .bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED, + .bFunctionProtocol = UAC_VERSION_2, +}; + +/* Audio Control Interface */ +static struct usb_interface_descriptor std_ac_if_desc = { + .bLength = sizeof std_ac_if_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Clock source for IN traffic */ +struct uac_clock_source_descriptor in_clk_src_desc = { + .bLength = sizeof in_clk_src_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC2_CLOCK_SOURCE, + .bClockID = USB_IN_CLK_ID, + .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, + .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bAssocTerminal = 0, +}; + +/* Clock source for OUT traffic */ +struct uac_clock_source_descriptor out_clk_src_desc = { + .bLength = sizeof out_clk_src_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC2_CLOCK_SOURCE, + .bClockID = USB_OUT_CLK_ID, + .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, + .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bAssocTerminal = 0, +}; + +/* Input Terminal for USB_OUT */ +struct uac2_input_terminal_descriptor usb_out_it_desc = { + .bLength = sizeof usb_out_it_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = USB_OUT_IT_ID, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), + .bAssocTerminal = 0, + .bCSourceID = USB_OUT_CLK_ID, + .iChannelNames = 0, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Input Terminal for I/O-In */ +struct uac2_input_terminal_descriptor io_in_it_desc = { + .bLength = sizeof io_in_it_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = IO_IN_IT_ID, + .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED), + .bAssocTerminal = 0, + .bCSourceID = USB_IN_CLK_ID, + .iChannelNames = 0, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Ouput Terminal for USB_IN */ +struct uac2_output_terminal_descriptor usb_in_ot_desc = { + .bLength = sizeof usb_in_ot_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = USB_IN_OT_ID, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), + .bAssocTerminal = 0, + .bSourceID = IO_IN_IT_ID, + .bCSourceID = USB_IN_CLK_ID, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Ouput Terminal for I/O-Out */ +struct uac2_output_terminal_descriptor io_out_ot_desc = { + .bLength = sizeof io_out_ot_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = IO_OUT_OT_ID, + .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED), + .bAssocTerminal = 0, + .bSourceID = USB_OUT_IT_ID, + .bCSourceID = USB_OUT_CLK_ID, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +struct uac2_ac_header_descriptor ac_hdr_desc = { + .bLength = sizeof ac_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_MS_HEADER, + .bcdADC = cpu_to_le16(0x200), + .bCategory = UAC2_FUNCTION_IO_BOX, + .wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc + + sizeof usb_out_it_desc + sizeof io_in_it_desc + + sizeof usb_in_ot_desc + sizeof io_out_ot_desc, + .bmControls = 0, +}; + +/* Audio Streaming OUT Interface - Alt0 */ +static struct usb_interface_descriptor std_as_out_if0_desc = { + .bLength = sizeof std_as_out_if0_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Streaming OUT Interface - Alt1 */ +static struct usb_interface_descriptor std_as_out_if1_desc = { + .bLength = sizeof std_as_out_if1_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Stream OUT Intface Desc */ +struct uac2_as_header_descriptor as_out_hdr_desc = { + .bLength = sizeof as_out_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = USB_OUT_IT_ID, + .bmControls = 0, + .bFormatType = UAC_FORMAT_TYPE_I, + .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), + .iChannelNames = 0, +}; + +/* Audio USB_OUT Format */ +struct uac2_format_type_i_descriptor as_out_fmt1_desc = { + .bLength = sizeof as_out_fmt1_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, +}; + +/* STD AS ISO OUT Endpoint */ +struct usb_endpoint_descriptor fs_epout_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 1, +}; + +struct usb_endpoint_descriptor hs_epout_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 4, +}; + +/* CS AS ISO OUT Endpoint */ +static struct uac2_iso_endpoint_descriptor as_iso_out_desc = { + .bLength = sizeof as_iso_out_desc, + .bDescriptorType = USB_DT_CS_ENDPOINT, + + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 0, + .bmControls = 0, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +/* Audio Streaming IN Interface - Alt0 */ +static struct usb_interface_descriptor std_as_in_if0_desc = { + .bLength = sizeof std_as_in_if0_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Streaming IN Interface - Alt1 */ +static struct usb_interface_descriptor std_as_in_if1_desc = { + .bLength = sizeof std_as_in_if1_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Stream IN Intface Desc */ +struct uac2_as_header_descriptor as_in_hdr_desc = { + .bLength = sizeof as_in_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = USB_IN_OT_ID, + .bmControls = 0, + .bFormatType = UAC_FORMAT_TYPE_I, + .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), + .iChannelNames = 0, +}; + +/* Audio USB_IN Format */ +struct uac2_format_type_i_descriptor as_in_fmt1_desc = { + .bLength = sizeof as_in_fmt1_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, +}; + +/* STD AS ISO IN Endpoint */ +struct usb_endpoint_descriptor fs_epin_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 1, +}; + +struct usb_endpoint_descriptor hs_epin_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 4, +}; + +/* CS AS ISO IN Endpoint */ +static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = sizeof as_iso_in_desc, + .bDescriptorType = USB_DT_CS_ENDPOINT, + + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 0, + .bmControls = 0, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&iad_desc, + (struct usb_descriptor_header *)&std_ac_if_desc, + + (struct usb_descriptor_header *)&ac_hdr_desc, + (struct usb_descriptor_header *)&in_clk_src_desc, + (struct usb_descriptor_header *)&out_clk_src_desc, + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + + (struct usb_descriptor_header *)&std_as_out_if0_desc, + (struct usb_descriptor_header *)&std_as_out_if1_desc, + + (struct usb_descriptor_header *)&as_out_hdr_desc, + (struct usb_descriptor_header *)&as_out_fmt1_desc, + (struct usb_descriptor_header *)&fs_epout_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&std_as_in_if0_desc, + (struct usb_descriptor_header *)&std_as_in_if1_desc, + + (struct usb_descriptor_header *)&as_in_hdr_desc, + (struct usb_descriptor_header *)&as_in_fmt1_desc, + (struct usb_descriptor_header *)&fs_epin_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&iad_desc, + (struct usb_descriptor_header *)&std_ac_if_desc, + + (struct usb_descriptor_header *)&ac_hdr_desc, + (struct usb_descriptor_header *)&in_clk_src_desc, + (struct usb_descriptor_header *)&out_clk_src_desc, + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + + (struct usb_descriptor_header *)&std_as_out_if0_desc, + (struct usb_descriptor_header *)&std_as_out_if1_desc, + + (struct usb_descriptor_header *)&as_out_hdr_desc, + (struct usb_descriptor_header *)&as_out_fmt1_desc, + (struct usb_descriptor_header *)&hs_epout_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&std_as_in_if0_desc, + (struct usb_descriptor_header *)&std_as_in_if1_desc, + + (struct usb_descriptor_header *)&as_in_hdr_desc, + (struct usb_descriptor_header *)&as_in_fmt1_desc, + (struct usb_descriptor_header *)&hs_epin_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +struct cntrl_cur_lay3 { + __u32 dCUR; +}; + +struct cntrl_range_lay3 { + __u16 wNumSubRanges; + __u32 dMIN; + __u32 dMAX; + __u32 dRES; +} __packed; + +static inline void +free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) +{ + struct snd_uac2_chip *uac2 = prm->uac2; + int i; + + prm->ep_enabled = false; + + for (i = 0; i < USB_XFERS; i++) { + if (prm->ureq[i].req) { + usb_ep_dequeue(ep, prm->ureq[i].req); + usb_ep_free_request(ep, prm->ureq[i].req); + prm->ureq[i].req = NULL; + } + } + + if (usb_ep_disable(ep)) + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); +} + +static int __init +afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_composite_dev *cdev = cfg->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct uac2_rtd_params *prm; + int ret; + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_ac_if_desc.bInterfaceNumber = ret; + agdev->ac_intf = ret; + agdev->ac_alt = 0; + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_as_out_if0_desc.bInterfaceNumber = ret; + std_as_out_if1_desc.bInterfaceNumber = ret; + agdev->as_out_intf = ret; + agdev->as_out_alt = 0; + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_as_in_if0_desc.bInterfaceNumber = ret; + std_as_in_if1_desc.bInterfaceNumber = ret; + agdev->as_in_intf = ret; + agdev->as_in_alt = 0; + + agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); + if (!agdev->out_ep) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + goto err; + } + agdev->out_ep->driver_data = agdev; + + agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); + if (!agdev->in_ep) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + goto err; + } + 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; + hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize; + + ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL); + if (ret) + goto err; + + prm = &agdev->uac2.c_prm; + prm->max_psize = hs_epout_desc.wMaxPacketSize; + prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + if (!prm->rbuf) { + prm->max_psize = 0; + goto err; + } + + prm = &agdev->uac2.p_prm; + prm->max_psize = hs_epin_desc.wMaxPacketSize; + prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + if (!prm->rbuf) { + prm->max_psize = 0; + goto err; + } + + ret = alsa_uac2_init(agdev); + if (ret) + goto err; + return 0; +err: + kfree(agdev->uac2.p_prm.rbuf); + kfree(agdev->uac2.c_prm.rbuf); + usb_free_all_descriptors(fn); + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; + return -EINVAL; +} + +static void +afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct uac2_rtd_params *prm; + + alsa_uac2_exit(agdev); + + prm = &agdev->uac2.p_prm; + kfree(prm->rbuf); + + prm = &agdev->uac2.c_prm; + kfree(prm->rbuf); + usb_free_all_descriptors(fn); + + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; +} + +static int +afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) +{ + struct usb_composite_dev *cdev = fn->config->cdev; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_gadget *gadget = cdev->gadget; + struct usb_request *req; + struct usb_ep *ep; + struct uac2_rtd_params *prm; + int i; + + /* No i/f has more than 2 alt settings */ + if (alt > 1) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + if (intf == agdev->ac_intf) { + /* Control I/f has only 1 AltSetting - 0 */ + if (alt) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + return 0; + } + + if (intf == agdev->as_out_intf) { + ep = agdev->out_ep; + prm = &uac2->c_prm; + config_ep_by_speed(gadget, fn, ep); + agdev->as_out_alt = alt; + } else if (intf == agdev->as_in_intf) { + ep = agdev->in_ep; + prm = &uac2->p_prm; + config_ep_by_speed(gadget, fn, ep); + agdev->as_in_alt = alt; + } else { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + if (alt == 0) { + free_ep(prm, ep); + return 0; + } + + prm->ep_enabled = true; + usb_ep_enable(ep); + + for (i = 0; i < USB_XFERS; i++) { + if (prm->ureq[i].req) { + if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", + __LINE__); + continue; + } + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req == NULL) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + prm->ureq[i].req = req; + prm->ureq[i].pp = prm; + + req->zero = 0; + req->context = &prm->ureq[i]; + req->length = prm->max_psize; + req->complete = agdev_iso_complete; + req->buf = prm->rbuf + i * req->length; + + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + } + + return 0; +} + +static int +afunc_get_alt(struct usb_function *fn, unsigned intf) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + + if (intf == agdev->ac_intf) + return agdev->ac_alt; + else if (intf == agdev->as_out_intf) + return agdev->as_out_alt; + else if (intf == agdev->as_in_intf) + return agdev->as_in_alt; + else + dev_err(&uac2->pdev.dev, + "%s:%d Invalid Interface %d!\n", + __func__, __LINE__, intf); + + return -EINVAL; +} + +static void +afunc_disable(struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + + free_ep(&uac2->p_prm, agdev->in_ep); + agdev->as_in_alt = 0; + + free_ep(&uac2->c_prm, agdev->out_ep); + agdev->as_out_alt = 0; +} + +static int +in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + struct cntrl_cur_lay3 c; + + if (entity_id == USB_IN_CLK_ID) + c.dCUR = p_srate; + else if (entity_id == USB_OUT_CLK_ID) + c.dCUR = c_srate; + + value = min_t(unsigned, w_length, sizeof c); + memcpy(req->buf, &c, value); + } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { + *(u8 *)req->buf = 1; + value = min_t(unsigned, w_length, 1); + } else { + dev_err(&uac2->pdev.dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + + return value; +} + +static int +in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + struct cntrl_range_lay3 r; + int value = -EOPNOTSUPP; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + if (entity_id == USB_IN_CLK_ID) + r.dMIN = p_srate; + else if (entity_id == USB_OUT_CLK_ID) + r.dMIN = c_srate; + else + return -EOPNOTSUPP; + + r.dMAX = r.dMIN; + r.dRES = 0; + r.wNumSubRanges = 1; + + value = min_t(unsigned, w_length, sizeof r); + memcpy(req->buf, &r, value); + } else { + dev_err(&uac2->pdev.dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + + return value; +} + +static int +ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + if (cr->bRequest == UAC2_CS_CUR) + return in_rq_cur(fn, cr); + else if (cr->bRequest == UAC2_CS_RANGE) + return in_rq_range(fn, cr); + else + return -EOPNOTSUPP; +} + +static int +out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_value = le16_to_cpu(cr->wValue); + u8 control_selector = w_value >> 8; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) + return w_length; + + return -EOPNOTSUPP; +} + +static int +setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_index = le16_to_cpu(cr->wIndex); + u8 intf = w_index & 0xff; + + if (intf != agdev->ac_intf) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EOPNOTSUPP; + } + + if (cr->bRequestType & USB_DIR_IN) + return ac_rq_in(fn, cr); + else if (cr->bRequest == UAC2_CS_CUR) + return out_rq_cur(fn, cr); + + return -EOPNOTSUPP; +} + +static int +afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_composite_dev *cdev = fn->config->cdev; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_request *req = cdev->req; + u16 w_length = le16_to_cpu(cr->wLength); + int value = -EOPNOTSUPP; + + /* Only Class specific requests are supposed to reach here */ + if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) + return -EOPNOTSUPP; + + if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) + value = setup_rq_inf(fn, cr); + else + dev_err(&uac2->pdev.dev, "%s:%d Error!\n", __func__, __LINE__); + + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + req->status = 0; + } + } + + return value; +} + +static int audio_bind_config(struct usb_configuration *cfg) +{ + int res; + + agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL); + if (agdev_g == NULL) + return -ENOMEM; + + res = usb_string_ids_tab(cfg->cdev, strings_fn); + if (res) + return res; + iad_desc.iFunction = strings_fn[STR_ASSOC].id; + std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id; + in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id; + out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id; + usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id; + io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id; + usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id; + io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id; + std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id; + std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id; + std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; + std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; + + agdev_g->func.name = "uac2_func"; + agdev_g->func.strings = fn_strings; + agdev_g->func.bind = afunc_bind; + agdev_g->func.unbind = afunc_unbind; + agdev_g->func.set_alt = afunc_set_alt; + agdev_g->func.get_alt = afunc_get_alt; + agdev_g->func.disable = afunc_disable; + agdev_g->func.setup = afunc_setup; + + /* Initialize the configurable parameters */ + usb_out_it_desc.bNrChannels = num_channels(c_chmask); + usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask); + io_in_it_desc.bNrChannels = num_channels(p_chmask); + io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask); + as_out_hdr_desc.bNrChannels = num_channels(c_chmask); + as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask); + as_in_hdr_desc.bNrChannels = num_channels(p_chmask); + as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask); + as_out_fmt1_desc.bSubslotSize = c_ssize; + as_out_fmt1_desc.bBitResolution = c_ssize * 8; + as_in_fmt1_desc.bSubslotSize = p_ssize; + as_in_fmt1_desc.bBitResolution = p_ssize * 8; + + snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate); + snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate); + + res = usb_add_function(cfg, &agdev_g->func); + if (res < 0) + kfree(agdev_g); + + return res; +} + +static void +uac2_unbind_config(struct usb_configuration *cfg) +{ + kfree(agdev_g); + agdev_g = NULL; +} diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c new file mode 100644 index 00000000000..e2a1f50bd93 --- /dev/null +++ b/drivers/usb/gadget/f_uvc.c @@ -0,0 +1,836 @@ +/* + * uvc_gadget.c -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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> +#include <linux/device.h> +#include <linux/errno.h> +#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> +#include <linux/vmalloc.h> +#include <linux/wait.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> + +#include "uvc.h" + +unsigned int uvc_gadget_trace_param; + +/*-------------------------------------------------------------------------*/ + +/* module parameters specific to the Video streaming endpoint */ +static unsigned int streaming_interval = 1; +module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +static unsigned int streaming_maxpacket = 1024; +module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); + +static unsigned int streaming_maxburst; +module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + +/* -------------------------------------------------------------------------- + * Function descriptors + */ + +/* string IDs are assigned dynamically */ + +#define UVC_STRING_CONTROL_IDX 0 +#define UVC_STRING_STREAMING_IDX 1 + +static struct usb_string uvc_en_us_strings[] = { + [UVC_STRING_CONTROL_IDX].s = "UVC Camera", + [UVC_STRING_STREAMING_IDX].s = "Video Streaming", + { } +}; + +static struct usb_gadget_strings uvc_stringtab = { + .language = 0x0409, /* en-us */ + .strings = uvc_en_us_strings, +}; + +static struct usb_gadget_strings *uvc_function_strings[] = { + &uvc_stringtab, + NULL, +}; + +#define UVC_INTF_VIDEO_CONTROL 0 +#define UVC_INTF_VIDEO_STREAMING 1 + +#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ + +static struct usb_interface_assoc_descriptor uvc_iad __initdata = { + .bLength = sizeof(uvc_iad), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_VIDEO, + .bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION, + .bFunctionProtocol = 0x00, + .iFunction = 0, +}; + +static struct usb_interface_descriptor uvc_control_intf __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = UVC_INTF_VIDEO_CONTROL, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = UVC_SC_VIDEOCONTROL, + .bInterfaceProtocol = 0x00, + .iInterface = 0, +}; + +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(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(UVC_STATUS_MAX_PACKET_SIZE), +}; + +static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, + .bInterfaceProtocol = 0x00, + .iInterface = 0, +}; + +static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, + .bInterfaceProtocol = 0x00, + .iInterface = 0, +}; + +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_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 __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .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_endpoint_descriptor uvc_ss_streaming_ep __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .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 __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[] = { + (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, + (struct usb_descriptor_header *) &uvc_fs_streaming_ep, + NULL, +}; + +static const struct usb_descriptor_header * const uvc_hs_streaming[] = { + (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, + (struct usb_descriptor_header *) &uvc_hs_streaming_ep, + NULL, +}; + +static const struct usb_descriptor_header * const uvc_ss_streaming[] = { + (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, + (struct usb_descriptor_header *) &uvc_ss_streaming_ep, + (struct usb_descriptor_header *) &uvc_ss_streaming_comp, + NULL, +}; + +/* -------------------------------------------------------------------------- + * Control requests + */ + +static void +uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct uvc_device *uvc = req->context; + struct v4l2_event v4l2_event; + struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + + if (uvc->event_setup_out) { + uvc->event_setup_out = 0; + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_DATA; + uvc_event->data.length = req->actual; + memcpy(&uvc_event->data.data, req->buf, req->actual); + v4l2_event_queue(uvc->vdev, &v4l2_event); + } +} + +static int +uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct uvc_device *uvc = to_uvc(f); + struct v4l2_event v4l2_event; + struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + + /* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n", + * ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue), + * le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength)); + */ + + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) { + INFO(f->config->cdev, "invalid request type\n"); + return -EINVAL; + } + + /* Stall too big requests. */ + if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) + return -EINVAL; + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_SETUP; + memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); + v4l2_event_queue(uvc->vdev, &v4l2_event); + + 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) +{ + struct uvc_device *uvc = to_uvc(f); + + INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface); + + if (interface == uvc->control_intf) + return 0; + else if (interface != uvc->streaming_intf) + return -EINVAL; + else + return uvc->state == UVC_STATE_STREAMING ? 1 : 0; +} + +static int +uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) +{ + struct uvc_device *uvc = to_uvc(f); + struct v4l2_event v4l2_event; + struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + int ret; + + INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); + + if (interface == uvc->control_intf) { + if (alt) + return -EINVAL; + + if (uvc->state == UVC_STATE_DISCONNECTED) { + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_CONNECT; + uvc_event->speed = f->config->cdev->gadget->speed; + v4l2_event_queue(uvc->vdev, &v4l2_event); + + uvc->state = UVC_STATE_CONNECTED; + } + + return 0; + } + + if (interface != uvc->streaming_intf) + return -EINVAL; + + /* TODO + if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep)) + return alt ? -EINVAL : 0; + */ + + switch (alt) { + case 0: + if (uvc->state != UVC_STATE_STREAMING) + return 0; + + if (uvc->video.ep) + usb_ep_disable(uvc->video.ep); + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_STREAMOFF; + v4l2_event_queue(uvc->vdev, &v4l2_event); + + uvc->state = UVC_STATE_CONNECTED; + return 0; + + case 1: + if (uvc->state != UVC_STATE_CONNECTED) + return 0; + + if (uvc->video.ep) { + ret = config_ep_by_speed(f->config->cdev->gadget, + &(uvc->func), uvc->video.ep); + if (ret) + return ret; + usb_ep_enable(uvc->video.ep); + } + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_STREAMON; + v4l2_event_queue(uvc->vdev, &v4l2_event); + return USB_GADGET_DELAYED_STATUS; + + default: + return -EINVAL; + } +} + +static void +uvc_function_disable(struct usb_function *f) +{ + struct uvc_device *uvc = to_uvc(f); + struct v4l2_event v4l2_event; + + INFO(f->config->cdev, "uvc_function_disable\n"); + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_DISCONNECT; + v4l2_event_queue(uvc->vdev, &v4l2_event); + + uvc->state = UVC_STATE_DISCONNECTED; +} + +/* -------------------------------------------------------------------------- + * Connection / disconnection + */ + +void +uvc_function_connect(struct uvc_device *uvc) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + int ret; + + if ((ret = usb_function_activate(&uvc->func)) < 0) + INFO(cdev, "UVC connect failed with %d\n", ret); +} + +void +uvc_function_disconnect(struct uvc_device *uvc) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + int ret; + + if ((ret = usb_function_deactivate(&uvc->func)) < 0) + INFO(cdev, "UVC disconnect failed with %d\n", ret); +} + +/* -------------------------------------------------------------------------- + * USB probe and disconnect + */ + +static int +uvc_register_video(struct uvc_device *uvc) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + struct video_device *video; + + /* TODO reference counting. */ + video = video_device_alloc(); + if (video == NULL) + return -ENOMEM; + + video->v4l2_dev = &uvc->v4l2_dev; + video->fops = &uvc_v4l2_fops; + video->release = video_device_release; + strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); + + uvc->vdev = video; + video_set_drvdata(video, uvc); + + return video_register_device(video, VFL_TYPE_GRABBER, -1); +} + +#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \ + do { \ + memcpy(mem, desc, (desc)->bLength); \ + *(dst)++ = mem; \ + mem += (desc)->bLength; \ + } while (0); + +#define UVC_COPY_DESCRIPTORS(mem, dst, src) \ + do { \ + const struct usb_descriptor_header * const *__src; \ + for (__src = src; *__src; ++__src) { \ + memcpy(mem, *__src, (*__src)->bLength); \ + *dst++ = mem; \ + mem += (*__src)->bLength; \ + } \ + } while (0) + +static struct usb_descriptor_header ** __init +uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) +{ + struct uvc_input_header_descriptor *uvc_streaming_header; + struct uvc_header_descriptor *uvc_control_header; + const struct uvc_descriptor_header * const *uvc_control_desc; + 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; + struct usb_descriptor_header **dst; + struct usb_descriptor_header **hdr; + unsigned int control_size; + unsigned int streaming_size; + unsigned int n_desc; + unsigned int bytes; + void *mem; + + switch (speed) { + case USB_SPEED_SUPER: + uvc_control_desc = uvc->desc.ss_control; + uvc_streaming_cls = uvc->desc.ss_streaming; + uvc_streaming_std = uvc_ss_streaming; + 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; + break; + + case USB_SPEED_FULL: + default: + uvc_control_desc = uvc->desc.fs_control; + uvc_streaming_cls = uvc->desc.fs_streaming; + uvc_streaming_std = uvc_fs_streaming; + break; + } + + /* Descriptors layout + * + * uvc_iad + * uvc_control_intf + * 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 + */ + + /* Count descriptors and compute their size. */ + control_size = 0; + streaming_size = 0; + bytes = uvc_iad.bLength + uvc_control_intf.bLength + + uvc_control_ep.bLength + uvc_control_cs_ep.bLength + + uvc_streaming_intf_alt0.bLength; + + if (speed == USB_SPEED_SUPER) { + bytes += uvc_ss_control_comp.bLength; + n_desc = 6; + } else { + n_desc = 5; + } + + for (src = (const struct usb_descriptor_header **)uvc_control_desc; + *src; ++src) { + control_size += (*src)->bLength; + bytes += (*src)->bLength; + n_desc++; + } + for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; + *src; ++src) { + streaming_size += (*src)->bLength; + bytes += (*src)->bLength; + n_desc++; + } + for (src = uvc_streaming_std; *src; ++src) { + bytes += (*src)->bLength; + n_desc++; + } + + mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL); + if (mem == NULL) + return NULL; + + hdr = mem; + dst = mem; + mem += (n_desc + 1) * sizeof(*src); + + /* Copy the descriptors. */ + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad); + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf); + + uvc_control_header = mem; + UVC_COPY_DESCRIPTORS(mem, dst, + (const struct usb_descriptor_header **)uvc_control_desc); + uvc_control_header->wTotalLength = cpu_to_le16(control_size); + uvc_control_header->bInCollection = 1; + uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; + + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep); + if (speed == USB_SPEED_SUPER) + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); + + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep); + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0); + + uvc_streaming_header = mem; + 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->video.ep->address; + + UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std); + + *dst = NULL; + return hdr; +} + +static void +uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct uvc_device *uvc = to_uvc(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_CONTROL_IDX].id = 0; + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + kfree(uvc->control_buf); + + usb_free_all_descriptors(f); + + kfree(uvc); +} + +static int __init +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. + */ + 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_control_ep); + if (!ep) { + INFO(cdev, "Unable to allocate control EP\n"); + goto error; + } + uvc->control_ep = ep; + ep->driver_data = uvc; + + 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; + } + 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; + uvc_iad.bFirstInterface = ret; + uvc_control_intf.bInterfaceNumber = ret; + uvc->control_intf = ret; + + if ((ret = usb_interface_id(c, f)) < 0) + goto error; + uvc_streaming_intf_alt0.bInterfaceNumber = ret; + uvc_streaming_intf_alt1.bInterfaceNumber = ret; + uvc->streaming_intf = ret; + + /* Copy descriptors */ + f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); + if (gadget_is_dualspeed(cdev->gadget)) + f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + if (gadget_is_superspeed(c->cdev->gadget)) + f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); + + /* Preallocate control endpoint request. */ + uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); + uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); + if (uvc->control_req == NULL || uvc->control_buf == NULL) { + ret = -ENOMEM; + goto error; + } + + uvc->control_req->buf = uvc->control_buf; + uvc->control_req->complete = uvc_function_ep0_complete; + uvc->control_req->context = uvc; + + /* Avoid letting this gadget enumerate until the userspace server is + * active. + */ + 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) + goto error; + + /* Register a V4L2 device. */ + ret = uvc_register_video(uvc); + if (ret < 0) { + printk(KERN_INFO "Unable to register video device\n"); + goto error; + } + + return 0; + +error: + v4l2_device_unregister(&uvc->v4l2_dev); + if (uvc->vdev) + video_device_release(uvc->vdev); + + if (uvc->control_ep) + uvc->control_ep->driver_data = NULL; + if (uvc->video.ep) + uvc->video.ep->driver_data = NULL; + + if (uvc->control_req) { + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + kfree(uvc->control_buf); + } + + usb_free_all_descriptors(f); + return ret; +} + +/* -------------------------------------------------------------------------- + * USB gadget function + */ + +/** + * uvc_bind_config - add a UVC function to a configuration + * @c: the configuration to support the UVC instance + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @uvc_setup(). Caller is also responsible for + * calling @uvc_cleanup() before module unload. + */ +int __init +uvc_bind_config(struct usb_configuration *c, + const struct uvc_descriptor_header * const *fs_control, + const struct uvc_descriptor_header * const *ss_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) +{ + struct uvc_device *uvc; + int ret = 0; + + /* TODO Check if the USB device controller supports the required + * features. + */ + if (!gadget_is_dualspeed(c->cdev->gadget)) + return -EINVAL; + + uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); + if (uvc == NULL) + return -ENOMEM; + + uvc->state = UVC_STATE_DISCONNECTED; + + /* Validate the descriptors. */ + if (fs_control == NULL || fs_control[0] == NULL || + fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) + goto error; + + if (ss_control == NULL || ss_control[0] == NULL || + 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) + goto error; + + if (hs_streaming == NULL || hs_streaming[0] == NULL || + 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) + goto error; + + uvc->desc.fs_control = fs_control; + uvc->desc.ss_control = ss_control; + uvc->desc.fs_streaming = fs_streaming; + uvc->desc.hs_streaming = hs_streaming; + uvc->desc.ss_streaming = ss_streaming; + + /* 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_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; + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; + } + + /* Register the function. */ + uvc->func.name = "uvc"; + uvc->func.strings = uvc_function_strings; + uvc->func.bind = uvc_function_bind; + uvc->func.unbind = uvc_function_unbind; + uvc->func.get_alt = uvc_function_get_alt; + uvc->func.set_alt = uvc_function_set_alt; + uvc->func.disable = uvc_function_disable; + uvc->func.setup = uvc_function_setup; + + ret = usb_add_function(c, &uvc->func); + if (ret) + kfree(uvc); + + return ret; + +error: + kfree(uvc); + return ret; +} + +module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); + diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h new file mode 100644 index 00000000000..ec52752f732 --- /dev/null +++ b/drivers/usb/gadget/f_uvc.h @@ -0,0 +1,27 @@ +/* + * f_uvc.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ + +#ifndef _F_UVC_H_ +#define _F_UVC_H_ + +#include <linux/usb/composite.h> +#include <linux/usb/video.h> + +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/file_storage.c b/drivers/usb/gadget/file_storage.c deleted file mode 100644 index 381a53b3e11..00000000000 --- a/drivers/usb/gadget/file_storage.c +++ /dev/null @@ -1,4278 +0,0 @@ -/* - * file_storage.c -- File-backed USB Storage Gadget, for USB development - * - * Copyright (C) 2003-2008 Alan Stern - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The names of the above-listed copyright holders may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -/* - * The File-backed Storage Gadget acts as a USB Mass Storage device, - * appearing to the host as a disk drive or as a CD-ROM drive. In addition - * to providing an example of a genuinely useful gadget driver for a USB - * device, it also illustrates a technique of double-buffering for increased - * throughput. Last but not least, it gives an easy way to probe the - * behavior of the Mass Storage drivers in a USB host. - * - * Backing storage is provided by a regular file or a block device, specified - * by the "file" module parameter. Access can be limited to read-only by - * setting the optional "ro" module parameter. (For CD-ROM emulation, - * access is always read-only.) The gadget will indicate that it has - * removable media if the optional "removable" module parameter is set. - * - * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), - * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected - * by the optional "transport" module parameter. It also supports the - * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), - * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by - * the optional "protocol" module parameter. In addition, the default - * Vendor ID, Product ID, and release number can be overridden. - * - * There is support for multiple logical units (LUNs), each of which has - * its own backing file. The number of LUNs can be set using the optional - * "luns" module parameter (anywhere from 1 to 8), and the corresponding - * files are specified using comma-separated lists for "file" and "ro". - * The default number of LUNs is taken from the number of "file" elements; - * it is 1 if "file" is not given. If "removable" is not set then a backing - * file must be specified for each LUN. If it is set, then an unspecified - * or empty backing filename means the LUN's medium is not loaded. Ideally - * each LUN would be settable independently as a disk drive or a CD-ROM - * drive, but currently all LUNs have to be the same type. The CD-ROM - * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. Note also that the CD-ROM block - * length is set to 512 rather than the more common value 2048. - * - * Requirements are modest; only a bulk-in and a bulk-out endpoint are - * needed (an interrupt-out endpoint is also needed for CBI). The memory - * requirement amounts to two 16K buffers, size configurable by a parameter. - * Support is included for both full-speed and high-speed operation. - * - * Note that the driver is slightly non-portable in that it assumes a - * single memory/DMA buffer will be useable for bulk-in, bulk-out, and - * interrupt-in endpoints. With most device controllers this isn't an - * issue, but there may be some with hardware restrictions that prevent - * a buffer from being used by more than one endpoint. - * - * Module options: - * - * file=filename[,filename...] - * Required if "removable" is not set, names of - * the files or block devices used for - * backing storage - * ro=b[,b...] Default false, booleans for read-only access - * removable Default false, boolean for removable media - * luns=N Default N = number of filenames, number of - * LUNs to support - * stall Default determined according to the type of - * USB device controller (usually true), - * boolean to permit the driver to halt - * bulk endpoints - * cdrom Default false, boolean for whether to emulate - * a CD-ROM drive - * transport=XXX Default BBB, transport name (CB, CBI, or BBB) - * protocol=YYY Default SCSI, protocol name (RBC, 8020 or - * ATAPI, QIC, UFI, 8070, or SCSI; - * also 1 - 6) - * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID - * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID - * release=0xRRRR Override the USB release number (bcdDevice) - * buflen=N Default N=16384, buffer size used (will be - * rounded down to a multiple of - * PAGE_CACHE_SIZE) - * - * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", "luns", "stall", and "cdrom" options are available; default - * values are used for everything else. - * - * The pathnames of the backing files and the ro settings are available in - * the attribute files "file" and "ro" in the lun<n> subdirectory of the - * gadget's sysfs directory. If the "removable" option is set, writing to - * these files will simulate ejecting/loading the medium (writing an empty - * line means eject) and adjusting a write-enable tab. Changes to the ro - * setting are not allowed when the medium is loaded or if CD-ROM emulation - * is being used. - * - * This gadget driver is heavily based on "Gadget Zero" by David Brownell. - * The driver's SCSI command interface was based on the "Information - * technology - Small Computer System Interface - 2" document from - * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at - * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. The single exception - * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the - * "Universal Serial Bus Mass Storage Class UFI Command Specification" - * document, Revision 1.0, December 14, 1998, available at - * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. - */ - - -/* - * Driver Design - * - * The FSG driver is fairly straightforward. There is a main kernel - * thread that handles most of the work. Interrupt routines field - * callbacks from the controller driver: bulk- and interrupt-request - * completion notifications, endpoint-0 events, and disconnect events. - * Completion events are passed to the main thread by wakeup calls. Many - * ep0 requests are handled at interrupt time, but SetInterface, - * SetConfiguration, and device reset requests are forwarded to the - * thread in the form of "exceptions" using SIGUSR1 signals (since they - * should interrupt any ongoing file I/O operations). - * - * The thread's main routine implements the standard command/data/status - * parts of a SCSI interaction. It and its subroutines are full of tests - * for pending signals/exceptions -- all this polling is necessary since - * the kernel has no setjmp/longjmp equivalents. (Maybe this is an - * indication that the driver really wants to be running in userspace.) - * An important point is that so long as the thread is alive it keeps an - * open reference to the backing file. This will prevent unmounting - * the backing file's underlying filesystem and could cause problems - * during system shutdown, for example. To prevent such problems, the - * thread catches INT, TERM, and KILL signals and converts them into - * an EXIT exception. - * - * In normal operation the main thread is started during the gadget's - * fsg_bind() callback and stopped during fsg_unbind(). But it can also - * exit when it receives a signal, and there's no point leaving the - * gadget running when the thread is dead. So just before the thread - * exits, it deregisters the gadget driver. This makes things a little - * tricky: The driver is deregistered at two places, and the exiting - * thread can indirectly call fsg_unbind() which in turn can tell the - * thread to exit. The first problem is resolved through the use of the - * REGISTERED atomic bitflag; the driver will only be deregistered once. - * The second problem is resolved by having fsg_unbind() check - * fsg->state; it won't try to stop the thread if the state is already - * FSG_STATE_TERMINATED. - * - * To provide maximum throughput, the driver uses a circular pipeline of - * buffer heads (struct fsg_buffhd). In principle the pipeline can be - * arbitrarily long; in practice the benefits don't justify having more - * than 2 stages (i.e., double buffering). But it helps to think of the - * pipeline as being a long one. Each buffer head contains a bulk-in and - * a bulk-out request pointer (since the buffer can be used for both - * output and input -- directions always are given from the host's - * point of view) as well as a pointer to the buffer and various state - * variables. - * - * Use of the pipeline follows a simple protocol. There is a variable - * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. - * At any time that buffer head may still be in use from an earlier - * request, so each buffer head has a state variable indicating whether - * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the - * buffer head to be EMPTY, filling the buffer either by file I/O or by - * USB I/O (during which the buffer head is BUSY), and marking the buffer - * head FULL when the I/O is complete. Then the buffer will be emptied - * (again possibly by USB I/O, during which it is marked BUSY) and - * finally marked EMPTY again (possibly by a completion routine). - * - * A module parameter tells the driver to avoid stalling the bulk - * endpoints wherever the transport specification allows. This is - * necessary for some UDCs like the SuperH, which cannot reliably clear a - * halt on a bulk endpoint. However, under certain circumstances the - * Bulk-only specification requires a stall. In such cases the driver - * will halt the endpoint and set a flag indicating that it should clear - * the halt in software during the next device reset. Hopefully this - * will permit everything to work correctly. Furthermore, although the - * specification allows the bulk-out endpoint to halt when the host sends - * too much data, implementing this would cause an unavoidable race. - * The driver will always use the "no-stall" approach for OUT transfers. - * - * One subtle point concerns sending status-stage responses for ep0 - * requests. Some of these requests, such as device reset, can involve - * interrupting an ongoing file I/O operation, which might take an - * arbitrarily long time. During that delay the host might give up on - * the original ep0 request and issue a new one. When that happens the - * driver should not notify the host about completion of the original - * request, as the host will no longer be waiting for it. So the driver - * assigns to each ep0 request a unique tag, and it keeps track of the - * tag value of the request associated with a long-running exception - * (device-reset, interface-change, or configuration-change). When the - * exception handler is finished, the status-stage response is submitted - * only if the current ep0 request tag is equal to the exception request - * tag. Thus only the most recently received ep0 request will get a - * status-stage response. - * - * Warning: This driver source file is too long. It ought to be split up - * into a header file plus about 3 separate .c files, to handle the details - * of the Gadget, USB Mass Storage, and SCSI protocols. - */ - - -/* #define VERBOSE_DEBUG */ -/* #define DUMP_MSGS */ - - -#include <linux/blkdev.h> -#include <linux/completion.h> -#include <linux/dcache.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/fcntl.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/kref.h> -#include <linux/kthread.h> -#include <linux/limits.h> -#include <linux/rwsem.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/string.h> -#include <linux/freezer.h> -#include <linux/utsname.h> - -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.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 "usbstring.c" -#include "config.c" -#include "epautoconf.c" - -/*-------------------------------------------------------------------------*/ - -#define DRIVER_DESC "File-backed Storage Gadget" -#define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "20 November 2008" - -static const char longname[] = DRIVER_DESC; -static const char shortname[] = DRIVER_NAME; - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Alan Stern"); -MODULE_LICENSE("Dual BSD/GPL"); - -/* 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 DRIVER_VENDOR_ID 0x0525 // NetChip -#define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget - - -/* - * This driver assumes self-powered hardware and has no way for users to - * trigger remote wakeup. It uses autoconfiguration to select endpoints - * and endpoint addresses. - */ - - -/*-------------------------------------------------------------------------*/ - -#define LDBG(lun,fmt,args...) \ - dev_dbg(&(lun)->dev , fmt , ## args) -#define MDBG(fmt,args...) \ - pr_debug(DRIVER_NAME ": " fmt , ## args) - -#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 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) - -#define MINFO(fmt,args...) \ - pr_info(DRIVER_NAME ": " fmt , ## args) - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARNING(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - - -/*-------------------------------------------------------------------------*/ - -/* Encapsulate the module parameter settings */ - -#define MAX_LUNS 8 - -static struct { - char *file[MAX_LUNS]; - int ro[MAX_LUNS]; - unsigned int num_filenames; - unsigned int num_ros; - unsigned int nluns; - - int removable; - int can_stall; - int cdrom; - - char *transport_parm; - char *protocol_parm; - unsigned short vendor; - unsigned short product; - unsigned short release; - unsigned int buflen; - - int transport_type; - char *transport_name; - int protocol_type; - char *protocol_name; - -} mod_data = { // Default values - .transport_parm = "BBB", - .protocol_parm = "SCSI", - .removable = 0, - .can_stall = 1, - .cdrom = 0, - .vendor = DRIVER_VENDOR_ID, - .product = DRIVER_PRODUCT_ID, - .release = 0xffff, // Use controller chip type - .buflen = 16384, - }; - - -module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames, - S_IRUGO); -MODULE_PARM_DESC(file, "names of backing files or devices"); - -module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); -MODULE_PARM_DESC(ro, "true to force read-only"); - -module_param_named(luns, mod_data.nluns, uint, S_IRUGO); -MODULE_PARM_DESC(luns, "number of LUNs"); - -module_param_named(removable, mod_data.removable, bool, S_IRUGO); -MODULE_PARM_DESC(removable, "true to simulate removable media"); - -module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); -MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); - -module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); -MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); - - -/* In the non-TEST version, only the module parameters listed above - * are available. */ -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO); -MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); - -module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO); -MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " - "8070, or SCSI)"); - -module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO); -MODULE_PARM_DESC(vendor, "USB Vendor ID"); - -module_param_named(product, mod_data.product, ushort, S_IRUGO); -MODULE_PARM_DESC(product, "USB Product ID"); - -module_param_named(release, mod_data.release, ushort, S_IRUGO); -MODULE_PARM_DESC(release, "USB release number"); - -module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); -MODULE_PARM_DESC(buflen, "I/O buffer size"); - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/*-------------------------------------------------------------------------*/ - -/* SCSI device types */ -#define TYPE_DISK 0x00 -#define TYPE_CDROM 0x05 - -/* USB protocol value = the transport method */ -#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt -#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt -#define USB_PR_BULK 0x50 // Bulk-only - -/* USB subclass value = the protocol encapsulation */ -#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) -#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) -#define USB_SC_QIC 0x03 // QIC-157 (tape) -#define USB_SC_UFI 0x04 // UFI (floppy) -#define USB_SC_8070 0x05 // SFF-8070i (removable) -#define USB_SC_SCSI 0x06 // Transparent SCSI - -/* Bulk-only data structures */ - -/* Command Block Wrapper */ -struct bulk_cb_wrap { - __le32 Signature; // Contains 'USBC' - u32 Tag; // Unique per command id - __le32 DataTransferLength; // Size of the data - u8 Flags; // Direction in bit 7 - u8 Lun; // LUN (normally 0) - u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE - u8 CDB[16]; // Command Data Block -}; - -#define USB_BULK_CB_WRAP_LEN 31 -#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC -#define USB_BULK_IN_FLAG 0x80 - -/* Command Status Wrapper */ -struct bulk_cs_wrap { - __le32 Signature; // Should = 'USBS' - u32 Tag; // Same as original command - __le32 Residue; // Amount not transferred - u8 Status; // See below -}; - -#define USB_BULK_CS_WRAP_LEN 13 -#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' -#define USB_STATUS_PASS 0 -#define USB_STATUS_FAIL 1 -#define USB_STATUS_PHASE_ERROR 2 - -/* Bulk-only class specific requests */ -#define USB_BULK_RESET_REQUEST 0xff -#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe - - -/* 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 - - -#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block - -/* SCSI commands that we recognize */ -#define SC_FORMAT_UNIT 0x04 -#define SC_INQUIRY 0x12 -#define SC_MODE_SELECT_6 0x15 -#define SC_MODE_SELECT_10 0x55 -#define SC_MODE_SENSE_6 0x1a -#define SC_MODE_SENSE_10 0x5a -#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -#define SC_READ_6 0x08 -#define SC_READ_10 0x28 -#define SC_READ_12 0xa8 -#define SC_READ_CAPACITY 0x25 -#define SC_READ_FORMAT_CAPACITIES 0x23 -#define SC_READ_HEADER 0x44 -#define SC_READ_TOC 0x43 -#define SC_RELEASE 0x17 -#define SC_REQUEST_SENSE 0x03 -#define SC_RESERVE 0x16 -#define SC_SEND_DIAGNOSTIC 0x1d -#define SC_START_STOP_UNIT 0x1b -#define SC_SYNCHRONIZE_CACHE 0x35 -#define SC_TEST_UNIT_READY 0x00 -#define SC_VERIFY 0x2f -#define SC_WRITE_6 0x0a -#define SC_WRITE_10 0x2a -#define SC_WRITE_12 0xaa - -/* 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)) - - -/*-------------------------------------------------------------------------*/ - -/* - * These definitions will permit the compiler to avoid generating code for - * parts of the driver that aren't used in the non-TEST version. Even gcc - * can recognize when a test of a constant expression yields a dead code - * path. - */ - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - -#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK) -#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI) -#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI) - -#else - -#define transport_is_bbb() 1 -#define transport_is_cbi() 0 -#define protocol_is_scsi() 1 - -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -struct lun { - struct file *filp; - loff_t file_length; - loff_t num_sectors; - - unsigned int ro : 1; - unsigned int prevent_medium_removal : 1; - unsigned int registered : 1; - unsigned int info_valid : 1; - - u32 sense_data; - u32 sense_data_info; - u32 unit_attention_data; - - struct device dev; -}; - -#define backing_file_is_open(curlun) ((curlun)->filp != NULL) - -static struct lun *dev_to_lun(struct device *dev) -{ - return container_of(dev, struct lun, dev); -} - - -/* Big enough to hold our biggest descriptor */ -#define EP0_BUFSIZE 256 -#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value - -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define NUM_BUFFERS 2 - -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 { - FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere - 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 -}; - -struct fsg_dev { - /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ - spinlock_t lock; - struct usb_gadget *gadget; - - /* filesem protects: backing files in use */ - struct rw_semaphore filesem; - - /* reference counting: wait until all LUNs are released */ - struct kref ref; - - struct usb_ep *ep0; // Handy copy of gadget->ep0 - struct usb_request *ep0req; // For control responses - unsigned int ep0_req_tag; - const char *ep0req_name; - - struct usb_request *intreq; // For interrupt responses - int intreq_busy; - struct fsg_buffhd *intr_buffhd; - - unsigned int bulk_out_maxpacket; - enum fsg_state state; // For exception handling - unsigned int exception_req_tag; - - u8 config, new_config; - - unsigned int running : 1; - unsigned int bulk_in_enabled : 1; - unsigned int bulk_out_enabled : 1; - unsigned int intr_in_enabled : 1; - unsigned int phase_error : 1; - unsigned int short_packet_received : 1; - unsigned int bad_lun_okay : 1; - - unsigned long atomic_bitflags; -#define REGISTERED 0 -#define IGNORE_BULK_OUT 1 -#define SUSPENDED 2 - - struct usb_ep *bulk_in; - struct usb_ep *bulk_out; - struct usb_ep *intr_in; - - struct fsg_buffhd *next_buffhd_to_fill; - struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[NUM_BUFFERS]; - - int thread_wakeup_needed; - struct completion thread_notifier; - struct task_struct *thread_task; - - int cmnd_size; - u8 cmnd[MAX_COMMAND_SIZE]; - enum data_direction data_dir; - u32 data_size; - u32 data_size_from_cmnd; - u32 tag; - unsigned int lun; - u32 residue; - u32 usb_amount_left; - - /* The CB protocol offers no way for a host to know when a command - * has completed. As a result the next command may arrive early, - * and we will still have to handle it. For that reason we need - * a buffer to store new commands when using CB (or CBI, which - * does not oblige a host to wait for command completion either). */ - int cbbuf_cmnd_size; - u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; - - unsigned int nluns; - struct lun *luns; - struct lun *curlun; -}; - -typedef void (*fsg_routine_t)(struct fsg_dev *); - -static int exception_in_progress(struct fsg_dev *fsg) -{ - return (fsg->state > FSG_STATE_IDLE); -} - -/* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_dev *fsg, - struct fsg_buffhd *bh, unsigned int length) -{ - unsigned int rem; - - bh->bulk_out_intended_length = length; - rem = length % fsg->bulk_out_maxpacket; - if (rem > 0) - length += fsg->bulk_out_maxpacket - rem; - bh->outreq->length = length; -} - -static struct fsg_dev *the_fsg; -static struct usb_gadget_driver fsg_driver; - -static void close_backing_file(struct lun *curlun); - - -/*-------------------------------------------------------------------------*/ - -#ifdef DUMP_MSGS - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{ - if (length < 512) { - DBG(fsg, "%s, length %u:\n", label, length); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, - 16, 1, buf, length, 0); - } -} - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#else - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{} - -#ifdef VERBOSE_DEBUG - -static void dump_cdb(struct fsg_dev *fsg) -{ - print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, - 16, 1, fsg->cmnd, fsg->cmnd_size, 0); -} - -#else - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#endif /* VERBOSE_DEBUG */ -#endif /* DUMP_MSGS */ - - -static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) -{ - const char *name; - - if (ep == fsg->bulk_in) - name = "bulk-in"; - else if (ep == fsg->bulk_out) - name = "bulk-out"; - else - name = ep->name; - DBG(fsg, "%s set halt\n", name); - return usb_ep_set_halt(ep); -} - - -/*-------------------------------------------------------------------------*/ - -/* Routines for unaligned data access */ - -static u16 get_be16(u8 *buf) -{ - return ((u16) buf[0] << 8) | ((u16) buf[1]); -} - -static u32 get_be32(u8 *buf) -{ - return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | - ((u32) buf[2] << 8) | ((u32) buf[3]); -} - -static void put_be16(u8 *buf, u16 val) -{ - buf[0] = val >> 8; - buf[1] = val; -} - -static void put_be32(u8 *buf, u32 val) -{ - buf[0] = val >> 24; - buf[1] = val >> 16; - buf[2] = val >> 8; - buf[3] = val & 0xff; -} - - -/*-------------------------------------------------------------------------*/ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Also the (static) config and interface - * descriptors are adjusted during fsg_bind(). - */ -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_SERIAL 3 -#define STRING_CONFIG 4 -#define STRING_INTERFACE 5 - -/* There is only one configuration. */ -#define CONFIG_VALUE 1 - -static struct usb_device_descriptor -device_desc = { - .bLength = sizeof device_desc, - .bDescriptorType = USB_DT_DEVICE, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - /* The next three values can be overridden by module parameters */ - .idVendor = cpu_to_le16(DRIVER_VENDOR_ID), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID), - .bcdDevice = cpu_to_le16(0xffff), - - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIAL, - .bNumConfigurations = 1, -}; - -static struct usb_config_descriptor -config_desc = { - .bLength = sizeof config_desc, - .bDescriptorType = USB_DT_CONFIG, - - /* wTotalLength computed by usb_gadget_config_buf() */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_VALUE, - .iConfiguration = STRING_CONFIG, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - -static struct usb_otg_descriptor -otg_desc = { - .bLength = sizeof(otg_desc), - .bDescriptorType = USB_DT_OTG, - - .bmAttributes = USB_OTG_SRP, -}; - -/* There is only one interface. */ - -static struct usb_interface_descriptor -intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, // Adjusted during fsg_bind() - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() - .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() - .iInterface = STRING_INTERFACE, -}; - -/* Three full-speed endpoint descriptors: bulk-in, bulk-out, - * and interrupt-in. */ - -static struct usb_endpoint_descriptor -fs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; - -static struct usb_endpoint_descriptor -fs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; - -static struct usb_endpoint_descriptor -fs_intr_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 32, // frames -> 32 ms -}; - -static const struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_bulk_in_desc, - (struct usb_descriptor_header *) &fs_bulk_out_desc, - (struct usb_descriptor_header *) &fs_intr_in_desc, - NULL, -}; -#define FS_FUNCTION_PRE_EP_ENTRIES 2 - - -/* - * USB 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - * - * That means alternate endpoint descriptors (bigger packets) - * and a "device qualifier" ... plus more construction options - * for the config descriptor. - */ -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - .bNumConfigurations = 1, -}; - -static struct usb_endpoint_descriptor -hs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor -hs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), - .bInterval = 1, // NAK every 1 uframe -}; - -static struct usb_endpoint_descriptor -hs_intr_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = cpu_to_le16(2), - .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms -}; - -static const struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &otg_desc, - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_bulk_in_desc, - (struct usb_descriptor_header *) &hs_bulk_out_desc, - (struct usb_descriptor_header *) &hs_intr_in_desc, - NULL, -}; -#define HS_FUNCTION_PRE_EP_ENTRIES 2 - -/* Maxpacket and other transfer characteristics vary by speed. */ -static struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} - - -/* The CBI specification limits the serial string to 12 uppercase hexadecimal - * characters. */ -static char manufacturer[64]; -static char serial[13]; - -/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -static struct usb_string strings[] = { - {STRING_MANUFACTURER, manufacturer}, - {STRING_PRODUCT, longname}, - {STRING_SERIAL, serial}, - {STRING_CONFIG, "Self-powered"}, - {STRING_INTERFACE, "Mass Storage"}, - {} -}; - -static struct usb_gadget_strings stringtab = { - .language = 0x0409, // en-us - .strings = strings, -}; - - -/* - * Config descriptors must agree with the code that sets configurations - * and with code managing interfaces and their altsettings. They must - * also handle different speeds and other-speed requests. - */ -static int populate_config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - enum usb_device_speed speed = gadget->speed; - int len; - const struct usb_descriptor_header **function; - - if (index > 0) - return -EINVAL; - - if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) - speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; - if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) - function = hs_function; - else - function = fs_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - - -/*-------------------------------------------------------------------------*/ - -/* These routines may be called in process context or in_irq */ - -/* Caller must hold fsg->lock */ -static void wakeup_thread(struct fsg_dev *fsg) -{ - /* Tell the main thread that something has happened */ - fsg->thread_wakeup_needed = 1; - if (fsg->thread_task) - wake_up_process(fsg->thread_task); -} - - -static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) -{ - unsigned long flags; - - /* Do nothing if a higher-priority exception is already in progress. - * If a lower-or-equal priority exception is in progress, preempt it - * and notify the main thread by sending it a signal. */ - spin_lock_irqsave(&fsg->lock, flags); - if (fsg->state <= new_state) { - fsg->exception_req_tag = fsg->ep0_req_tag; - fsg->state = new_state; - if (fsg->thread_task) - send_sig_info(SIGUSR1, SEND_SIG_FORCED, - fsg->thread_task); - } - spin_unlock_irqrestore(&fsg->lock, flags); -} - - -/*-------------------------------------------------------------------------*/ - -/* The disconnect callback and ep0 routines. These always run in_irq, - * except that ep0_queue() is called in the main thread to acknowledge - * completion of various requests: set config, set interface, and - * Bulk-only device reset. */ - -static void fsg_disconnect(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "disconnect or port reset\n"); - raise_exception(fsg, FSG_STATE_DISCONNECT); -} - - -static int ep0_queue(struct fsg_dev *fsg) -{ - int rc; - - rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); - if (rc != 0 && rc != -ESHUTDOWN) { - - /* We can't do much more than wait for a reset */ - WARNING(fsg, "error in submission: %s --> %d\n", - fsg->ep0->name, rc); - } - return rc; -} - -static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - - if (req->actual > 0) - dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - if (req->status == 0 && req->context) - ((fsg_routine_t) (req->context))(fsg); -} - - -/*-------------------------------------------------------------------------*/ - -/* Bulk and interrupt endpoint completion handlers. - * These always run in_irq. */ - -static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - bh->inreq_busy = 0; - bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - dump_msg(fsg, "bulk-out", req->buf, req->actual); - if (req->status || req->actual != bh->bulk_out_intended_length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, - bh->bulk_out_intended_length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - bh->outreq_busy = 0; - bh->state = BUF_STATE_FULL; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - - -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct fsg_dev *fsg = ep->driver_data; - struct fsg_buffhd *bh = req->context; - - if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, - req->status, req->actual, req->length); - if (req->status == -ECONNRESET) // Request was cancelled - usb_ep_fifo_flush(ep); - - /* Hold the lock while we update the request and buffer states */ - smp_wmb(); - spin_lock(&fsg->lock); - fsg->intreq_busy = 0; - bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -/*-------------------------------------------------------------------------*/ - -/* Ep0 class-specific handlers. These always run in_irq. */ - -#ifdef CONFIG_USB_FILE_STORAGE_TEST -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct usb_request *req = fsg->ep0req; - static u8 cbi_reset_cmnd[6] = { - SC_SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; - - /* Error in command transfer? */ - if (req->status || req->length != req->actual || - req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { - - /* Not all controllers allow a protocol stall after - * receiving control-out data, but we'll try anyway. */ - fsg_set_halt(fsg, fsg->ep0); - return; // Wait for reset - } - - /* Is it the special reset command? */ - if (req->actual >= sizeof cbi_reset_cmnd && - memcmp(req->buf, cbi_reset_cmnd, - sizeof cbi_reset_cmnd) == 0) { - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "cbi reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - return; - } - - VDBG(fsg, "CB[I] accept device-specific command\n"); - spin_lock(&fsg->lock); - - /* Save the command for later */ - if (fsg->cbbuf_cmnd_size) - WARNING(fsg, "CB[I] overwriting previous command\n"); - fsg->cbbuf_cmnd_size = req->actual; - memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); - - wakeup_thread(fsg); - spin_unlock(&fsg->lock); -} - -#else -static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{} -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - -static int class_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - if (!fsg->config) - return value; - - /* Handle Bulk-only class-specific requests */ - if (transport_is_bbb()) { - switch (ctrl->bRequest) { - - case USB_BULK_RESET_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "bulk reset request\n"); - raise_exception(fsg, FSG_STATE_RESET); - value = DELAYED_STATUS; - break; - - case USB_BULK_GET_MAX_LUN_REQUEST: - if (ctrl->bRequestType != (USB_DIR_IN | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get max LUN\n"); - *(u8 *) req->buf = fsg->nluns - 1; - value = 1; - break; - } - } - - /* Handle CBI class-specific requests */ - else { - switch (ctrl->bRequest) { - - case USB_CBI_ADSC_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_index != 0 || w_value != 0) { - value = -EDOM; - break; - } - if (w_length > MAX_COMMAND_SIZE) { - value = -EOVERFLOW; - break; - } - value = w_length; - fsg->ep0req->context = received_cbi_adsc; - break; - } - } - - if (value == -EOPNOTSUPP) - VDBG(fsg, - "unknown class-specific control req " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - le16_to_cpu(ctrl->wValue), w_index, w_length); - return value; -} - - -/*-------------------------------------------------------------------------*/ - -/* Ep0 standard request handlers. These always run in_irq. */ - -static int standard_setup_req(struct fsg_dev *fsg, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_request *req = fsg->ep0req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - - /* Usually this just stores reply data in the pre-allocated ep0 buffer, - * but config change events will also reconfigure hardware. */ - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - VDBG(fsg, "get device descriptor\n"); - value = sizeof device_desc; - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) - break; - value = sizeof dev_qualifier; - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget)) - break; - goto get_config; - case USB_DT_CONFIG: - VDBG(fsg, "get configuration descriptor\n"); -get_config: - value = populate_config_buf(fsg->gadget, - req->buf, - w_value >> 8, - w_value & 0xff); - break; - - case USB_DT_STRING: - VDBG(fsg, "get string descriptor\n"); - - /* wIndex == language code */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - break; - } - break; - - /* One config, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "set configuration\n"); - if (w_value == CONFIG_VALUE || w_value == 0) { - fsg->new_config = w_value; - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and set the new config. */ - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_DEVICE)) - break; - VDBG(fsg, "get configuration\n"); - *(u8 *) req->buf = fsg->config; - value = 1; - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (fsg->config && w_index == 0) { - - /* Raise an exception to wipe out previous transaction - * state (queued bufs, etc) and install the new - * interface altsetting. */ - raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); - value = DELAYED_STATUS; - } - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | - USB_RECIP_INTERFACE)) - break; - if (!fsg->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get interface\n"); - *(u8 *) req->buf = 0; - value = 1; - break; - - default: - VDBG(fsg, - "unknown control req %02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, le16_to_cpu(ctrl->wLength)); - } - - return value; -} - - -static int fsg_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int rc; - int w_length = le16_to_cpu(ctrl->wLength); - - ++fsg->ep0_req_tag; // Record arrival of a new request - fsg->ep0req->context = NULL; - fsg->ep0req->length = 0; - dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); - - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) - rc = class_setup_req(fsg, ctrl); - else - rc = standard_setup_req(fsg, ctrl); - - /* Respond with data/status or defer until later? */ - if (rc >= 0 && rc != DELAYED_STATUS) { - rc = min(rc, w_length); - fsg->ep0req->length = rc; - fsg->ep0req->zero = rc < w_length; - fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? - "ep0-in" : "ep0-out"); - rc = ep0_queue(fsg); - } - - /* Device either stalls (rc < 0) or reports success */ - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -/* All the following routines run in process context */ - - -/* Use this for bulk or interrupt transfers, not ep0 */ -static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, - struct usb_request *req, int *pbusy, - enum fsg_buffer_state *state) -{ - int rc; - - if (ep == fsg->bulk_in) - dump_msg(fsg, "bulk-in", req->buf, req->length); - else if (ep == fsg->intr_in) - dump_msg(fsg, "intr-in", req->buf, req->length); - - spin_lock_irq(&fsg->lock); - *pbusy = 1; - *state = BUF_STATE_BUSY; - spin_unlock_irq(&fsg->lock); - rc = usb_ep_queue(ep, req, GFP_KERNEL); - if (rc != 0) { - *pbusy = 0; - *state = BUF_STATE_EMPTY; - - /* We can't do much more than wait for a reset */ - - /* Note: currently the net2280 driver fails zero-length - * submissions if DMA is enabled. */ - if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && - req->length == 0)) - WARNING(fsg, "error in submission: %s --> %d\n", - ep->name, rc); - } -} - - -static int sleep_thread(struct fsg_dev *fsg) -{ - int rc = 0; - - /* Wait until a signal arrives or we are woken up */ - for (;;) { - try_to_freeze(); - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - rc = -EINTR; - break; - } - if (fsg->thread_wakeup_needed) - break; - schedule(); - } - __set_current_state(TASK_RUNNING); - fsg->thread_wakeup_needed = 0; - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static int do_read(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - u32 lba; - struct fsg_buffhd *bh; - int rc; - u32 amount_left; - loff_t file_offset, file_offset_tmp; - unsigned int amount; - unsigned int partial_page; - ssize_t nread; - - /* Get the starting Logical Block Address and check that it's - * not too big */ - if (fsg->cmnd[0] == SC_READ_6) - lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); - else { - lba = get_be32(&fsg->cmnd[2]); - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) and FUA (Force Unit Access = don't read from the - * cache), but we don't implement them. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - file_offset = ((loff_t) lba) << 9; - - /* Carry out the file reads */ - amount_left = fsg->data_size_from_cmnd; - if (unlikely(amount_left == 0)) - return -EIO; // No default reply - - for (;;) { - - /* Figure out how much we need to read: - * Try to read the remaining amount. - * But don't read more than the buffer size. - * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ - amount = min((unsigned int) amount_left, mod_data.buflen); - amount = min((loff_t) amount, - curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - - partial_page); - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* If we were asked to read past the end of file, - * end with an empty buffer. */ - if (amount == 0) { - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - bh->inreq->length = 0; - bh->state = BUF_STATE_FULL; - break; - } - - /* Perform the read */ - file_offset_tmp = file_offset; - nread = vfs_read(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nread); - if (signal_pending(current)) - return -EINTR; - - if (nread < 0) { - LDBG(curlun, "error in file read: %d\n", - (int) nread); - nread = 0; - } else if (nread < amount) { - LDBG(curlun, "partial file read: %d/%u\n", - (int) nread, amount); - nread -= (nread & 511); // Round down to a block - } - file_offset += nread; - amount_left -= nread; - fsg->residue -= nread; - bh->inreq->length = nread; - bh->state = BUF_STATE_FULL; - - /* If an error occurred, report it and its position */ - if (nread < amount) { - curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - break; - } - - if (amount_left == 0) - break; // No more left to read - - /* Send this buffer and go read some more */ - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - - return -EIO; // No default reply -} - - -/*-------------------------------------------------------------------------*/ - -static int do_write(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - u32 lba; - struct fsg_buffhd *bh; - int get_some_more; - u32 amount_left_to_req, amount_left_to_write; - loff_t usb_offset, file_offset, file_offset_tmp; - unsigned int amount; - unsigned int partial_page; - ssize_t nwritten; - int rc; - - if (curlun->ro) { - curlun->sense_data = SS_WRITE_PROTECTED; - return -EINVAL; - } - spin_lock(&curlun->filp->f_lock); - curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait - spin_unlock(&curlun->filp->f_lock); - - /* Get the starting Logical Block Address and check that it's - * not too big */ - if (fsg->cmnd[0] == SC_WRITE_6) - lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); - else { - lba = get_be32(&fsg->cmnd[2]); - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) and FUA (Force Unit Access = write directly to the - * medium). We don't implement DPO; we implement FUA by - * performing synchronous output. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - if (fsg->cmnd[1] & 0x08) { // FUA - spin_lock(&curlun->filp->f_lock); - curlun->filp->f_flags |= O_SYNC; - spin_unlock(&curlun->filp->f_lock); - } - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - /* Carry out the file writes */ - get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; - amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; - - while (amount_left_to_write > 0) { - - /* Queue a request for more data from the host */ - bh = fsg->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && get_some_more) { - - /* Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. */ - amount = min(amount_left_to_req, mod_data.buflen); - amount = min((loff_t) amount, curlun->file_length - - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int) PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { - get_some_more = 0; - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; - curlun->info_valid = 1; - continue; - } - amount -= (amount & 511); - if (amount == 0) { - - /* Why were we were asked to transfer a - * partial block? */ - get_some_more = 0; - continue; - } - - /* Get the next buffer */ - usb_offset += amount; - fsg->usb_amount_left -= amount; - amount_left_to_req -= amount; - if (amount_left_to_req == 0) - get_some_more = 0; - - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - continue; - } - - /* Write the received data to the backing file */ - bh = fsg->next_buffhd_to_drain; - if (bh->state == BUF_STATE_EMPTY && !get_some_more) - break; // We stopped early - if (bh->state == BUF_STATE_FULL) { - smp_rmb(); - fsg->next_buffhd_to_drain = bh->next; - bh->state = BUF_STATE_EMPTY; - - /* Did something go wrong with the transfer? */ - if (bh->outreq->status != 0) { - curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - break; - } - - amount = bh->outreq->actual; - if (curlun->file_length - file_offset < amount) { - LERROR(curlun, - "write %u @ %llu beyond end %llu\n", - amount, (unsigned long long) file_offset, - (unsigned long long) curlun->file_length); - amount = curlun->file_length - file_offset; - } - - /* Perform the write */ - file_offset_tmp = file_offset; - nwritten = vfs_write(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nwritten); - if (signal_pending(current)) - return -EINTR; // Interrupted! - - if (nwritten < 0) { - LDBG(curlun, "error in file write: %d\n", - (int) nwritten); - nwritten = 0; - } else if (nwritten < amount) { - LDBG(curlun, "partial file write: %d/%u\n", - (int) nwritten, amount); - nwritten -= (nwritten & 511); - // Round down to a block - } - file_offset += nwritten; - amount_left_to_write -= nwritten; - fsg->residue -= nwritten; - - /* If an error occurred, report it and its position */ - if (nwritten < amount) { - curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - break; - } - - /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { - fsg->short_packet_received = 1; - break; - } - continue; - } - - /* Wait for something to happen */ - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - return -EIO; // No default reply -} - - -/*-------------------------------------------------------------------------*/ - -/* Sync the file data, don't bother with the metadata. - * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsync_sub(struct lun *curlun) -{ - struct file *filp = curlun->filp; - - if (curlun->ro || !filp) - return 0; - return vfs_fsync(filp, filp->f_path.dentry, 1); -} - -static void fsync_all(struct fsg_dev *fsg) -{ - int i; - - for (i = 0; i < fsg->nluns; ++i) - fsync_sub(&fsg->luns[i]); -} - -static int do_synchronize_cache(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - int rc; - - /* We ignore the requested LBA and write out all file's - * dirty data buffers. */ - rc = fsync_sub(curlun); - if (rc) - curlun->sense_data = SS_WRITE_ERROR; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static void invalidate_sub(struct lun *curlun) -{ - struct file *filp = curlun->filp; - struct inode *inode = filp->f_path.dentry->d_inode; - unsigned long rc; - - rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); - VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); -} - -static int do_verify(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - u32 lba; - u32 verification_length; - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - loff_t file_offset, file_offset_tmp; - u32 amount_left; - unsigned int amount; - ssize_t nread; - - /* Get the starting Logical Block Address and check that it's - * not too big */ - lba = get_be32(&fsg->cmnd[2]); - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - /* We allow DPO (Disable Page Out = don't save data in the - * cache) but we don't implement it. */ - if ((fsg->cmnd[1] & ~0x10) != 0) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - verification_length = get_be16(&fsg->cmnd[7]); - if (unlikely(verification_length == 0)) - return -EIO; // No default reply - - /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; - - /* Write out all the dirty buffers before invalidating them */ - fsync_sub(curlun); - if (signal_pending(current)) - return -EINTR; - - invalidate_sub(curlun); - if (signal_pending(current)) - return -EINTR; - - /* Just try to read the requested blocks */ - while (amount_left > 0) { - - /* Figure out how much we need to read: - * Try to read the remaining amount, but not more than - * the buffer size. - * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ - amount = min((unsigned int) amount_left, mod_data.buflen); - amount = min((loff_t) amount, - curlun->file_length - file_offset); - if (amount == 0) { - curlun->sense_data = - SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - break; - } - - /* Perform the read */ - file_offset_tmp = file_offset; - nread = vfs_read(curlun->filp, - (char __user *) bh->buf, - amount, &file_offset_tmp); - VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, - (unsigned long long) file_offset, - (int) nread); - if (signal_pending(current)) - return -EINTR; - - if (nread < 0) { - LDBG(curlun, "error in file verify: %d\n", - (int) nread); - nread = 0; - } else if (nread < amount) { - LDBG(curlun, "partial file verify: %d/%u\n", - (int) nread, amount); - nread -= (nread & 511); // Round down to a sector - } - if (nread == 0) { - curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; - curlun->info_valid = 1; - break; - } - file_offset += nread; - amount_left -= nread; - } - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - u8 *buf = (u8 *) bh->buf; - - static char vendor_id[] = "Linux "; - static char product_disk_id[] = "File-Stor Gadget"; - static char product_cdrom_id[] = "File-CD Gadget "; - - if (!fsg->curlun) { // Unsupported LUNs are okay - fsg->bad_lun_okay = 1; - memset(buf, 0, 36); - buf[0] = 0x7f; // Unsupported, no device-type - buf[4] = 31; // Additional length - return 36; - } - - memset(buf, 0, 8); - buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); - if (mod_data.removable) - buf[1] = 0x80; - buf[2] = 2; // ANSI SCSI level 2 - buf[3] = 2; // SCSI-2 INQUIRY data format - buf[4] = 31; // Additional length - // No special options - sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, - (mod_data.cdrom ? product_cdrom_id : - product_disk_id), - mod_data.release); - return 36; -} - - -static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - u8 *buf = (u8 *) bh->buf; - u32 sd, sdinfo; - int valid; - - /* - * From the SCSI-2 spec., section 7.9 (Unit attention condition): - * - * If a REQUEST SENSE command is received from an initiator - * with a pending unit attention condition (before the target - * generates the contingent allegiance condition), then the - * target shall either: - * a) report any pending sense data and preserve the unit - * attention condition on the logical unit, or, - * b) report the unit attention condition, may discard any - * pending sense data, and clear the unit attention - * condition on the logical unit for that initiator. - * - * FSG normally uses option a); enable this code to use option b). - */ -#if 0 - if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { - curlun->sense_data = curlun->unit_attention_data; - curlun->unit_attention_data = SS_NO_SENSE; - } -#endif - - if (!curlun) { // Unsupported LUNs are okay - fsg->bad_lun_okay = 1; - sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - sdinfo = 0; - valid = 0; - } else { - sd = curlun->sense_data; - sdinfo = curlun->sense_data_info; - valid = curlun->info_valid << 7; - curlun->sense_data = SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - - memset(buf, 0, 18); - buf[0] = valid | 0x70; // Valid, current error - buf[2] = SK(sd); - put_be32(&buf[3], sdinfo); // Sense information - buf[7] = 18 - 8; // Additional sense length - buf[12] = ASC(sd); - buf[13] = ASCQ(sd); - return 18; -} - - -static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - u32 lba = get_be32(&fsg->cmnd[2]); - int pmi = fsg->cmnd[8]; - u8 *buf = (u8 *) bh->buf; - - /* Check the PMI and LBA fields */ - if (pmi > 1 || (pmi == 0 && lba != 0)) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - put_be32(&buf[0], curlun->num_sectors - 1); // Max logical block - put_be32(&buf[4], 512); // Block length - return 8; -} - - -static void store_cdrom_address(u8 *dest, int msf, u32 addr) -{ - if (msf) { - /* Convert to Minutes-Seconds-Frames */ - addr >>= 2; /* Convert to 2048-byte frames */ - addr += 2*75; /* Lead-in occupies 2 seconds */ - dest[3] = addr % 75; /* Frames */ - addr /= 75; - dest[2] = addr % 60; /* Seconds */ - addr /= 60; - dest[1] = addr; /* Minutes */ - dest[0] = 0; /* Reserved */ - } else { - /* Absolute sector */ - put_be32(dest, addr); - } -} - -static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - u32 lba = get_be32(&fsg->cmnd[2]); - u8 *buf = (u8 *) bh->buf; - - if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - if (lba >= curlun->num_sectors) { - curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - return -EINVAL; - } - - memset(buf, 0, 8); - buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ - store_cdrom_address(&buf[4], msf, lba); - return 8; -} - - -static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - int start_track = fsg->cmnd[6]; - u8 *buf = (u8 *) bh->buf; - - if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ - start_track > 1) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - memset(buf, 0, 20); - buf[1] = (20-2); /* TOC data length */ - buf[2] = 1; /* First track number */ - buf[3] = 1; /* Last track number */ - buf[5] = 0x16; /* Data track, copying allowed */ - buf[6] = 0x01; /* Only track is number 1 */ - store_cdrom_address(&buf[8], msf, 0); - - buf[13] = 0x16; /* Lead-out track is data */ - buf[14] = 0xAA; /* Lead-out track number */ - store_cdrom_address(&buf[16], msf, curlun->num_sectors); - return 20; -} - - -static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - int mscmnd = fsg->cmnd[0]; - u8 *buf = (u8 *) bh->buf; - u8 *buf0 = buf; - int pc, page_code; - int changeable_values, all_pages; - int valid_page = 0; - int len, limit; - - if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - pc = fsg->cmnd[2] >> 6; - page_code = fsg->cmnd[2] & 0x3f; - if (pc == 3) { - curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; - return -EINVAL; - } - changeable_values = (pc == 1); - all_pages = (page_code == 0x3f); - - /* Write the mode parameter header. Fixed values are: default - * medium type, no cache control (DPOFUA), and no block descriptors. - * The only variable value is the WriteProtect bit. We will fill in - * the mode data length later. */ - memset(buf, 0, 8); - if (mscmnd == SC_MODE_SENSE_6) { - buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA - buf += 4; - limit = 255; - } else { // SC_MODE_SENSE_10 - buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA - buf += 8; - limit = 65535; // Should really be mod_data.buflen - } - - /* No block descriptors */ - - /* The mode pages, in numerical order. The only page we support - * is the Caching page. */ - if (page_code == 0x08 || all_pages) { - valid_page = 1; - buf[0] = 0x08; // Page code - buf[1] = 10; // Page length - memset(buf+2, 0, 10); // None of the fields are changeable - - if (!changeable_values) { - buf[2] = 0x04; // Write cache enable, - // Read cache not disabled - // No cache retention priorities - put_be16(&buf[4], 0xffff); // Don't disable prefetch - // Minimum prefetch = 0 - put_be16(&buf[8], 0xffff); // Maximum prefetch - put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling - } - buf += 12; - } - - /* Check that a valid page was requested and the mode data length - * isn't too long. */ - len = buf - buf0; - if (!valid_page || len > limit) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - /* Store the mode data length */ - if (mscmnd == SC_MODE_SENSE_6) - buf0[0] = len - 1; - else - put_be16(buf0, len - 2); - return len; -} - - -static int do_start_stop(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - int loej, start; - - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; - } - - // int immed = fsg->cmnd[1] & 0x01; - loej = fsg->cmnd[4] & 0x02; - start = fsg->cmnd[4] & 0x01; - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed - (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - if (!start) { - - /* Are we allowed to unload the media? */ - if (curlun->prevent_medium_removal) { - LDBG(curlun, "unload attempt prevented\n"); - curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; - return -EINVAL; - } - if (loej) { // Simulate an unload/eject - up_read(&fsg->filesem); - down_write(&fsg->filesem); - close_backing_file(curlun); - up_write(&fsg->filesem); - down_read(&fsg->filesem); - } - } else { - - /* Our emulation doesn't support mounting; the medium is - * available for use as soon as it is loaded. */ - if (!backing_file_is_open(curlun)) { - curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - return -EINVAL; - } - } -#endif - return 0; -} - - -static int do_prevent_allow(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - int prevent; - - if (!mod_data.removable) { - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; - } - - prevent = fsg->cmnd[4] & 0x01; - if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - - if (curlun->prevent_medium_removal && !prevent) - fsync_sub(curlun); - curlun->prevent_medium_removal = prevent; - return 0; -} - - -static int do_read_format_capacities(struct fsg_dev *fsg, - struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - u8 *buf = (u8 *) bh->buf; - - buf[0] = buf[1] = buf[2] = 0; - buf[3] = 8; // Only the Current/Maximum Capacity Descriptor - buf += 4; - - put_be32(&buf[0], curlun->num_sectors); // Number of blocks - put_be32(&buf[4], 512); // Block length - buf[4] = 0x02; // Current capacity - return 12; -} - - -static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - - /* We don't support MODE SELECT */ - curlun->sense_data = SS_INVALID_COMMAND; - return -EINVAL; -} - - -/*-------------------------------------------------------------------------*/ - -static int halt_bulk_in_endpoint(struct fsg_dev *fsg) -{ - int rc; - - rc = fsg_set_halt(fsg, fsg->bulk_in); - if (rc == -EAGAIN) - VDBG(fsg, "delayed bulk-in endpoint halt\n"); - while (rc != 0) { - if (rc != -EAGAIN) { - WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); - rc = 0; - break; - } - - /* Wait for a short time and then try again */ - if (msleep_interruptible(100) != 0) - return -EINTR; - rc = usb_ep_set_halt(fsg->bulk_in); - } - return rc; -} - -static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) -{ - int rc; - - DBG(fsg, "bulk-in set wedge\n"); - rc = usb_ep_set_wedge(fsg->bulk_in); - if (rc == -EAGAIN) - VDBG(fsg, "delayed bulk-in endpoint wedge\n"); - while (rc != 0) { - if (rc != -EAGAIN) { - WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); - rc = 0; - break; - } - - /* Wait for a short time and then try again */ - if (msleep_interruptible(100) != 0) - return -EINTR; - rc = usb_ep_set_wedge(fsg->bulk_in); - } - return rc; -} - -static int pad_with_zeros(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - u32 nkeep = bh->inreq->length; - u32 nsend; - int rc; - - bh->state = BUF_STATE_EMPTY; // For the first iteration - fsg->usb_amount_left = nkeep + fsg->residue; - while (fsg->usb_amount_left > 0) { - - /* Wait for the next buffer to be free */ - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen); - memset(bh->buf + nkeep, 0, nsend - nkeep); - bh->inreq->length = nsend; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - bh = fsg->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= nsend; - nkeep = 0; - } - return 0; -} - -static int throw_away_data(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - u32 amount; - int rc; - - while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || - fsg->usb_amount_left > 0) { - - /* Throw away the data in a filled buffer */ - if (bh->state == BUF_STATE_FULL) { - smp_rmb(); - bh->state = BUF_STATE_EMPTY; - fsg->next_buffhd_to_drain = bh->next; - - /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || - bh->outreq->status != 0) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - return -EINTR; - } - continue; - } - - /* Try to submit another request if we need one */ - bh = fsg->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { - amount = min(fsg->usb_amount_left, - (u32) mod_data.buflen); - - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= amount; - continue; - } - - /* Otherwise wait for something to happen */ - rc = sleep_thread(fsg); - if (rc) - return rc; - } - return 0; -} - - -static int finish_reply(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; - int rc = 0; - - switch (fsg->data_dir) { - case DATA_DIR_NONE: - break; // Nothing to send - - /* If we don't know whether the host wants to read or write, - * this must be CB or CBI with an unknown command. We mustn't - * try to send or receive any data. So stall both bulk pipes - * if we can and wait for a reset. */ - case DATA_DIR_UNKNOWN: - if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - rc = halt_bulk_in_endpoint(fsg); - } - break; - - /* All but the last buffer of data must have already been sent */ - case DATA_DIR_TO_HOST: - if (fsg->data_size == 0) - ; // Nothing to send - - /* If there's no residue, simply send the last buffer */ - else if (fsg->residue == 0) { - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - - /* There is a residue. For CB and CBI, simply mark the end - * of the data with a short packet. However, if we are - * allowed to stall, there was no data at all (residue == - * data_size), and the command failed (invalid LUN or - * sense data is set), then halt the bulk-in endpoint - * instead. */ - else if (!transport_is_bbb()) { - if (mod_data.can_stall && - fsg->residue == fsg->data_size && - (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { - bh->state = BUF_STATE_EMPTY; - rc = halt_bulk_in_endpoint(fsg); - } else { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - } - } - - /* For Bulk-only, if we're allowed to stall then send the - * short packet and halt the bulk-in endpoint. If we can't - * stall, pad out the remaining data with 0's. */ - else { - if (mod_data.can_stall) { - bh->inreq->zero = 1; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - rc = halt_bulk_in_endpoint(fsg); - } else - rc = pad_with_zeros(fsg); - } - break; - - /* We have processed all we want from the data the host has sent. - * There may still be outstanding bulk-out requests. */ - case DATA_DIR_FROM_HOST: - if (fsg->residue == 0) - ; // Nothing to receive - - /* Did the host stop sending unexpectedly early? */ - else if (fsg->short_packet_received) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - rc = -EINTR; - } - - /* We haven't processed all the incoming data. Even though - * we may be allowed to stall, doing so would cause a race. - * The controller may already have ACK'ed all the remaining - * bulk-out packets, in which case the host wouldn't see a - * STALL. Not realizing the endpoint was halted, it wouldn't - * clear the halt -- leading to problems later on. */ -#if 0 - else if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - rc = -EINTR; - } -#endif - - /* We can't stall. Read in the excess data and throw it - * all away. */ - else - rc = throw_away_data(fsg); - break; - } - return rc; -} - - -static int send_status(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; - struct fsg_buffhd *bh; - int rc; - u8 status = USB_STATUS_PASS; - u32 sd, sdinfo = 0; - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - if (curlun) { - sd = curlun->sense_data; - sdinfo = curlun->sense_data_info; - } else if (fsg->bad_lun_okay) - sd = SS_NO_SENSE; - else - sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - - if (fsg->phase_error) { - DBG(fsg, "sending phase-error status\n"); - status = USB_STATUS_PHASE_ERROR; - sd = SS_INVALID_COMMAND; - } else if (sd != SS_NO_SENSE) { - DBG(fsg, "sending command-failure status\n"); - status = USB_STATUS_FAIL; - VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" - " info x%x\n", - SK(sd), ASC(sd), ASCQ(sd), sdinfo); - } - - if (transport_is_bbb()) { - struct bulk_cs_wrap *csw = bh->buf; - - /* Store and send the Bulk-only CSW */ - csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); - csw->Tag = fsg->tag; - csw->Residue = cpu_to_le32(fsg->residue); - csw->Status = status; - - bh->inreq->length = USB_BULK_CS_WRAP_LEN; - bh->inreq->zero = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - - } else if (mod_data.transport_type == USB_PR_CB) { - - /* Control-Bulk transport has no status phase! */ - return 0; - - } else { // USB_PR_CBI - struct interrupt_data *buf = bh->buf; - - /* Store and send the Interrupt data. UFI sends the ASC - * and ASCQ bytes. Everything else sends a Type (which - * is always 0) and the status Value. */ - if (mod_data.protocol_type == USB_SC_UFI) { - buf->bType = ASC(sd); - buf->bValue = ASCQ(sd); - } else { - buf->bType = 0; - buf->bValue = status; - } - fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; - - fsg->intr_buffhd = bh; // Point to the right buffhd - fsg->intreq->buf = bh->inreq->buf; - fsg->intreq->context = bh; - start_transfer(fsg, fsg->intr_in, fsg->intreq, - &fsg->intreq_busy, &bh->state); - } - - fsg->next_buffhd_to_fill = bh->next; - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -/* Check whether the command is properly formed and whether its data size - * and direction agree with the values we already have. */ -static int check_command(struct fsg_dev *fsg, int cmnd_size, - enum data_direction data_dir, unsigned int mask, - int needs_medium, const char *name) -{ - int i; - int lun = fsg->cmnd[1] >> 5; - static const char dirletter[4] = {'u', 'o', 'i', 'n'}; - char hdlen[20]; - struct lun *curlun; - - /* Adjust the expected cmnd_size for protocol encapsulation padding. - * Transparent SCSI doesn't pad. */ - if (protocol_is_scsi()) - ; - - /* There's some disagreement as to whether RBC pads commands or not. - * We'll play it safe and accept either form. */ - else if (mod_data.protocol_type == USB_SC_RBC) { - if (fsg->cmnd_size == 12) - cmnd_size = 12; - - /* All the other protocols pad to 12 bytes */ - } else - cmnd_size = 12; - - hdlen[0] = 0; - if (fsg->data_dir != DATA_DIR_UNKNOWN) - sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], - fsg->data_size); - VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", - name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); - - /* We can't reply at all until we know the correct data direction - * and size. */ - if (fsg->data_size_from_cmnd == 0) - data_dir = DATA_DIR_NONE; - if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI - fsg->data_dir = data_dir; - fsg->data_size = fsg->data_size_from_cmnd; - - } else { // Bulk-only - if (fsg->data_size < fsg->data_size_from_cmnd) { - - /* Host data size < Device data size is a phase error. - * Carry out the command, but only transfer as much - * as we are allowed. */ - fsg->data_size_from_cmnd = fsg->data_size; - fsg->phase_error = 1; - } - } - fsg->residue = fsg->usb_amount_left = fsg->data_size; - - /* Conflicting data directions is a phase error */ - if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { - fsg->phase_error = 1; - return -EINVAL; - } - - /* Verify the length of the command itself */ - if (cmnd_size != fsg->cmnd_size) { - - /* Special case workaround: There are plenty of buggy SCSI - * implementations. Many have issues with cbw->Length - * field passing a wrong command size. For those cases we - * always try to work around the problem by using the length - * sent by the host side provided it is at least as large - * as the correct command length. - * Examples of such cases would be MS-Windows, which issues - * REQUEST SENSE with cbw->Length == 12 where it should - * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and - * REQUEST SENSE with cbw->Length == 10 where it should - * be 6 as well. - */ - if (cmnd_size <= fsg->cmnd_size) { - DBG(fsg, "%s is buggy! Expected length %d " - "but we got %d\n", name, - cmnd_size, fsg->cmnd_size); - cmnd_size = fsg->cmnd_size; - } else { - fsg->phase_error = 1; - return -EINVAL; - } - } - - /* Check that the LUN values are consistent */ - if (transport_is_bbb()) { - if (fsg->lun != lun) - DBG(fsg, "using LUN %d from CBW, " - "not LUN %d from CDB\n", - fsg->lun, lun); - } else - fsg->lun = lun; // Use LUN from the command - - /* Check the LUN */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { - fsg->curlun = curlun = &fsg->luns[fsg->lun]; - if (fsg->cmnd[0] != SC_REQUEST_SENSE) { - curlun->sense_data = SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - } else { - fsg->curlun = curlun = NULL; - fsg->bad_lun_okay = 0; - - /* INQUIRY and REQUEST SENSE commands are explicitly allowed - * to use unsupported LUNs; all others may not. */ - if (fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { - DBG(fsg, "unsupported LUN %d\n", fsg->lun); - return -EINVAL; - } - } - - /* If a unit attention condition exists, only INQUIRY and - * REQUEST SENSE commands are allowed; anything else must fail. */ - if (curlun && curlun->unit_attention_data != SS_NO_SENSE && - fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { - curlun->sense_data = curlun->unit_attention_data; - curlun->unit_attention_data = SS_NO_SENSE; - return -EINVAL; - } - - /* Check that only command bytes listed in the mask are non-zero */ - fsg->cmnd[1] &= 0x1f; // Mask away the LUN - for (i = 1; i < cmnd_size; ++i) { - if (fsg->cmnd[i] && !(mask & (1 << i))) { - if (curlun) - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; - } - } - - /* If the medium isn't mounted and the command needs to access - * it, return an error. */ - if (curlun && !backing_file_is_open(curlun) && needs_medium) { - curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - return -EINVAL; - } - - return 0; -} - - -static int do_scsi_command(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - int rc; - int reply = -EINVAL; - int i; - static char unknown[16]; - - dump_cdb(fsg); - - /* Wait for the next buffer to become available for data or status */ - bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - fsg->phase_error = 0; - fsg->short_packet_received = 0; - - down_read(&fsg->filesem); // We're using the backing file - switch (fsg->cmnd[0]) { - - case SC_INQUIRY: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "INQUIRY")) == 0) - reply = do_inquiry(fsg, bh); - break; - - case SC_MODE_SELECT_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (1<<1) | (1<<4), 0, - "MODE SELECT(6)")) == 0) - reply = do_mode_select(fsg, bh); - break; - - case SC_MODE_SELECT_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (3<<7), 0, - "MODE SELECT(10)")) == 0) - reply = do_mode_select(fsg, bh); - break; - - case SC_MODE_SENSE_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (1<<4), 0, - "MODE SENSE(6)")) == 0) - reply = do_mode_sense(fsg, bh); - break; - - case SC_MODE_SENSE_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (3<<7), 0, - "MODE SENSE(10)")) == 0) - reply = do_mode_sense(fsg, bh); - break; - - case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<4), 0, - "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) - reply = do_prevent_allow(fsg); - break; - - case SC_READ_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (7<<1) | (1<<4), 1, - "READ(6)")) == 0) - reply = do_read(fsg); - break; - - case SC_READ_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "READ(10)")) == 0) - reply = do_read(fsg); - break; - - case SC_READ_12: - fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "READ(12)")) == 0) - reply = do_read(fsg); - break; - - case SC_READ_CAPACITY: - fsg->data_size_from_cmnd = 8; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (0xf<<2) | (1<<8), 1, - "READ CAPACITY")) == 0) - reply = do_read_capacity(fsg, bh); - break; - - case SC_READ_HEADER: - if (!mod_data.cdrom) - goto unknown_cmnd; - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7) | (0x1f<<1), 1, - "READ HEADER")) == 0) - reply = do_read_header(fsg, bh); - break; - - case SC_READ_TOC: - if (!mod_data.cdrom) - goto unknown_cmnd; - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, - "READ TOC")) == 0) - reply = do_read_toc(fsg, bh); - break; - - case SC_READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7), 1, - "READ FORMAT CAPACITIES")) == 0) - reply = do_read_format_capacities(fsg, bh); - break; - - case SC_REQUEST_SENSE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "REQUEST SENSE")) == 0) - reply = do_request_sense(fsg, bh); - break; - - case SC_START_STOP_UNIT: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<1) | (1<<4), 0, - "START-STOP UNIT")) == 0) - reply = do_start_stop(fsg); - break; - - case SC_SYNCHRONIZE_CACHE: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (0xf<<2) | (3<<7), 1, - "SYNCHRONIZE CACHE")) == 0) - reply = do_synchronize_cache(fsg); - break; - - case SC_TEST_UNIT_READY: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, - 0, 1, - "TEST UNIT READY"); - break; - - /* Although optional, this command is used by MS-Windows. We - * support a minimal version: BytChk must be 0. */ - case SC_VERIFY: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (1<<1) | (0xf<<2) | (3<<7), 1, - "VERIFY")) == 0) - reply = do_verify(fsg); - break; - - case SC_WRITE_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (7<<1) | (1<<4), 1, - "WRITE(6)")) == 0) - reply = do_write(fsg); - break; - - case SC_WRITE_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "WRITE(10)")) == 0) - reply = do_write(fsg); - break; - - case SC_WRITE_12: - fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "WRITE(12)")) == 0) - reply = do_write(fsg); - break; - - /* Some mandatory commands that we recognize but don't implement. - * They don't mean much in this setting. It's left as an exercise - * for anyone interested to implement RESERVE and RELEASE in terms - * of Posix locks. */ - case SC_FORMAT_UNIT: - case SC_RELEASE: - case SC_RESERVE: - case SC_SEND_DIAGNOSTIC: - // Fall through - - default: - unknown_cmnd: - fsg->data_size_from_cmnd = 0; - sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); - if ((reply = check_command(fsg, fsg->cmnd_size, - DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { - fsg->curlun->sense_data = SS_INVALID_COMMAND; - reply = -EINVAL; - } - break; - } - up_read(&fsg->filesem); - - if (reply == -EINTR || signal_pending(current)) - return -EINTR; - - /* Set up the single reply buffer for finish_reply() */ - if (reply == -EINVAL) - reply = 0; // Error reply length - if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32) reply, fsg->data_size_from_cmnd); - bh->inreq->length = reply; - bh->state = BUF_STATE_FULL; - fsg->residue -= reply; - } // Otherwise it's already set - - return 0; -} - - -/*-------------------------------------------------------------------------*/ - -static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct usb_request *req = bh->outreq; - struct bulk_cb_wrap *cbw = req->buf; - - /* Was this a real packet? Should it be ignored? */ - if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) - return -EINVAL; - - /* Is the CBW valid? */ - if (req->actual != USB_BULK_CB_WRAP_LEN || - cbw->Signature != cpu_to_le32( - USB_BULK_CB_SIG)) { - DBG(fsg, "invalid CBW: len %u sig 0x%x\n", - req->actual, - le32_to_cpu(cbw->Signature)); - - /* The Bulk-only spec says we MUST stall the IN endpoint - * (6.6.1), so it's unavoidable. It also says we must - * retain this state until the next reset, but there's - * no way to tell the controller driver it should ignore - * Clear-Feature(HALT) requests. - * - * We aren't required to halt the OUT endpoint; instead - * we can simply accept and discard any data received - * until the next reset. */ - wedge_bulk_in_endpoint(fsg); - set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - return -EINVAL; - } - - /* Is the CBW meaningful? */ - if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || - cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { - DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " - "cmdlen %u\n", - cbw->Lun, cbw->Flags, cbw->Length); - - /* We can do anything we want here, so let's stall the - * bulk pipes if we are allowed to. */ - if (mod_data.can_stall) { - fsg_set_halt(fsg, fsg->bulk_out); - halt_bulk_in_endpoint(fsg); - } - return -EINVAL; - } - - /* Save the command for later */ - fsg->cmnd_size = cbw->Length; - memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); - if (cbw->Flags & USB_BULK_IN_FLAG) - fsg->data_dir = DATA_DIR_TO_HOST; - else - fsg->data_dir = DATA_DIR_FROM_HOST; - fsg->data_size = le32_to_cpu(cbw->DataTransferLength); - if (fsg->data_size == 0) - fsg->data_dir = DATA_DIR_NONE; - fsg->lun = cbw->Lun; - fsg->tag = cbw->Tag; - return 0; -} - - -static int get_next_command(struct fsg_dev *fsg) -{ - struct fsg_buffhd *bh; - int rc = 0; - - if (transport_is_bbb()) { - - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - - /* We will drain the buffer in software, which means we - * can reuse it for the next filling. No need to advance - * next_buffhd_to_fill. */ - - /* Wait for the CBW to arrive */ - while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - smp_rmb(); - rc = received_cbw(fsg, bh); - bh->state = BUF_STATE_EMPTY; - - } else { // USB_PR_CB or USB_PR_CBI - - /* Wait for the next command to arrive */ - while (fsg->cbbuf_cmnd_size == 0) { - rc = sleep_thread(fsg); - if (rc) - return rc; - } - - /* Is the previous status interrupt request still busy? - * The host is allowed to skip reading the status, - * so we must cancel it. */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); - - /* Copy the command and mark the buffer empty */ - fsg->data_dir = DATA_DIR_UNKNOWN; - spin_lock_irq(&fsg->lock); - fsg->cmnd_size = fsg->cbbuf_cmnd_size; - memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); - fsg->cbbuf_cmnd_size = 0; - spin_unlock_irq(&fsg->lock); - } - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, - const struct usb_endpoint_descriptor *d) -{ - int rc; - - ep->driver_data = fsg; - rc = usb_ep_enable(ep, d); - if (rc) - ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); - return rc; -} - -static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, - struct usb_request **preq) -{ - *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (*preq) - return 0; - ERROR(fsg, "can't allocate request for %s\n", ep->name); - return -ENOMEM; -} - -/* - * Reset interface setting and re-init endpoint state (toggle etc). - * Call with altsetting < 0 to disable the interface. The only other - * available altsetting is 0, which enables the interface. - */ -static int do_set_interface(struct fsg_dev *fsg, int altsetting) -{ - int rc = 0; - int i; - const struct usb_endpoint_descriptor *d; - - if (fsg->running) - DBG(fsg, "reset interface\n"); - -reset: - /* Deallocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if (bh->inreq) { - usb_ep_free_request(fsg->bulk_in, bh->inreq); - bh->inreq = NULL; - } - if (bh->outreq) { - usb_ep_free_request(fsg->bulk_out, bh->outreq); - bh->outreq = NULL; - } - } - if (fsg->intreq) { - usb_ep_free_request(fsg->intr_in, fsg->intreq); - fsg->intreq = NULL; - } - - /* Disable the endpoints */ - if (fsg->bulk_in_enabled) { - usb_ep_disable(fsg->bulk_in); - fsg->bulk_in_enabled = 0; - } - if (fsg->bulk_out_enabled) { - usb_ep_disable(fsg->bulk_out); - fsg->bulk_out_enabled = 0; - } - if (fsg->intr_in_enabled) { - usb_ep_disable(fsg->intr_in); - fsg->intr_in_enabled = 0; - } - - fsg->running = 0; - if (altsetting < 0 || rc != 0) - return rc; - - DBG(fsg, "set interface %d\n", altsetting); - - /* Enable the endpoints */ - d = ep_desc(fsg->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) - goto reset; - fsg->bulk_in_enabled = 1; - - d = ep_desc(fsg->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) - goto reset; - fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); - clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - - if (transport_is_cbi()) { - d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc); - if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) - goto reset; - fsg->intr_in_enabled = 1; - } - - /* Allocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) - goto reset; - if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) - goto reset; - bh->inreq->buf = bh->outreq->buf = bh->buf; - bh->inreq->context = bh->outreq->context = bh; - bh->inreq->complete = bulk_in_complete; - bh->outreq->complete = bulk_out_complete; - } - if (transport_is_cbi()) { - if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) - goto reset; - fsg->intreq->complete = intr_in_complete; - } - - fsg->running = 1; - for (i = 0; i < fsg->nluns; ++i) - fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; - return rc; -} - - -/* - * Change our operational configuration. This code must agree with the code - * that returns config descriptors, and with interface altsetting code. - * - * It's also responsible for power management interactions. Some - * configurations might not work with our current power sources. - * For now we just assume the gadget is always self-powered. - */ -static int do_set_config(struct fsg_dev *fsg, u8 new_config) -{ - int rc = 0; - - /* Disable the single interface */ - if (fsg->config != 0) { - DBG(fsg, "reset config\n"); - fsg->config = 0; - rc = do_set_interface(fsg, -1); - } - - /* Enable the interface */ - if (new_config != 0) { - fsg->config = new_config; - if ((rc = do_set_interface(fsg, 0)) != 0) - fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } - } - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static void handle_exception(struct fsg_dev *fsg) -{ - siginfo_t info; - int sig; - int i; - int num_active; - struct fsg_buffhd *bh; - enum fsg_state old_state; - u8 new_config; - struct lun *curlun; - unsigned int exception_req_tag; - int rc; - - /* Clear the existing signals. Anything but SIGUSR1 is converted - * into a high-priority EXIT exception. */ - for (;;) { - sig = dequeue_signal_lock(current, ¤t->blocked, &info); - if (!sig) - break; - if (sig != SIGUSR1) { - if (fsg->state < FSG_STATE_EXIT) - DBG(fsg, "Main thread exiting on signal\n"); - raise_exception(fsg, FSG_STATE_EXIT); - } - } - - /* Cancel all the pending transfers */ - if (fsg->intreq_busy) - usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; - if (bh->inreq_busy) - usb_ep_dequeue(fsg->bulk_in, bh->inreq); - if (bh->outreq_busy) - usb_ep_dequeue(fsg->bulk_out, bh->outreq); - } - - /* Wait until everything is idle */ - for (;;) { - num_active = fsg->intreq_busy; - for (i = 0; i < NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; - num_active += bh->inreq_busy + bh->outreq_busy; - } - if (num_active == 0) - break; - if (sleep_thread(fsg)) - return; - } - - /* Clear out the controller's fifos */ - if (fsg->bulk_in_enabled) - usb_ep_fifo_flush(fsg->bulk_in); - if (fsg->bulk_out_enabled) - usb_ep_fifo_flush(fsg->bulk_out); - if (fsg->intr_in_enabled) - usb_ep_fifo_flush(fsg->intr_in); - - /* Reset the I/O buffer states and pointers, the SCSI - * state, and the exception. Then invoke the handler. */ - spin_lock_irq(&fsg->lock); - - for (i = 0; i < NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; - bh->state = BUF_STATE_EMPTY; - } - fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = - &fsg->buffhds[0]; - - exception_req_tag = fsg->exception_req_tag; - new_config = fsg->new_config; - old_state = fsg->state; - - if (old_state == FSG_STATE_ABORT_BULK_OUT) - fsg->state = FSG_STATE_STATUS_PHASE; - else { - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - curlun->prevent_medium_removal = 0; - curlun->sense_data = curlun->unit_attention_data = - SS_NO_SENSE; - curlun->sense_data_info = 0; - curlun->info_valid = 0; - } - fsg->state = FSG_STATE_IDLE; - } - spin_unlock_irq(&fsg->lock); - - /* Carry out any extra actions required for the exception */ - switch (old_state) { - default: - break; - - case FSG_STATE_ABORT_BULK_OUT: - send_status(fsg); - spin_lock_irq(&fsg->lock); - if (fsg->state == FSG_STATE_STATUS_PHASE) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); - break; - - case FSG_STATE_RESET: - /* In case we were forced against our will to halt a - * bulk endpoint, clear the halt now. (The SuperH UDC - * requires this.) */ - if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) - usb_ep_clear_halt(fsg->bulk_in); - - if (transport_is_bbb()) { - if (fsg->ep0_req_tag == exception_req_tag) - ep0_queue(fsg); // Complete the status stage - - } else if (transport_is_cbi()) - send_status(fsg); // Status by interrupt pipe - - /* Technically this should go here, but it would only be - * a waste of time. Ditto for the INTERFACE_CHANGE and - * CONFIG_CHANGE cases. */ - // for (i = 0; i < fsg->nluns; ++i) - // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; - break; - - case FSG_STATE_INTERFACE_CHANGE: - rc = do_set_interface(fsg, 0); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - - case FSG_STATE_CONFIG_CHANGE: - rc = do_set_config(fsg, new_config); - if (fsg->ep0_req_tag != exception_req_tag) - break; - if (rc != 0) // STALL on errors - fsg_set_halt(fsg, fsg->ep0); - else // Complete the status stage - ep0_queue(fsg); - break; - - case FSG_STATE_DISCONNECT: - fsync_all(fsg); - do_set_config(fsg, 0); // Unconfigured state - break; - - case FSG_STATE_EXIT: - case FSG_STATE_TERMINATED: - do_set_config(fsg, 0); // Free resources - spin_lock_irq(&fsg->lock); - fsg->state = FSG_STATE_TERMINATED; // Stop the thread - spin_unlock_irq(&fsg->lock); - break; - } -} - - -/*-------------------------------------------------------------------------*/ - -static int fsg_main_thread(void *fsg_) -{ - struct fsg_dev *fsg = fsg_; - - /* Allow the thread to be killed by a signal, but set the signal mask - * to block everything but INT, TERM, KILL, and USR1. */ - allow_signal(SIGINT); - allow_signal(SIGTERM); - allow_signal(SIGKILL); - allow_signal(SIGUSR1); - - /* Allow the thread to be frozen */ - set_freezable(); - - /* Arrange for userspace references to be interpreted as kernel - * pointers. That way we can pass a kernel pointer to a routine - * that expects a __user pointer and it will work okay. */ - set_fs(get_ds()); - - /* The main loop */ - while (fsg->state != FSG_STATE_TERMINATED) { - if (exception_in_progress(fsg) || signal_pending(current)) { - handle_exception(fsg); - continue; - } - - if (!fsg->running) { - sleep_thread(fsg); - continue; - } - - if (get_next_command(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_DATA_PHASE; - spin_unlock_irq(&fsg->lock); - - if (do_scsi_command(fsg) || finish_reply(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_STATUS_PHASE; - spin_unlock_irq(&fsg->lock); - - if (send_status(fsg)) - continue; - - spin_lock_irq(&fsg->lock); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irq(&fsg->lock); - } - - spin_lock_irq(&fsg->lock); - fsg->thread_task = NULL; - spin_unlock_irq(&fsg->lock); - - /* If we are exiting because of a signal, unregister the - * gadget driver. */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); - - /* Let the unbind and cleanup routines know the thread has exited */ - complete_and_exit(&fsg->thread_notifier, 0); -} - - -/*-------------------------------------------------------------------------*/ - -/* If the next two routines are called while the gadget is registered, - * the caller must own fsg->filesem for writing. */ - -static int open_backing_file(struct lun *curlun, const char *filename) -{ - int ro; - struct file *filp = NULL; - int rc = -EINVAL; - struct inode *inode = NULL; - loff_t size; - loff_t num_sectors; - loff_t min_sectors; - - /* R/W if we can, R/O if we must */ - ro = curlun->ro; - if (!ro) { - filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); - if (-EROFS == PTR_ERR(filp)) - ro = 1; - } - if (ro) - filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(filp)) { - LINFO(curlun, "unable to open backing file: %s\n", filename); - return PTR_ERR(filp); - } - - if (!(filp->f_mode & FMODE_WRITE)) - ro = 1; - - if (filp->f_path.dentry) - inode = filp->f_path.dentry->d_inode; - if (inode && S_ISBLK(inode->i_mode)) { - if (bdev_read_only(inode->i_bdev)) - ro = 1; - } else if (!inode || !S_ISREG(inode->i_mode)) { - LINFO(curlun, "invalid file type: %s\n", filename); - goto out; - } - - /* 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 || !(filp->f_op->read || filp->f_op->aio_read)) { - LINFO(curlun, "file not readable: %s\n", filename); - goto out; - } - if (!(filp->f_op->write || filp->f_op->aio_write)) - ro = 1; - - size = i_size_read(inode->i_mapping->host); - if (size < 0) { - LINFO(curlun, "unable to find file size: %s\n", filename); - rc = (int) size; - goto out; - } - num_sectors = size >> 9; // File size in 512-byte blocks - min_sectors = 1; - if (mod_data.cdrom) { - num_sectors &= ~3; // Reduce to a multiple of 2048 - min_sectors = 300*4; // Smallest track is 300 frames - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; - LINFO(curlun, "file too big: %s\n", filename); - LINFO(curlun, "using only first %d blocks\n", - (int) num_sectors); - } - } - if (num_sectors < min_sectors) { - LINFO(curlun, "file too small: %s\n", filename); - rc = -ETOOSMALL; - goto out; - } - - get_file(filp); - curlun->ro = ro; - curlun->filp = filp; - curlun->file_length = size; - curlun->num_sectors = num_sectors; - LDBG(curlun, "open backing file: %s\n", filename); - rc = 0; - -out: - filp_close(filp, current->files); - return rc; -} - - -static void close_backing_file(struct lun *curlun) -{ - if (curlun->filp) { - LDBG(curlun, "close backing file\n"); - fput(curlun->filp); - curlun->filp = NULL; - } -} - - -static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lun *curlun = dev_to_lun(dev); - - return sprintf(buf, "%d\n", curlun->ro); -} - -static ssize_t show_file(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - char *p; - ssize_t rc; - - down_read(&fsg->filesem); - if (backing_file_is_open(curlun)) { // Get the complete pathname - p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); - if (IS_ERR(p)) - rc = PTR_ERR(p); - else { - rc = strlen(p); - memmove(buf, p, rc); - buf[rc] = '\n'; // Add a newline - buf[++rc] = 0; - } - } else { // No file, return 0 bytes - *buf = 0; - rc = 0; - } - up_read(&fsg->filesem); - return rc; -} - - -static ssize_t store_ro(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - ssize_t rc = count; - struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - int i; - - if (sscanf(buf, "%d", &i) != 1) - return -EINVAL; - - /* Allow the write-enable status to change only while the backing file - * is closed. */ - down_read(&fsg->filesem); - if (backing_file_is_open(curlun)) { - LDBG(curlun, "read-only status change prevented\n"); - rc = -EBUSY; - } else { - curlun->ro = !!i; - LDBG(curlun, "read-only status set to %d\n", curlun->ro); - } - up_read(&fsg->filesem); - return rc; -} - -static ssize_t store_file(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - int rc = 0; - - if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) { - LDBG(curlun, "eject attempt prevented\n"); - return -EBUSY; // "Door is locked" - } - - /* Remove a trailing newline */ - if (count > 0 && buf[count-1] == '\n') - ((char *) buf)[count-1] = 0; // Ugh! - - /* Eject current medium */ - down_write(&fsg->filesem); - if (backing_file_is_open(curlun)) { - close_backing_file(curlun); - curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; - } - - /* Load new medium */ - if (count > 0 && buf[0]) { - rc = open_backing_file(curlun, buf); - if (rc == 0) - curlun->unit_attention_data = - SS_NOT_READY_TO_READY_TRANSITION; - } - up_write(&fsg->filesem); - return (rc < 0 ? rc : count); -} - - -/* The write permissions and store_xxx pointers are set in fsg_bind() */ -static DEVICE_ATTR(ro, 0444, show_ro, NULL); -static DEVICE_ATTR(file, 0444, show_file, NULL); - - -/*-------------------------------------------------------------------------*/ - -static void fsg_release(struct kref *ref) -{ - struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); - - kfree(fsg->luns); - kfree(fsg); -} - -static void lun_release(struct device *dev) -{ - struct fsg_dev *fsg = dev_get_drvdata(dev); - - kref_put(&fsg->ref, fsg_release); -} - -static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - int i; - struct lun *curlun; - struct usb_request *req = fsg->ep0req; - - DBG(fsg, "unbind\n"); - clear_bit(REGISTERED, &fsg->atomic_bitflags); - - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_ro); - device_remove_file(&curlun->dev, &dev_attr_file); - close_backing_file(curlun); - device_unregister(&curlun->dev); - curlun->registered = 0; - } - } - - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - - /* Free the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) - kfree(fsg->buffhds[i].buf); - - /* Free the request and buffer for endpoint 0 */ - if (req) { - kfree(req->buf); - usb_ep_free_request(fsg->ep0, req); - } - - set_gadget_data(gadget, NULL); -} - - -static int __init check_parameters(struct fsg_dev *fsg) -{ - int prot; - int gcnum; - - /* Store the default values */ - mod_data.transport_type = USB_PR_BULK; - mod_data.transport_name = "Bulk-only"; - mod_data.protocol_type = USB_SC_SCSI; - mod_data.protocol_name = "Transparent SCSI"; - - /* Some peripheral controllers are known not to be able to - * halt bulk endpoints correctly. If one of them is present, - * disable stalls. - */ - if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) - mod_data.can_stall = 0; - - if (mod_data.release == 0xffff) { // Parameter wasn't set - /* The sa1100 controller is not supported */ - if (gadget_is_sa1100(fsg->gadget)) - gcnum = -1; - else - gcnum = usb_gadget_controller_number(fsg->gadget); - if (gcnum >= 0) - mod_data.release = 0x0300 + gcnum; - else { - WARNING(fsg, "controller '%s' not recognized\n", - fsg->gadget->name); - mod_data.release = 0x0399; - } - } - - prot = simple_strtol(mod_data.protocol_parm, NULL, 0); - -#ifdef CONFIG_USB_FILE_STORAGE_TEST - if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) { - ; // Use default setting - } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) { - mod_data.transport_type = USB_PR_CB; - mod_data.transport_name = "Control-Bulk"; - } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) { - mod_data.transport_type = USB_PR_CBI; - mod_data.transport_name = "Control-Bulk-Interrupt"; - } else { - ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); - return -EINVAL; - } - - if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 || - prot == USB_SC_SCSI) { - ; // Use default setting - } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 || - prot == USB_SC_RBC) { - mod_data.protocol_type = USB_SC_RBC; - mod_data.protocol_name = "RBC"; - } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 || - strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 || - prot == USB_SC_8020) { - mod_data.protocol_type = USB_SC_8020; - mod_data.protocol_name = "8020i (ATAPI)"; - } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 || - prot == USB_SC_QIC) { - mod_data.protocol_type = USB_SC_QIC; - mod_data.protocol_name = "QIC-157"; - } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 || - prot == USB_SC_UFI) { - mod_data.protocol_type = USB_SC_UFI; - mod_data.protocol_name = "UFI"; - } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 || - prot == USB_SC_8070) { - mod_data.protocol_type = USB_SC_8070; - mod_data.protocol_name = "8070i"; - } else { - ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); - return -EINVAL; - } - - mod_data.buflen &= PAGE_CACHE_MASK; - if (mod_data.buflen <= 0) { - ERROR(fsg, "invalid buflen\n"); - return -ETOOSMALL; - } -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - - return 0; -} - - -static int __init fsg_bind(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = the_fsg; - int rc; - int i; - struct lun *curlun; - struct usb_ep *ep; - struct usb_request *req; - char *pathbuf, *p; - - fsg->gadget = gadget; - set_gadget_data(gadget, fsg); - fsg->ep0 = gadget->ep0; - fsg->ep0->driver_data = fsg; - - if ((rc = check_parameters(fsg)) != 0) - goto out; - - if (mod_data.removable) { // Enable the store_xxx attributes - dev_attr_file.attr.mode = 0644; - dev_attr_file.store = store_file; - if (!mod_data.cdrom) { - dev_attr_ro.attr.mode = 0644; - dev_attr_ro.store = store_ro; - } - } - - /* Find out how many LUNs there should be */ - i = mod_data.nluns; - if (i == 0) - i = max(mod_data.num_filenames, 1u); - if (i > MAX_LUNS) { - ERROR(fsg, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; - } - - /* Create the LUNs, open their backing files, and register the - * LUN devices in sysfs. */ - fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL); - if (!fsg->luns) { - rc = -ENOMEM; - goto out; - } - fsg->nluns = i; - - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - curlun->ro = mod_data.ro[i]; - if (mod_data.cdrom) - curlun->ro = 1; - curlun->dev.release = lun_release; - curlun->dev.parent = &gadget->dev; - curlun->dev.driver = &fsg_driver.driver; - dev_set_drvdata(&curlun->dev, fsg); - dev_set_name(&curlun->dev,"%s-lun%d", - dev_name(&gadget->dev), i); - - if ((rc = device_register(&curlun->dev)) != 0) { - INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - goto out; - } - if ((rc = device_create_file(&curlun->dev, - &dev_attr_ro)) != 0 || - (rc = device_create_file(&curlun->dev, - &dev_attr_file)) != 0) { - device_unregister(&curlun->dev); - goto out; - } - curlun->registered = 1; - kref_get(&fsg->ref); - - if (mod_data.file[i] && *mod_data.file[i]) { - if ((rc = open_backing_file(curlun, - mod_data.file[i])) != 0) - goto out; - } else if (!mod_data.removable) { - ERROR(fsg, "no file given for LUN%d\n", i); - rc = -EINVAL; - goto out; - } - } - - /* Find all the endpoints we will use */ - usb_ep_autoconfig_reset(gadget); - ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->bulk_in = ep; - - ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->bulk_out = ep; - - if (transport_is_cbi()) { - ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; // claim the endpoint - fsg->intr_in = ep; - } - - /* Fix up the descriptors */ - device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; - device_desc.idVendor = cpu_to_le16(mod_data.vendor); - device_desc.idProduct = cpu_to_le16(mod_data.product); - device_desc.bcdDevice = cpu_to_le16(mod_data.release); - - i = (transport_is_cbi() ? 3 : 2); // Number of endpoints - intf_desc.bNumEndpoints = i; - intf_desc.bInterfaceSubClass = mod_data.protocol_type; - intf_desc.bInterfaceProtocol = mod_data.transport_type; - fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; - - if (gadget_is_dualspeed(gadget)) { - hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; - - /* Assume ep0 uses the same maxpacket value for both speeds */ - dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; - - /* Assume endpoint addresses are the same for both speeds */ - hs_bulk_in_desc.bEndpointAddress = - fs_bulk_in_desc.bEndpointAddress; - hs_bulk_out_desc.bEndpointAddress = - fs_bulk_out_desc.bEndpointAddress; - hs_intr_in_desc.bEndpointAddress = - fs_intr_in_desc.bEndpointAddress; - } - - if (gadget_is_otg(gadget)) - otg_desc.bmAttributes |= USB_OTG_HNP; - - rc = -ENOMEM; - - /* Allocate the request and buffer for endpoint 0 */ - fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); - if (!req) - goto out; - req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); - if (!req->buf) - goto out; - req->complete = ep0_complete; - - /* Allocate the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - /* Allocate for the bulk-in endpoint. We assume that - * the buffer will also work with the bulk-out (and - * interrupt-in) endpoint. */ - bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); - if (!bh->buf) - goto out; - bh->next = bh + 1; - } - fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0]; - - /* This should reflect the actual gadget power source */ - usb_gadget_set_selfpowered(gadget); - - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - /* On a real device, serial[] would be loaded from permanent - * storage. We just encode it from the driver version string. */ - for (i = 0; i < sizeof(serial) - 2; i += 2) { - unsigned char c = DRIVER_VERSION[i / 2]; - - if (!c) - break; - sprintf(&serial[i], "%02X", c); - } - - fsg->thread_task = kthread_create(fsg_main_thread, fsg, - "file-storage-gadget"); - if (IS_ERR(fsg->thread_task)) { - rc = PTR_ERR(fsg->thread_task); - goto out; - } - - INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); - - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (backing_file_is_open(curlun)) { - p = NULL; - if (pathbuf) { - p = d_path(&curlun->filp->f_path, - pathbuf, PATH_MAX); - if (IS_ERR(p)) - p = NULL; - } - LINFO(curlun, "ro=%d, file: %s\n", - curlun->ro, (p ? p : "(error)")); - } - } - kfree(pathbuf); - - DBG(fsg, "transport=%s (x%02x)\n", - mod_data.transport_name, mod_data.transport_type); - DBG(fsg, "protocol=%s (x%02x)\n", - mod_data.protocol_name, mod_data.protocol_type); - DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", - mod_data.vendor, mod_data.product, mod_data.release); - DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", - mod_data.removable, mod_data.can_stall, - mod_data.cdrom, mod_data.buflen); - DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); - - set_bit(REGISTERED, &fsg->atomic_bitflags); - - /* Tell the thread to start working */ - wake_up_process(fsg->thread_task); - return 0; - -autoconf_fail: - ERROR(fsg, "unable to autoconfigure all endpoints\n"); - rc = -ENOTSUPP; - -out: - fsg->state = FSG_STATE_TERMINATED; // The thread is dead - fsg_unbind(gadget); - complete(&fsg->thread_notifier); - return rc; -} - - -/*-------------------------------------------------------------------------*/ - -static void fsg_suspend(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "suspend\n"); - set_bit(SUSPENDED, &fsg->atomic_bitflags); -} - -static void fsg_resume(struct usb_gadget *gadget) -{ - struct fsg_dev *fsg = get_gadget_data(gadget); - - DBG(fsg, "resume\n"); - clear_bit(SUSPENDED, &fsg->atomic_bitflags); -} - - -/*-------------------------------------------------------------------------*/ - -static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *) longname, - .bind = fsg_bind, - .unbind = fsg_unbind, - .disconnect = fsg_disconnect, - .setup = fsg_setup, - .suspend = fsg_suspend, - .resume = fsg_resume, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - // .release = ... - // .suspend = ... - // .resume = ... - }, -}; - - -static int __init fsg_alloc(void) -{ - struct fsg_dev *fsg; - - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->filesem); - kref_init(&fsg->ref); - init_completion(&fsg->thread_notifier); - - the_fsg = fsg; - return 0; -} - - -static int __init fsg_init(void) -{ - int rc; - struct fsg_dev *fsg; - - if ((rc = fsg_alloc()) != 0) - return rc; - fsg = the_fsg; - if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) - kref_put(&fsg->ref, fsg_release); - return rc; -} -module_init(fsg_init); - - -static void __exit fsg_cleanup(void) -{ - struct fsg_dev *fsg = the_fsg; - - /* Unregister the driver iff the thread hasn't already done so */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - usb_gadget_unregister_driver(&fsg_driver); - - /* Wait for the thread to finish up */ - wait_for_completion(&fsg->thread_notifier); - - kref_put(&fsg->ref, fsg_release); -} -module_exit(fsg_cleanup); 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 new file mode 100644 index 00000000000..9b140fc4d3b --- /dev/null +++ b/drivers/usb/gadget/fsl_mxc_udc.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * Description: + * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c + * driver to function correctly on these systems. + * + * 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/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fsl_devices.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +static struct clk *mxc_ahb_clk; +static struct clk *mxc_per_clk; +static struct clk *mxc_ipg_clk; + +/* workaround ENGcm09152 for i.MX35 */ +#define MX35_USBPHYCTRL_OFFSET 0x600 +#define USBPHYCTRL_OTGBASE_OFFSET 0x8 +#define USBPHYCTRL_EVDO (1 << 23) + +int fsl_udc_clk_init(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata; + unsigned long freq; + int ret; + + pdata = dev_get_platdata(&pdev->dev); + + mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(mxc_ipg_clk)) { + dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n"); + return PTR_ERR(mxc_ipg_clk); + } + + mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(mxc_ahb_clk)) { + dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n"); + return PTR_ERR(mxc_ahb_clk); + } + + mxc_per_clk = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(mxc_per_clk)) { + dev_err(&pdev->dev, "clk_get(\"per\") failed\n"); + return PTR_ERR(mxc_per_clk); + } + + clk_prepare_enable(mxc_ipg_clk); + clk_prepare_enable(mxc_ahb_clk); + clk_prepare_enable(mxc_per_clk); + + /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ + if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) { + freq = clk_get_rate(mxc_per_clk); + if (pdata->phy_mode != FSL_USB2_PHY_ULPI && + (freq < 59999000 || freq > 60001000)) { + dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq); + ret = -EINVAL; + goto eclkrate; + } + } + + return 0; + +eclkrate: + clk_disable_unprepare(mxc_ipg_clk); + clk_disable_unprepare(mxc_ahb_clk); + clk_disable_unprepare(mxc_per_clk); + mxc_per_clk = NULL; + return ret; +} + +int fsl_udc_clk_finalize(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret = 0; + + /* workaround ENGcm09152 for i.MX35 */ + if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { + unsigned int v; + struct resource *res = platform_get_resource + (pdev, IORESOURCE_MEM, 0); + void __iomem *phy_regs = ioremap(res->start + + MX35_USBPHYCTRL_OFFSET, 512); + if (!phy_regs) { + dev_err(&pdev->dev, "ioremap for phy address fails\n"); + ret = -EINVAL; + goto ioremap_err; + } + + v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + writel(v | USBPHYCTRL_EVDO, + phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + + iounmap(phy_regs); + } + + +ioremap_err: + /* ULPI transceivers don't need usbpll */ + if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { + clk_disable_unprepare(mxc_per_clk); + mxc_per_clk = NULL; + } + + return ret; +} + +void fsl_udc_clk_release(void) +{ + if (mxc_per_clk) + clk_disable_unprepare(mxc_per_clk); + clk_disable_unprepare(mxc_ahb_clk); + clk_disable_unprepare(mxc_ipg_clk); +} diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index d701bf4698d..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> @@ -32,6 +31,8 @@ #include <linux/interrupt.h> #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> @@ -70,9 +71,6 @@ static struct usb_endpoint_descriptor qe_ep0_desc = { .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, }; -/* it is initialized in probe() */ -static struct qe_udc *udc_controller; - /******************************************************************** * Internal Used Function Start ********************************************************************/ @@ -187,8 +185,8 @@ static int qe_ep0_stall(struct qe_udc *udc) { qe_eptx_stall_change(&udc->eps[0], 1); qe_eprx_stall_change(&udc->eps[0], 1); - udc_controller->ep0_state = WAIT_FOR_SETUP; - udc_controller->ep0_dir = 0; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; return 0; } @@ -449,13 +447,13 @@ static int qe_ep_rxbd_update(struct qe_ep *ep) ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer); if (ep->rxbuf_d == DMA_ADDR_INVALID) { - ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent, + ep->rxbuf_d = dma_map_single(ep->udc->gadget.dev.parent, ep->rxbuffer, size, DMA_FROM_DEVICE); ep->rxbufmap = 1; } else { - dma_sync_single_for_device(udc_controller->gadget.dev.parent, + dma_sync_single_for_device(ep->udc->gadget.dev.parent, ep->rxbuf_d, size, DMA_FROM_DEVICE); ep->rxbufmap = 0; @@ -488,10 +486,10 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) epparam = udc->ep_param[pipe_num]; usep = 0; - logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + logepnum = (ep->ep.desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); usep |= (logepnum << USB_EPNUM_SHIFT); - switch (ep->desc->bmAttributes & 0x03) { + switch (ep->ep.desc->bmAttributes & 0x03) { case USB_ENDPOINT_XFER_BULK: usep |= USB_TRANS_BULK; break; @@ -539,7 +537,7 @@ static int qe_ep_init(struct qe_udc *udc, int reval = 0; u16 max = 0; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* check the max package size validate for this endpoint */ /* Refer to USB2.0 spec table 9-13, @@ -643,7 +641,7 @@ static int qe_ep_init(struct qe_udc *udc, /* initialize ep structure */ ep->ep.maxpacket = max; ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - ep->desc = desc; + ep->ep.desc = desc; ep->stopped = 0; ep->init = 1; @@ -697,14 +695,14 @@ en_done: return -ENODEV; } -static inline void qe_usb_enable(void) +static inline void qe_usb_enable(struct qe_udc *udc) { - setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN); + setbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN); } -static inline void qe_usb_disable(void) +static inline void qe_usb_disable(struct qe_udc *udc) { - clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN); + clrbits8(&udc->usb_regs->usb_usmod, USB_MODE_EN); } /*----------------------------------------------------------------------------* @@ -1147,6 +1145,12 @@ static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame) static int txcomplete(struct qe_ep *ep, unsigned char restart) { if (ep->tx_req != NULL) { + struct qe_req *req = ep->tx_req; + unsigned zlp = 0, last_len = 0; + + last_len = min_t(unsigned, req->req.length - ep->sent, + ep->ep.maxpacket); + if (!restart) { int asent = ep->last; ep->sent += asent; @@ -1155,9 +1159,18 @@ static int txcomplete(struct qe_ep *ep, unsigned char restart) ep->last = 0; } + /* zlp needed when req->re.zero is set */ + if (req->req.zero) { + if (last_len == 0 || + (req->req.length % ep->ep.maxpacket) != 0) + zlp = 0; + else + zlp = 1; + } else + zlp = 0; + /* a request already were transmitted completely */ - if ((ep->tx_req->req.length - ep->sent) <= 0) { - ep->tx_req->req.actual = (unsigned int)ep->sent; + if (((ep->tx_req->req.length - ep->sent) <= 0) && !zlp) { done(ep, ep->tx_req, 0); ep->tx_req = NULL; ep->last = 0; @@ -1190,6 +1203,7 @@ static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame) buf = (u8 *)ep->tx_req->req.buf + ep->sent; if (buf && size) { ep->last = size; + ep->tx_req->req.actual += size; frame_set_data(frame, buf); frame_set_length(frame, size); frame_set_status(frame, FRAME_OK); @@ -1582,7 +1596,7 @@ static int qe_ep_enable(struct usb_ep *_ep, ep = container_of(_ep, struct qe_ep, ep); /* catch various bogus parameters */ - if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] || + if (!_ep || !desc || _ep->name == ep_name[0] || (desc->bDescriptorType != USB_DT_ENDPOINT)) return -EINVAL; @@ -1612,7 +1626,7 @@ static int qe_ep_disable(struct usb_ep *_ep) ep = container_of(_ep, struct qe_ep, ep); udc = ep->udc; - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { dev_dbg(udc->dev, "%s not enabled\n", _ep ? ep->ep.name : NULL); return -EINVAL; } @@ -1620,7 +1634,7 @@ static int qe_ep_disable(struct usb_ep *_ep) spin_lock_irqsave(&udc->lock, flags); /* Nuke all pending requests (does flush) */ nuke(ep, -ESHUTDOWN); - ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; ep->tx_req = NULL; qe_ep_reset(udc, ep->epnum); @@ -1638,13 +1652,13 @@ static int qe_ep_disable(struct usb_ep *_ep) if (ep->dir != USB_DIR_IN) { kfree(ep->rxframe); if (ep->rxbufmap) { - dma_unmap_single(udc_controller->gadget.dev.parent, + dma_unmap_single(udc->gadget.dev.parent, ep->rxbuf_d, size, DMA_FROM_DEVICE); ep->rxbuf_d = DMA_ADDR_INVALID; } else { dma_sync_single_for_cpu( - udc_controller->gadget.dev.parent, + udc->gadget.dev.parent, ep->rxbuf_d, size, DMA_FROM_DEVICE); } @@ -1697,7 +1711,7 @@ static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(udc->dev, "bad params\n"); return -EINVAL; } - if (!_ep || (!ep->desc && ep_index(ep))) { + if (!_ep || (!ep->ep.desc && ep_index(ep))) { dev_dbg(udc->dev, "bad ep\n"); return -EINVAL; } @@ -1808,7 +1822,7 @@ static int qe_ep_set_halt(struct usb_ep *_ep, int value) struct qe_udc *udc; ep = container_of(_ep, struct qe_ep, ep); - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { status = -EINVAL; goto out; } @@ -1862,9 +1876,10 @@ static struct usb_ep_ops qe_ep_ops = { /* Get the current frame number */ static int qe_get_frame(struct usb_gadget *gadget) { + struct qe_udc *udc = container_of(gadget, struct qe_udc, gadget); u16 tmp; - tmp = in_be16(&udc_controller->usb_param->frame_n); + tmp = in_be16(&udc->usb_param->frame_n); if (tmp & 0x8000) tmp = tmp & 0x07ff; else @@ -1873,51 +1888,16 @@ static int qe_get_frame(struct usb_gadget *gadget) return (int)tmp; } -/* Tries to wake up the host connected to this gadget - * - * Return : 0-success - * Negative-this feature not enabled by host or not supported by device hw - */ -static int qe_wakeup(struct usb_gadget *gadget) -{ - return -ENOTSUPP; -} - -/* Notify controller that VBUS is powered, Called by whatever - detects VBUS sessions */ -static int qe_vbus_session(struct usb_gadget *gadget, int is_active) -{ - return -ENOTSUPP; -} - -/* constrain controller's VBUS power usage - * This call is used by gadget drivers during SET_CONFIGURATION calls, - * reporting how much power the device may consume. For example, this - * could affect how quickly batteries are recharged. - * - * Returns zero on success, else negative errno. - */ -static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA) -{ - return -ENOTSUPP; -} - -/* Change Data+ pullup status - * this func is used by usb_gadget_connect/disconnect - */ -static int qe_pullup(struct usb_gadget *gadget, int is_on) -{ - return -ENOTSUPP; -} +static int fsl_qe_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +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, - .wakeup = qe_wakeup, -/* .set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */ - .vbus_session = qe_vbus_session, - .vbus_draw = qe_vbus_draw, - .pullup = qe_pullup, + .udc_start = fsl_qe_start, + .udc_stop = fsl_qe_stop, }; /*------------------------------------------------------------------------- @@ -1991,7 +1971,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, u16 usep; /* stall if endpoint doesn't exist */ - if (!target_ep->desc) + if (!target_ep->ep.desc) goto stall; usep = in_be16(&udc->usb_regs->usb_usep[pipe]); @@ -2166,7 +2146,7 @@ static int reset_irq(struct qe_udc *udc) if (udc->usb_state == USB_STATE_DEFAULT) return 0; - qe_usb_disable(); + qe_usb_disable(udc); out_8(&udc->usb_regs->usb_usadr, 0); for (i = 0; i < USB_MAX_ENDPOINTS; i++) { @@ -2178,7 +2158,7 @@ static int reset_irq(struct qe_udc *udc) udc->usb_state = USB_STATE_DEFAULT; udc->ep0_state = WAIT_FOR_SETUP; udc->ep0_dir = USB_DIR_OUT; - qe_usb_enable(); + qe_usb_enable(udc); return 0; } @@ -2301,106 +2281,74 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc) } /*------------------------------------------------------------------------- - Gadget driver register and unregister. + Gadget driver probe and unregister. --------------------------------------------------------------------------*/ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +static int fsl_qe_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - int retval; - unsigned long flags = 0; - - /* standard operations */ - if (!udc_controller) - return -ENODEV; - - if (!driver || (driver->speed != USB_SPEED_FULL - && driver->speed != USB_SPEED_HIGH) - || !driver->bind || !driver->disconnect - || !driver->setup) - return -EINVAL; - - if (udc_controller->driver) - return -EBUSY; + struct qe_udc *udc; + unsigned long flags; + udc = container_of(gadget, struct qe_udc, gadget); /* lock is needed but whether should use this lock or another */ - spin_lock_irqsave(&udc_controller->lock, flags); + spin_lock_irqsave(&udc->lock, flags); driver->driver.bus = NULL; /* hook up the driver */ - udc_controller->driver = driver; - udc_controller->gadget.dev.driver = &driver->driver; - udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed); - spin_unlock_irqrestore(&udc_controller->lock, flags); - - retval = driver->bind(&udc_controller->gadget); - if (retval) { - dev_err(udc_controller->dev, "bind to %s --> %d", - driver->driver.name, retval); - udc_controller->gadget.dev.driver = NULL; - udc_controller->driver = NULL; - return retval; - } + udc->driver = driver; + udc->gadget.speed = driver->max_speed; /* Enable IRQ reg and Set usbcmd reg EN bit */ - qe_usb_enable(); - - out_be16(&udc_controller->usb_regs->usb_usber, 0xffff); - out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE); - udc_controller->usb_state = USB_STATE_ATTACHED; - udc_controller->ep0_state = WAIT_FOR_SETUP; - udc_controller->ep0_dir = USB_DIR_OUT; - dev_info(udc_controller->dev, "%s bind to driver %s \n", - udc_controller->gadget.name, driver->driver.name); + qe_usb_enable(udc); + + out_be16(&udc->usb_regs->usb_usber, 0xffff); + out_be16(&udc->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE); + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = USB_DIR_OUT; + spin_unlock_irqrestore(&udc->lock, flags); + + dev_info(udc->dev, "%s bind to driver %s\n", udc->gadget.name, + driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_register_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int fsl_qe_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { + struct qe_udc *udc; struct qe_ep *loop_ep; unsigned long flags; - if (!udc_controller) - return -ENODEV; - - if (!driver || driver != udc_controller->driver) - return -EINVAL; - + udc = container_of(gadget, struct qe_udc, gadget); /* stop usb controller, disable intr */ - qe_usb_disable(); + qe_usb_disable(udc); /* in fact, no needed */ - udc_controller->usb_state = USB_STATE_ATTACHED; - udc_controller->ep0_state = WAIT_FOR_SETUP; - udc_controller->ep0_dir = 0; + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; /* stand operation */ - spin_lock_irqsave(&udc_controller->lock, flags); - udc_controller->gadget.speed = USB_SPEED_UNKNOWN; - nuke(&udc_controller->eps[0], -ESHUTDOWN); - list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, - ep.ep_list) + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc->gadget.ep_list, ep.ep_list) nuke(loop_ep, -ESHUTDOWN); - spin_unlock_irqrestore(&udc_controller->lock, flags); - - /* report disconnect; the controller is already quiesced */ - driver->disconnect(&udc_controller->gadget); + spin_unlock_irqrestore(&udc->lock, flags); - /* unbind gadget and unhook driver. */ - driver->unbind(&udc_controller->gadget); - udc_controller->gadget.dev.driver = NULL; - udc_controller->driver = NULL; + udc->driver = NULL; - dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n", + dev_info(udc->dev, "unregistered gadget driver '%s'\r\n", driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /* udc structure's alloc and setup, include ep-param alloc */ -static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev) +static struct qe_udc *qe_udc_config(struct platform_device *ofdev) { struct qe_udc *udc; - struct device_node *np = ofdev->node; + struct device_node *np = ofdev->dev.of_node; unsigned int tmp_addr = 0; struct usb_device_para __iomem *usbpram; unsigned int i; @@ -2452,7 +2400,7 @@ cleanup: } /* USB Controller register init */ -static int __devinit qe_udc_reg_init(struct qe_udc *udc) +static int qe_udc_reg_init(struct qe_udc *udc) { struct usb_ctlr __iomem *qe_usbregs; qe_usbregs = udc->usb_regs; @@ -2470,7 +2418,7 @@ static int __devinit qe_udc_reg_init(struct qe_udc *udc) return 0; } -static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) +static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) { struct qe_ep *ep = &udc->eps[pipe_num]; @@ -2480,8 +2428,8 @@ static int __devinit 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; - ep->desc = NULL; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); + ep->ep.desc = NULL; ep->dir = 0xff; ep->epnum = (u8)pipe_num; ep->sent = 0; @@ -2510,70 +2458,70 @@ static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) *----------------------------------------------------------------------*/ static void qe_udc_release(struct device *dev) { - int i = 0; + struct qe_udc *udc = container_of(dev, struct qe_udc, gadget.dev); + int i; - complete(udc_controller->done); - cpm_muram_free(cpm_muram_offset(udc_controller->ep_param[0])); + complete(udc->done); + cpm_muram_free(cpm_muram_offset(udc->ep_param[0])); for (i = 0; i < USB_MAX_ENDPOINTS; i++) - udc_controller->ep_param[i] = NULL; + udc->ep_param[i] = NULL; - kfree(udc_controller); - udc_controller = NULL; + kfree(udc); } /* Driver probe functions */ -static int __devinit qe_udc_probe(struct of_device *ofdev, - const struct of_device_id *match) +static const struct of_device_id qe_udc_match[]; +static int qe_udc_probe(struct platform_device *ofdev) { - struct device_node *np = ofdev->node; + struct qe_udc *udc; + const struct of_device_id *match; + struct device_node *np = ofdev->dev.of_node; struct qe_ep *ep; unsigned int ret = 0; unsigned int i; const void *prop; + match = of_match_device(qe_udc_match, &ofdev->dev); + if (!match) + return -EINVAL; + prop = of_get_property(np, "mode", NULL); if (!prop || strcmp(prop, "peripheral")) return -ENODEV; /* Initialize the udc structure including QH member and other member */ - udc_controller = qe_udc_config(ofdev); - if (!udc_controller) { + udc = qe_udc_config(ofdev); + if (!udc) { dev_err(&ofdev->dev, "failed to initialize\n"); return -ENOMEM; } - udc_controller->soc_type = (unsigned long)match->data; - udc_controller->usb_regs = of_iomap(np, 0); - if (!udc_controller->usb_regs) { + udc->soc_type = (unsigned long)match->data; + udc->usb_regs = of_iomap(np, 0); + if (!udc->usb_regs) { ret = -ENOMEM; goto err1; } /* initialize usb hw reg except for regs for EP, * leave usbintr reg untouched*/ - qe_udc_reg_init(udc_controller); + qe_udc_reg_init(udc); /* here comes the stand operations for probe * set the qe_udc->gadget.xxx */ - udc_controller->gadget.ops = &qe_gadget_ops; + udc->gadget.ops = &qe_gadget_ops; /* gadget.ep0 is a pointer */ - udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; + udc->gadget.ep0 = &udc->eps[0].ep; - INIT_LIST_HEAD(&udc_controller->gadget.ep_list); + INIT_LIST_HEAD(&udc->gadget.ep_list); /* modify in register gadget process */ - udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.speed = USB_SPEED_UNKNOWN; /* name: Identifies the controller hardware type. */ - udc_controller->gadget.name = driver_name; - - device_initialize(&udc_controller->gadget.dev); - - dev_set_name(&udc_controller->gadget.dev, "gadget"); - - udc_controller->gadget.dev.release = qe_udc_release; - udc_controller->gadget.dev.parent = &ofdev->dev; + udc->gadget.name = driver_name; + udc->gadget.dev.parent = &ofdev->dev; /* initialize qe_ep struct */ for (i = 0; i < USB_MAX_ENDPOINTS ; i++) { @@ -2582,151 +2530,151 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, /* setup the qe_ep struct and link ep.ep.list * into gadget.ep_list */ - qe_ep_config(udc_controller, (unsigned char)i); + qe_ep_config(udc, (unsigned char)i); } /* ep0 initialization in here */ - ret = qe_ep_init(udc_controller, 0, &qe_ep0_desc); + ret = qe_ep_init(udc, 0, &qe_ep0_desc); if (ret) goto err2; /* create a buf for ZLP send, need to remain zeroed */ - udc_controller->nullbuf = kzalloc(256, GFP_KERNEL); - if (udc_controller->nullbuf == NULL) { - dev_err(udc_controller->dev, "cannot alloc nullbuf\n"); + udc->nullbuf = kzalloc(256, GFP_KERNEL); + if (udc->nullbuf == NULL) { + dev_err(udc->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; goto err3; } /* buffer for data of get_status request */ - udc_controller->statusbuf = kzalloc(2, GFP_KERNEL); - if (udc_controller->statusbuf == NULL) { + udc->statusbuf = kzalloc(2, GFP_KERNEL); + if (udc->statusbuf == NULL) { ret = -ENOMEM; goto err4; } - udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf); - if (udc_controller->nullp == DMA_ADDR_INVALID) { - udc_controller->nullp = dma_map_single( - udc_controller->gadget.dev.parent, - udc_controller->nullbuf, + udc->nullp = virt_to_phys((void *)udc->nullbuf); + if (udc->nullp == DMA_ADDR_INVALID) { + udc->nullp = dma_map_single( + udc->gadget.dev.parent, + udc->nullbuf, 256, DMA_TO_DEVICE); - udc_controller->nullmap = 1; + udc->nullmap = 1; } else { - dma_sync_single_for_device(udc_controller->gadget.dev.parent, - udc_controller->nullp, 256, + dma_sync_single_for_device(udc->gadget.dev.parent, + udc->nullp, 256, DMA_TO_DEVICE); } - tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet, - (unsigned long)udc_controller); + tasklet_init(&udc->rx_tasklet, ep_rx_tasklet, + (unsigned long)udc); /* request irq and disable DR */ - udc_controller->usb_irq = irq_of_parse_and_map(np, 0); - if (!udc_controller->usb_irq) { + udc->usb_irq = irq_of_parse_and_map(np, 0); + if (!udc->usb_irq) { ret = -EINVAL; goto err_noirq; } - ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0, - driver_name, udc_controller); + ret = request_irq(udc->usb_irq, qe_udc_irq, 0, + driver_name, udc); if (ret) { - dev_err(udc_controller->dev, "cannot request irq %d err %d \n", - udc_controller->usb_irq, ret); + dev_err(udc->dev, "cannot request irq %d err %d\n", + udc->usb_irq, ret); goto err5; } - ret = device_add(&udc_controller->gadget.dev); + ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, + qe_udc_release); if (ret) goto err6; - dev_info(udc_controller->dev, + platform_set_drvdata(ofdev, udc); + dev_info(udc->dev, "%s USB controller initialized as device\n", - (udc_controller->soc_type == PORT_QE) ? "QE" : "CPM"); + (udc->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; err6: - free_irq(udc_controller->usb_irq, udc_controller); + free_irq(udc->usb_irq, udc); err5: - irq_dispose_mapping(udc_controller->usb_irq); + irq_dispose_mapping(udc->usb_irq); err_noirq: - if (udc_controller->nullmap) { - dma_unmap_single(udc_controller->gadget.dev.parent, - udc_controller->nullp, 256, + if (udc->nullmap) { + dma_unmap_single(udc->gadget.dev.parent, + udc->nullp, 256, DMA_TO_DEVICE); - udc_controller->nullp = DMA_ADDR_INVALID; + udc->nullp = DMA_ADDR_INVALID; } else { - dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, - udc_controller->nullp, 256, + dma_sync_single_for_cpu(udc->gadget.dev.parent, + udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc_controller->statusbuf); + kfree(udc->statusbuf); err4: - kfree(udc_controller->nullbuf); + kfree(udc->nullbuf); err3: - ep = &udc_controller->eps[0]; + ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); kfree(ep->rxframe); kfree(ep->rxbuffer); kfree(ep->txframe); err2: - iounmap(udc_controller->usb_regs); + iounmap(udc->usb_regs); err1: - kfree(udc_controller); - udc_controller = NULL; + kfree(udc); return ret; } #ifdef CONFIG_PM -static int qe_udc_suspend(struct of_device *dev, pm_message_t state) +static int qe_udc_suspend(struct platform_device *dev, pm_message_t state) { return -ENOTSUPP; } -static int qe_udc_resume(struct of_device *dev) +static int qe_udc_resume(struct platform_device *dev) { return -ENOTSUPP; } #endif -static int __devexit qe_udc_remove(struct of_device *ofdev) +static int qe_udc_remove(struct platform_device *ofdev) { + struct qe_udc *udc = platform_get_drvdata(ofdev); struct qe_ep *ep; unsigned int size; - DECLARE_COMPLETION(done); - if (!udc_controller) - return -ENODEV; + usb_del_gadget_udc(&udc->gadget); - udc_controller->done = &done; - tasklet_disable(&udc_controller->rx_tasklet); + udc->done = &done; + tasklet_disable(&udc->rx_tasklet); - if (udc_controller->nullmap) { - dma_unmap_single(udc_controller->gadget.dev.parent, - udc_controller->nullp, 256, + if (udc->nullmap) { + dma_unmap_single(udc->gadget.dev.parent, + udc->nullp, 256, DMA_TO_DEVICE); - udc_controller->nullp = DMA_ADDR_INVALID; + udc->nullp = DMA_ADDR_INVALID; } else { - dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, - udc_controller->nullp, 256, + dma_sync_single_for_cpu(udc->gadget.dev.parent, + udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc_controller->statusbuf); - kfree(udc_controller->nullbuf); + kfree(udc->statusbuf); + kfree(udc->nullbuf); - ep = &udc_controller->eps[0]; + ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (USB_BDRING_LEN + 1); kfree(ep->rxframe); if (ep->rxbufmap) { - dma_unmap_single(udc_controller->gadget.dev.parent, + dma_unmap_single(udc->gadget.dev.parent, ep->rxbuf_d, size, DMA_FROM_DEVICE); ep->rxbuf_d = DMA_ADDR_INVALID; } else { - dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, + dma_sync_single_for_cpu(udc->gadget.dev.parent, ep->rxbuf_d, size, DMA_FROM_DEVICE); } @@ -2734,14 +2682,13 @@ static int __devexit qe_udc_remove(struct of_device *ofdev) kfree(ep->rxbuffer); kfree(ep->txframe); - free_irq(udc_controller->usb_irq, udc_controller); - irq_dispose_mapping(udc_controller->usb_irq); + free_irq(udc->usb_irq, udc); + irq_dispose_mapping(udc->usb_irq); - tasklet_kill(&udc_controller->rx_tasklet); + tasklet_kill(&udc->rx_tasklet); - iounmap(udc_controller->usb_regs); + iounmap(udc->usb_regs); - device_unregister(&udc_controller->gadget.dev); /* wait for release() of gadget.dev to free udc */ wait_for_completion(&done); @@ -2749,7 +2696,11 @@ static int __devexit qe_udc_remove(struct of_device *ofdev) } /*-------------------------------------------------------------------------*/ -static struct of_device_id __devinitdata qe_udc_match[] = { +static const struct of_device_id qe_udc_match[] = { + { + .compatible = "fsl,mpc8323-qe-usb", + .data = (void *)PORT_QE, + }, { .compatible = "fsl,mpc8360-qe-usb", .data = (void *)PORT_QE, @@ -2763,31 +2714,21 @@ static struct of_device_id __devinitdata qe_udc_match[] = { MODULE_DEVICE_TABLE(of, qe_udc_match); -static struct of_platform_driver udc_driver = { - .name = (char *)driver_name, - .match_table = qe_udc_match, +static struct platform_driver udc_driver = { + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + .of_match_table = qe_udc_match, + }, .probe = qe_udc_probe, - .remove = __devexit_p(qe_udc_remove), + .remove = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, #endif }; -static int __init qe_udc_init(void) -{ - printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc, - DRIVER_VERSION); - return of_register_platform_driver(&udc_driver); -} - -static void __exit qe_udc_exit(void) -{ - of_unregister_platform_driver(&udc_driver); -} - -module_init(qe_udc_init); -module_exit(qe_udc_exit); +module_platform_driver(udc_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h index 31b2710882e..7026919fc90 100644 --- a/drivers/usb/gadget/fsl_qe_udc.h +++ b/drivers/usb/gadget/fsl_qe_udc.h @@ -153,10 +153,10 @@ struct usb_ep_para{ #define USB_BUSMODE_DTB 0x02 /* Endpoint basic handle */ -#define ep_index(EP) ((EP)->desc->bEndpointAddress & 0xF) +#define ep_index(EP) ((EP)->ep.desc->bEndpointAddress & 0xF) #define ep_maxpacket(EP) ((EP)->ep.maxpacket) #define ep_is_in(EP) ((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ - USB_DIR_IN) : ((EP)->desc->bEndpointAddress \ + USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \ & USB_DIR_IN) == USB_DIR_IN) /* ep0 transfer state */ @@ -207,15 +207,15 @@ struct qe_frame{ /* Frame status field */ /* Receive side */ -#define FRAME_OK 0x00000000 /* Frame tranmitted or received OK */ -#define FRAME_ERROR 0x80000000 /* Error occured on frame */ +#define FRAME_OK 0x00000000 /* Frame transmitted or received OK */ +#define FRAME_ERROR 0x80000000 /* Error occurred on frame */ #define START_FRAME_LOST 0x40000000 /* START_FRAME_LOST */ #define END_FRAME_LOST 0x20000000 /* END_FRAME_LOST */ #define RX_ER_NONOCT 0x10000000 /* Rx Non Octet Aligned Packet */ #define RX_ER_BITSTUFF 0x08000000 /* Frame Aborted --Received packet with bit stuff error */ #define RX_ER_CRC 0x04000000 /* Received packet with CRC error */ -#define RX_ER_OVERUN 0x02000000 /* Over-run occured on reception */ +#define RX_ER_OVERUN 0x02000000 /* Over-run occurred on reception */ #define RX_ER_PID 0x01000000 /* Wrong PID received */ /* Tranmit side */ #define TX_ER_NAK 0x00800000 /* Received NAK handshake */ @@ -266,7 +266,6 @@ struct qe_ep { struct usb_ep ep; struct list_head queue; struct qe_udc *udc; - const struct usb_endpoint_descriptor *desc; struct usb_gadget *gadget; u8 state; @@ -379,7 +378,7 @@ struct qe_udc { #define T_LSP 0x01000000 /* Low-speed transaction */ #define T_PID 0x00c00000 /* packet id */ #define T_NAK 0x00100000 /* No ack. */ -#define T_STAL 0x00080000 /* Stall recieved */ +#define T_STAL 0x00080000 /* Stall received */ #define T_TO 0x00040000 /* time out */ #define T_UN 0x00020000 /* underrun */ @@ -419,19 +418,4 @@ struct qe_udc { #define CPM_USB_RESTART_TX_OPCODE 0x0b #define CPM_USB_EP_SHIFT 5 -#ifndef CONFIG_CPM -inline int cpm_command(u32 command, u8 opcode) -{ - return -EOPNOTSUPP; -} -#endif - -#ifndef CONFIG_QUICC_ENGINE -inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, - u32 cmd_input) -{ - return -EOPNOTSUPP; -} -#endif - #endif /* __FSL_QE_UDC_H */ diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_udc_core.c index 9d7b95d4e3d..28e4fc95702 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1,12 +1,13 @@ /* - * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc. + * All rights reserved. * * Author: Li Yang <leoli@freescale.com> * Jiang Bo <tanya.jiang@freescale.com> * * Description: * Freescale high-speed USB SOC DR module device controller driver. - * This can be found on MPC8349E/MPC8313E cpus. + * This can be found on MPC8349E/MPC8313E/MPC5121E cpus. * The driver is previously named as mpc_udc. Based on bare board * code from Dave Liu and Shlomi Gridish. * @@ -23,6 +24,7 @@ #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/list.h> @@ -38,10 +40,11 @@ #include <linux/platform_device.h> #include <linux/fsl_devices.h> #include <linux/dmapool.h> +#include <linux/delay.h> +#include <linux/of_device.h> #include <asm/byteorder.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/unaligned.h> #include <asm/dma.h> @@ -57,6 +60,7 @@ static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; static struct usb_dr_device *dr_regs; + static struct usb_sys_interface *usb_sys_regs; /* it is initialized in probe() */ @@ -74,12 +78,77 @@ fsl_ep0_desc = { static void fsl_ep_fifo_flush(struct usb_ep *_ep); #ifdef CONFIG_PPC32 -#define fsl_readl(addr) in_le32(addr) -#define fsl_writel(val32, addr) out_le32(addr, val32) -#else +/* + * On some SoCs, the USB controller registers can be big or little endian, + * depending on the version of the chip. In order to be able to run the + * same kernel binary on 2 different versions of an SoC, the BE/LE decision + * must be made at run time. _fsl_readl and fsl_writel are pointers to the + * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel() + * call through those pointers. Platform code for SoCs that have BE USB + * registers should set pdata->big_endian_mmio flag. + * + * This also applies to controller-to-cpu accessors for the USB descriptors, + * since their endianness is also SoC dependant. Platform code for SoCs that + * have BE USB descriptors should set pdata->big_endian_desc flag. + */ +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) +{ + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } +} + +static inline u32 cpu_to_hc32(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? (__force u32)cpu_to_be32(x) + : (__force u32)cpu_to_le32(x); +} + +static inline u32 hc32_to_cpu(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} +#else /* !CONFIG_PPC32 */ +static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {} + #define fsl_readl(addr) readl(addr) #define fsl_writel(val32, addr) writel(val32, addr) -#endif +#define cpu_to_hc32(x) cpu_to_le32(x) +#define hc32_to_cpu(x) le32_to_cpu(x) +#endif /* CONFIG_PPC32 */ /******************************************************************** * Internal Used Function @@ -116,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", @@ -174,10 +230,54 @@ static void nuke(struct fsl_ep *ep, int status) static int dr_controller_setup(struct fsl_udc *udc) { - unsigned int tmp = 0, portctrl = 0, ctrl = 0; + unsigned int tmp, portctrl, ep_num; + unsigned int max_no_of_ep; + unsigned int ctrl; unsigned long timeout; + #define FSL_UDC_RESET_TIMEOUT 1000 + /* Config PHY interface */ + portctrl = fsl_readl(&dr_regs->portsc1); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); + switch (udc->phy_mode) { + case FSL_USB2_PHY_ULPI: + if (udc->pdata->have_sysif_regs) { + if (udc->pdata->controller_ver) { + /* controller version 1.6 or above */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl &= ~USB_CTRL_UTMI_PHY_EN; + ctrl |= USB_CTRL_USB_EN; + __raw_writel(ctrl, &usb_sys_regs->control); + } + } + portctrl |= PORTSCX_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI_WIDE: + portctrl |= PORTSCX_PTW_16BIT; + /* fall through */ + case FSL_USB2_PHY_UTMI: + if (udc->pdata->have_sysif_regs) { + if (udc->pdata->controller_ver) { + /* controller version 1.6 or above */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= (USB_CTRL_UTMI_PHY_EN | + USB_CTRL_USB_EN); + __raw_writel(ctrl, &usb_sys_regs->control); + mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI + PHY CLK to become stable - 10ms*/ + } + } + portctrl |= PORTSCX_PTS_UTMI; + break; + case FSL_USB2_PHY_SERIAL: + portctrl |= PORTSCX_PTS_FSLS; + break; + default: + return -EINVAL; + } + fsl_writel(portctrl, &dr_regs->portsc1); + /* Stop and reset the usb controller */ tmp = fsl_readl(&dr_regs->usbcmd); tmp &= ~USB_CMD_RUN_STOP; @@ -199,9 +299,12 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Set the controller as device mode */ tmp = fsl_readl(&dr_regs->usbmode); + tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */ tmp |= USB_MODE_CTRL_MODE_DEVICE; /* Disable Setup Lockout */ tmp |= USB_MODE_SETUP_LOCK_OFF; + if (udc->pdata->es) + tmp |= USB_MODE_ES; fsl_writel(tmp, &dr_regs->usbmode); /* Clear the setup status */ @@ -215,41 +318,34 @@ static int dr_controller_setup(struct fsl_udc *udc) udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); - /* Config PHY interface */ - portctrl = fsl_readl(&dr_regs->portsc1); - portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); - switch (udc->phy_mode) { - case FSL_USB2_PHY_ULPI: - portctrl |= PORTSCX_PTS_ULPI; - break; - case FSL_USB2_PHY_UTMI_WIDE: - portctrl |= PORTSCX_PTW_16BIT; - /* fall through */ - case FSL_USB2_PHY_UTMI: - portctrl |= PORTSCX_PTS_UTMI; - break; - case FSL_USB2_PHY_SERIAL: - portctrl |= PORTSCX_PTS_FSLS; - break; - default: - return -EINVAL; + max_no_of_ep = (0x0000001F & fsl_readl(&dr_regs->dccparams)); + for (ep_num = 1; ep_num < max_no_of_ep; ep_num++) { + tmp = fsl_readl(&dr_regs->endptctrl[ep_num]); + tmp &= ~(EPCTRL_TX_TYPE | EPCTRL_RX_TYPE); + tmp |= (EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT) + | (EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT); + fsl_writel(tmp, &dr_regs->endptctrl[ep_num]); } - fsl_writel(portctrl, &dr_regs->portsc1); - /* Config control enable i/o output, cpu endian register */ - ctrl = __raw_readl(&usb_sys_regs->control); - ctrl |= USB_CTRL_IOENB; - __raw_writel(ctrl, &usb_sys_regs->control); +#ifndef CONFIG_ARCH_MXC + if (udc->pdata->have_sysif_regs) { + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); + } +#endif #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* Turn on cache snooping hardware, since some PowerPC platforms * wholly rely on hardware to deal with cache coherent. */ - /* Setup Snooping for all the 4GB space */ - tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop1); - tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop2); + if (udc->pdata->have_sysif_regs) { + /* Setup Snooping for all the 4GB space */ + tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop1); + tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop2); + } #endif return 0; @@ -279,14 +375,25 @@ static void dr_controller_run(struct fsl_udc *udc) temp = fsl_readl(&dr_regs->usbcmd); temp |= USB_CMD_RUN_STOP; fsl_writel(temp, &dr_regs->usbcmd); - - return; } static void dr_controller_stop(struct fsl_udc *udc) { unsigned int tmp; + pr_debug("%s\n", __func__); + + /* if we're in OTG mode, and the Host is currently using the port, + * stop now and don't rip the controller out from under the + * ehci driver + */ + if (udc->gadget.is_otg) { + if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { + pr_debug("udc: Leaving early\n"); + return; + } + } + /* disable all INTR */ fsl_writel(0, &dr_regs->usbintr); @@ -300,8 +407,6 @@ static void dr_controller_stop(struct fsl_udc *udc) tmp = fsl_readl(&dr_regs->usbcmd); tmp &= ~USB_CMD_RUN_STOP; fsl_writel(tmp, &dr_regs->usbcmd); - - return; } static void dr_ep_setup(unsigned char ep_num, unsigned char dir, @@ -314,12 +419,14 @@ static void dr_ep_setup(unsigned char ep_num, unsigned char dir, if (ep_num) tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl &= ~EPCTRL_TX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT); } else { if (ep_num) tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl &= ~EPCTRL_RX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT); } @@ -405,11 +512,9 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, if (zlt) tmp |= EP_QUEUE_HEAD_ZLT_SEL; - p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->max_pkt_length = cpu_to_hc32(tmp); p_QH->next_dtd_ptr = 1; p_QH->size_ioc_int_sts = 0; - - return; } /* Setup qh structure and ep register for ep0. */ @@ -451,7 +556,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, ep = container_of(_ep, struct fsl_ep, ep); /* catch various bogus parameters */ - if (!_ep || !desc || ep->desc + if (!_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT)) return -EINVAL; @@ -460,9 +565,9 @@ static int fsl_ep_enable(struct usb_ep *_ep, if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); - /* Disable automatic zlp generation. Driver is reponsible to indicate + /* Disable automatic zlp generation. Driver is responsible to indicate * explicitly through req->req.zero. This is needed to enable multi-td * request. */ zlt = 1; @@ -481,7 +586,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); - max = max & 0x8ff; /* bit 0~10 */ + max = max & 0x7ff; /* bit 0~10 */ /* 3 transactions at most */ if (mult > 3) goto en_done; @@ -492,7 +597,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, spin_lock_irqsave(&udc->lock, flags); ep->ep.maxpacket = max; - ep->desc = desc; + ep->ep.desc = desc; ep->stopped = 0; /* Controller related setup */ @@ -516,7 +621,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, retval = 0; VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name, - ep->desc->bEndpointAddress & 0x0f, + ep->ep.desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", max); en_done: @@ -536,7 +641,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) int ep_num; ep = container_of(_ep, struct fsl_ep, ep); - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { VDBG("%s not enabled", _ep ? ep->ep.name : NULL); return -EINVAL; } @@ -544,10 +649,13 @@ static int fsl_ep_disable(struct usb_ep *_ep) /* disable ep on controller */ ep_num = ep_index(ep); epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); - if (ep_is_in(ep)) - epctrl &= ~EPCTRL_TX_ENABLE; - else - epctrl &= ~EPCTRL_RX_ENABLE; + if (ep_is_in(ep)) { + epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT; + } else { + epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT; + } fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); udc = (struct fsl_udc *)ep->udc; @@ -556,7 +664,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) /* nuke all pending requests (does flush) */ nuke(ep, -ESHUTDOWN); - ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); @@ -594,12 +702,31 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -/*-------------------------------------------------------------------------*/ +/* Actually add a dTD chain to an empty dQH and let go */ +static void fsl_prime_ep(struct fsl_ep *ep, struct ep_td_struct *td) +{ + struct ep_queue_head *qh = get_qh_by_ep(ep); + + /* Write dQH next pointer and terminate bit to 0 */ + qh->next_dtd_ptr = cpu_to_hc32(td->td_dma + & EP_QUEUE_HEAD_NEXT_POINTER_MASK); + + /* Clear active and halt bit */ + qh->size_ioc_int_sts &= cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + + /* Ensure that updates to the QH will occur before priming. */ + wmb(); + + /* Prime endpoint by writing correct bit to ENDPTPRIME */ + fsl_writel(ep_is_in(ep) ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))), &dr_regs->endpointprime); +} + +/* Add dTD chain to the dQH of an EP */ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { - int i = ep_index(ep) * 2 + ep_is_in(ep); u32 temp, bitmask, tmp_stat; - struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ @@ -609,15 +736,17 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) : (1 << (ep_index(ep))); /* check if the pipe is empty */ - if (!(list_empty(&ep->queue))) { + if (!(list_empty(&ep->queue)) && !(ep_index(ep) == 0)) { /* Add td to the end */ struct fsl_req *lastreq; lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); lastreq->tail->next_td_ptr = - cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); + /* Ensure dTD's next dtd pointer to be updated */ + wmb(); /* Read prime bit, if 1 goto done */ if (fsl_readl(&dr_regs->endpointprime) & bitmask) - goto out; + return; do { /* Set ATDTW bit in USBCMD */ @@ -634,28 +763,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); if (tmp_stat) - goto out; + return; } - /* Write dQH next pointer and terminate bit to 0 */ - temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - dQH->next_dtd_ptr = cpu_to_le32(temp); - - /* Clear active and halt bit */ - temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE - | EP_QUEUE_HEAD_STATUS_HALT)); - dQH->size_ioc_int_sts &= temp; - - /* Ensure that updates to the QH will occure before priming. */ - wmb(); - - /* Prime endpoint by writing 1 to ENDPTPRIME */ - temp = ep_is_in(ep) - ? (1 << (ep_index(ep) + 16)) - : (1 << (ep_index(ep))); - fsl_writel(temp, &dr_regs->endpointprime); -out: - return; + fsl_prime_ep(ep, req->head); } /* Fill in the dTD structure @@ -665,7 +776,7 @@ out: * @is_last: return flag if it is the last dTD of the request * return: pointer to the built dTD */ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, - dma_addr_t *dma, int *is_last) + dma_addr_t *dma, int *is_last, gfp_t gfp_flags) { u32 swap_temp; struct ep_td_struct *dtd; @@ -674,23 +785,23 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, *length = min(req->req.length - req->req.actual, (unsigned)EP_MAX_LENGTH_TRANSFER); - dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma); + dtd = dma_pool_alloc(udc_controller->td_pool, gfp_flags, dma); if (dtd == NULL) return dtd; dtd->td_dma = *dma; /* Clear reserved field */ - swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp = hc32_to_cpu(dtd->size_ioc_sts); swap_temp &= ~DTD_RESERVED_FIELDS; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); /* Init all of buffer page pointers */ swap_temp = (u32) (req->req.dma + req->req.actual); - dtd->buff_ptr0 = cpu_to_le32(swap_temp); - dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); - dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); - dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); - dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + dtd->buff_ptr0 = cpu_to_hc32(swap_temp); + dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000); req->req.actual += *length; @@ -714,7 +825,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, if (*is_last && !req->req.no_interrupt) swap_temp |= DTD_IOC; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); mb(); @@ -724,7 +835,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, } /* Generate dtd chain for a request */ -static int fsl_req_to_dtd(struct fsl_req *req) +static int fsl_req_to_dtd(struct fsl_req *req, gfp_t gfp_flags) { unsigned count; int is_last; @@ -733,7 +844,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) dma_addr_t dma; do { - dtd = fsl_build_dtd(req, &count, &dma, &is_last); + dtd = fsl_build_dtd(req, &count, &dma, &is_last, gfp_flags); if (dtd == NULL) return -ENOMEM; @@ -741,7 +852,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) is_first = 0; req->head = dtd; } else { - last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_ptr = cpu_to_hc32(dma); last_dtd->next_td_virt = dtd; } last_dtd = dtd; @@ -749,7 +860,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) req->dtd_count++; } while (!is_last); - dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE); req->tail = dtd; @@ -764,7 +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 is_iso = 0; + int ret; /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf @@ -772,14 +883,13 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) VDBG("%s, bad params", __func__); return -EINVAL; } - if (unlikely(!_ep || !ep->desc)) { + if (unlikely(!_ep || !ep->ep.desc)) { VDBG("%s, bad ep", __func__); return -EINVAL; } - if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (usb_endpoint_xfer_isoc(ep->ep.desc)) { if (req->req.length > ep->ep.maxpacket) return -EMSGSIZE; - is_iso = 1; } udc = ep->udc; @@ -788,41 +898,22 @@ 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; req->dtd_count = 0; - spin_lock_irqsave(&udc->lock, flags); - /* build dtds and push them to device queue */ - if (!fsl_req_to_dtd(req)) { + if (!fsl_req_to_dtd(req, gfp_flags)) { + spin_lock_irqsave(&udc->lock, flags); fsl_queue_td(ep, req); } else { - spin_unlock_irqrestore(&udc->lock, flags); return -ENOMEM; } - /* Update ep0 state */ - if ((ep_index(ep) == 0)) - udc->ep0_state = DATA_STATE_XMIT; - /* irq handler advances the queue */ if (req != NULL) list_add_tail(&req->queue, &ep->queue); @@ -873,25 +964,20 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) /* The request isn't the last request in this ep queue */ if (req->queue.next != &ep->queue) { - struct ep_queue_head *qh; struct fsl_req *next_req; - qh = ep->qh; next_req = list_entry(req->queue.next, struct fsl_req, queue); - /* Point the QH to the first TD of next request */ - fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr); + /* prime with dTD of next request */ + fsl_prime_ep(ep, next_req->head); } - - /* The request hasn't been processed, patch up the TD chain */ + /* The request hasn't been processed, patch up the TD chain */ } else { struct fsl_req *prev_req; prev_req = list_entry(req->queue.prev, struct fsl_req, queue); - fsl_writel(fsl_readl(&req->tail->next_td_ptr), - &prev_req->tail->next_td_ptr); - + prev_req->tail->next_td_ptr = req->tail->next_td_ptr; } done(ep, req, -ECONNRESET); @@ -927,12 +1013,12 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value) ep = container_of(_ep, struct fsl_ep, ep); udc = ep->udc; - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { status = -EINVAL; goto out; } - if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (usb_endpoint_xfer_isoc(ep->ep.desc)) { status = -EOPNOTSUPP; goto out; } @@ -962,6 +1048,36 @@ out: return status; } +static int fsl_ep_fifo_status(struct usb_ep *_ep) +{ + struct fsl_ep *ep; + struct fsl_udc *udc; + int size = 0; + u32 bitmask; + struct ep_queue_head *qh; + + ep = container_of(_ep, struct fsl_ep, ep); + if (!_ep || (!ep->ep.desc && ep_index(ep) != 0)) + return -ENODEV; + + udc = (struct fsl_udc *)ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + qh = get_qh_by_ep(ep); + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + if (fsl_readl(&dr_regs->endptstatus) & bitmask) + size = (qh->size_ioc_int_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + + pr_debug("%s %u\n", __func__, size); + return size; +} + static void fsl_ep_fifo_flush(struct usb_ep *_ep) { struct fsl_ep *ep; @@ -974,7 +1090,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep) return; } else { ep = container_of(_ep, struct fsl_ep, ep); - if (!ep->desc) + if (!ep->ep.desc) return; } ep_num = ep_index(ep); @@ -1014,6 +1130,7 @@ static struct usb_ep_ops fsl_ep_ops = { .dequeue = fsl_ep_dequeue, .set_halt = fsl_ep_set_halt, + .fifo_status = fsl_ep_fifo_status, .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ }; @@ -1089,8 +1206,8 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); - if (udc->transceiver) - return otg_set_power(udc->transceiver, mA); + if (!IS_ERR_OR_NULL(udc->transceiver)) + return usb_phy_set_power(udc->transceiver, mA); return -ENOTSUPP; } @@ -1102,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), @@ -1113,14 +1234,20 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on) return 0; } +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, + .udc_start = fsl_udc_start, + .udc_stop = fsl_udc_stop, }; /* Set protocol stall on ep0, protocol stall will automatically be cleared @@ -1142,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; @@ -1149,7 +1277,8 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) udc->ep0_dir = USB_DIR_OUT; ep = &udc->eps[0]; - udc->ep0_state = WAIT_FOR_OUT_STATUS; + if (udc->ep0_state != DATA_STATE_XMIT) + udc->ep0_state = WAIT_FOR_OUT_STATUS; req->ep = ep; req->req.length = 0; @@ -1158,7 +1287,11 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) req->req.complete = NULL; req->dtd_count = 0; - if (fsl_req_to_dtd(req) == 0) + 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); else return -ENOMEM; @@ -1199,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]; @@ -1217,7 +1351,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); /* stall if endpoint doesn't exist */ - if (!target_ep->desc) + if (!target_ep->ep.desc) goto stall; tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep)) << USB_ENDPOINT_HALT; @@ -1228,6 +1362,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req = udc->status_req; /* Fill in the reqest structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); + req->ep = ep; req->req.length = 2; req->req.status = -EINPROGRESS; @@ -1235,14 +1370,21 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->req.complete = NULL; req->dtd_count = 0; + 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) == 0)) + if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0)) fsl_queue_td(ep, req); else /* no mem */ goto stall; list_add_tail(&req->queue, &ep->queue); udc->ep0_state = DATA_STATE_XMIT; + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + return; stall: ep0stall(udc); @@ -1280,13 +1422,14 @@ static void setup_received_irq(struct fsl_udc *udc, /* Status phase from udc */ { int rc = -EOPNOTSUPP; + u16 ptc = 0; if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { int pipe = get_pipe_by_windex(wIndex); struct fsl_ep *ep; - if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + if (wValue != 0 || wLength != 0 || pipe >= udc->max_ep) break; ep = get_ep_by_pipe(udc, pipe); @@ -1301,17 +1444,19 @@ static void setup_received_irq(struct fsl_udc *udc, | USB_TYPE_STANDARD)) { /* Note: The driver has not include OTG support yet. * This will be set when OTG support is added */ - if (!gadget_is_otg(&udc->gadget)) - break; - else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) - udc->gadget.b_hnp_enable = 1; - else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) - udc->gadget.a_hnp_support = 1; - else if (setup->bRequest == - USB_DEVICE_A_ALT_HNP_SUPPORT) - udc->gadget.a_alt_hnp_support = 1; - else - break; + if (wValue == USB_DEVICE_TEST_MODE) + ptc = wIndex >> 8; + else if (gadget_is_otg(&udc->gadget)) { + if (setup->bRequest == + USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == + USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + } rc = 0; } else break; @@ -1320,6 +1465,15 @@ static void setup_received_irq(struct fsl_udc *udc, if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc); } + if (ptc) { + u32 tmp; + + mdelay(10); + tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16); + fsl_writel(tmp, &dr_regs->portsc1); + printk(KERN_INFO "udc: switch to test mode %d.\n", ptc); + } + return; } @@ -1339,6 +1493,14 @@ static void setup_received_irq(struct fsl_udc *udc, spin_lock(&udc->lock); udc->ep0_state = (setup->bRequestType & USB_DIR_IN) ? DATA_STATE_XMIT : DATA_STATE_RECV; + /* + * If the data stage is IN, send status prime immediately. + * See 2.0 Spec chapter 8.5.3.3 for detail. + */ + if (udc->ep0_state == DATA_STATE_XMIT) + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + } else { /* No data phase, IN status from gadget */ udc->ep0_dir = USB_DIR_IN; @@ -1367,9 +1529,8 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, switch (udc->ep0_state) { case DATA_STATE_XMIT: - /* receive status phase */ - if (ep0_prime_status(udc, EP_DIR_OUT)) - ep0stall(udc); + /* already primed at setup_received_irq */ + udc->ep0_state = WAIT_FOR_OUT_STATUS; break; case DATA_STATE_RECV: /* send status phase */ @@ -1394,6 +1555,7 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) { u32 temp; struct ep_queue_head *qh; + struct fsl_usb2_platform_data *pdata = udc->pdata; qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; @@ -1408,7 +1570,16 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); /* Copy the setup packet to local buffer */ - memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + if (pdata->le_setup_buf) { + u32 *p = (u32 *)buffer_ptr; + u32 *s = (u32 *)qh->setup_buffer; + + /* Convert little endian setup buffer to CPU endian */ + *p++ = le32_to_cpu(*s++); + *p = le32_to_cpu(*s); + } else { + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); /* Clear Setup Tripwire */ @@ -1432,19 +1603,19 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { - remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; - if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & - DTD_ERROR_MASK)) { + errors = hc32_to_cpu(curr_td->size_ioc_sts); + if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { ERR("dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ - tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; - curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp); status = -EPIPE; /* FIXME: continue with next queued TD? */ @@ -1459,10 +1630,10 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, status = -EILSEQ; break; } else - ERR("Unknown error has occured (0x%x)!\n", + ERR("Unknown error has occurred (0x%x)!\n", errors); - } else if (le32_to_cpu(curr_td->size_ioc_sts) + } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { VDBG("Request not complete"); status = REQ_UNCOMPLETE; @@ -1508,7 +1679,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) if (!bit_pos) return; - for (i = 0; i < udc->max_ep * 2; i++) { + for (i = 0; i < udc->max_ep; i++) { ep_num = i >> 1; direction = i % 2; @@ -1546,31 +1717,31 @@ static void dtd_complete_irq(struct fsl_udc *udc) } } +static inline enum usb_device_speed portscx_device_speed(u32 reg) +{ + switch (reg & PORTSCX_PORT_SPEED_MASK) { + case PORTSCX_PORT_SPEED_HIGH: + return USB_SPEED_HIGH; + case PORTSCX_PORT_SPEED_FULL: + return USB_SPEED_FULL; + case PORTSCX_PORT_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* Process a port change interrupt */ static void port_change_irq(struct fsl_udc *udc) { - u32 speed; + if (udc->bus_reset) + udc->bus_reset = 0; /* Bus resetting is finished */ - if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) /* Get the speed */ - speed = (fsl_readl(&dr_regs->portsc1) - & PORTSCX_PORT_SPEED_MASK); - switch (speed) { - case PORTSCX_PORT_SPEED_HIGH: - udc->gadget.speed = USB_SPEED_HIGH; - break; - case PORTSCX_PORT_SPEED_FULL: - udc->gadget.speed = USB_SPEED_FULL; - break; - case PORTSCX_PORT_SPEED_LOW: - udc->gadget.speed = USB_SPEED_LOW; - break; - default: - udc->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - } + udc->gadget.speed = + portscx_device_speed(fsl_readl(&dr_regs->portsc1)); /* Update USB state */ if (!udc->resume_state) @@ -1658,6 +1829,8 @@ static void reset_irq(struct fsl_udc *udc) if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { VDBG("Bus reset"); + /* Bus is reseting */ + udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue * head and TR Queue */ reset_queues(udc); @@ -1735,6 +1908,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { + VDBG("reset int"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1757,71 +1931,57 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) * Hook to gadget drivers * Called by initialization code of gadget drivers *----------------------------------------------------------------*/ -int usb_gadget_register_driver(struct usb_gadget_driver *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->speed != USB_SPEED_FULL - && driver->speed != USB_SPEED_HIGH) - || !driver->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 = driver->bind(&udc_controller->gadget); - 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; + printk(KERN_INFO "Suspend udc for OTG auto detect\n"); + + /* connect to bus through transceiver */ + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { + retval = otg_set_peripheral( + udc_controller->transceiver->otg, + &udc_controller->gadget); + if (retval < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind(&udc_controller->gadget); + udc_controller->driver = 0; + return retval; + } + } + } else { + /* Enable DR IRQ reg and set USBCMD reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; } - /* Enable DR IRQ reg and Set usbcmd reg Run bit */ - dr_controller_run(udc_controller); - udc_controller->usb_state = USB_STATE_ATTACHED; - 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; } -EXPORT_SYMBOL(usb_gadget_register_driver); /* Disconnect from gadget driver */ -int usb_gadget_unregister_driver(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 (udc_controller->transceiver) - otg_set_peripheral(udc_controller->transceiver, NULL); + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) + otg_set_peripheral(udc_controller->transceiver->otg, NULL); /* stop DR, disable intr */ dr_controller_stop(udc_controller); @@ -1840,19 +2000,10 @@ int usb_gadget_unregister_driver(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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*------------------------------------------------------------------------- PROC File System Support @@ -1863,47 +2014,37 @@ EXPORT_SYMBOL(usb_gadget_unregister_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", @@ -1911,12 +2052,10 @@ 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, - "USB Intrrupt Enable Reg:\n" + seq_printf(m, + "USB Interrupt Enable Reg:\n" "Sleep Enable: %d SOF Received Enable: %d " "Reset Enable: %d\n" "System Error Enable: %d " @@ -1929,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 " @@ -1964,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; @@ -1975,20 +2106,8 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, default: s = "None"; break; } - s;} ), ( { - char *s; - switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { - case PORTSCX_PORT_SPEED_FULL: - s = "Full Speed"; break; - case PORTSCX_PORT_SPEED_LOW: - s = "Low Speed"; break; - case PORTSCX_PORT_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Undefined"; break; - } - s; - } ), + s;} ), + usb_speed_string(portscx_device_speed(tmp_reg)), (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? "Normal PHY mode" : "Low power mode", (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : @@ -2002,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; @@ -2021,99 +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); - tmp_reg = usb_sys_regs->snoop1; - t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); - size -= t; - next += t; +#ifndef CONFIG_ARCH_MXC + if (udc->pdata->have_sysif_regs) { + tmp_reg = usb_sys_regs->snoop1; + 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; + tmp_reg = usb_sys_regs->control; + 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->desc) { - t = scnprintf(next, size, + if (ep->ep.desc) { + 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 */ @@ -2129,7 +2234,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, static void fsl_udc_release(struct device *dev) { complete(udc_controller->done); - dma_free_coherent(dev, udc_controller->ep_qh_size, + dma_free_coherent(dev->parent, udc_controller->ep_qh_size, udc_controller->ep_qh, udc_controller->ep_qh_dma); kfree(udc_controller); } @@ -2147,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); @@ -2180,7 +2283,6 @@ static int __init struct_udc_setup(struct fsl_udc *udc, struct fsl_req, req); /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); - udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); udc->resume_state = USB_STATE_NOTATTACHED; udc->usb_state = USB_STATE_POWERED; @@ -2211,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); @@ -2231,46 +2333,75 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, */ static int __init fsl_udc_probe(struct platform_device *pdev) { + struct fsl_usb2_platform_data *pdata; struct resource *res; int ret = -ENODEV; unsigned int i; u32 dccparams; - if (strcmp(pdev->name, driver_name)) { - VDBG("Wrong device"); - return -ENODEV; - } - 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 = dev_get_platdata(&pdev->dev); + udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; +#ifdef CONFIG_USB_OTG + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { + ERR("Can't find OTG driver!\n"); + ret = -ENODEV; + goto err_kfree; + } + } +#endif + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENXIO; goto err_kfree; } - if (!request_mem_region(res->start, res->end - res->start + 1, - driver_name)) { - ERR("request mem region for %s failed\n", pdev->name); - ret = -EBUSY; - goto err_kfree; + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { + if (!request_mem_region(res->start, resource_size(res), + driver_name)) { + ERR("request mem region for %s failed\n", pdev->name); + ret = -EBUSY; + goto err_kfree; + } } - dr_regs = ioremap(res->start, res->end - res->start + 1); + dr_regs = ioremap(res->start, resource_size(res)); if (!dr_regs) { ret = -ENOMEM; goto err_release_mem_region; } - usb_sys_regs = (struct usb_sys_interface *) - ((u32)dr_regs + USB_DR_SYS_OFFSET); + pdata->regs = (void *)dr_regs; + + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (pdata->init && pdata->init(pdev)) { + ret = -ENODEV; + goto err_iounmap_noclk; + } + + /* Set accessors only after pdata->init() ! */ + fsl_set_accessors(pdata); + +#ifndef CONFIG_ARCH_MXC + if (pdata->have_sysif_regs) + usb_sys_regs = (void *)dr_regs + USB_DR_SYS_OFFSET; +#endif + + /* Initialize USB clocks */ + ret = fsl_udc_clk_init(pdev); + if (ret < 0) + goto err_iounmap_noclk; /* Read Device Controller Capability Parameters register */ dccparams = fsl_readl(&dr_regs->dccparams); @@ -2304,13 +2435,19 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - /* initialize usb hw reg except for regs for EP, - * leave usbintr reg untouched */ - dr_controller_setup(udc_controller); + if (IS_ERR_OR_NULL(udc_controller->transceiver)) { + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); + } + + ret = fsl_udc_clk_finalize(pdev); + if (ret) + goto err_free_irq; /* Setup gadget structure */ udc_controller->gadget.ops = &fsl_gadget_ops; - udc_controller->gadget.is_dualspeed = 1; + udc_controller->gadget.max_speed = USB_SPEED_HIGH; udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; INIT_LIST_HEAD(&udc_controller->gadget.ep_list); udc_controller->gadget.speed = USB_SPEED_UNKNOWN; @@ -2318,11 +2455,10 @@ 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; - ret = device_register(&udc_controller->gadget.dev); - if (ret < 0) - goto err_free_irq; + udc_controller->gadget.dev.of_node = pdev->dev.of_node; + + if (!IS_ERR_OR_NULL(udc_controller->transceiver)) + udc_controller->gadget.is_otg = 1; /* setup QH and epctrl for ep0 */ ep0_setup(udc_controller); @@ -2332,8 +2468,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) /* for ep0: the desc defined here; * for other eps, gadget layer called ep_enable with defined desc */ - udc_controller->eps[0].desc = &fsl_ep0_desc; - udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + udc_controller->eps[0].ep.desc = &fsl_ep0_desc; + 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 */ @@ -2352,19 +2489,30 @@ 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_release(&pdev->dev, &udc_controller->gadget, + fsl_udc_release); + if (ret) + goto err_del_udc; + create_proc_file(); return 0; -err_unregister: - device_unregister(&udc_controller->gadget.dev); +err_del_udc: + dma_pool_destroy(udc_controller->td_pool); err_free_irq: free_irq(udc_controller->irq, udc_controller); err_iounmap: + if (pdata->exit) + pdata->exit(pdev); + fsl_udc_clk_release(); +err_iounmap_noclk: iounmap(dr_regs); err_release_mem_region: - release_mem_region(res->start, res->end - res->start + 1); + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + release_mem_region(res->start, resource_size(res)); err_kfree: kfree(udc_controller); udc_controller = NULL; @@ -2377,12 +2525,17 @@ 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 = dev_get_platdata(&pdev->dev); DECLARE_COMPLETION(done); if (!udc_controller) return -ENODEV; + udc_controller->done = &done; + usb_del_gadget_udc(&udc_controller->gadget); + + fsl_udc_clk_release(); /* DR has been stopped in usb_gadget_unregister_driver() */ remove_proc_file(); @@ -2395,12 +2548,19 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) dma_pool_destroy(udc_controller->td_pool); free_irq(udc_controller->irq, udc_controller); iounmap(dr_regs); - release_mem_region(res->start, res->end - res->start + 1); + 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); + /* + * do platform specific un-initialization: + * release iomux pins, etc. + */ + if (pdata->exit) + pdata->exit(pdev); + return 0; } @@ -2431,36 +2591,91 @@ static int fsl_udc_resume(struct platform_device *pdev) return 0; } +static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state) +{ + struct fsl_udc *udc = udc_controller; + u32 mode, usbcmd; + + mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK; + + pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped); + + /* + * If the controller is already stopped, then this must be a + * PM suspend. Remember this fact, so that we will leave the + * controller stopped at PM resume time. + */ + if (udc->stopped) { + pr_debug("gadget already stopped, leaving early\n"); + udc->already_stopped = 1; + return 0; + } + + if (mode != USB_MODE_CTRL_MODE_DEVICE) { + pr_debug("gadget not in device mode, leaving early\n"); + return 0; + } + + /* stop the controller */ + usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP; + fsl_writel(usbcmd, &dr_regs->usbcmd); + + udc->stopped = 1; + + pr_info("USB Gadget suspended\n"); + + return 0; +} + +static int fsl_udc_otg_resume(struct device *dev) +{ + pr_debug("%s(): stopped %d already_stopped %d\n", __func__, + udc_controller->stopped, udc_controller->already_stopped); + + /* + * If the controller was stopped at suspend time, then + * don't resume it now. + */ + if (udc_controller->already_stopped) { + udc_controller->already_stopped = 0; + pr_debug("gadget was already stopped, leaving early\n"); + return 0; + } + + pr_info("USB Gadget resume\n"); + + return fsl_udc_resume(NULL); +} /*------------------------------------------------------------------------- Register entry point for the peripheral controller driver --------------------------------------------------------------------------*/ - +static const struct platform_device_id fsl_udc_devtype[] = { + { + .name = "imx-udc-mx27", + }, { + .name = "imx-udc-mx51", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); static struct platform_driver udc_driver = { - .remove = __exit_p(fsl_udc_remove), + .remove = __exit_p(fsl_udc_remove), + /* Just for FSL i.mx SoC currently */ + .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ - .suspend = fsl_udc_suspend, - .resume = fsl_udc_resume, - .driver = { - .name = (char *)driver_name, - .owner = THIS_MODULE, + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + /* udc suspend/resume called from OTG driver */ + .suspend = fsl_udc_otg_suspend, + .resume = fsl_udc_otg_resume, }, }; -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/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index e63ef12645f..c6703bb07b2 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -1,4 +1,12 @@ /* + * Copyright (C) 2004,2012 Freescale Semiconductor, Inc + * All rights reserved. + * + * 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. + * * Freescale USB device/endpoint management registers */ #ifndef __FSL_USB2_UDC_H @@ -15,7 +23,7 @@ struct usb_dr_device { u8 res1[256]; u16 caplength; /* Capability Register Length */ u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hcsparams; /* Host Controller Structural Parameters */ u32 hccparams; /* Host Controller Capability Parameters */ u8 res2[20]; u32 dciversion; /* Device Controller Interface Version */ @@ -52,7 +60,7 @@ struct usb_dr_host { u8 res1[256]; u16 caplength; /* Capability Register Length */ u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hcsparams; /* Host Controller Structural Parameters */ u32 hccparams; /* Host Controller Capability Parameters */ u8 res2[20]; u32 dciversion; /* Device Controller Interface Version */ @@ -275,7 +283,9 @@ struct usb_sys_interface { #define USB_MODE_CTRL_MODE_IDLE 0x00000000 #define USB_MODE_CTRL_MODE_DEVICE 0x00000002 #define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_MASK 0x00000003 #define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_ES 0x00000004 /* Endian Select */ #define USB_MODE_SETUP_LOCK_OFF 0x00000008 #define USB_MODE_STREAM_DISABLE 0x00000010 /* Endpoint Flush Register */ @@ -346,6 +356,9 @@ struct usb_sys_interface { /* control Register Bit Masks */ #define USB_CTRL_IOENB 0x00000004 #define USB_CTRL_ULPI_INT0EN 0x00000001 +#define USB_CTRL_UTMI_PHY_EN 0x00000200 +#define USB_CTRL_USB_EN 0x00000004 +#define USB_CTRL_ULPI_PHY_CLK_SEL 0x00000400 /* Endpoint Queue Head data struct * Rem: all the variables of qh are LittleEndian Mode @@ -448,7 +461,6 @@ struct fsl_ep { struct list_head queue; struct fsl_udc *udc; struct ep_queue_head *qh; - const struct usb_endpoint_descriptor *desc; struct usb_gadget *gadget; char name[14]; @@ -461,6 +473,7 @@ struct fsl_ep { struct fsl_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; + struct fsl_usb2_platform_data *pdata; struct completion *done; /* to make sure release() is done */ struct fsl_ep *eps; unsigned int max_ep; @@ -468,11 +481,13 @@ struct fsl_udc { struct usb_ctrlrequest local_setup_buff; spinlock_t lock; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; unsigned softconnect:1; unsigned vbus_active:1; unsigned stopped:1; unsigned remote_wakeup:1; + unsigned already_stopped:1; + unsigned big_endian_desc:1; struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ struct fsl_req *status_req; /* ep0 status request */ @@ -483,6 +498,7 @@ struct fsl_udc { dma_addr_t ep_qh_dma; /* dma address of QH */ u32 max_pipes; /* Device max pipes */ + u32 bus_reset; /* Device is bus resetting */ u32 resume_state; /* USB state to resume */ u32 usb_state; /* USB current state */ u32 ep0_state; /* Endpoint zero state */ @@ -552,10 +568,10 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) /* * ### internal used help routines. */ -#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_index(EP) ((EP)->ep.desc->bEndpointAddress&0xF) #define ep_maxpacket(EP) ((EP)->ep.maxpacket) #define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ - USB_DIR_IN ):((EP)->desc->bEndpointAddress \ + USB_DIR_IN) : ((EP)->ep.desc->bEndpointAddress \ & USB_DIR_IN)==USB_DIR_IN) #define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \ &udc->eps[pipe]) @@ -563,4 +579,33 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) #define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) +static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep) +{ + /* we only have one ep0 structure but two queue heads */ + if (ep_index(ep) != 0) + return ep->qh; + else + return &ep->udc->ep_qh[(ep->udc->ep0_dir == + USB_DIR_IN) ? 1 : 0]; +} + +struct platform_device; +#ifdef CONFIG_ARCH_MXC +int fsl_udc_clk_init(struct platform_device *pdev); +int fsl_udc_clk_finalize(struct platform_device *pdev); +void fsl_udc_clk_release(void); +#else +static inline int fsl_udc_clk_init(struct platform_device *pdev) +{ + return 0; +} +static inline int fsl_udc_clk_finalize(struct platform_device *pdev) +{ + return 0; +} +static inline void fsl_udc_clk_release(void) +{ +} +#endif + #endif 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 new file mode 100644 index 00000000000..3deb4e93807 --- /dev/null +++ b/drivers/usb/gadget/fusb300_udc.c @@ -0,0 +1,1501 @@ +/* + * Fusb300 UDC (USB gadget) + * + * Copyright (C) 2010 Faraday Technology Corp. + * + * 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 "fusb300_udc.h" + +MODULE_DESCRIPTION("FUSB300 USB gadget driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>"); +MODULE_ALIAS("platform:fusb300_udc"); + +#define DRIVER_VERSION "20 October 2010" + +static const char udc_name[] = "fusb300_udc"; +static const char * const fusb300_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9", + "ep10", "ep11", "ep12", "ep13", "ep14", "ep15" +}; + +static void done(struct fusb300_ep *ep, struct fusb300_request *req, + int status); + +static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + u32 reg = ioread32(fusb300->reg + offset); + + reg |= value; + iowrite32(reg, fusb300->reg + offset); +} + +static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + u32 reg = ioread32(fusb300->reg + offset); + + reg &= ~value; + iowrite32(reg, fusb300->reg + offset); +} + + +static void fusb300_ep_setting(struct fusb300_ep *ep, + struct fusb300_ep_info info) +{ + ep->epnum = info.epnum; + ep->type = info.type; +} + +static int fusb300_ep_release(struct fusb300_ep *ep) +{ + if (!ep->epnum) + return 0; + ep->epnum = 0; + ep->stall = 0; + ep->wedged = 0; + return 0; +} + +static void fusb300_set_fifo_entry(struct fusb300 *fusb300, + u32 ep) +{ + u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + + val &= ~FUSB300_EPSET1_FIFOENTRY_MSK; + val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM); + iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); +} + +static void fusb300_set_start_entry(struct fusb300 *fusb300, + u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM; + + reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ; + reg |= FUSB300_EPSET1_START_ENTRY(start_entry); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) { + fusb300->fifo_entry_num = 0; + fusb300->addrofs = 0; + pr_err("fifo entry is over the maximum number!\n"); + } else + fusb300->fifo_entry_num++; +} + +/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */ +static void fusb300_set_epaddrofs(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + + reg &= ~FUSB300_EPSET2_ADDROFS_MSK; + reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM; +} + +static void ep_fifo_setting(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + fusb300_set_fifo_entry(fusb300, info.epnum); + fusb300_set_start_entry(fusb300, info.epnum); + fusb300_set_epaddrofs(fusb300, info); +} + +static void fusb300_set_eptype(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_TYPE_MSK; + reg |= FUSB300_EPSET1_TYPE(info.type); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_epdir(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg; + + if (!info.dir_in) + return; + reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + reg &= ~FUSB300_EPSET1_DIR_MSK; + reg |= FUSB300_EPSET1_DIRIN; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_ep_active(struct fusb300 *fusb300, + u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + + reg |= FUSB300_EPSET1_ACTEN; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); +} + +static void fusb300_set_epmps(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + + reg &= ~FUSB300_EPSET2_MPS_MSK; + reg |= FUSB300_EPSET2_MPS(info.maxpacket); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); +} + +static void fusb300_set_interval(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_INTERVAL(0x7); + reg |= FUSB300_EPSET1_INTERVAL(info.interval); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_bwnum(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_BWNUM(0x3); + reg |= FUSB300_EPSET1_BWNUM(info.bw_num); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void set_ep_reg(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + fusb300_set_eptype(fusb300, info); + fusb300_set_epdir(fusb300, info); + fusb300_set_epmps(fusb300, info); + + if (info.interval) + fusb300_set_interval(fusb300, info); + + if (info.bw_num) + fusb300_set_bwnum(fusb300, info); + + fusb300_set_ep_active(fusb300, info.epnum); +} + +static int config_ep(struct fusb300_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fusb300 *fusb300 = ep->fusb300; + struct fusb300_ep_info info; + + ep->ep.desc = desc; + + info.interval = 0; + info.addrofs = 0; + info.bw_num = 0; + + info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; + info.maxpacket = usb_endpoint_maxp(desc); + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + + if ((info.type == USB_ENDPOINT_XFER_INT) || + (info.type == USB_ENDPOINT_XFER_ISOC)) { + info.interval = desc->bInterval; + if (info.type == USB_ENDPOINT_XFER_ISOC) + info.bw_num = ((desc->wMaxPacketSize & 0x1800) >> 11); + } + + ep_fifo_setting(fusb300, info); + + set_ep_reg(fusb300, info); + + fusb300_ep_setting(ep, info); + + fusb300->ep[info.epnum] = ep; + + return 0; +} + +static int fusb300_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fusb300_ep *ep; + + ep = container_of(_ep, struct fusb300_ep, ep); + + if (ep->fusb300->reenum) { + ep->fusb300->fifo_entry_num = 0; + ep->fusb300->addrofs = 0; + ep->fusb300->reenum = 0; + } + + return config_ep(ep, desc); +} + +static int fusb300_disable(struct usb_ep *_ep) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fusb300_ep, ep); + + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct fusb300_request, queue); + spin_lock_irqsave(&ep->fusb300->lock, flags); + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + } + + return fusb300_ep_release(ep); +} + +static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct fusb300_request *req; + + req = kzalloc(sizeof(struct fusb300_request), gfp_flags); + if (!req) + return NULL; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fusb300_request *req; + + req = container_of(_req, struct fusb300_request, req); + kfree(req); +} + +static int enable_fifo_int(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + + if (ep->epnum) { + fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); + } else { + pr_err("can't enable_fifo_int ep0\n"); + return -EINVAL; + } + + return 0; +} + +static int disable_fifo_int(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + + if (ep->epnum) { + fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); + } else { + pr_err("can't disable_fifo_int ep0\n"); + return -EINVAL; + } + + return 0; +} + +static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length) +{ + u32 reg; + + reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); + reg &= ~FUSB300_CSR_LEN_MSK; + reg |= FUSB300_CSR_LEN(length); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR); +} + +/* write data to cx fifo */ +static void fusb300_wrcxf(struct fusb300_ep *ep, + struct fusb300_request *req) +{ + int i = 0; + u8 *tmp; + u32 data; + struct fusb300 *fusb300 = ep->fusb300; + u32 length = req->req.length - req->req.actual; + + tmp = req->req.buf + req->req.actual; + + if (length > SS_CTL_MAX_PACKET_SIZE) { + fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE); + for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) { + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | + *(tmp + 3) << 24; + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + tmp += 4; + } + req->req.actual += SS_CTL_MAX_PACKET_SIZE; + } else { /* length is less than max packet size */ + fusb300_set_cxlen(fusb300, length); + for (i = length >> 2; i > 0; i--) { + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | + *(tmp + 3) << 24; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + tmp = tmp + 4; + } + switch (length % 4) { + case 1: + data = *tmp; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + case 2: + data = *tmp | *(tmp + 1) << 8; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + case 3: + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + default: + break; + } + req->req.actual += length; + } +} + +static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), + FUSB300_EPSET0_STL); +} + +static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + + if (reg & FUSB300_EPSET0_STL) { + printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep); + reg |= FUSB300_EPSET0_STL_CLR; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + } +} + +static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req) +{ + if (ep->fusb300->ep0_dir) { /* if IN */ + if (req->req.length) { + fusb300_wrcxf(ep, req); + } else + printk(KERN_DEBUG "%s : req->req.length = 0x%x\n", + __func__, req->req.length); + if ((req->req.length == req->req.actual) || + (req->req.actual < ep->ep.maxpacket)) + done(ep, req, 0); + } else { /* OUT */ + if (!req->req.length) + done(ep, req, 0); + else + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1, + FUSB300_IGER1_CX_OUT_INT); + } +} + +static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct fusb300_ep, ep); + req = container_of(_req, struct fusb300_request, req); + + if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->fusb300->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->ep.desc == NULL) /* ep0 */ + ep0_queue(ep, req); + else if (request && !ep->stall) + enable_fifo_int(ep); + + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + + return 0; +} + +static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fusb300_ep, ep); + req = container_of(_req, struct fusb300_request, req); + + spin_lock_irqsave(&ep->fusb300->lock, flags); + if (!list_empty(&ep->queue)) + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + + return 0; +} + +static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +{ + struct fusb300_ep *ep; + struct fusb300 *fusb300; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct fusb300_ep, ep); + + fusb300 = ep->fusb300; + + spin_lock_irqsave(&ep->fusb300->lock, flags); + + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + + if (value) { + fusb300_set_epnstall(fusb300, ep->epnum); + ep->stall = 1; + if (wedge) + ep->wedged = 1; + } else { + fusb300_clear_epnstall(fusb300, ep->epnum); + ep->stall = 0; + ep->wedged = 0; + } + +out: + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + return ret; +} + +static int fusb300_set_halt(struct usb_ep *_ep, int value) +{ + return fusb300_set_halt_and_wedge(_ep, value, 0); +} + +static int fusb300_set_wedge(struct usb_ep *_ep) +{ + return fusb300_set_halt_and_wedge(_ep, 1, 1); +} + +static void fusb300_fifo_flush(struct usb_ep *_ep) +{ +} + +static struct usb_ep_ops fusb300_ep_ops = { + .enable = fusb300_enable, + .disable = fusb300_disable, + + .alloc_request = fusb300_alloc_request, + .free_request = fusb300_free_request, + + .queue = fusb300_queue, + .dequeue = fusb300_dequeue, + + .set_halt = fusb300_set_halt, + .fifo_flush = fusb300_fifo_flush, + .set_wedge = fusb300_set_wedge, +}; + +/*****************************************************************************/ +static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + iowrite32(value, fusb300->reg + offset); +} + +static void fusb300_reset(void) +{ +} + +static void fusb300_set_cxstall(struct fusb300 *fusb300) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, + FUSB300_CSR_STL); +} + +static void fusb300_set_cxdone(struct fusb300 *fusb300) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, + FUSB300_CSR_DONE); +} + +/* read data from cx fifo */ +static void fusb300_rdcxf(struct fusb300 *fusb300, + u8 *buffer, u32 length) +{ + int i = 0; + u8 *tmp; + u32 data; + + tmp = buffer; + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *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(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } +} + +static void fusb300_rdfifo(struct fusb300_ep *ep, + struct fusb300_request *req, + u32 length) +{ + int i = 0; + u8 *tmp; + u32 data, reg; + struct fusb300 *fusb300 = ep->fusb300; + + tmp = req->req.buf + req->req.actual; + req->req.actual += length; + + if (req->req.actual > req->req.length) + printk(KERN_DEBUG "req->req.actual > req->req.length\n"); + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *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(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } + + do { + reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); + reg &= FUSB300_IGR1_SYNF0_EMPTY_INT; + if (i) + printk(KERN_INFO "sync fifo is not empty!\n"); + i++; + } while (!reg); +} + +static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep) +{ + u8 value; + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + + value = reg & FUSB300_EPSET0_STL; + + return value; +} + +static u8 fusb300_get_cxstall(struct fusb300 *fusb300) +{ + u8 value; + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); + + value = (reg & FUSB300_CSR_STL) >> 1; + + return value; +} + +static void request_error(struct fusb300 *fusb300) +{ + fusb300_set_cxstall(fusb300); + printk(KERN_DEBUG "request error!!\n"); +} + +static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +__releases(fusb300->lock) +__acquires(fusb300->lock) +{ + u8 ep; + u16 status = 0; + u16 w_index = ctrl->wIndex; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = w_index & USB_ENDPOINT_NUMBER_MASK; + if (ep) { + if (fusb300_get_epnstall(fusb300, ep)) + status = 1 << USB_ENDPOINT_HALT; + } else { + if (fusb300_get_cxstall(fusb300)) + status = 0; + } + break; + + default: + request_error(fusb300); + return; /* exit */ + } + + fusb300->ep0_data = cpu_to_le16(status); + fusb300->ep0_req->buf = &fusb300->ep0_data; + fusb300->ep0_req->length = 2; + + spin_unlock(&fusb300->lock); + fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL); + spin_lock(&fusb300->lock); +} + +static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + u8 ep; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_INTERFACE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_ENDPOINT: { + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = w_index & USB_ENDPOINT_NUMBER_MASK; + if (ep) + fusb300_set_epnstall(fusb300, ep); + else + fusb300_set_cxstall(fusb300); + fusb300_set_cxdone(fusb300); + } + break; + default: + request_error(fusb300); + break; + } +} + +static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), + FUSB300_EPSET0_CLRSEQNUM); +} + +static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + struct fusb300_ep *ep = + fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_INTERFACE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_ENDPOINT: + if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { + if (ep->wedged) { + fusb300_set_cxdone(fusb300); + break; + } + if (ep->stall) { + ep->stall = 0; + fusb300_clear_seqnum(fusb300, ep->epnum); + fusb300_clear_epnstall(fusb300, ep->epnum); + if (!list_empty(&ep->queue)) + enable_fifo_int(ep); + } + } + fusb300_set_cxdone(fusb300); + break; + default: + request_error(fusb300); + break; + } +} + +static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR); + + reg &= ~FUSB300_DAR_DRVADDR_MSK; + reg |= FUSB300_DAR_DRVADDR(addr); + + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR); +} + +static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + if (ctrl->wValue >= 0x0100) + request_error(fusb300); + else { + fusb300_set_dev_addr(fusb300, ctrl->wValue); + fusb300_set_cxdone(fusb300); + } +} + +#define UVC_COPY_DESCRIPTORS(mem, src) \ + do { \ + const struct usb_descriptor_header * const *__src; \ + for (__src = src; *__src; ++__src) { \ + memcpy(mem, *__src, (*__src)->bLength); \ + mem += (*__src)->bLength; \ + } \ + } while (0) + +static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + u8 *p = (u8 *)ctrl; + u8 ret = 0; + u8 i = 0; + + fusb300_rdcxf(fusb300, p, 8); + fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN; + fusb300->ep0_length = ctrl->wLength; + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(fusb300, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(fusb300, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(fusb300, ctrl); + break; + case USB_REQ_SET_ADDRESS: + set_address(fusb300, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR, + FUSB300_DAR_SETCONFG); + /* clear sequence number */ + for (i = 1; i <= FUSB300_MAX_NUM_EP; i++) + fusb300_clear_seqnum(fusb300, i); + fusb300->reenum = 1; + ret = 1; + break; + default: + ret = 1; + break; + } + } else + ret = 1; + + return ret; +} + +static void done(struct fusb300_ep *ep, struct fusb300_request *req, + int status) +{ + list_del_init(&req->queue); + + /* don't modify queue heads during completion callback */ + if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + spin_unlock(&ep->fusb300->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->fusb300->lock); + + if (ep->epnum) { + disable_fifo_int(ep); + if (!list_empty(&ep->queue)) + enable_fifo_int(ep); + } else + fusb300_set_cxdone(ep->fusb300); +} + +static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d, + u32 len) +{ + u32 value; + u32 reg; + + /* wait SW owner */ + do { + reg = ioread32(ep->fusb300->reg + + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); + reg &= FUSB300_EPPRD0_H; + } while (reg); + + iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum)); + + value = FUSB300_EPPRD0_BTC(len) | FUSB300_EPPRD0_H | + FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I; + iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); + + iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum)); + + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY, + FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum)); +} + +static void fusb300_wait_idma_finished(struct fusb300_ep *ep) +{ + u32 reg; + + do { + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1); + if ((reg & FUSB300_IGR1_VBUS_CHG_INT) || + (reg & FUSB300_IGR1_WARM_RST_INT) || + (reg & FUSB300_IGR1_HOT_RST_INT) || + (reg & FUSB300_IGR1_USBRST_INT) + ) + goto IDMA_RESET; + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0); + reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum); + } while (!reg); + + fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0, + FUSB300_IGR0_EPn_PRD_INT(ep->epnum)); + return; + +IDMA_RESET: + 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, + struct fusb300_request *req) +{ + int ret; + + ret = usb_gadget_map_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); + if (ret) + return; + + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); + + fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length); + /* check idma is done */ + fusb300_wait_idma_finished(ep); + + usb_gadget_unmap_request(&ep->fusb300->gadget, + &req->req, DMA_TO_DEVICE); +} + +static void in_ep_fifo_handler(struct fusb300_ep *ep) +{ + struct fusb300_request *req = list_entry(ep->queue.next, + struct fusb300_request, queue); + + if (req->req.length) + fusb300_set_idma(ep, req); + done(ep, req, 0); +} + +static void out_ep_fifo_handler(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + struct fusb300_request *req = list_entry(ep->queue.next, + struct fusb300_request, queue); + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); + u32 length = reg & FUSB300_FFR_BYCNT; + + fusb300_rdfifo(ep, req, length); + + /* finish out transfer */ + if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket)) + done(ep, req, 0); +} + +static void check_device_mode(struct fusb300 *fusb300) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR); + + switch (reg & FUSB300_GCR_DEVEN_MSK) { + case FUSB300_GCR_DEVEN_SS: + fusb300->gadget.speed = USB_SPEED_SUPER; + break; + case FUSB300_GCR_DEVEN_HS: + fusb300->gadget.speed = USB_SPEED_HIGH; + break; + case FUSB300_GCR_DEVEN_FS: + fusb300->gadget.speed = USB_SPEED_FULL; + break; + default: + fusb300->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + printk(KERN_INFO "dev_mode = %d\n", (reg & FUSB300_GCR_DEVEN_MSK)); +} + + +static void fusb300_ep0out(struct fusb300 *fusb300) +{ + struct fusb300_ep *ep = fusb300->ep[0]; + u32 reg; + + if (!list_empty(&ep->queue)) { + struct fusb300_request *req; + + req = list_first_entry(&ep->queue, + struct fusb300_request, queue); + if (req->req.length) + fusb300_rdcxf(ep->fusb300, req->req.buf, + req->req.length); + done(ep, req, 0); + reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1); + reg &= ~FUSB300_IGER1_CX_OUT_INT; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_IGER1); + } else + pr_err("%s : empty queue\n", __func__); +} + +static void fusb300_ep0in(struct fusb300 *fusb300) +{ + struct fusb300_request *req; + struct fusb300_ep *ep = fusb300->ep[0]; + + if ((!list_empty(&ep->queue)) && (fusb300->ep0_dir)) { + req = list_entry(ep->queue.next, + struct fusb300_request, queue); + if (req->req.length) + fusb300_wrcxf(ep, req); + if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + done(ep, req, 0); + } else + fusb300_set_cxdone(fusb300); +} + +static void fusb300_grp2_handler(void) +{ +} + +static void fusb300_grp3_handler(void) +{ +} + +static void fusb300_grp4_handler(void) +{ +} + +static void fusb300_grp5_handler(void) +{ +} + +static irqreturn_t fusb300_irq(int irq, void *_fusb300) +{ + struct fusb300 *fusb300 = _fusb300; + u32 int_grp1 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); + u32 int_grp1_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1); + u32 int_grp0 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR0); + u32 int_grp0_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER0); + struct usb_ctrlrequest ctrl; + u8 in; + u32 reg; + int i; + + spin_lock(&fusb300->lock); + + int_grp1 &= int_grp1_en; + int_grp0 &= int_grp0_en; + + if (int_grp1 & FUSB300_IGR1_WARM_RST_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_WARM_RST_INT); + printk(KERN_INFO"fusb300_warmreset\n"); + fusb300_reset(); + } + + if (int_grp1 & FUSB300_IGR1_HOT_RST_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_HOT_RST_INT); + printk(KERN_INFO"fusb300_hotreset\n"); + fusb300_reset(); + } + + if (int_grp1 & FUSB300_IGR1_USBRST_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_USBRST_INT); + fusb300_reset(); + } + /* COMABT_INT has a highest priority */ + + if (int_grp1 & FUSB300_IGR1_CX_COMABT_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_CX_COMABT_INT); + printk(KERN_INFO"fusb300_ep0abt\n"); + } + + if (int_grp1 & FUSB300_IGR1_VBUS_CHG_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_VBUS_CHG_INT); + printk(KERN_INFO"fusb300_vbus_change\n"); + } + + if (int_grp1 & FUSB300_IGR1_U3_EXIT_FAIL_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U3_EXIT_FAIL_INT); + } + + if (int_grp1 & FUSB300_IGR1_U2_EXIT_FAIL_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U2_EXIT_FAIL_INT); + } + + if (int_grp1 & FUSB300_IGR1_U1_EXIT_FAIL_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U1_EXIT_FAIL_INT); + } + + if (int_grp1 & FUSB300_IGR1_U2_ENTRY_FAIL_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U2_ENTRY_FAIL_INT); + } + + if (int_grp1 & FUSB300_IGR1_U1_ENTRY_FAIL_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U1_ENTRY_FAIL_INT); + } + + if (int_grp1 & FUSB300_IGR1_U3_EXIT_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U3_EXIT_INT); + printk(KERN_INFO "FUSB300_IGR1_U3_EXIT_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_U2_EXIT_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U2_EXIT_INT); + printk(KERN_INFO "FUSB300_IGR1_U2_EXIT_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_U1_EXIT_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U1_EXIT_INT); + printk(KERN_INFO "FUSB300_IGR1_U1_EXIT_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_U3_ENTRY_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U3_ENTRY_INT); + printk(KERN_INFO "FUSB300_IGR1_U3_ENTRY_INT\n"); + fusb300_enable_bit(fusb300, FUSB300_OFFSET_SSCR1, + FUSB300_SSCR1_GO_U3_DONE); + } + + if (int_grp1 & FUSB300_IGR1_U2_ENTRY_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U2_ENTRY_INT); + printk(KERN_INFO "FUSB300_IGR1_U2_ENTRY_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_U1_ENTRY_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_U1_ENTRY_INT); + printk(KERN_INFO "FUSB300_IGR1_U1_ENTRY_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_RESM_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_RESM_INT); + printk(KERN_INFO "fusb300_resume\n"); + } + + if (int_grp1 & FUSB300_IGR1_SUSP_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_SUSP_INT); + printk(KERN_INFO "fusb300_suspend\n"); + } + + if (int_grp1 & FUSB300_IGR1_HS_LPM_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_HS_LPM_INT); + printk(KERN_INFO "fusb300_HS_LPM_INT\n"); + } + + if (int_grp1 & FUSB300_IGR1_DEV_MODE_CHG_INT) { + fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, + FUSB300_IGR1_DEV_MODE_CHG_INT); + check_device_mode(fusb300); + } + + if (int_grp1 & FUSB300_IGR1_CX_COMFAIL_INT) { + fusb300_set_cxstall(fusb300); + printk(KERN_INFO "fusb300_ep0fail\n"); + } + + if (int_grp1 & FUSB300_IGR1_CX_SETUP_INT) { + printk(KERN_INFO "fusb300_ep0setup\n"); + if (setup_packet(fusb300, &ctrl)) { + spin_unlock(&fusb300->lock); + if (fusb300->driver->setup(&fusb300->gadget, &ctrl) < 0) + fusb300_set_cxstall(fusb300); + spin_lock(&fusb300->lock); + } + } + + if (int_grp1 & FUSB300_IGR1_CX_CMDEND_INT) + printk(KERN_INFO "fusb300_cmdend\n"); + + + if (int_grp1 & FUSB300_IGR1_CX_OUT_INT) { + printk(KERN_INFO "fusb300_cxout\n"); + fusb300_ep0out(fusb300); + } + + if (int_grp1 & FUSB300_IGR1_CX_IN_INT) { + printk(KERN_INFO "fusb300_cxin\n"); + fusb300_ep0in(fusb300); + } + + if (int_grp1 & FUSB300_IGR1_INTGRP5) + fusb300_grp5_handler(); + + if (int_grp1 & FUSB300_IGR1_INTGRP4) + fusb300_grp4_handler(); + + if (int_grp1 & FUSB300_IGR1_INTGRP3) + fusb300_grp3_handler(); + + if (int_grp1 & FUSB300_IGR1_INTGRP2) + fusb300_grp2_handler(); + + if (int_grp0) { + for (i = 1; i < FUSB300_MAX_NUM_EP; i++) { + if (int_grp0 & FUSB300_IGR0_EPn_FIFO_INT(i)) { + reg = ioread32(fusb300->reg + + FUSB300_OFFSET_EPSET1(i)); + in = (reg & FUSB300_EPSET1_DIRIN) ? 1 : 0; + if (in) + in_ep_fifo_handler(fusb300->ep[i]); + else + out_ep_fifo_handler(fusb300->ep[i]); + } + } + } + + spin_unlock(&fusb300->lock); + + return IRQ_HANDLED; +} + +static void fusb300_set_u2_timeout(struct fusb300 *fusb300, + u32 time) +{ + u32 reg; + + reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT); + reg &= ~0xff; + reg |= FUSB300_SSCR2_U2TIMEOUT(time); + + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT); +} + +static void fusb300_set_u1_timeout(struct fusb300 *fusb300, + u32 time) +{ + u32 reg; + + reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT); + reg &= ~(0xff << 8); + reg |= FUSB300_SSCR2_U1TIMEOUT(time); + + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT); +} + +static void init_controller(struct fusb300 *fusb300) +{ + u32 reg; + u32 mask = 0; + u32 val = 0; + + /* split on */ + mask = val = FUSB300_AHBBCR_S0_SPLIT_ON | FUSB300_AHBBCR_S1_SPLIT_ON; + reg = ioread32(fusb300->reg + FUSB300_OFFSET_AHBCR); + reg &= ~mask; + reg |= val; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_AHBCR); + + /* enable high-speed LPM */ + mask = val = FUSB300_HSCR_HS_LPM_PERMIT; + reg = ioread32(fusb300->reg + FUSB300_OFFSET_HSCR); + reg &= ~mask; + reg |= val; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); + + /*set u1 u2 timmer*/ + fusb300_set_u2_timeout(fusb300, 0xff); + fusb300_set_u1_timeout(fusb300, 0xff); + + /* enable all grp1 interrupt */ + iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1); +} +/*------------------------------------------------------------------------*/ +static int fusb300_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fusb300 *fusb300 = to_fusb300(g); + + /* hook up the driver */ + driver->driver.bus = NULL; + fusb300->driver = driver; + + return 0; +} + +static int fusb300_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fusb300 *fusb300 = to_fusb300(g); + + driver->unbind(&fusb300->gadget); + + init_controller(fusb300); + fusb300->driver = NULL; + + return 0; +} +/*--------------------------------------------------------------------------*/ + +static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active) +{ + return 0; +} + +static const struct usb_gadget_ops fusb300_gadget_ops = { + .pullup = fusb300_udc_pullup, + .udc_start = fusb300_udc_start, + .udc_stop = fusb300_udc_stop, +}; + +static int __exit fusb300_remove(struct platform_device *pdev) +{ + struct fusb300 *fusb300 = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&fusb300->gadget); + iounmap(fusb300->reg); + free_irq(platform_get_irq(pdev, 0), fusb300); + + fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); + kfree(fusb300); + + return 0; +} + +static int __init fusb300_probe(struct platform_device *pdev) +{ + struct resource *res, *ires, *ires1; + void __iomem *reg = NULL; + struct fusb300 *fusb300 = NULL; + struct fusb300_ep *_ep[FUSB300_MAX_NUM_EP]; + int ret = 0; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + pr_err("platform_get_resource error.\n"); + goto clean_up; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { + ret = -ENODEV; + dev_err(&pdev->dev, + "platform_get_resource IORESOURCE_IRQ error.\n"); + goto clean_up; + } + + ires1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!ires1) { + ret = -ENODEV; + dev_err(&pdev->dev, + "platform_get_resource IORESOURCE_IRQ 1 error.\n"); + goto clean_up; + } + + reg = ioremap(res->start, resource_size(res)); + if (reg == NULL) { + ret = -ENOMEM; + pr_err("ioremap error.\n"); + goto clean_up; + } + + /* initialize udc */ + fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); + 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) + goto clean_up; + fusb300->ep[i] = _ep[i]; + } + + spin_lock_init(&fusb300->lock); + + platform_set_drvdata(pdev, fusb300); + + fusb300->gadget.ops = &fusb300_gadget_ops; + + fusb300->gadget.max_speed = USB_SPEED_HIGH; + fusb300->gadget.name = udc_name; + fusb300->reg = reg; + + ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, + udc_name, fusb300); + if (ret < 0) { + pr_err("request_irq error (%d)\n", ret); + goto clean_up; + } + + ret = request_irq(ires1->start, fusb300_irq, + IRQF_SHARED, udc_name, fusb300); + if (ret < 0) { + pr_err("request_irq1 error (%d)\n", ret); + goto clean_up; + } + + INIT_LIST_HEAD(&fusb300->gadget.ep_list); + + for (i = 0; i < FUSB300_MAX_NUM_EP ; i++) { + struct fusb300_ep *ep = fusb300->ep[i]; + + if (i != 0) { + INIT_LIST_HEAD(&fusb300->ep[i]->ep.ep_list); + list_add_tail(&fusb300->ep[i]->ep.ep_list, + &fusb300->gadget.ep_list); + } + ep->fusb300 = fusb300; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = fusb300_ep_name[i]; + ep->ep.ops = &fusb300_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_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); + + fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep, + GFP_KERNEL); + if (fusb300->ep0_req == NULL) { + ret = -ENOMEM; + goto clean_up3; + } + + init_controller(fusb300); + ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget); + if (ret) + goto err_add_udc; + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + + return 0; + +err_add_udc: + fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); + +clean_up3: + free_irq(ires->start, fusb300); + +clean_up: + if (fusb300) { + if (fusb300->ep0_req) + fusb300_free_request(&fusb300->ep[0]->ep, + fusb300->ep0_req); + kfree(fusb300); + } + if (reg) + iounmap(reg); + + return ret; +} + +static struct platform_driver fusb300_driver = { + .remove = __exit_p(fusb300_remove), + .driver = { + .name = (char *) udc_name, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver_probe(fusb300_driver, fusb300_probe); diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h new file mode 100644 index 00000000000..ae811d8d38b --- /dev/null +++ b/drivers/usb/gadget/fusb300_udc.h @@ -0,0 +1,678 @@ +/* + * Fusb300 UDC (USB gadget) + * + * Copyright (C) 2010 Faraday Technology Corp. + * + * 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. + */ + + +#ifndef __FUSB300_UDC_H__ +#define __FUSB300_UDC_H_ + +#include <linux/kernel.h> + +#define FUSB300_OFFSET_GCR 0x00 +#define FUSB300_OFFSET_GTM 0x04 +#define FUSB300_OFFSET_DAR 0x08 +#define FUSB300_OFFSET_CSR 0x0C +#define FUSB300_OFFSET_CXPORT 0x10 +#define FUSB300_OFFSET_EPSET0(n) (0x20 + (n - 1) * 0x30) +#define FUSB300_OFFSET_EPSET1(n) (0x24 + (n - 1) * 0x30) +#define FUSB300_OFFSET_EPSET2(n) (0x28 + (n - 1) * 0x30) +#define FUSB300_OFFSET_EPFFR(n) (0x2c + (n - 1) * 0x30) +#define FUSB300_OFFSET_EPSTRID(n) (0x40 + (n - 1) * 0x30) +#define FUSB300_OFFSET_HSPTM 0x300 +#define FUSB300_OFFSET_HSCR 0x304 +#define FUSB300_OFFSET_SSCR0 0x308 +#define FUSB300_OFFSET_SSCR1 0x30C +#define FUSB300_OFFSET_TT 0x310 +#define FUSB300_OFFSET_DEVNOTF 0x314 +#define FUSB300_OFFSET_DNC1 0x318 +#define FUSB300_OFFSET_CS 0x31C +#define FUSB300_OFFSET_SOF 0x324 +#define FUSB300_OFFSET_EFCS 0x328 +#define FUSB300_OFFSET_IGR0 0x400 +#define FUSB300_OFFSET_IGR1 0x404 +#define FUSB300_OFFSET_IGR2 0x408 +#define FUSB300_OFFSET_IGR3 0x40C +#define FUSB300_OFFSET_IGR4 0x410 +#define FUSB300_OFFSET_IGR5 0x414 +#define FUSB300_OFFSET_IGER0 0x420 +#define FUSB300_OFFSET_IGER1 0x424 +#define FUSB300_OFFSET_IGER2 0x428 +#define FUSB300_OFFSET_IGER3 0x42C +#define FUSB300_OFFSET_IGER4 0x430 +#define FUSB300_OFFSET_IGER5 0x434 +#define FUSB300_OFFSET_DMAHMER 0x500 +#define FUSB300_OFFSET_EPPRDRDY 0x504 +#define FUSB300_OFFSET_DMAEPMR 0x508 +#define FUSB300_OFFSET_DMAENR 0x50C +#define FUSB300_OFFSET_DMAAPR 0x510 +#define FUSB300_OFFSET_AHBCR 0x514 +#define FUSB300_OFFSET_EPPRD_W0(n) (0x520 + (n - 1) * 0x10) +#define FUSB300_OFFSET_EPPRD_W1(n) (0x524 + (n - 1) * 0x10) +#define FUSB300_OFFSET_EPPRD_W2(n) (0x528 + (n - 1) * 0x10) +#define FUSB300_OFFSET_EPRD_PTR(n) (0x52C + (n - 1) * 0x10) +#define FUSB300_OFFSET_BUFDBG_START 0x800 +#define FUSB300_OFFSET_BUFDBG_END 0xBFC +#define FUSB300_OFFSET_EPPORT(n) (0x1010 + (n - 1) * 0x10) + +/* + * * Global Control Register (offset = 000H) + * */ +#define FUSB300_GCR_SF_RST (1 << 8) +#define FUSB300_GCR_VBUS_STATUS (1 << 7) +#define FUSB300_GCR_FORCE_HS_SUSP (1 << 6) +#define FUSB300_GCR_SYNC_FIFO1_CLR (1 << 5) +#define FUSB300_GCR_SYNC_FIFO0_CLR (1 << 4) +#define FUSB300_GCR_FIFOCLR (1 << 3) +#define FUSB300_GCR_GLINTEN (1 << 2) +#define FUSB300_GCR_DEVEN_FS 0x3 +#define FUSB300_GCR_DEVEN_HS 0x2 +#define FUSB300_GCR_DEVEN_SS 0x1 +#define FUSB300_GCR_DEVDIS 0x0 +#define FUSB300_GCR_DEVEN_MSK 0x3 + + +/* + * *Global Test Mode (offset = 004H) + * */ +#define FUSB300_GTM_TST_DIS_SOFGEN (1 << 16) +#define FUSB300_GTM_TST_CUR_EP_ENTRY(n) ((n & 0xF) << 12) +#define FUSB300_GTM_TST_EP_ENTRY(n) ((n & 0xF) << 8) +#define FUSB300_GTM_TST_EP_NUM(n) ((n & 0xF) << 4) +#define FUSB300_GTM_TST_FIFO_DEG (1 << 1) +#define FUSB300_GTM_TSTMODE (1 << 0) + +/* + * * Device Address Register (offset = 008H) + * */ +#define FUSB300_DAR_SETCONFG (1 << 7) +#define FUSB300_DAR_DRVADDR(x) (x & 0x7F) +#define FUSB300_DAR_DRVADDR_MSK 0x7F + +/* + * *Control Transfer Configuration and Status Register + * (CX_Config_Status, offset = 00CH) + * */ +#define FUSB300_CSR_LEN(x) ((x & 0xFFFF) << 8) +#define FUSB300_CSR_LEN_MSK (0xFFFF << 8) +#define FUSB300_CSR_EMP (1 << 4) +#define FUSB300_CSR_FUL (1 << 3) +#define FUSB300_CSR_CLR (1 << 2) +#define FUSB300_CSR_STL (1 << 1) +#define FUSB300_CSR_DONE (1 << 0) + +/* + * * 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_STL (1 << 0) + +/* + * * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15) + * */ +#define FUSB300_EPSET1_START_ENTRY(x) ((x & 0xFF) << 24) +#define FUSB300_EPSET1_START_ENTRY_MSK (0xFF << 24) +#define FUSB300_EPSET1_FIFOENTRY(x) ((x & 0x1F) << 12) +#define FUSB300_EPSET1_FIFOENTRY_MSK (0x1f << 12) +#define FUSB300_EPSET1_INTERVAL(x) ((x & 0x7) << 6) +#define FUSB300_EPSET1_BWNUM(x) ((x & 0x3) << 4) +#define FUSB300_EPSET1_TYPEISO (1 << 2) +#define FUSB300_EPSET1_TYPEBLK (2 << 2) +#define FUSB300_EPSET1_TYPEINT (3 << 2) +#define FUSB300_EPSET1_TYPE(x) ((x & 0x3) << 2) +#define FUSB300_EPSET1_TYPE_MSK (0x3 << 2) +#define FUSB300_EPSET1_DIROUT (0 << 1) +#define FUSB300_EPSET1_DIRIN (1 << 1) +#define FUSB300_EPSET1_DIR(x) ((x & 0x1) << 1) +#define FUSB300_EPSET1_DIRIN (1 << 1) +#define FUSB300_EPSET1_DIR_MSK ((0x1) << 1) +#define FUSB300_EPSET1_ACTDIS 0 +#define FUSB300_EPSET1_ACTEN 1 + +/* + * *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15) + * */ +#define FUSB300_EPSET2_ADDROFS(x) ((x & 0x7FFF) << 16) +#define FUSB300_EPSET2_ADDROFS_MSK (0x7fff << 16) +#define FUSB300_EPSET2_MPS(x) (x & 0x7FF) +#define FUSB300_EPSET2_MPS_MSK 0x7FF + +/* + * * EPn FIFO Register (offset = 2cH+(n-1)*30H) + * */ +#define FUSB300_FFR_RST (1 << 31) +#define FUSB300_FF_FUL (1 << 30) +#define FUSB300_FF_EMPTY (1 << 29) +#define FUSB300_FFR_BYCNT 0x1FFFF + +/* + * *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15) + * */ +#define FUSB300_STRID_STREN (1 << 16) +#define FUSB300_STRID_STRID(x) (x & 0xFFFF) + +/* + * *HS PHY Test Mode (offset = 300H) + * */ +#define FUSB300_HSPTM_TSTPKDONE (1 << 4) +#define FUSB300_HSPTM_TSTPKT (1 << 3) +#define FUSB300_HSPTM_TSTSET0NAK (1 << 2) +#define FUSB300_HSPTM_TSTKSTA (1 << 1) +#define FUSB300_HSPTM_TSTJSTA (1 << 0) + +/* + * *HS Control Register (offset = 304H) + * */ +#define FUSB300_HSCR_HS_LPM_PERMIT (1 << 8) +#define FUSB300_HSCR_HS_LPM_RMWKUP (1 << 7) +#define FUSB300_HSCR_CAP_LPM_RMWKUP (1 << 6) +#define FUSB300_HSCR_HS_GOSUSP (1 << 5) +#define FUSB300_HSCR_HS_GORMWKU (1 << 4) +#define FUSB300_HSCR_CAP_RMWKUP (1 << 3) +#define FUSB300_HSCR_IDLECNT_0MS 0 +#define FUSB300_HSCR_IDLECNT_1MS 1 +#define FUSB300_HSCR_IDLECNT_2MS 2 +#define FUSB300_HSCR_IDLECNT_3MS 3 +#define FUSB300_HSCR_IDLECNT_4MS 4 +#define FUSB300_HSCR_IDLECNT_5MS 5 +#define FUSB300_HSCR_IDLECNT_6MS 6 +#define FUSB300_HSCR_IDLECNT_7MS 7 + +/* + * * SS Controller Register 0 (offset = 308H) + * */ +#define FUSB300_SSCR0_MAX_INTERVAL(x) ((x & 0x7) << 4) +#define FUSB300_SSCR0_U2_FUN_EN (1 << 1) +#define FUSB300_SSCR0_U1_FUN_EN (1 << 0) + +/* + * * SS Controller Register 1 (offset = 30CH) + * */ +#define FUSB300_SSCR1_GO_U3_DONE (1 << 8) +#define FUSB300_SSCR1_TXDEEMPH_LEVEL (1 << 7) +#define FUSB300_SSCR1_DIS_SCRMB (1 << 6) +#define FUSB300_SSCR1_FORCE_RECOVERY (1 << 5) +#define FUSB300_SSCR1_U3_WAKEUP_EN (1 << 4) +#define FUSB300_SSCR1_U2_EXIT_EN (1 << 3) +#define FUSB300_SSCR1_U1_EXIT_EN (1 << 2) +#define FUSB300_SSCR1_U2_ENTRY_EN (1 << 1) +#define FUSB300_SSCR1_U1_ENTRY_EN (1 << 0) + +/* + * *SS Controller Register 2 (offset = 310H) + * */ +#define FUSB300_SSCR2_SS_TX_SWING (1 << 25) +#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT (1 << 24) +#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x) ((x & 0xFF) << 16) +#define FUSB300_SSCR2_U1TIMEOUT(x) ((x & 0xFF) << 8) +#define FUSB300_SSCR2_U2TIMEOUT(x) (x & 0xFF) + +/* + * *SS Device Notification Control (DEV_NOTF, offset = 314H) + * */ +#define FUSB300_DEVNOTF_CONTEXT0(x) ((x & 0xFFFFFF) << 8) +#define FUSB300_DEVNOTF_TYPE_DIS 0 +#define FUSB300_DEVNOTF_TYPE_FUNCWAKE 1 +#define FUSB300_DEVNOTF_TYPE_LTM 2 +#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG 3 + +/* + * *BFM Arbiter Priority Register (BFM_ARB offset = 31CH) + * */ +#define FUSB300_BFMARB_ARB_M1 (1 << 3) +#define FUSB300_BFMARB_ARB_M0 (1 << 2) +#define FUSB300_BFMARB_ARB_S1 (1 << 1) +#define FUSB300_BFMARB_ARB_S0 1 + +/* + * *Vendor Specific IO Control Register (offset = 320H) + * */ +#define FUSB300_VSIC_VCTLOAD_N (1 << 8) +#define FUSB300_VSIC_VCTL(x) (x & 0x3F) + +/* + * *SOF Mask Timer (offset = 324H) + * */ +#define FUSB300_SOF_MASK_TIMER_HS 0x044c +#define FUSB300_SOF_MASK_TIMER_FS 0x2710 + +/* + * *Error Flag and Control Status (offset = 328H) + * */ +#define FUSB300_EFCS_PM_STATE_U3 3 +#define FUSB300_EFCS_PM_STATE_U2 2 +#define FUSB300_EFCS_PM_STATE_U1 1 +#define FUSB300_EFCS_PM_STATE_U0 0 + +/* + * *Interrupt Group 0 Register (offset = 400H) + * */ +#define FUSB300_IGR0_EP15_PRD_INT (1 << 31) +#define FUSB300_IGR0_EP14_PRD_INT (1 << 30) +#define FUSB300_IGR0_EP13_PRD_INT (1 << 29) +#define FUSB300_IGR0_EP12_PRD_INT (1 << 28) +#define FUSB300_IGR0_EP11_PRD_INT (1 << 27) +#define FUSB300_IGR0_EP10_PRD_INT (1 << 26) +#define FUSB300_IGR0_EP9_PRD_INT (1 << 25) +#define FUSB300_IGR0_EP8_PRD_INT (1 << 24) +#define FUSB300_IGR0_EP7_PRD_INT (1 << 23) +#define FUSB300_IGR0_EP6_PRD_INT (1 << 22) +#define FUSB300_IGR0_EP5_PRD_INT (1 << 21) +#define FUSB300_IGR0_EP4_PRD_INT (1 << 20) +#define FUSB300_IGR0_EP3_PRD_INT (1 << 19) +#define FUSB300_IGR0_EP2_PRD_INT (1 << 18) +#define FUSB300_IGR0_EP1_PRD_INT (1 << 17) +#define FUSB300_IGR0_EPn_PRD_INT(n) (1 << (n + 16)) + +#define FUSB300_IGR0_EP15_FIFO_INT (1 << 15) +#define FUSB300_IGR0_EP14_FIFO_INT (1 << 14) +#define FUSB300_IGR0_EP13_FIFO_INT (1 << 13) +#define FUSB300_IGR0_EP12_FIFO_INT (1 << 12) +#define FUSB300_IGR0_EP11_FIFO_INT (1 << 11) +#define FUSB300_IGR0_EP10_FIFO_INT (1 << 10) +#define FUSB300_IGR0_EP9_FIFO_INT (1 << 9) +#define FUSB300_IGR0_EP8_FIFO_INT (1 << 8) +#define FUSB300_IGR0_EP7_FIFO_INT (1 << 7) +#define FUSB300_IGR0_EP6_FIFO_INT (1 << 6) +#define FUSB300_IGR0_EP5_FIFO_INT (1 << 5) +#define FUSB300_IGR0_EP4_FIFO_INT (1 << 4) +#define FUSB300_IGR0_EP3_FIFO_INT (1 << 3) +#define FUSB300_IGR0_EP2_FIFO_INT (1 << 2) +#define FUSB300_IGR0_EP1_FIFO_INT (1 << 1) +#define FUSB300_IGR0_EPn_FIFO_INT(n) (1 << n) + +/* + * *Interrupt Group 1 Register (offset = 404H) + * */ +#define FUSB300_IGR1_INTGRP5 (1 << 31) +#define FUSB300_IGR1_VBUS_CHG_INT (1 << 30) +#define FUSB300_IGR1_SYNF1_EMPTY_INT (1 << 29) +#define FUSB300_IGR1_SYNF0_EMPTY_INT (1 << 28) +#define FUSB300_IGR1_U3_EXIT_FAIL_INT (1 << 27) +#define FUSB300_IGR1_U2_EXIT_FAIL_INT (1 << 26) +#define FUSB300_IGR1_U1_EXIT_FAIL_INT (1 << 25) +#define FUSB300_IGR1_U2_ENTRY_FAIL_INT (1 << 24) +#define FUSB300_IGR1_U1_ENTRY_FAIL_INT (1 << 23) +#define FUSB300_IGR1_U3_EXIT_INT (1 << 22) +#define FUSB300_IGR1_U2_EXIT_INT (1 << 21) +#define FUSB300_IGR1_U1_EXIT_INT (1 << 20) +#define FUSB300_IGR1_U3_ENTRY_INT (1 << 19) +#define FUSB300_IGR1_U2_ENTRY_INT (1 << 18) +#define FUSB300_IGR1_U1_ENTRY_INT (1 << 17) +#define FUSB300_IGR1_HOT_RST_INT (1 << 16) +#define FUSB300_IGR1_WARM_RST_INT (1 << 15) +#define FUSB300_IGR1_RESM_INT (1 << 14) +#define FUSB300_IGR1_SUSP_INT (1 << 13) +#define FUSB300_IGR1_HS_LPM_INT (1 << 12) +#define FUSB300_IGR1_USBRST_INT (1 << 11) +#define FUSB300_IGR1_DEV_MODE_CHG_INT (1 << 9) +#define FUSB300_IGR1_CX_COMABT_INT (1 << 8) +#define FUSB300_IGR1_CX_COMFAIL_INT (1 << 7) +#define FUSB300_IGR1_CX_CMDEND_INT (1 << 6) +#define FUSB300_IGR1_CX_OUT_INT (1 << 5) +#define FUSB300_IGR1_CX_IN_INT (1 << 4) +#define FUSB300_IGR1_CX_SETUP_INT (1 << 3) +#define FUSB300_IGR1_INTGRP4 (1 << 2) +#define FUSB300_IGR1_INTGRP3 (1 << 1) +#define FUSB300_IGR1_INTGRP2 (1 << 0) + +/* + * *Interrupt Group 2 Register (offset = 408H) + * */ +#define FUSB300_IGR2_EP6_STR_ACCEPT_INT (1 << 29) +#define FUSB300_IGR2_EP6_STR_RESUME_INT (1 << 28) +#define FUSB300_IGR2_EP6_STR_REQ_INT (1 << 27) +#define FUSB300_IGR2_EP6_STR_NOTRDY_INT (1 << 26) +#define FUSB300_IGR2_EP6_STR_PRIME_INT (1 << 25) +#define FUSB300_IGR2_EP5_STR_ACCEPT_INT (1 << 24) +#define FUSB300_IGR2_EP5_STR_RESUME_INT (1 << 23) +#define FUSB300_IGR2_EP5_STR_REQ_INT (1 << 22) +#define FUSB300_IGR2_EP5_STR_NOTRDY_INT (1 << 21) +#define FUSB300_IGR2_EP5_STR_PRIME_INT (1 << 20) +#define FUSB300_IGR2_EP4_STR_ACCEPT_INT (1 << 19) +#define FUSB300_IGR2_EP4_STR_RESUME_INT (1 << 18) +#define FUSB300_IGR2_EP4_STR_REQ_INT (1 << 17) +#define FUSB300_IGR2_EP4_STR_NOTRDY_INT (1 << 16) +#define FUSB300_IGR2_EP4_STR_PRIME_INT (1 << 15) +#define FUSB300_IGR2_EP3_STR_ACCEPT_INT (1 << 14) +#define FUSB300_IGR2_EP3_STR_RESUME_INT (1 << 13) +#define FUSB300_IGR2_EP3_STR_REQ_INT (1 << 12) +#define FUSB300_IGR2_EP3_STR_NOTRDY_INT (1 << 11) +#define FUSB300_IGR2_EP3_STR_PRIME_INT (1 << 10) +#define FUSB300_IGR2_EP2_STR_ACCEPT_INT (1 << 9) +#define FUSB300_IGR2_EP2_STR_RESUME_INT (1 << 8) +#define FUSB300_IGR2_EP2_STR_REQ_INT (1 << 7) +#define FUSB300_IGR2_EP2_STR_NOTRDY_INT (1 << 6) +#define FUSB300_IGR2_EP2_STR_PRIME_INT (1 << 5) +#define FUSB300_IGR2_EP1_STR_ACCEPT_INT (1 << 4) +#define FUSB300_IGR2_EP1_STR_RESUME_INT (1 << 3) +#define FUSB300_IGR2_EP1_STR_REQ_INT (1 << 2) +#define FUSB300_IGR2_EP1_STR_NOTRDY_INT (1 << 1) +#define FUSB300_IGR2_EP1_STR_PRIME_INT (1 << 0) + +#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n) (1 << (5 * n - 1)) +#define FUSB300_IGR2_EP_STR_RESUME_INT(n) (1 << (5 * n - 2)) +#define FUSB300_IGR2_EP_STR_REQ_INT(n) (1 << (5 * n - 3)) +#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n) (1 << (5 * n - 4)) +#define FUSB300_IGR2_EP_STR_PRIME_INT(n) (1 << (5 * n - 5)) + +/* + * *Interrupt Group 3 Register (offset = 40CH) + * */ +#define FUSB300_IGR3_EP12_STR_ACCEPT_INT (1 << 29) +#define FUSB300_IGR3_EP12_STR_RESUME_INT (1 << 28) +#define FUSB300_IGR3_EP12_STR_REQ_INT (1 << 27) +#define FUSB300_IGR3_EP12_STR_NOTRDY_INT (1 << 26) +#define FUSB300_IGR3_EP12_STR_PRIME_INT (1 << 25) +#define FUSB300_IGR3_EP11_STR_ACCEPT_INT (1 << 24) +#define FUSB300_IGR3_EP11_STR_RESUME_INT (1 << 23) +#define FUSB300_IGR3_EP11_STR_REQ_INT (1 << 22) +#define FUSB300_IGR3_EP11_STR_NOTRDY_INT (1 << 21) +#define FUSB300_IGR3_EP11_STR_PRIME_INT (1 << 20) +#define FUSB300_IGR3_EP10_STR_ACCEPT_INT (1 << 19) +#define FUSB300_IGR3_EP10_STR_RESUME_INT (1 << 18) +#define FUSB300_IGR3_EP10_STR_REQ_INT (1 << 17) +#define FUSB300_IGR3_EP10_STR_NOTRDY_INT (1 << 16) +#define FUSB300_IGR3_EP10_STR_PRIME_INT (1 << 15) +#define FUSB300_IGR3_EP9_STR_ACCEPT_INT (1 << 14) +#define FUSB300_IGR3_EP9_STR_RESUME_INT (1 << 13) +#define FUSB300_IGR3_EP9_STR_REQ_INT (1 << 12) +#define FUSB300_IGR3_EP9_STR_NOTRDY_INT (1 << 11) +#define FUSB300_IGR3_EP9_STR_PRIME_INT (1 << 10) +#define FUSB300_IGR3_EP8_STR_ACCEPT_INT (1 << 9) +#define FUSB300_IGR3_EP8_STR_RESUME_INT (1 << 8) +#define FUSB300_IGR3_EP8_STR_REQ_INT (1 << 7) +#define FUSB300_IGR3_EP8_STR_NOTRDY_INT (1 << 6) +#define FUSB300_IGR3_EP8_STR_PRIME_INT (1 << 5) +#define FUSB300_IGR3_EP7_STR_ACCEPT_INT (1 << 4) +#define FUSB300_IGR3_EP7_STR_RESUME_INT (1 << 3) +#define FUSB300_IGR3_EP7_STR_REQ_INT (1 << 2) +#define FUSB300_IGR3_EP7_STR_NOTRDY_INT (1 << 1) +#define FUSB300_IGR3_EP7_STR_PRIME_INT (1 << 0) + +#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) +#define FUSB300_IGR3_EP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) +#define FUSB300_IGR3_EP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) +#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) +#define FUSB300_IGR3_EP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) + +/* + * *Interrupt Group 4 Register (offset = 410H) + * */ +#define FUSB300_IGR4_EP15_RX0_INT (1 << 31) +#define FUSB300_IGR4_EP14_RX0_INT (1 << 30) +#define FUSB300_IGR4_EP13_RX0_INT (1 << 29) +#define FUSB300_IGR4_EP12_RX0_INT (1 << 28) +#define FUSB300_IGR4_EP11_RX0_INT (1 << 27) +#define FUSB300_IGR4_EP10_RX0_INT (1 << 26) +#define FUSB300_IGR4_EP9_RX0_INT (1 << 25) +#define FUSB300_IGR4_EP8_RX0_INT (1 << 24) +#define FUSB300_IGR4_EP7_RX0_INT (1 << 23) +#define FUSB300_IGR4_EP6_RX0_INT (1 << 22) +#define FUSB300_IGR4_EP5_RX0_INT (1 << 21) +#define FUSB300_IGR4_EP4_RX0_INT (1 << 20) +#define FUSB300_IGR4_EP3_RX0_INT (1 << 19) +#define FUSB300_IGR4_EP2_RX0_INT (1 << 18) +#define FUSB300_IGR4_EP1_RX0_INT (1 << 17) +#define FUSB300_IGR4_EP_RX0_INT(x) (1 << (x + 16)) +#define FUSB300_IGR4_EP15_STR_ACCEPT_INT (1 << 14) +#define FUSB300_IGR4_EP15_STR_RESUME_INT (1 << 13) +#define FUSB300_IGR4_EP15_STR_REQ_INT (1 << 12) +#define FUSB300_IGR4_EP15_STR_NOTRDY_INT (1 << 11) +#define FUSB300_IGR4_EP15_STR_PRIME_INT (1 << 10) +#define FUSB300_IGR4_EP14_STR_ACCEPT_INT (1 << 9) +#define FUSB300_IGR4_EP14_STR_RESUME_INT (1 << 8) +#define FUSB300_IGR4_EP14_STR_REQ_INT (1 << 7) +#define FUSB300_IGR4_EP14_STR_NOTRDY_INT (1 << 6) +#define FUSB300_IGR4_EP14_STR_PRIME_INT (1 << 5) +#define FUSB300_IGR4_EP13_STR_ACCEPT_INT (1 << 4) +#define FUSB300_IGR4_EP13_STR_RESUME_INT (1 << 3) +#define FUSB300_IGR4_EP13_STR_REQ_INT (1 << 2) +#define FUSB300_IGR4_EP13_STR_NOTRDY_INT (1 << 1) +#define FUSB300_IGR4_EP13_STR_PRIME_INT (1 << 0) + +#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 12) - 1)) +#define FUSB300_IGR4_EP_STR_RESUME_INT(n) (1 << (5 * (n - 12) - 2)) +#define FUSB300_IGR4_EP_STR_REQ_INT(n) (1 << (5 * (n - 12) - 3)) +#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 12) - 4)) +#define FUSB300_IGR4_EP_STR_PRIME_INT(n) (1 << (5 * (n - 12) - 5)) + +/* + * *Interrupt Group 5 Register (offset = 414H) + * */ +#define FUSB300_IGR5_EP_STL_INT(n) (1 << n) + +/* + * *Interrupt Enable Group 0 Register (offset = 420H) + * */ +#define FUSB300_IGER0_EEP15_PRD_INT (1 << 31) +#define FUSB300_IGER0_EEP14_PRD_INT (1 << 30) +#define FUSB300_IGER0_EEP13_PRD_INT (1 << 29) +#define FUSB300_IGER0_EEP12_PRD_INT (1 << 28) +#define FUSB300_IGER0_EEP11_PRD_INT (1 << 27) +#define FUSB300_IGER0_EEP10_PRD_INT (1 << 26) +#define FUSB300_IGER0_EEP9_PRD_INT (1 << 25) +#define FUSB300_IGER0_EP8_PRD_INT (1 << 24) +#define FUSB300_IGER0_EEP7_PRD_INT (1 << 23) +#define FUSB300_IGER0_EEP6_PRD_INT (1 << 22) +#define FUSB300_IGER0_EEP5_PRD_INT (1 << 21) +#define FUSB300_IGER0_EEP4_PRD_INT (1 << 20) +#define FUSB300_IGER0_EEP3_PRD_INT (1 << 19) +#define FUSB300_IGER0_EEP2_PRD_INT (1 << 18) +#define FUSB300_IGER0_EEP1_PRD_INT (1 << 17) +#define FUSB300_IGER0_EEPn_PRD_INT(n) (1 << (n + 16)) + +#define FUSB300_IGER0_EEP15_FIFO_INT (1 << 15) +#define FUSB300_IGER0_EEP14_FIFO_INT (1 << 14) +#define FUSB300_IGER0_EEP13_FIFO_INT (1 << 13) +#define FUSB300_IGER0_EEP12_FIFO_INT (1 << 12) +#define FUSB300_IGER0_EEP11_FIFO_INT (1 << 11) +#define FUSB300_IGER0_EEP10_FIFO_INT (1 << 10) +#define FUSB300_IGER0_EEP9_FIFO_INT (1 << 9) +#define FUSB300_IGER0_EEP8_FIFO_INT (1 << 8) +#define FUSB300_IGER0_EEP7_FIFO_INT (1 << 7) +#define FUSB300_IGER0_EEP6_FIFO_INT (1 << 6) +#define FUSB300_IGER0_EEP5_FIFO_INT (1 << 5) +#define FUSB300_IGER0_EEP4_FIFO_INT (1 << 4) +#define FUSB300_IGER0_EEP3_FIFO_INT (1 << 3) +#define FUSB300_IGER0_EEP2_FIFO_INT (1 << 2) +#define FUSB300_IGER0_EEP1_FIFO_INT (1 << 1) +#define FUSB300_IGER0_EEPn_FIFO_INT(n) (1 << n) + +/* + * *Interrupt Enable Group 1 Register (offset = 424H) + * */ +#define FUSB300_IGER1_EINT_GRP5 (1 << 31) +#define FUSB300_IGER1_VBUS_CHG_INT (1 << 30) +#define FUSB300_IGER1_SYNF1_EMPTY_INT (1 << 29) +#define FUSB300_IGER1_SYNF0_EMPTY_INT (1 << 28) +#define FUSB300_IGER1_U3_EXIT_FAIL_INT (1 << 27) +#define FUSB300_IGER1_U2_EXIT_FAIL_INT (1 << 26) +#define FUSB300_IGER1_U1_EXIT_FAIL_INT (1 << 25) +#define FUSB300_IGER1_U2_ENTRY_FAIL_INT (1 << 24) +#define FUSB300_IGER1_U1_ENTRY_FAIL_INT (1 << 23) +#define FUSB300_IGER1_U3_EXIT_INT (1 << 22) +#define FUSB300_IGER1_U2_EXIT_INT (1 << 21) +#define FUSB300_IGER1_U1_EXIT_INT (1 << 20) +#define FUSB300_IGER1_U3_ENTRY_INT (1 << 19) +#define FUSB300_IGER1_U2_ENTRY_INT (1 << 18) +#define FUSB300_IGER1_U1_ENTRY_INT (1 << 17) +#define FUSB300_IGER1_HOT_RST_INT (1 << 16) +#define FUSB300_IGER1_WARM_RST_INT (1 << 15) +#define FUSB300_IGER1_RESM_INT (1 << 14) +#define FUSB300_IGER1_SUSP_INT (1 << 13) +#define FUSB300_IGER1_LPM_INT (1 << 12) +#define FUSB300_IGER1_HS_RST_INT (1 << 11) +#define FUSB300_IGER1_EDEV_MODE_CHG_INT (1 << 9) +#define FUSB300_IGER1_CX_COMABT_INT (1 << 8) +#define FUSB300_IGER1_CX_COMFAIL_INT (1 << 7) +#define FUSB300_IGER1_CX_CMDEND_INT (1 << 6) +#define FUSB300_IGER1_CX_OUT_INT (1 << 5) +#define FUSB300_IGER1_CX_IN_INT (1 << 4) +#define FUSB300_IGER1_CX_SETUP_INT (1 << 3) +#define FUSB300_IGER1_INTGRP4 (1 << 2) +#define FUSB300_IGER1_INTGRP3 (1 << 1) +#define FUSB300_IGER1_INTGRP2 (1 << 0) + +/* + * *Interrupt Enable Group 2 Register (offset = 428H) + * */ +#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n) (1 << (5 * n - 1)) +#define FUSB300_IGER2_EEP_STR_RESUME_INT(n) (1 << (5 * n - 2)) +#define FUSB300_IGER2_EEP_STR_REQ_INT(n) (1 << (5 * n - 3)) +#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n) (1 << (5 * n - 4)) +#define FUSB300_IGER2_EEP_STR_PRIME_INT(n) (1 << (5 * n - 5)) + +/* + * *Interrupt Enable Group 3 Register (offset = 42CH) + * */ + +#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) +#define FUSB300_IGER3_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) +#define FUSB300_IGER3_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) +#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) +#define FUSB300_IGER3_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) + +/* + * *Interrupt Enable Group 4 Register (offset = 430H) + * */ + +#define FUSB300_IGER4_EEP_RX0_INT(n) (1 << (n + 16)) +#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) +#define FUSB300_IGER4_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) +#define FUSB300_IGER4_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) +#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) +#define FUSB300_IGER4_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) + +/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */ + +#define FUSB300_EPPRDR_EP15_PRD_RDY (1 << 15) +#define FUSB300_EPPRDR_EP14_PRD_RDY (1 << 14) +#define FUSB300_EPPRDR_EP13_PRD_RDY (1 << 13) +#define FUSB300_EPPRDR_EP12_PRD_RDY (1 << 12) +#define FUSB300_EPPRDR_EP11_PRD_RDY (1 << 11) +#define FUSB300_EPPRDR_EP10_PRD_RDY (1 << 10) +#define FUSB300_EPPRDR_EP9_PRD_RDY (1 << 9) +#define FUSB300_EPPRDR_EP8_PRD_RDY (1 << 8) +#define FUSB300_EPPRDR_EP7_PRD_RDY (1 << 7) +#define FUSB300_EPPRDR_EP6_PRD_RDY (1 << 6) +#define FUSB300_EPPRDR_EP5_PRD_RDY (1 << 5) +#define FUSB300_EPPRDR_EP4_PRD_RDY (1 << 4) +#define FUSB300_EPPRDR_EP3_PRD_RDY (1 << 3) +#define FUSB300_EPPRDR_EP2_PRD_RDY (1 << 2) +#define FUSB300_EPPRDR_EP1_PRD_RDY (1 << 1) +#define FUSB300_EPPRDR_EP_PRD_RDY(n) (1 << n) + +/* AHB Bus Control Register (offset = 514H) */ +#define FUSB300_AHBBCR_S1_SPLIT_ON (1 << 17) +#define FUSB300_AHBBCR_S0_SPLIT_ON (1 << 16) +#define FUSB300_AHBBCR_S1_1entry (0 << 12) +#define FUSB300_AHBBCR_S1_4entry (3 << 12) +#define FUSB300_AHBBCR_S1_8entry (5 << 12) +#define FUSB300_AHBBCR_S1_16entry (7 << 12) +#define FUSB300_AHBBCR_S0_1entry (0 << 8) +#define FUSB300_AHBBCR_S0_4entry (3 << 8) +#define FUSB300_AHBBCR_S0_8entry (5 << 8) +#define FUSB300_AHBBCR_S0_16entry (7 << 8) +#define FUSB300_AHBBCR_M1_BURST_SINGLE (0 << 4) +#define FUSB300_AHBBCR_M1_BURST_INCR (1 << 4) +#define FUSB300_AHBBCR_M1_BURST_INCR4 (3 << 4) +#define FUSB300_AHBBCR_M1_BURST_INCR8 (5 << 4) +#define FUSB300_AHBBCR_M1_BURST_INCR16 (7 << 4) +#define FUSB300_AHBBCR_M0_BURST_SINGLE 0 +#define FUSB300_AHBBCR_M0_BURST_INCR 1 +#define FUSB300_AHBBCR_M0_BURST_INCR4 3 +#define FUSB300_AHBBCR_M0_BURST_INCR8 5 +#define FUSB300_AHBBCR_M0_BURST_INCR16 7 +#define FUSB300_IGER5_EEP_STL_INT(n) (1 << n) + +/* WORD 0 Data Structure of PRD Table */ +#define FUSB300_EPPRD0_M (1 << 30) +#define FUSB300_EPPRD0_O (1 << 29) +/* The finished prd */ +#define FUSB300_EPPRD0_F (1 << 28) +#define FUSB300_EPPRD0_I (1 << 27) +#define FUSB300_EPPRD0_A (1 << 26) +/* To decide HW point to first prd at next time */ +#define FUSB300_EPPRD0_L (1 << 25) +#define FUSB300_EPPRD0_H (1 << 24) +#define FUSB300_EPPRD0_BTC(n) (n & 0xFFFFFF) + +/*----------------------------------------------------------------------*/ +#define FUSB300_MAX_NUM_EP 16 + +#define FUSB300_FIFO_ENTRY_NUM 8 +#define FUSB300_MAX_FIFO_ENTRY 8 + +#define SS_CTL_MAX_PACKET_SIZE 0x200 +#define SS_BULK_MAX_PACKET_SIZE 0x400 +#define SS_INT_MAX_PACKET_SIZE 0x400 +#define SS_ISO_MAX_PACKET_SIZE 0x400 + +#define HS_BULK_MAX_PACKET_SIZE 0x200 +#define HS_CTL_MAX_PACKET_SIZE 0x40 +#define HS_INT_MAX_PACKET_SIZE 0x400 +#define HS_ISO_MAX_PACKET_SIZE 0x400 + +struct fusb300_ep_info { + u8 epnum; + u8 type; + u8 interval; + u8 dir_in; + u16 maxpacket; + u16 addrofs; + u16 bw_num; +}; + +struct fusb300_request { + + struct usb_request req; + struct list_head queue; +}; + + +struct fusb300_ep { + struct usb_ep ep; + struct fusb300 *fusb300; + + struct list_head queue; + unsigned stall:1; + unsigned wedged:1; + unsigned use_dma:1; + + unsigned char epnum; + unsigned char type; +}; + +struct fusb300 { + spinlock_t lock; + void __iomem *reg; + + unsigned long irq_trigger; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct fusb300_ep *ep[FUSB300_MAX_NUM_EP]; + + struct usb_request *ep0_req; /* for internal request */ + __le16 ep0_data; + u32 ep0_length; /* for internal request */ + u8 ep0_dir; /* 0/0x80 out/in */ + + u8 fifo_entry_num; /* next start fifo entry */ + u32 addrofs; /* next fifo address offset */ + 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 new file mode 100644 index 00000000000..fe12e6a2744 --- /dev/null +++ b/drivers/usb/gadget/g_ffs.c @@ -0,0 +1,582 @@ +/* + * g_ffs.c -- user mode file system API for USB composite function controllers + * + * Copyright (C) 2010 Samsung Electronics + * Author: Michal Nazarewicz <mina86@mina86.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. + */ + +#define pr_fmt(fmt) "g_ffs: " fmt + +#include <linux/module.h> + +#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 +# ifdef CONFIG_USB_FUNCTIONFS_RNDIS +# define USB_ETH_RNDIS y +# endif + +# include "u_ecm.h" +# include "u_gether.h" +# ifdef USB_ETH_RNDIS +# include "u_rndis.h" +# include "rndis.h" +# endif +# include "u_ether.h" + +USB_ETHERNET_MODULE_PARAMETERS(); + +# ifdef CONFIG_USB_FUNCTIONFS_ETH +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 +#endif + +#include "u_fs.h" + +#define DRIVER_NAME "g_ffs" +#define DRIVER_DESC "USB Function Filesystem" +#define DRIVER_VERSION "24 Aug 2004" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + +#define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ +#define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ + +#define GFS_MAX_DEVS 10 + +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct usb_device_descriptor gfs_dev_desc = { + .bLength = sizeof gfs_dev_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + .idVendor = cpu_to_le16(GFS_VENDOR_ID), + .idProduct = cpu_to_le16(GFS_PRODUCT_ID), +}; + +static char *func_names[GFS_MAX_DEVS]; +static unsigned int func_num; + +module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); +MODULE_PARM_DESC(bDeviceClass, "USB Device class"); +module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); +MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); +module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); +MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); +module_param_array_named(functions, func_names, charp, &func_num, 0); +MODULE_PARM_DESC(functions, "USB Functions list"); + +static const struct usb_descriptor_header *gfs_otg_desc[] = { + (const struct usb_descriptor_header *) + &(const struct usb_otg_descriptor) { + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }, + + NULL +}; + +/* String IDs are assigned dynamically */ +static struct usb_string gfs_strings[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { .s = "FunctionFS + RNDIS" }, +#endif +#ifdef CONFIG_USB_FUNCTIONFS_ETH + { .s = "FunctionFS + ECM" }, +#endif +#ifdef CONFIG_USB_FUNCTIONFS_GENERIC + { .s = "FunctionFS" }, +#endif + { } /* end of list */ +}; + +static struct usb_gadget_strings *gfs_dev_strings[] = { + &(struct usb_gadget_strings) { + .language = 0x0409, /* en-us */ + .strings = gfs_strings, + }, + NULL, +}; + +struct gfs_configuration { + struct usb_configuration c; + int (*eth)(struct usb_configuration *c); + int num; +} gfs_configurations[] = { +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { + .eth = bind_rndis_config, + }, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_ETH + { + .eth = eth_bind_config, + }, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_GENERIC + { + }, +#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, + .strings = gfs_dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = gfs_bind, + .unbind = gfs_unbind, +}; + +static unsigned int missing_funcs; +static bool gfs_registered; +static bool gfs_single_func; +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 < 2) { + gfs_single_func = true; + func_num = 1; + } + + /* + * 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; + + 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 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(); + + if (gfs_registered) + usb_composite_unregister(&gfs_driver); + gfs_registered = false; + + kfree(f_ffs[0]); + + for (i = 0; i < func_num; i++) + usb_put_function_instance(fi_ffs[i]); + + kfree(fi_ffs); +} +module_exit(gfs_exit); + +static void *functionfs_acquire_dev(struct ffs_dev *dev) +{ + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(-ENODEV); + + return 0; +} + +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) +{ + int ret = 0; + + if (--missing_funcs) + return 0; + + if (gfs_registered) + return -EBUSY; + + gfs_registered = true; + + ret = usb_composite_probe(&gfs_driver); + if (unlikely(ret < 0)) + gfs_registered = false; + + return ret; +} + +/* + * The caller of this function takes ffs_lock + */ +static void functionfs_closed_callback(struct ffs_data *ffs) +{ + missing_funcs++; + + if (gfs_registered) + usb_composite_unregister(&gfs_driver); + gfs_registered = false; +} + +/* + * 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) +{ +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + struct net_device *net; +#endif + int ret, i; + + ENTER(); + + 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 + +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { + struct f_rndis_opts *rndis_opts; + + 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 + +#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 + +#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; + + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; + + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + ecm_opts->bound = true; + } else { + struct f_gether_opts *geth_opts; + + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + geth_opts->bound = 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_rndis; + gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; + + for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { + struct gfs_configuration *c = gfs_configurations + i; + int sid = USB_GADGET_FIRST_AVAIL_IDX + i; + + c->c.label = gfs_strings[sid].s; + c->c.iConfiguration = gfs_strings[sid].id; + 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; + } + usb_composite_overwrite_options(cdev, &coverwrite); + return 0; + +/* TODO */ +error_unbind: +error_rndis: +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + usb_put_function_instance(fi_rndis); +error: +#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 ffs_lock is held + */ +static int gfs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + ENTER(); + + +#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 + * ffs_lock is held + */ +static int gfs_do_config(struct usb_configuration *c) +{ + struct gfs_configuration *gc = + container_of(c, struct gfs_configuration, c); + int i; + int ret; + + if (missing_funcs) + return -ENODEV; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = gfs_otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + if (gc->eth) { + ret = gc->eth(c); + if (unlikely(ret < 0)) + return ret; + } + + for (i = 0; i < func_num; i++) { + 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; + } + } + + /* + * After previous do_configs there may be some invalid + * pointers in c->interface array. This happens every time + * a user space function with fewer interfaces than a user + * space function that was run before the new one is run. The + * compasit's set_config() assumes that if there is no more + * then MAX_CONFIG_INTERFACES interfaces in a configuration + * then there is a NULL pointer after the last interface in + * c->interface array. We need to make sure this is true. + */ + if (c->next_interface_id < ARRAY_SIZE(c->interface)) + 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) +{ + 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 e84b3c47ed3..15f180904f8 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -6,20 +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); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, - struct usb_ep *in, struct usb_ep *out); - -/* configuration-specific linkup */ -int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume); -int loopback_add(struct usb_composite_dev *cdev, bool autoresume); + struct usb_ep *in, struct usb_ep *out, + struct usb_ep *iso_in, struct usb_ep *iso_out); #endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ec6d439a2aa..bcd04bc66b9 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -15,225 +15,24 @@ #ifndef __GADGET_CHIPS_H #define __GADGET_CHIPS_H -#ifdef CONFIG_USB_GADGET_NET2280 -#define gadget_is_net2280(g) !strcmp("net2280", (g)->name) -#else -#define gadget_is_net2280(g) 0 -#endif +#include <linux/usb/gadget.h> -#ifdef CONFIG_USB_GADGET_AMD5536UDC -#define gadget_is_amd5536udc(g) !strcmp("amd5536udc", (g)->name) -#else -#define gadget_is_amd5536udc(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_DUMMY_HCD -#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) -#else -#define gadget_is_dummy(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_PXA25X -#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name) -#else -#define gadget_is_pxa(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_GOKU -#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name) -#else -#define gadget_is_goku(g) 0 -#endif - -/* SH3 UDC -- not yet ported 2.4 --> 2.6 */ -#ifdef CONFIG_USB_GADGET_SUPERH -#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) -#else -#define gadget_is_sh(g) 0 -#endif - -/* not yet stable on 2.6 (would help "original Zaurus") */ -#ifdef CONFIG_USB_GADGET_SA1100 -#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) -#else -#define gadget_is_sa1100(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_LH7A40X -#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name) -#else -#define gadget_is_lh7a40x(g) 0 -#endif - -/* handhelds.org tree (?) */ -#ifdef CONFIG_USB_GADGET_MQ11XX -#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) -#else -#define gadget_is_mq11xx(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_OMAP -#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) -#else -#define gadget_is_omap(g) 0 -#endif - -/* not yet ported 2.4 --> 2.6 */ -#ifdef CONFIG_USB_GADGET_N9604 -#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) -#else -#define gadget_is_n9604(g) 0 -#endif - -/* various unstable versions available */ -#ifdef CONFIG_USB_GADGET_PXA27X -#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) -#else -#define gadget_is_pxa27x(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_ATMEL_USBA -#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name) -#else -#define gadget_is_atmel_usba(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_S3C2410 -#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) -#else -#define gadget_is_s3c2410(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_AT91 -#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name) -#else -#define gadget_is_at91(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_IMX -#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) -#else -#define gadget_is_imx(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_FSL_USB2 -#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name) -#else -#define gadget_is_fsl_usb2(g) 0 -#endif - -/* Mentor high speed function controller */ -/* from Montavista kernel (?) */ -#ifdef CONFIG_USB_GADGET_MUSBHSFC -#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) -#else -#define gadget_is_musbhsfc(g) 0 -#endif - -/* Mentor high speed "dual role" controller, in peripheral role */ -#ifdef CONFIG_USB_GADGET_MUSB_HDRC -#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name) -#else -#define gadget_is_musbhdrc(g) 0 -#endif - -/* from Montavista kernel (?) */ -#ifdef CONFIG_USB_GADGET_MPC8272 -#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) -#else -#define gadget_is_mpc8272(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_M66592 -#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name) -#else -#define gadget_is_m66592(g) 0 -#endif - -/* Freescale CPM/QE UDC SUPPORT */ -#ifdef CONFIG_USB_GADGET_FSL_QE -#define gadget_is_fsl_qe(g) !strcmp("fsl_qe_udc", (g)->name) -#else -#define gadget_is_fsl_qe(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_CI13XXX -#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name)) -#else -#define gadget_is_ci13xxx(g) 0 -#endif - -// CONFIG_USB_GADGET_SX2 -// CONFIG_USB_GADGET_AU1X00 -// ... - - -/** - * usb_gadget_controller_number - support bcdDevice id convention - * @gadget: the controller being driven - * - * Return a 2-digit BCD value associated with the peripheral controller, - * suitable for use as part of a bcdDevice value, or a negative error code. +/* + * NOTICE: the entries below are alphabetical and should be kept + * that way. * - * NOTE: this convention is purely optional, and has no meaning in terms of - * any USB specification. If you want to use a different convention in your - * gadget driver firmware -- maybe a more formal revision ID -- feel free. + * Always be sure to add new entries to the correct position or + * accept the bashing later. * - * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!) - * to change their behavior accordingly. For example it might help avoiding - * some chip bug. + * If you have forgotten the alphabetical order let VIM/EMACS + * do that for you. */ -static inline int usb_gadget_controller_number(struct usb_gadget *gadget) -{ - if (gadget_is_net2280(gadget)) - return 0x01; - else if (gadget_is_dummy(gadget)) - return 0x02; - else if (gadget_is_pxa(gadget)) - return 0x03; - else if (gadget_is_sh(gadget)) - return 0x04; - else if (gadget_is_sa1100(gadget)) - return 0x05; - else if (gadget_is_goku(gadget)) - return 0x06; - else if (gadget_is_mq11xx(gadget)) - return 0x07; - else if (gadget_is_omap(gadget)) - return 0x08; - else if (gadget_is_lh7a40x(gadget)) - return 0x09; - else if (gadget_is_n9604(gadget)) - return 0x10; - else if (gadget_is_pxa27x(gadget)) - return 0x11; - else if (gadget_is_s3c2410(gadget)) - return 0x12; - else if (gadget_is_at91(gadget)) - return 0x13; - else if (gadget_is_imx(gadget)) - return 0x14; - else if (gadget_is_musbhsfc(gadget)) - return 0x15; - else if (gadget_is_musbhdrc(gadget)) - return 0x16; - else if (gadget_is_mpc8272(gadget)) - return 0x17; - else if (gadget_is_atmel_usba(gadget)) - return 0x18; - else if (gadget_is_fsl_usb2(gadget)) - return 0x19; - else if (gadget_is_amd5536udc(gadget)) - return 0x20; - else if (gadget_is_m66592(gadget)) - return 0x21; - else if (gadget_is_fsl_qe(gadget)) - return 0x22; - else if (gadget_is_ci13xxx(gadget)) - return 0x23; - return -ENOENT; -} - +#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) +#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) +#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) +#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) +#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) +#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name)) /** * gadget_supports_altsettings - return true if altsettings work @@ -249,10 +48,6 @@ static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) if (gadget_is_pxa27x(gadget)) return false; - /* SH3 hardware just doesn't do altsettings */ - if (gadget_is_sh(gadget)) - return false; - /* Everything else is *presumably* fine ... */ return true; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index b9312dc6e04..e879e2c9f46 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -21,7 +21,8 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> +#include <linux/slab.h> +#include <linux/module.h> #include <linux/device.h> #include <sound/core.h> @@ -29,140 +30,48 @@ #include <sound/rawmidi.h> #include <linux/usb/ch9.h> +#include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/usb/audio.h> #include <linux/usb/midi.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 "usbstring.c" -#include "config.c" -#include "epautoconf.c" +#include "f_midi.c" /*-------------------------------------------------------------------------*/ - MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); -#define DRIVER_VERSION "25 Jul 2006" - static const char shortname[] = "g_midi"; static const char longname[] = "MIDI Gadget"; -static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; +USB_GADGET_COMPOSITE_OPTIONS(); -module_param(index, int, 0444); +static int index = SNDRV_DEFAULT_IDX1; +module_param(index, int, S_IRUGO); MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); -module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); - -/* Some systems will want different product identifers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); - -/* - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; - - -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - - -/* This is a gadget, and the IN/OUT naming is from the host's perspective. - USB -> OUT endpoint -> rawmidi - USB <- IN endpoint <- rawmidi */ -struct gmidi_in_port { - struct gmidi_device* dev; - int active; - uint8_t cable; /* cable number << 4 */ - uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 - uint8_t data[2]; -}; - -struct gmidi_device { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - u8 config; - struct usb_ep *in_ep, *out_ep; - struct snd_card *card; - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *in_substream; - struct snd_rawmidi_substream *out_substream; - - /* For the moment we only support one port in - each direction, but in_port is kept as a - separate struct so we can have more later. */ - struct gmidi_in_port in_port; - unsigned long out_triggered; - struct tasklet_struct tasklet; -}; - -static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); - - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) +static char *id = SNDRV_DEFAULT_STR1; +module_param(id, charp, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); -static unsigned buflen = 256; -static unsigned qlen = 32; - +static unsigned int buflen = 256; module_param(buflen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "MIDI buffer length"); + +static unsigned int qlen = 32; module_param(qlen, uint, S_IRUGO); +MODULE_PARM_DESC(qlen, "USB read request queue length"); +static unsigned int in_ports = 1; +module_param(in_ports, uint, S_IRUGO); +MODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); + +static unsigned int out_ports = 1; +module_param(out_ports, uint, S_IRUGO); +MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); /* Thanks to Grey Innovation for donating this product ID. * @@ -172,1154 +81,97 @@ module_param(qlen, uint, S_IRUGO); #define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */ #define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */ +/* string IDs are assigned dynamically */ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_MIDI_GADGET 250 - -/* We only have the one configuration, it's number 1. */ -#define GMIDI_CONFIG 1 - -/* We have two interfaces- AudioControl and MIDIStreaming */ -#define GMIDI_AC_INTERFACE 0 -#define GMIDI_MS_INTERFACE 1 -#define GMIDI_NUM_INTERFACES 2 +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX -DECLARE_USB_AC_HEADER_DESCRIPTOR(1); -DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); - -/* B.1 Device Descriptor */ static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(0x0200), + .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_PER_INTERFACE, - .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, }; -/* B.2 Configuration Descriptor */ -static struct usb_config_descriptor config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* compute wTotalLength on the fly */ - .bNumInterfaces = GMIDI_NUM_INTERFACES, - .bConfigurationValue = GMIDI_CONFIG, - .iConfiguration = STRING_MIDI_GADGET, - /* - * FIXME: When embedding this driver in a device, - * these need to be set to reflect the actual - * power properties of the device. Is it selfpowered? - */ - .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - -/* B.3.1 Standard AC Interface Descriptor */ -static const struct usb_interface_descriptor ac_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_AC_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct usb_ac_header_descriptor_1 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_AC_HEADER_SIZE(1)), - .bInCollection = 1, - .baInterfaceNr = { - [0] = GMIDI_MS_INTERFACE, - } +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation", + [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget", + [USB_GADGET_SERIAL_IDX].s = "", + [STRING_DESCRIPTION_IDX].s = "MIDI", + { } /* end of list */ }; -/* B.4.1 Standard MS Interface Descriptor */ -static const struct usb_interface_descriptor ms_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_MS_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.4.2 Class-Specific MS Interface Descriptor */ -static const struct usb_ms_header_descriptor ms_header_desc = { - .bLength = USB_DT_MS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdMSC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE - + 2*USB_DT_MIDI_IN_SIZE - + 2*USB_DT_MIDI_OUT_SIZE(1)), -}; - -#define JACK_IN_EMB 1 -#define JACK_IN_EXT 2 -#define JACK_OUT_EMB 3 -#define JACK_OUT_EXT 4 - -/* B.4.3 MIDI IN Jack Descriptors */ -static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_IN_EMB, -}; - -static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_IN_EXT, -}; - -/* B.4.4 MIDI OUT Jack Descriptors */ -static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_OUT_EMB, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EXT, - .baSourcePin = 1, - } - } -}; - -static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_OUT_EXT, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EMB, - .baSourcePin = 1, - } - } -}; - -/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_IN_EMB, - } -}; - -/* B.6.1 Standard Bulk IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_OUT_EMB, - } -}; - -static const struct usb_descriptor_header *gmidi_function [] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&ms_interface_desc, - - (struct usb_descriptor_header *)&ms_header_desc, - (struct usb_descriptor_header *)&jack_in_emb_desc, - (struct usb_descriptor_header *)&jack_in_ext_desc, - (struct usb_descriptor_header *)&jack_out_emb_desc, - (struct usb_descriptor_header *)&jack_out_ext_desc, - /* If you add more jacks, update ms_header_desc.wTotalLength */ - - (struct usb_descriptor_header *)&bulk_out_desc, - (struct usb_descriptor_header *)&ms_out_desc, - (struct usb_descriptor_header *)&bulk_in_desc, - (struct usb_descriptor_header *)&ms_in_desc, - NULL, -}; - -static char manufacturer[50]; -static char product_desc[40] = "MIDI Gadget"; -static char serial_number[20]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIAL, serial_number, }, - { STRING_MIDI_GADGET, longname, }, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int len; - - /* only one configuration */ - if (index != 0) { - return -EINVAL; - } - len = usb_gadget_config_buf(&config_desc, - buf, USB_BUFSIZ, gmidi_function); - if (len < 0) { - return len; - } - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - return len; -} - -static struct usb_request *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; -} - -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static const uint8_t gmidi_cin_length[] = { - 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -/* - * Receives a chunk of MIDI data. - */ -static void gmidi_read_data(struct usb_ep *ep, int cable, - uint8_t *data, int length) -{ - struct gmidi_device *dev = ep->driver_data; - /* cable is ignored, because for now we only have one. */ - - if (!dev->out_substream) { - /* Nobody is listening - throw it on the floor. */ - return; - } - if (!test_bit(dev->out_substream->number, &dev->out_triggered)) { - return; - } - snd_rawmidi_receive(dev->out_substream, data, length); -} - -static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req) -{ - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i + 3 < req->actual; i += 4) { - if (buf[i] != 0) { - int cable = buf[i] >> 4; - int length = gmidi_cin_length[buf[i] & 0x0f]; - gmidi_read_data(ep, cable, &buf[i + 1], length); - } - } -} - -static void gmidi_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gmidi_device *dev = ep->driver_data; - int status = req->status; - - switch (status) { - case 0: /* normal completion */ - if (ep == dev->out_ep) { - /* we received stuff. - req is queued again, below */ - gmidi_handle_out_data(ep, req); - } else if (ep == dev->in_ep) { - /* our transmit completed. - see if there's more to go. - gmidi_transmit eats req, don't queue it again. */ - gmidi_transmit(dev, req); - return; - } - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) { - gmidi_handle_out_data(ep, req); - } - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - break; - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags) -{ - int err = 0; - struct usb_request *req; - struct usb_ep *ep; - unsigned i; - - err = usb_ep_enable(dev->in_ep, &bulk_in_desc); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err); - goto fail; - } - dev->in_ep->driver_data = dev; - - err = usb_ep_enable(dev->out_ep, &bulk_out_desc); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err); - goto fail; - } - dev->out_ep->driver_data = dev; - - /* allocate a bunch of read buffers and queue them all at once. */ - ep = dev->out_ep; - for (i = 0; i < qlen && err == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = gmidi_complete; - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) { - DBG(dev, "%s queue req: %d\n", ep->name, err); - } - } else { - err = -ENOMEM; - } - } -fail: - /* caller is responsible for cleanup on error */ - return err; -} - - -static void gmidi_reset_config(struct gmidi_device *dev) -{ - if (dev->config == 0) { - return; - } - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - usb_ep_disable(dev->in_ep); - usb_ep_disable(dev->out_ep); - dev->config = 0; -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int -gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if 0 - /* FIXME */ - /* Hacking this bit out fixes a bug where on receipt of two - USB_REQ_SET_CONFIGURATION messages, we end up with no - buffered OUT requests waiting for data. This is clearly - hiding a bug elsewhere, because if the config didn't - change then we really shouldn't do anything. */ - /* Having said that, when we do "change" from config 1 - to config 1, we at least gmidi_reset_config() which - clears out any requests on endpoints, so it's not like - we leak or anything. */ - if (number == dev->config) { - return 0; - } -#endif - - if (gadget_is_sa1100(gadget) && dev->config) { - /* tx fifo is full, but we can't clear it...*/ - ERROR(dev, "can't change configurations\n"); - return -ESPIPE; - } - gmidi_reset_config(dev); - - switch (number) { - case GMIDI_CONFIG: - result = set_gmidi_config(dev, gfp_flags); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) { - result = -ENODEV; - } - if (result) { - gmidi_reset_config(dev); - } else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed\n", speed); - } - return result; -} - - -static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) { - DBG((struct gmidi_device *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - } -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int gmidi_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - switch (w_value >> 8) { - - case USB_DT_DEVICE: - value = min(w_length, (u16) sizeof(device_desc)); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) { - goto unknown; - } - if (gadget->a_hnp_support) { - DBG(dev, "HNP available\n"); - } else if (gadget->a_alt_hnp_support) { - DBG(dev, "HNP needs a different root port\n"); - } else { - VDBG(dev, "HNP inactive\n"); - } - spin_lock(&dev->lock); - value = gmidi_set_config(dev, w_value, GFP_ATOMIC); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16)1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) { - goto unknown; - } - spin_lock(&dev->lock); - if (dev->config && w_index < GMIDI_NUM_INTERFACES - && w_value == 0) - { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - gmidi_reset_config(dev); - gmidi_set_config(dev, config, GFP_ATOMIC); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) { - goto unknown; - } - if (!dev->config) { - break; - } - if (w_index >= GMIDI_NUM_INTERFACES) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16)1); - break; - - default: -unknown: - VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - gmidi_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void gmidi_disconnect(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - gmidi_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct snd_card *card; - - DBG(dev, "unbind\n"); - - card = dev->card; - dev->card = NULL; - if (card) { - snd_card_free(card); - } - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int gmidi_snd_free(struct snd_device *device) +static int __exit midi_unbind(struct usb_composite_dev *dev) { return 0; } -static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - -/* - * Converts MIDI commands to USB MIDI packets. - */ -static void gmidi_transmit_byte(struct usb_request *req, - struct gmidi_in_port *port, uint8_t b) -{ - uint8_t p0 = port->cable; - - if (b >= 0xf8) { - gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { - switch (b) { - case 0xf0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case 0xf1: - case 0xf3: - port->data[0] = b; - port->state = STATE_1PARAM; - break; - case 0xf2: - port->data[0] = b; - port->state = STATE_2PARAM_1; - break; - case 0xf4: - case 0xf5: - port->state = STATE_UNKNOWN; - break; - case 0xf6: - gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - gmidi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - gmidi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; - break; - } - } else if (b >= 0x80) { - port->data[0] = b; - if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; - else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ - switch (port->state) { - case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, p0, port->data[0], b, 0); - break; - case STATE_2PARAM_1: - port->data[1] = b; - port->state = STATE_2PARAM_2; - break; - case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, - p0, port->data[0], port->data[1], b); - break; - case STATE_SYSEX_0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case STATE_SYSEX_1: - port->data[1] = b; - port->state = STATE_SYSEX_2; - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; - break; - } - } -} - -static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req) -{ - struct usb_ep *ep = dev->in_ep; - struct gmidi_in_port *port = &dev->in_port; - - if (!ep) { - return; - } - if (!req) { - req = alloc_ep_req(ep, buflen); - } - if (!req) { - ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n"); - return; - } - req->length = 0; - req->complete = gmidi_complete; - - if (port->active) { - while (req->length + 3 < buflen) { - uint8_t b; - if (snd_rawmidi_transmit(dev->in_substream, &b, 1) - != 1) - { - port->active = 0; - break; - } - gmidi_transmit_byte(req, port, b); - } - } - if (req->length > 0) { - usb_ep_queue(ep, req, GFP_ATOMIC); - } else { - free_ep_req(ep, req); - } -} - -static void gmidi_in_tasklet(unsigned long data) -{ - struct gmidi_device *dev = (struct gmidi_device *)data; - - gmidi_transmit(dev, NULL); -} - -static int gmidi_in_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_open\n"); - dev->in_substream = substream; - dev->in_port.state = STATE_UNKNOWN; - return 0; -} - -static int gmidi_in_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_close\n"); - return 0; -} - -static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_trigger %d\n", up); - dev->in_port.active = up; - if (up) { - tasklet_hi_schedule(&dev->tasklet); - } -} - -static int gmidi_out_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_open\n"); - dev->out_substream = substream; - return 0; -} - -static int gmidi_out_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_close\n"); - return 0; -} - -static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_trigger %d\n", up); - if (up) { - set_bit(substream->number, &dev->out_triggered); - } else { - clear_bit(substream->number, &dev->out_triggered); - } -} - -static struct snd_rawmidi_ops gmidi_in_ops = { - .open = gmidi_in_open, - .close = gmidi_in_close, - .trigger = gmidi_in_trigger, -}; - -static struct snd_rawmidi_ops gmidi_out_ops = { - .open = gmidi_out_open, - .close = gmidi_out_close, - .trigger = gmidi_out_trigger +static struct usb_configuration midi_config = { + .label = "MIDI Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, }; -/* register as a sound "card" */ -static int gmidi_register_card(struct gmidi_device *dev) +static int __init midi_bind_config(struct usb_configuration *c) { - struct snd_card *card; - struct snd_rawmidi *rmidi; - int err; - int out_ports = 1; - int in_ports = 1; - static struct snd_device_ops ops = { - .dev_free = gmidi_snd_free, - }; - - err = snd_card_create(index, id, THIS_MODULE, 0, &card); - if (err < 0) { - ERROR(dev, "snd_card_create failed\n"); - goto fail; - } - dev->card = card; - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops); - if (err < 0) { - ERROR(dev, "snd_device_new failed: error %d\n", err); - goto fail; - } - - strcpy(card->driver, longname); - strcpy(card->longname, longname); - strcpy(card->shortname, shortname); - - /* Set up rawmidi */ - dev->in_port.dev = dev; - dev->in_port.active = 0; - snd_component_add(card, "MIDI"); - err = snd_rawmidi_new(card, "USB MIDI Gadget", 0, - out_ports, in_ports, &rmidi); - if (err < 0) { - ERROR(dev, "snd_rawmidi_new failed: error %d\n", err); - goto fail; - } - dev->rmidi = rmidi; - strcpy(rmidi->name, card->shortname); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->private_data = dev; - - /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. - It's an upside-down world being a gadget. */ - 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, &dev->gadget->dev); - - /* register it - we're ready to go */ - err = snd_card_register(card); - if (err < 0) { - ERROR(dev, "snd_card_register failed\n"); - goto fail; - } - - VDBG(dev, "gmidi_register_card finished ok\n"); - return 0; - -fail: - if (dev->card) { - snd_card_free(dev->card); - dev->card = NULL; - } - return err; + return f_midi_bind_config(c, index, id, + in_ports, out_ports, + buflen, qlen); } -/* - * Creates an output endpoint, and initializes output ports. - */ -static int __init gmidi_bind(struct usb_gadget *gadget) +static int __init midi_bind(struct usb_composite_dev *cdev) { - struct gmidi_device *dev; - struct usb_ep *in_ep, *out_ep; - int gcnum, err = 0; - - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - pr_err("idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) { - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - } - if (iManufacturer) { - strlcpy(manufacturer, iManufacturer, sizeof(manufacturer)); - } else { - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - } - if (iProduct) { - strlcpy(product_desc, iProduct, sizeof(product_desc)); - } - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIAL, - strlcpy(serial_number, iSerialNumber, sizeof(serial_number)); - } - - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. - */ - usb_ep_autoconfig_reset(gadget); - in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc); - if (!in_ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; - } - EP_IN_NAME = in_ep->name; - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc); - if (!out_ep) { - goto autoconf_fail; - } - EP_OUT_NAME = out_ep->name; - out_ep->driver_data = out_ep; /* claim */ - - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { - /* gmidi is so simple (no altsettings) that - * it SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrecognized controllers, don't panic. - */ - pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); - device_desc.bcdDevice = cpu_to_le16(0x9999); - } - - - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - return -ENOMEM; - } - spin_lock_init(&dev->lock); - dev->gadget = gadget; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - set_gadget_data(gadget, dev); - tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); - - /* preallocate control response and buffer */ - dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); - if (!dev->req) { - err = -ENOMEM; - goto fail; - } - - dev->req->complete = gmidi_setup_complete; - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); + int status; - /* register as an ALSA sound card */ - err = gmidi_register_card(dev); - if (err < 0) { - goto fail; - } + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + return status; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id; - VDBG(dev, "gmidi_bind finished ok\n"); + status = usb_add_config(cdev, &midi_config, midi_bind_config); + if (status < 0) + return status; + usb_composite_overwrite_options(cdev, &coverwrite); + pr_info("%s\n", longname); return 0; - -fail: - gmidi_unbind(gadget); - return err; -} - - -static void gmidi_suspend(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) { - return; - } - - DBG(dev, "suspend\n"); } -static void gmidi_resume(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); -} - - -static struct usb_gadget_driver gmidi_driver = { - .speed = USB_SPEED_FULL, - .function = (char *)longname, - .bind = gmidi_bind, - .unbind = gmidi_unbind, - - .setup = gmidi_setup, - .disconnect = gmidi_disconnect, - - .suspend = gmidi_suspend, - .resume = gmidi_resume, - - .driver = { - .name = (char *)shortname, - .owner = THIS_MODULE, - }, +static __refdata struct usb_composite_driver midi_driver = { + .name = (char *) longname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = midi_bind, + .unbind = __exit_p(midi_unbind), }; -static int __init gmidi_init(void) +static int __init midi_init(void) { - return usb_gadget_register_driver(&gmidi_driver); + return usb_composite_probe(&midi_driver); } -module_init(gmidi_init); +module_init(midi_init); -static void __exit gmidi_cleanup(void) +static void __exit midi_cleanup(void) { - usb_gadget_unregister_driver(&gmidi_driver); + usb_composite_unregister(&midi_driver); } -module_exit(gmidi_cleanup); +module_exit(midi_cleanup); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index de010c939db..6c85839e15a 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -30,19 +30,19 @@ #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> +#include <linux/prefetch.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/unaligned.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; @@ -102,7 +100,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) unsigned long flags; ep = container_of(_ep, struct goku_ep, ep); - if (!_ep || !desc || ep->desc + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; @@ -110,10 +108,10 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - if (ep->num != (desc->bEndpointAddress & 0x0f)) + if (ep->num != usb_endpoint_num(desc)) return -EINVAL; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: break; @@ -142,7 +140,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* ep1/ep2 dma direction is chosen early; it works in the other * direction, with pio. be cautious with out-dma. */ - ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; + ep->is_in = usb_endpoint_dir_in(desc); if (ep->is_in) { mode |= 1; ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT); @@ -176,7 +174,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) command(ep->dev->regs, COMMAND_RESET, ep->num); ep->ep.maxpacket = max; ep->stopped = 0; - ep->desc = desc; + ep->ep.desc = desc; spin_unlock_irqrestore(&ep->dev->lock, flags); DBG(dev, "enable %s %s %s maxpacket %u\n", ep->ep.name, @@ -232,8 +230,8 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep) } } - ep->ep.maxpacket = MAX_FIFO_SIZE; - ep->desc = NULL; + usb_ep_set_maxpacket_limit(&ep->ep, MAX_FIFO_SIZE); + ep->ep.desc = NULL; ep->stopped = 1; ep->irqs = 0; ep->dma = 0; @@ -246,7 +244,7 @@ static int goku_ep_disable(struct usb_ep *_ep) unsigned long flags; ep = container_of(_ep, struct goku_ep, ep); - if (!_ep || !ep->desc) + if (!_ep || !ep->ep.desc) return -ENODEV; dev = ep->dev; if (dev->ep0state == EP0_SUSPEND) @@ -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; } @@ -309,12 +306,9 @@ done(struct goku_ep *ep, struct goku_request *req, int status) status = req->req.status; dev = ep->dev; - if (req->mapped) { - pci_unmap_single(dev->pdev, req->req.dma, req->req.length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); #ifndef USB_TRACE if (status && status != -ESHUTDOWN) @@ -724,7 +718,7 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) || !_req->buf || !list_empty(&req->queue))) return -EINVAL; ep = container_of(_ep, struct goku_ep, ep); - if (unlikely(!_ep || (!ep->desc && ep->num != 0))) + if (unlikely(!_ep || (!ep->ep.desc && ep->num != 0))) return -EINVAL; dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) @@ -735,10 +729,11 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -EBUSY; /* set up dma mapping in case the caller didn't */ - if (ep->dma && _req->dma == DMA_ADDR_INVALID) { - _req->dma = pci_map_single(dev->pdev, _req->buf, _req->length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->mapped = 1; + if (ep->dma) { + status = usb_gadget_map_request(&dev->gadget, &req->req, + ep->is_in); + if (status) + return status; } #ifdef USB_TRACE @@ -776,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)) @@ -816,7 +811,7 @@ static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req) unsigned long flags; ep = container_of(_ep, struct goku_ep, ep); - if (!_ep || !_req || (!ep->desc && ep->num != 0)) + if (!_ep || !_req || (!ep->ep.desc && ep->num != 0)) return -EINVAL; dev = ep->dev; if (!dev->driver) @@ -897,7 +892,7 @@ static int goku_set_halt(struct usb_ep *_ep, int value) return -EINVAL; /* don't change EPxSTATUS_EP_INVALID to READY */ - } else if (!ep->desc) { + } else if (!ep->ep.desc) { DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name); return -EINVAL; } @@ -956,7 +951,7 @@ static void goku_fifo_flush(struct usb_ep *_ep) VDBG(ep->dev, "%s %s\n", __func__, ep->ep.name); /* don't change EPxSTATUS_EP_INVALID to READY */ - if (!ep->desc && ep->num != 0) { + if (!ep->ep.desc && ep->num != 0) { DBG(ep->dev, "%s %s inactive?\n", __func__, ep->ep.name); return; } @@ -995,15 +990,22 @@ static int goku_get_frame(struct usb_gadget *_gadget) return -EOPNOTSUPP; } +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, + .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)"; @@ -1020,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" : "", @@ -1053,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" @@ -1091,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; @@ -1103,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+" : "", @@ -1138,20 +1124,19 @@ 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++) { struct goku_ep *ep = &dev->ep [i]; struct goku_request *req; - if (i && !ep->desc) + if (i && !ep->ep.desc) 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, @@ -1179,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) { @@ -1209,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 */ /*-------------------------------------------------------------------------*/ @@ -1260,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); } @@ -1335,100 +1325,58 @@ 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. */ -int usb_gadget_register_driver(struct usb_gadget_driver *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->speed < USB_SPEED_FULL - || !driver->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 = driver->bind(&dev->gadget); - 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; } -EXPORT_SYMBOL(usb_gadget_register_driver); -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); } -int usb_gadget_unregister_driver(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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -1470,7 +1418,8 @@ static void ep0_setup(struct goku_udc *dev) case USB_RECIP_ENDPOINT: tmp = le16_to_cpu(ctrl.wIndex) & 0x0f; /* active endpoint */ - if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0)) + if (tmp > 3 || + (!dev->ep[tmp].ep.desc && tmp != 0)) goto stall; if (ctrl.wIndex & cpu_to_le16( USB_DIR_IN)) { @@ -1567,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 @@ -1582,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); @@ -1728,6 +1677,8 @@ static void goku_remove(struct pci_dev *pdev) DBG(dev, "%s\n", __func__); + usb_del_gadget_udc(&dev->gadget); + BUG_ON(dev->driver); #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -1744,11 +1695,8 @@ static void goku_remove(struct pci_dev *pdev) pci_resource_len (pdev, 0)); if (dev->enabled) pci_disable_device(pdev); - device_unregister(&dev->gadget.dev); - pci_set_drvdata(pdev, NULL); dev->regs = NULL; - the_controller = NULL; INFO(dev, "unbind\n"); } @@ -1764,17 +1712,10 @@ 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) { - WARNING(dev, "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; - goto done; + goto err; } /* alloc, and start init */ @@ -1782,25 +1723,22 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (dev == NULL){ pr_debug("enomem %s\n", pci_name(pdev)); retval = -ENOMEM; - goto done; + goto err; } spin_lock_init(&dev->lock); dev->pdev = pdev; dev->gadget.ops = &goku_ops; + 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 ... */ retval = pci_enable_device(pdev); if (retval < 0) { DBG(dev, "can't enable, %d\n", retval); - goto done; + goto err; } dev->enabled = 1; @@ -1809,7 +1747,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!request_mem_region(resource, len, driver_name)) { DBG(dev, "controller already in use\n"); retval = -EBUSY; - goto done; + goto err; } dev->got_region = 1; @@ -1817,7 +1755,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (base == NULL) { DBG(dev, "can't map memory\n"); retval = -EFAULT; - goto done; + goto err; } dev->regs = (struct goku_udc_regs __iomem *) base; @@ -1829,11 +1767,11 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* init to known state, then setup irqs */ udc_reset(dev); udc_reinit (dev); - if (request_irq(pdev->irq, goku_irq, IRQF_SHARED/*|IRQF_SAMPLE_RANDOM*/, + if (request_irq(pdev->irq, goku_irq, IRQF_SHARED, driver_name, dev) != 0) { DBG(dev, "request interrupt %d failed\n", pdev->irq); retval = -EBUSY; - goto done; + goto err; } dev->got_irq = 1; if (use_dma) @@ -1841,16 +1779,17 @@ 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 - /* done */ - the_controller = dev; - retval = device_register(&dev->gadget.dev); - if (retval == 0) - return 0; + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto err; -done: + return 0; + +err: if (dev) goku_remove (pdev); return retval; @@ -1859,7 +1798,7 @@ done: /*-------------------------------------------------------------------------*/ -static struct pci_device_id pci_ids [] = { { +static const struct pci_device_id pci_ids[] = { { .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), .class_mask = ~0, .vendor = 0x102f, /* Toshiba */ @@ -1881,14 +1820,4 @@ static struct pci_driver goku_pci_driver = { /* FIXME add power management support */ }; -static int __init init (void) -{ - return pci_register_driver (&goku_pci_driver); -} -module_init (init); - -static void __exit cleanup (void) -{ - pci_unregister_driver (&goku_pci_driver); -} -module_exit (cleanup); +module_pci_driver(goku_pci_driver); diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index 566cb231905..86d2adafe14 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -216,7 +216,6 @@ struct goku_ep { /* analogous to a host-side qh */ struct list_head queue; - const struct usb_endpoint_descriptor *desc; u32 __iomem *reg_fifo; u32 __iomem *reg_mode; @@ -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 new file mode 100644 index 00000000000..778613eb37a --- /dev/null +++ b/drivers/usb/gadget/hid.c @@ -0,0 +1,266 @@ +/* + * hid.c -- HID Composite driver + * + * Based on multi.c + * + * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb/composite.h> + +#include "gadget_chips.h" +#define DRIVER_DESC "HID Gadget" +#define DRIVER_VERSION "2010/03/16" + +/*-------------------------------------------------------------------------*/ + +#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */ +#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID 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_hid.c" + + +struct hidg_func_node { + struct list_head node; + struct hidg_func_descriptor *func; +}; + +static LIST_HEAD(hidg_func_list); + +/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + /* .bDeviceClass = USB_CLASS_COMM, */ + /* .bDeviceSubClass = 0, */ + /* .bDeviceProtocol = 0, */ + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(HIDG_VENDOR_NUM), + .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + + + +/****************************** Configurations ******************************/ + +static int __init do_config(struct usb_configuration *c) +{ + struct hidg_func_node *e; + int func = 0, status = 0; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + list_for_each_entry(e, &hidg_func_list, node) { + status = hidg_bind_config(c, e->func, func++); + if (status) + break; + } + + return status; +} + +static struct usb_configuration config_driver = { + .label = "HID Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/****************************** Gadget Bind ******************************/ + +static int __init hid_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct list_head *tmp; + int status, funcs = 0; + + list_for_each(tmp, &hidg_func_list) + funcs++; + + if (!funcs) + return -ENODEV; + + /* set up HID */ + status = ghid_setup(cdev->gadget, funcs); + if (status < 0) + return status; + + /* 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) + return status; + 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, &config_driver, do_config); + if (status < 0) + return status; + + usb_composite_overwrite_options(cdev, &coverwrite); + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + + return 0; +} + +static int __exit hid_unbind(struct usb_composite_dev *cdev) +{ + ghid_cleanup(); + return 0; +} + +static int __init hidg_plat_driver_probe(struct platform_device *pdev) +{ + struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev); + struct hidg_func_node *entry; + + if (!func) { + dev_err(&pdev->dev, "Platform data missing\n"); + return -ENODEV; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->func = func; + list_add_tail(&entry->node, &hidg_func_list); + + return 0; +} + +static int hidg_plat_driver_remove(struct platform_device *pdev) +{ + struct hidg_func_node *e, *n; + + list_for_each_entry_safe(e, n, &hidg_func_list, node) { + list_del(&e->node); + kfree(e); + } + + return 0; +} + + +/****************************** Some noise ******************************/ + + +static __refdata struct usb_composite_driver hidg_driver = { + .name = "g_hid", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = hid_bind, + .unbind = __exit_p(hid_unbind), +}; + +static struct platform_driver hidg_plat_driver = { + .remove = hidg_plat_driver_remove, + .driver = { + .owner = THIS_MODULE, + .name = "hidg", + }, +}; + + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard"); +MODULE_LICENSE("GPL"); + +static int __init hidg_init(void) +{ + int status; + + status = platform_driver_probe(&hidg_plat_driver, + hidg_plat_driver_probe); + if (status < 0) + return status; + + status = usb_composite_probe(&hidg_driver); + if (status < 0) + platform_driver_unregister(&hidg_plat_driver); + + return status; +} +module_init(hidg_init); + +static void __exit hidg_cleanup(void) +{ + platform_driver_unregister(&hidg_plat_driver); + usb_composite_unregister(&hidg_driver); +} +module_exit(hidg_cleanup); diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c deleted file mode 100644 index 168658b4b4e..00000000000 --- a/drivers/usb/gadget/imx_udc.c +++ /dev/null @@ -1,1568 +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/usb/ch9.h> -#include <linux/usb/gadget.h> - -#include <mach/usb.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; - - 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 < le16_to_cpu(desc->wMaxPacketSize)) { - 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; - - req = kzalloc(sizeof *req, gfp_flags); - if (!req || !usb_ep) - return 0; - - 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; -} - -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 - USBD_INT0]; - int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); - - dump_ep_intr(__func__, irq - USBD_INT0, 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 const struct usb_gadget_ops imx_udc_ops = { - .get_frame = imx_udc_get_frame, - .wakeup = imx_udc_wakeup, -}; - -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 gadged driver functions - ******************************************************************************* - */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb = &controller; - int retval; - - if (!driver - || driver->speed < USB_SPEED_FULL - || !driver->bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!imx_usb) - return -ENODEV; - if (imx_usb->driver) - return -EBUSY; - - /* 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; - retval = driver->bind(&imx_usb->gadget); - if (retval) { - D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n", - __func__, driver->driver.name, retval); - device_del(&imx_usb->gadget.dev); - - 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; -} -EXPORT_SYMBOL(usb_gadget_register_driver); - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct imx_udc_struct *imx_usb = &controller; - - if (!imx_usb) - return -ENODEV; - if (!driver || driver != imx_usb->driver || !driver->unbind) - return -EINVAL; - - udc_stop_activity(imx_usb, driver); - imx_udc_disable(imx_usb); - del_timer(&imx_usb->timer); - - driver->unbind(&imx_usb->gadget); - 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; -} -EXPORT_SYMBOL(usb_gadget_unregister_driver); - -/******************************************************************************* - * 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, 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 = res->end - res->start + 1; - 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_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), - IRQF_DISABLED, 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; - - return 0; - -fail3: - clk_put(clk); - clk_disable(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; - - 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(imx_usb->clk); - iounmap(imx_usb->base); - - release_mem_region(imx_usb->res->start, - imx_usb->res->end - imx_usb->res->start + 1); - - 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 b48ad59603d..00000000000 --- a/drivers/usb/gadget/imx_udc.h +++ /dev/null @@ -1,359 +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. - * - * 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. - */ - -#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 irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) \ - ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/ -#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0) -#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 d20937f28a1..2e4ce770490 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -30,9 +21,11 @@ #include <linux/wait.h> #include <linux/compiler.h> #include <asm/uaccess.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> -#include <linux/smp_lock.h> +#include <linux/mmu_context.h> +#include <linux/aio.h> #include <linux/device.h> #include <linux/moduleparam.h> @@ -85,7 +78,6 @@ MODULE_LICENSE ("GPL"); /*----------------------------------------------------------------------*/ #define GADGETFS_MAGIC 0xaee71ee7 -#define DMA_ADDR_INVALID (~(dma_addr_t)0) /* /dev/gadget/$CHIP represents ep0 and the whole device */ enum ep0_state { @@ -193,7 +185,7 @@ enum ep_state { }; struct ep_data { - struct semaphore lock; + struct mutex lock; enum ep_state state; atomic_t count; struct dev_data *dev; @@ -297,10 +289,10 @@ get_ready_ep (unsigned f_flags, struct ep_data *epdata) int val; if (f_flags & O_NONBLOCK) { - if (down_trylock (&epdata->lock) != 0) + if (!mutex_trylock(&epdata->lock)) goto nonblock; if (epdata->state != STATE_EP_ENABLED) { - up (&epdata->lock); + mutex_unlock(&epdata->lock); nonblock: val = -EAGAIN; } else @@ -308,7 +300,8 @@ nonblock: return val; } - if ((val = down_interruptible (&epdata->lock)) < 0) + val = mutex_lock_interruptible(&epdata->lock); + if (val < 0) return val; switch (epdata->state) { @@ -322,7 +315,7 @@ nonblock: // FALLTHROUGH case STATE_EP_UNBOUND: /* clean disconnect */ val = -ENODEV; - up (&epdata->lock); + mutex_unlock(&epdata->lock); } return val; } @@ -384,16 +377,17 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ - if (data->desc.bEndpointAddress & USB_DIR_IN) { - if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_ISOC) + if (usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) { + mutex_unlock(&data->lock); return -EINVAL; + } DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); if (likely (data->ep != NULL)) usb_ep_set_halt (data->ep); spin_unlock_irq (&data->dev->lock); - up (&data->lock); + mutex_unlock(&data->lock); return -EBADMSG; } @@ -411,7 +405,7 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) value = -EFAULT; free1: - up (&data->lock); + mutex_unlock(&data->lock); kfree (kbuf); return value; } @@ -428,27 +422,26 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ - if (!(data->desc.bEndpointAddress & USB_DIR_IN)) { - if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_ISOC) + if (!usb_endpoint_dir_in(&data->desc)) { + if (usb_endpoint_xfer_isoc(&data->desc)) { + mutex_unlock(&data->lock); return -EINVAL; + } DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); if (likely (data->ep != NULL)) usb_ep_set_halt (data->ep); spin_unlock_irq (&data->dev->lock); - up (&data->lock); + mutex_unlock(&data->lock); return -EBADMSG; } /* 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; } @@ -456,8 +449,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) VDEBUG (data->dev, "%s write %zu IN, status %d\n", data->name, len, (int) value); free1: - up (&data->lock); - kfree (kbuf); + mutex_unlock(&data->lock); return value; } @@ -467,7 +459,8 @@ ep_release (struct inode *inode, struct file *fd) struct ep_data *data = fd->private_data; int value; - if ((value = down_interruptible(&data->lock)) < 0) + value = mutex_lock_interruptible(&data->lock); + if (value < 0) return value; /* clean up if this can be reopened */ @@ -477,7 +470,7 @@ ep_release (struct inode *inode, struct file *fd) data->hs_desc.bDescriptorType = 0; usb_ep_disable(data->ep); } - up (&data->lock); + mutex_unlock(&data->lock); put_ep (data); return 0; } @@ -508,7 +501,7 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) } else status = -ENODEV; spin_unlock_irq (&data->dev->lock); - up (&data->lock); + mutex_unlock(&data->lock); return status; } @@ -519,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; @@ -534,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 @@ -542,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; @@ -574,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) @@ -602,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); @@ -639,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)) { @@ -648,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,13 +685,13 @@ fail: value = -ENODEV; spin_unlock_irq(&epdata->dev->lock); - up(&epdata->lock); + mutex_unlock(&epdata->lock); if (unlikely(value)) { kfree(priv); put_ep(epdata); } else - value = (iv ? -EIOCBRETRY : -EIOCBQUEUED); + value = -EIOCBQUEUED; return value; } @@ -691,15 +702,14 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, struct ep_data *epdata = iocb->ki_filp->private_data; char *buf; - if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN)) + 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 @@ -711,10 +721,10 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, size_t len = 0; int i = 0; - if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN))) + 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; @@ -766,7 +776,8 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) u32 tag; int value, length = len; - if ((value = down_interruptible (&data->lock)) < 0) + value = mutex_lock_interruptible(&data->lock); + if (value < 0) return value; if (data->state != STATE_EP_READY) { @@ -827,18 +838,18 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) switch (data->dev->gadget->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: - value = usb_ep_enable (ep, &data->desc); + ep->desc = &data->desc; + value = usb_ep_enable(ep); if (value == 0) data->state = STATE_EP_ENABLED; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_SPEED_HIGH: /* fails if caller didn't provide that descriptor... */ - value = usb_ep_enable (ep, &data->hs_desc); + ep->desc = &data->hs_desc; + value = usb_ep_enable(ep); if (value == 0) data->state = STATE_EP_ENABLED; break; -#endif default: DBG(data->dev, "unconnected, %s init abandoned\n", data->name); @@ -855,7 +866,7 @@ fail: data->desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0; } - up (&data->lock); + mutex_unlock(&data->lock); return value; fail0: value = -EINVAL; @@ -871,7 +882,7 @@ ep_open (struct inode *inode, struct file *fd) struct ep_data *data = inode->i_private; int value = -EBUSY; - if (down_interruptible (&data->lock) != 0) + if (mutex_lock_interruptible(&data->lock) != 0) return -EINTR; spin_lock_irq (&data->dev->lock); if (data->dev->state == STATE_DEV_UNBOUND) @@ -886,13 +897,12 @@ ep_open (struct inode *inode, struct file *fd) DBG (data->dev, "%s state %d\n", data->name, data->state); spin_unlock_irq (&data->dev->lock); - up (&data->lock); + mutex_unlock(&data->lock); return value; } /* used before endpoint configuration */ static const struct file_operations ep_config_operations = { - .owner = THIS_MODULE, .llseek = no_llseek, .open = ep_open, @@ -922,7 +932,6 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req) if (req->buf != dev->rbuf) { kfree(req->buf); req->buf = dev->rbuf; - req->dma = DMA_ADDR_INVALID; } req->complete = epio_complete; dev->setup_out_ready = 0; @@ -1045,6 +1054,8 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) // FIXME don't call this with the spinlock held ... if (copy_to_user (buf, dev->req->buf, len)) retval = -EFAULT; + else + retval = len; clean_req (dev->gadget->ep0, dev->req); /* NOTE userspace can't yet choose to stall */ } @@ -1253,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; } @@ -1297,11 +1309,9 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) struct usb_gadget *gadget = dev->gadget; long ret = -ENOTTY; - if (gadget->ops->ioctl) { - lock_kernel(); + if (gadget->ops->ioctl) ret = gadget->ops->ioctl (gadget, code, value); - unlock_kernel(); - } + return ret; } @@ -1326,7 +1336,6 @@ static const struct file_operations ep0_io_operations = { * Unrecognized ep0 requests may be handled in user space. */ -#ifdef CONFIG_USB_GADGET_DUALSPEED static void make_qualifier (struct dev_data *dev) { struct usb_qualifier_descriptor qual; @@ -1342,14 +1351,13 @@ static void make_qualifier (struct dev_data *dev) qual.bDeviceProtocol = desc->bDeviceProtocol; /* assumes ep0 uses the same value for both speeds ... */ - qual.bMaxPacketSize0 = desc->bMaxPacketSize0; + qual.bMaxPacketSize0 = dev->gadget->ep0->maxpacket; qual.bNumConfigurations = 1; qual.bRESERVED = 0; memcpy (dev->rbuf, &qual, sizeof qual); } -#endif static int config_buf (struct dev_data *dev, u8 type, unsigned index) @@ -1399,7 +1407,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } dev->state = STATE_DEV_CONNECTED; - dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; INFO (dev, "connected\n"); event = next_event (dev, GADGETFS_CONNECT); @@ -1415,7 +1422,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) dev->setup_abort = 1; req->buf = dev->rbuf; - req->dma = DMA_ADDR_INVALID; req->context = NULL; value = -EOPNOTSUPP; switch (ctrl->bRequest) { @@ -1427,9 +1433,9 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE: value = min (w_length, (u16) sizeof *dev->dev); + dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket; req->buf = dev->dev; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: if (!dev->hs_config) break; @@ -1439,7 +1445,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; case USB_DT_OTHER_SPEED_CONFIG: // FALLTHROUGH -#endif case USB_DT_CONFIG: value = config_buf (dev, w_value >> 8, @@ -1494,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; @@ -1501,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) @@ -1573,20 +1579,17 @@ delegate: static void destroy_ep_files (struct dev_data *dev) { - struct list_head *entry, *tmp; - DBG (dev, "%s %d\n", __func__, dev->state); /* dev->state must prevent interference */ -restart: spin_lock_irq (&dev->lock); - list_for_each_safe (entry, tmp, &dev->epfiles) { + while (!list_empty(&dev->epfiles)) { struct ep_data *ep; struct inode *parent; struct dentry *dentry; /* break link to FS */ - ep = list_entry (entry, struct ep_data, epfiles); + ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles); list_del_init (&ep->epfiles); dentry = ep->dentry; ep->dentry = NULL; @@ -1609,8 +1612,7 @@ restart: dput (dentry); mutex_unlock (&parent->i_mutex); - /* fds may still be open */ - goto restart; + spin_lock_irq (&dev->lock); } spin_unlock_irq (&dev->lock); } @@ -1632,7 +1634,7 @@ static int activate_ep_files (struct dev_data *dev) if (!data) goto enomem0; data->state = STATE_EP_DISABLED; - init_MUTEX (&data->lock); + mutex_init(&data->lock); init_waitqueue_head (&data->wait); strncpy (data->name, ep->name, sizeof (data->name) - 1); @@ -1691,8 +1693,8 @@ gadgetfs_unbind (struct usb_gadget *gadget) static struct dev_data *the_device; -static int -gadgetfs_bind (struct usb_gadget *gadget) +static int gadgetfs_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { struct dev_data *dev = the_device; @@ -1707,7 +1709,6 @@ gadgetfs_bind (struct usb_gadget *gadget) set_gadget_data (gadget, dev); dev->gadget = gadget; gadget->ep0->driver_data = dev; - dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; /* preallocate control response and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); @@ -1735,8 +1736,9 @@ static void gadgetfs_disconnect (struct usb_gadget *gadget) { struct dev_data *dev = get_gadget_data (gadget); + unsigned long flags; - spin_lock (&dev->lock); + spin_lock_irqsave (&dev->lock, flags); if (dev->state == STATE_DEV_UNCONNECTED) goto exit; dev->state = STATE_DEV_UNCONNECTED; @@ -1745,7 +1747,7 @@ gadgetfs_disconnect (struct usb_gadget *gadget) next_event (dev, GADGETFS_DISCONNECT); ep0_readable (dev); exit: - spin_unlock (&dev->lock); + spin_unlock_irqrestore (&dev->lock, flags); } static void @@ -1769,11 +1771,6 @@ gadgetfs_suspend (struct usb_gadget *gadget) } static struct usb_gadget_driver gadgetfs_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif .function = (char *) driver_desc, .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, @@ -1790,14 +1787,15 @@ static struct usb_gadget_driver gadgetfs_driver = { static void gadgetfs_nop(struct usb_gadget *arg) { } -static int gadgetfs_probe (struct usb_gadget *gadget) +static int gadgetfs_probe(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { CHIP = gadget->name; return -EISNAM; } static struct usb_gadget_driver probe_driver = { - .speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_HIGH, .bind = gadgetfs_probe, .unbind = gadgetfs_nop, .setup = (void *)gadgetfs_nop, @@ -1865,13 +1863,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) buf += 4; length -= 4; - kbuf = kmalloc (length, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; - if (copy_from_user (kbuf, buf, length)) { - kfree (kbuf); - return -EFAULT; - } + kbuf = memdup_user(buf, length); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); spin_lock_irq (&dev->lock); value = -EINVAL; @@ -1912,7 +1906,12 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) /* triggers gadgetfs_bind(); then we can enumerate. */ spin_unlock_irq (&dev->lock); - value = usb_gadget_register_driver (&gadgetfs_driver); + if (dev->hs_config) + gadgetfs_driver.max_speed = USB_SPEED_HIGH; + else + gadgetfs_driver.max_speed = USB_SPEED_FULL; + + value = usb_gadget_probe_driver(&gadgetfs_driver); if (value != 0) { kfree (dev->buf); dev->buf = NULL; @@ -1958,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, @@ -1998,9 +1996,10 @@ gadgetfs_make_inode (struct super_block *sb, struct inode *inode = new_inode (sb); if (inode) { + inode->i_ino = get_next_ino(); inode->i_mode = mode; - inode->i_uid = default_uid; - inode->i_gid = default_gid; + inode->i_uid = make_kuid(&init_user_ns, default_uid); + inode->i_gid = make_kgid(&init_user_ns, default_gid); inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = data; @@ -2035,7 +2034,7 @@ gadgetfs_create_file (struct super_block *sb, char const *name, return inode; } -static struct super_operations gadget_fs_operations = { +static const struct super_operations gadget_fs_operations = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, }; @@ -2044,14 +2043,14 @@ static int gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) { struct inode *inode; - struct dentry *d; struct dev_data *dev; if (the_device) return -ESRCH; /* fake probe to determine $CHIP */ - (void) usb_gadget_register_driver (&probe_driver); + CHIP = NULL; + usb_gadget_probe_driver(&probe_driver); if (!CHIP) return -ENODEV; @@ -2067,24 +2066,25 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) NULL, &simple_dir_operations, S_IFDIR | S_IRUGO | S_IXUGO); if (!inode) - goto enomem0; + goto Enomem; inode->i_op = &simple_dir_inode_operations; - if (!(d = d_alloc_root (inode))) - goto enomem1; - sb->s_root = d; + if (!(sb->s_root = d_make_root (inode))) + goto Enomem; /* the ep0 file is named after the controller we expect; * user mode code can use it for sanity checks, like we do. */ dev = dev_new (); if (!dev) - goto enomem2; + goto Enomem; dev->sb = sb; if (!gadgetfs_create_file (sb, CHIP, dev, &dev_init_operations, - &dev->dentry)) - goto enomem3; + &dev->dentry)) { + put_dev(dev); + goto Enomem; + } /* other endpoint files are available after hardware setup, * from binding to a controller. @@ -2092,22 +2092,16 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) the_device = dev; return 0; -enomem3: - put_dev (dev); -enomem2: - dput (d); -enomem1: - iput (inode); -enomem0: +Enomem: return -ENOMEM; } /* "mount -t gadgetfs path /dev/gadget" ends up here */ -static int -gadgetfs_get_sb (struct file_system_type *t, int flags, - const char *path, void *opts, struct vfsmount *mnt) +static struct dentry * +gadgetfs_mount (struct file_system_type *t, int flags, + const char *path, void *opts) { - return get_sb_single (t, flags, opts, gadgetfs_fill_super, mnt); + return mount_single (t, flags, opts, gadgetfs_fill_super); } static void @@ -2125,9 +2119,10 @@ gadgetfs_kill_sb (struct super_block *sb) static struct file_system_type gadgetfs_type = { .owner = THIS_MODULE, .name = shortname, - .get_sb = gadgetfs_get_sb, + .mount = gadgetfs_mount, .kill_sb = gadgetfs_kill_sb, }; +MODULE_ALIAS_FS("gadgetfs"); /*----------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c deleted file mode 100644 index 6cd3d54f564..00000000000 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ /dev/null @@ -1,2151 +0,0 @@ -/* - * linux/drivers/usb/gadget/lh7a40x_udc.c - * Sharp LH7A40x on-chip full speed USB device controllers - * - * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID - * Copyright (C) 2004 Bo Henriksen, Nordic ID - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/platform_device.h> - -#include "lh7a40x_udc.h" - -//#define DEBUG printk -//#define DEBUG_EP0 printk -//#define DEBUG_SETUP printk - -#ifndef DEBUG_EP0 -# define DEBUG_EP0(fmt,args...) -#endif -#ifndef DEBUG_SETUP -# define DEBUG_SETUP(fmt,args...) -#endif -#ifndef DEBUG -# define NO_STATES -# define DEBUG(fmt,args...) -#endif - -#define DRIVER_DESC "LH7A40x USB Device Controller" -#define DRIVER_VERSION __DATE__ - -#ifndef _BIT /* FIXME - what happended to _BIT in 2.6.7bk18? */ -#define _BIT(x) (1<<(x)) -#endif - -struct lh7a40x_udc *the_controller; - -static const char driver_name[] = "lh7a40x_udc"; -static const char driver_desc[] = DRIVER_DESC; -static const char ep0name[] = "ep0-control"; - -/* - Local definintions. -*/ - -#ifndef NO_STATES -static char *state_names[] = { - "WAIT_FOR_SETUP", - "DATA_STATE_XMIT", - "DATA_STATE_NEED_ZLP", - "WAIT_FOR_OUT_STATUS", - "DATA_STATE_RECV" -}; -#endif - -/* - Local declarations. -*/ -static int lh7a40x_ep_enable(struct usb_ep *ep, - const struct usb_endpoint_descriptor *); -static int lh7a40x_ep_disable(struct usb_ep *ep); -static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t); -static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *); -static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t); -static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *); -static int lh7a40x_set_halt(struct usb_ep *ep, int); -static int lh7a40x_fifo_status(struct usb_ep *ep); -static void lh7a40x_fifo_flush(struct usb_ep *ep); -static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep); -static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr); - -static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, - int status); -static void pio_irq_enable(int bEndpointAddress); -static void pio_irq_disable(int bEndpointAddress); -static void stop_activity(struct lh7a40x_udc *dev, - struct usb_gadget_driver *driver); -static void flush(struct lh7a40x_ep *ep); -static void udc_enable(struct lh7a40x_udc *dev); -static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address); - -static struct usb_ep_ops lh7a40x_ep_ops = { - .enable = lh7a40x_ep_enable, - .disable = lh7a40x_ep_disable, - - .alloc_request = lh7a40x_alloc_request, - .free_request = lh7a40x_free_request, - - .queue = lh7a40x_queue, - .dequeue = lh7a40x_dequeue, - - .set_halt = lh7a40x_set_halt, - .fifo_status = lh7a40x_fifo_status, - .fifo_flush = lh7a40x_fifo_flush, -}; - -/* Inline code */ - -static __inline__ int write_packet(struct lh7a40x_ep *ep, - struct lh7a40x_request *req, int max) -{ - u8 *buf; - int length, count; - volatile u32 *fifo = (volatile u32 *)ep->fifo; - - buf = req->req.buf + req->req.actual; - prefetch(buf); - - length = req->req.length - req->req.actual; - length = min(length, max); - req->req.actual += length; - - DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); - - count = length; - while (count--) { - *fifo = *buf++; - } - - return length; -} - -static __inline__ void usb_set_index(u32 ep) -{ - *(volatile u32 *)io_p2v(USB_INDEX) = ep; -} - -static __inline__ u32 usb_read(u32 port) -{ - return *(volatile u32 *)io_p2v(port); -} - -static __inline__ void usb_write(u32 val, u32 port) -{ - *(volatile u32 *)io_p2v(port) = val; -} - -static __inline__ void usb_set(u32 val, u32 port) -{ - volatile u32 *ioport = (volatile u32 *)io_p2v(port); - u32 after = (*ioport) | val; - *ioport = after; -} - -static __inline__ void usb_clear(u32 val, u32 port) -{ - volatile u32 *ioport = (volatile u32 *)io_p2v(port); - u32 after = (*ioport) & ~val; - *ioport = after; -} - -/*-------------------------------------------------------------------------*/ - -#define GPIO_PORTC_DR (0x80000E08) -#define GPIO_PORTC_DDR (0x80000E18) -#define GPIO_PORTC_PDR (0x80000E70) - -/* get port C pin data register */ -#define get_portc_pdr(bit) ((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0) -/* get port C data direction register */ -#define get_portc_ddr(bit) ((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0) -/* set port C data register */ -#define set_portc_dr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR)) -/* set port C data direction register */ -#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR)) - -/* - * LPD7A404 GPIO's: - * Port C bit 1 = USB Port 1 Power Enable - * Port C bit 2 = USB Port 1 Data Carrier Detect - */ -#define is_usb_connected() get_portc_pdr(2) - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -static const char proc_node_name[] = "driver/udc"; - -static int -udc_proc_read(char *page, char **start, off_t off, int count, - int *eof, void *_dev) -{ - char *buf = page; - struct lh7a40x_udc *dev = _dev; - char *next = buf; - unsigned size = count; - unsigned long flags; - int t; - - if (off != 0) - return 0; - - local_irq_save(flags); - - /* basic device status */ - t = scnprintf(next, size, - DRIVER_DESC "\n" - "%s version: %s\n" - "Gadget driver: %s\n" - "Host: %s\n\n", - driver_name, DRIVER_VERSION, - dev->driver ? dev->driver->driver.name : "(none)", - is_usb_connected()? "full speed" : "disconnected"); - size -= t; - next += t; - - t = scnprintf(next, size, - "GPIO:\n" - " Port C bit 1: %d, dir %d\n" - " Port C bit 2: %d, dir %d\n\n", - get_portc_pdr(1), get_portc_ddr(1), - get_portc_pdr(2), get_portc_ddr(2) - ); - size -= t; - next += t; - - t = scnprintf(next, size, - "DCP pullup: %d\n\n", - (usb_read(USB_PM) & PM_USB_DCP) != 0); - size -= t; - next += t; - - local_irq_restore(flags); - *eof = 1; - return count - size; -} - -#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) -#define remove_proc_files() remove_proc_entry(proc_node_name, NULL) - -#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ - -#define create_proc_files() do {} while (0) -#define remove_proc_files() do {} while (0) - -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - -/* - * udc_disable - disable USB device controller - */ -static void udc_disable(struct lh7a40x_udc *dev) -{ - DEBUG("%s, %p\n", __func__, dev); - - udc_set_address(dev, 0); - - /* Disable interrupts */ - usb_write(0, USB_IN_INT_EN); - usb_write(0, USB_OUT_INT_EN); - usb_write(0, USB_INT_EN); - - /* Disable the USB */ - usb_write(0, USB_PM); - -#ifdef CONFIG_ARCH_LH7A404 - /* Disable USB power */ - set_portc_dr(1, 0); -#endif - - /* if hardware supports it, disconnect from usb */ - /* make_usb_disappear(); */ - - dev->ep0state = WAIT_FOR_SETUP; - dev->gadget.speed = USB_SPEED_UNKNOWN; - dev->usb_address = 0; -} - -/* - * udc_reinit - initialize software state - */ -static void udc_reinit(struct lh7a40x_udc *dev) -{ - u32 i; - - DEBUG("%s, %p\n", __func__, dev); - - /* device/ep0 records init */ - INIT_LIST_HEAD(&dev->gadget.ep_list); - INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); - dev->ep0state = WAIT_FOR_SETUP; - - /* basic endpoint records init */ - for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { - struct lh7a40x_ep *ep = &dev->ep[i]; - - if (i != 0) - list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); - - ep->desc = 0; - ep->stopped = 0; - INIT_LIST_HEAD(&ep->queue); - ep->pio_irqs = 0; - } - - /* the rest was statically initialized, and is read-only */ -} - -#define BYTES2MAXP(x) (x / 8) -#define MAXP2BYTES(x) (x * 8) - -/* until it's enabled, this UDC should be completely invisible - * to any USB host. - */ -static void udc_enable(struct lh7a40x_udc *dev) -{ - int ep; - - DEBUG("%s, %p\n", __func__, dev); - - dev->gadget.speed = USB_SPEED_UNKNOWN; - -#ifdef CONFIG_ARCH_LH7A404 - /* Set Port C bit 1 & 2 as output */ - set_portc_ddr(1, 1); - set_portc_ddr(2, 1); - - /* Enable USB power */ - set_portc_dr(1, 0); -#endif - - /* - * C.f Chapter 18.1.3.1 Initializing the USB - */ - - /* Disable the USB */ - usb_clear(PM_USB_ENABLE, USB_PM); - - /* Reset APB & I/O sides of the USB */ - usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET); - mdelay(5); - usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); - - /* Set MAXP values for each */ - for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { - struct lh7a40x_ep *ep_reg = &dev->ep[ep]; - u32 csr; - - usb_set_index(ep); - - switch (ep_reg->ep_type) { - case ep_bulk_in: - case ep_interrupt: - usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, - ep_reg->csr2); - /* Fall through */ - case ep_control: - usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), - USB_IN_MAXP); - break; - case ep_bulk_out: - usb_clear(USB_OUT_CSR2_USB_DMA_EN | - USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); - usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), - USB_OUT_MAXP); - break; - } - - /* Read & Write CSR1, just in case */ - csr = usb_read(ep_reg->csr1); - usb_write(csr, ep_reg->csr1); - - flush(ep_reg); - } - - /* Disable interrupts */ - usb_write(0, USB_IN_INT_EN); - usb_write(0, USB_OUT_INT_EN); - usb_write(0, USB_INT_EN); - - /* Enable interrupts */ - usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); - usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); - /* Dont enable rest of the interrupts */ - /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); - usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ - - /* Enable SUSPEND */ - usb_set(PM_ENABLE_SUSPEND, USB_PM); - - /* Enable the USB */ - usb_set(PM_USB_ENABLE, USB_PM); - -#ifdef CONFIG_ARCH_LH7A404 - /* NOTE: DOES NOT WORK! */ - /* Let host detect UDC: - * Software must write a 0 to the PMR:DCP_CTRL bit to turn this - * transistor on and pull the USBDP pin HIGH. - */ - /* usb_clear(PM_USB_DCP, USB_PM); - usb_set(PM_USB_DCP, USB_PM); */ -#endif -} - -/* - Register entry point for the peripheral controller driver. -*/ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) -{ - struct lh7a40x_udc *dev = the_controller; - int retval; - - DEBUG("%s: %s\n", __func__, driver->driver.name); - - if (!driver - || driver->speed != USB_SPEED_FULL - || !driver->bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; - - /* first hook up the driver ... */ - dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - - device_add(&dev->gadget.dev); - retval = driver->bind(&dev->gadget); - if (retval) { - printk(KERN_WARNING "%s: bind to driver %s --> error %d\n", - dev->gadget.name, driver->driver.name, retval); - device_del(&dev->gadget.dev); - - dev->driver = 0; - dev->gadget.dev.driver = 0; - return retval; - } - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - * NOTE: this shouldn't power up until later. - */ - printk(KERN_WARNING "%s: registered gadget driver '%s'\n", - dev->gadget.name, driver->driver.name); - - udc_enable(dev); - - return 0; -} - -EXPORT_SYMBOL(usb_gadget_register_driver); - -/* - Unregister entry point for the peripheral controller driver. -*/ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct lh7a40x_udc *dev = the_controller; - unsigned long flags; - - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; - - spin_lock_irqsave(&dev->lock, flags); - dev->driver = 0; - stop_activity(dev, driver); - spin_unlock_irqrestore(&dev->lock, flags); - - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; - device_del(&dev->gadget.dev); - - udc_disable(dev); - - DEBUG("unregistered gadget driver '%s'\n", driver->driver.name); - return 0; -} - -EXPORT_SYMBOL(usb_gadget_unregister_driver); - -/*-------------------------------------------------------------------------*/ - -/** Write request to FIFO (max write == maxp size) - * Return: 0 = still running, 1 = completed, negative = errno - * NOTE: INDEX register must be set for EP - */ -static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) -{ - u32 max; - u32 csr; - - max = le16_to_cpu(ep->desc->wMaxPacketSize); - - csr = usb_read(ep->csr1); - DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY); - - if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) { - unsigned count; - int is_last, is_short; - - count = write_packet(ep, req, max); - usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1); - - /* last packet is usually short (or a zlp) */ - if (unlikely(count != max)) - is_last = is_short = 1; - else { - if (likely(req->req.length != req->req.actual) - || req->req.zero) - is_last = 0; - else - is_last = 1; - /* interrupt/iso maxpacket may not fill the fifo */ - is_short = unlikely(max < ep_maxpacket(ep)); - } - - DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __func__, - ep->ep.name, count, - is_last ? "/L" : "", is_short ? "/S" : "", - req->req.length - req->req.actual, req); - - /* requests complete when all IN data is in the FIFO */ - if (is_last) { - done(ep, req, 0); - if (list_empty(&ep->queue)) { - pio_irq_disable(ep_index(ep)); - } - return 1; - } - } else { - DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); - } - - return 0; -} - -/** Read to request from FIFO (max read == bytes in fifo) - * Return: 0 = still running, 1 = completed, negative = errno - * NOTE: INDEX register must be set for EP - */ -static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) -{ - u32 csr; - u8 *buf; - unsigned bufferspace, count, is_short; - volatile u32 *fifo = (volatile u32 *)ep->fifo; - - /* make sure there's a packet in the FIFO. */ - csr = usb_read(ep->csr1); - if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) { - DEBUG("%s: Packet NOT ready!\n", __func__); - return -EINVAL; - } - - buf = req->req.buf + req->req.actual; - prefetchw(buf); - bufferspace = req->req.length - req->req.actual; - - /* read all bytes from this packet */ - count = usb_read(USB_OUT_FIFO_WC1); - req->req.actual += min(count, bufferspace); - - is_short = (count < ep->ep.maxpacket); - DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", - ep->ep.name, csr, count, - is_short ? "/S" : "", req, req->req.actual, req->req.length); - - while (likely(count-- != 0)) { - u8 byte = (u8) (*fifo & 0xff); - - if (unlikely(bufferspace == 0)) { - /* this happens when the driver's buffer - * is smaller than what the host sent. - * discard the extra data. - */ - if (req->req.status != -EOVERFLOW) - printk(KERN_WARNING "%s overflow %d\n", - ep->ep.name, count); - req->req.status = -EOVERFLOW; - } else { - *buf++ = byte; - bufferspace--; - } - } - - usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1); - - /* completion */ - if (is_short || req->req.actual == req->req.length) { - done(ep, req, 0); - usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); - - if (list_empty(&ep->queue)) - pio_irq_disable(ep_index(ep)); - return 1; - } - - /* finished that packet. the next one may be waiting... */ - return 0; -} - -/* - * done - retire a request; caller blocked irqs - * INDEX register is preserved to keep same - */ -static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status) -{ - unsigned int stopped = ep->stopped; - u32 index; - - DEBUG("%s, %p\n", __func__, ep); - list_del_init(&req->queue); - - if (likely(req->req.status == -EINPROGRESS)) - req->req.status = status; - else - status = req->req.status; - - if (status && status != -ESHUTDOWN) - DEBUG("complete %s req %p stat %d len %u/%u\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - /* Read current index (completion may modify it) */ - index = usb_read(USB_INDEX); - - spin_unlock(&ep->dev->lock); - req->req.complete(&ep->ep, &req->req); - spin_lock(&ep->dev->lock); - - /* Restore index */ - usb_set_index(index); - ep->stopped = stopped; -} - -/** Enable EP interrupt */ -static void pio_irq_enable(int ep) -{ - DEBUG("%s: %d\n", __func__, ep); - - switch (ep) { - case 1: - usb_set(USB_IN_INT_EP1, USB_IN_INT_EN); - break; - case 2: - usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); - break; - case 3: - usb_set(USB_IN_INT_EP3, USB_IN_INT_EN); - break; - default: - DEBUG("Unknown endpoint: %d\n", ep); - break; - } -} - -/** Disable EP interrupt */ -static void pio_irq_disable(int ep) -{ - DEBUG("%s: %d\n", __func__, ep); - - switch (ep) { - case 1: - usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN); - break; - case 2: - usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN); - break; - case 3: - usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN); - break; - default: - DEBUG("Unknown endpoint: %d\n", ep); - break; - } -} - -/* - * nuke - dequeue ALL requests - */ -void nuke(struct lh7a40x_ep *ep, int status) -{ - struct lh7a40x_request *req; - - DEBUG("%s, %p\n", __func__, ep); - - /* Flush FIFO */ - flush(ep); - - /* called with irqs blocked */ - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, struct lh7a40x_request, queue); - done(ep, req, status); - } - - /* Disable IRQ if EP is enabled (has descriptor) */ - if (ep->desc) - pio_irq_disable(ep_index(ep)); -} - -/* -void nuke_all(struct lh7a40x_udc *dev) -{ - int n; - for(n=0; n<UDC_MAX_ENDPOINTS; n++) { - struct lh7a40x_ep *ep = &dev->ep[n]; - usb_set_index(n); - nuke(ep, 0); - } -}*/ - -/* -static void flush_all(struct lh7a40x_udc *dev) -{ - int n; - for (n = 0; n < UDC_MAX_ENDPOINTS; n++) - { - struct lh7a40x_ep *ep = &dev->ep[n]; - flush(ep); - } -} -*/ - -/** Flush EP - * NOTE: INDEX register must be set before this call - */ -static void flush(struct lh7a40x_ep *ep) -{ - DEBUG("%s, %p\n", __func__, ep); - - switch (ep->ep_type) { - case ep_control: - /* check, by implication c.f. 15.1.2.11 */ - break; - - case ep_bulk_in: - case ep_interrupt: - /* if(csr & USB_IN_CSR1_IN_PKT_RDY) */ - usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1); - break; - - case ep_bulk_out: - /* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */ - usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); - break; - } -} - -/** - * lh7a40x_in_epn - handle IN interrupt - */ -static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) -{ - u32 csr; - struct lh7a40x_ep *ep = &dev->ep[ep_idx]; - struct lh7a40x_request *req; - - usb_set_index(ep_idx); - - csr = usb_read(ep->csr1); - DEBUG("%s: %d, csr %x\n", __func__, ep_idx, csr); - - if (csr & USB_IN_CSR1_SENT_STALL) { - DEBUG("USB_IN_CSR1_SENT_STALL\n"); - usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ , - ep->csr1); - return; - } - - if (!ep->desc) { - DEBUG("%s: NO EP DESC\n", __func__); - return; - } - - if (list_empty(&ep->queue)) - req = 0; - else - req = list_entry(ep->queue.next, struct lh7a40x_request, queue); - - DEBUG("req: %p\n", req); - - if (!req) - return; - - write_fifo(ep, req); -} - -/* ********************************************************************************************* */ -/* Bulk OUT (recv) - */ - -static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) -{ - struct lh7a40x_ep *ep = &dev->ep[ep_idx]; - struct lh7a40x_request *req; - - DEBUG("%s: %d\n", __func__, ep_idx); - - usb_set_index(ep_idx); - - if (ep->desc) { - u32 csr; - csr = usb_read(ep->csr1); - - while ((csr = - usb_read(ep-> - csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY | - USB_OUT_CSR1_SENT_STALL)) { - DEBUG("%s: %x\n", __func__, csr); - - if (csr & USB_OUT_CSR1_SENT_STALL) { - DEBUG("%s: stall sent, flush fifo\n", - __func__); - /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ - flush(ep); - } else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) { - if (list_empty(&ep->queue)) - req = 0; - else - req = - list_entry(ep->queue.next, - struct lh7a40x_request, - queue); - - if (!req) { - printk(KERN_WARNING - "%s: NULL REQ %d\n", - __func__, ep_idx); - flush(ep); - break; - } else { - read_fifo(ep, req); - } - } - - } - - } else { - /* Throw packet away.. */ - printk(KERN_WARNING "%s: No descriptor?!?\n", __func__); - flush(ep); - } -} - -static void stop_activity(struct lh7a40x_udc *dev, - struct usb_gadget_driver *driver) -{ - int i; - - /* don't disconnect drivers more than once */ - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = 0; - dev->gadget.speed = USB_SPEED_UNKNOWN; - - /* prevent new request submissions, kill any outstanding requests */ - for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { - struct lh7a40x_ep *ep = &dev->ep[i]; - ep->stopped = 1; - - usb_set_index(i); - nuke(ep, -ESHUTDOWN); - } - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - - /* re-init driver-visible data structures */ - udc_reinit(dev); -} - -/** Handle USB RESET interrupt - */ -static void lh7a40x_reset_intr(struct lh7a40x_udc *dev) -{ -#if 0 /* def CONFIG_ARCH_LH7A404 */ - /* Does not work always... */ - - DEBUG("%s: %d\n", __func__, dev->usb_address); - - if (!dev->usb_address) { - /*usb_set(USB_RESET_IO, USB_RESET); - mdelay(5); - usb_clear(USB_RESET_IO, USB_RESET); */ - return; - } - /* Put the USB controller into reset. */ - usb_set(USB_RESET_IO, USB_RESET); - - /* Set Device ID to 0 */ - udc_set_address(dev, 0); - - /* Let PLL2 settle down */ - mdelay(5); - - /* Release the USB controller from reset */ - usb_clear(USB_RESET_IO, USB_RESET); - - /* Re-enable UDC */ - udc_enable(dev); - -#endif - dev->gadget.speed = USB_SPEED_FULL; -} - -/* - * lh7a40x usb client interrupt handler. - */ -static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev) -{ - struct lh7a40x_udc *dev = _dev; - - DEBUG("\n\n"); - - spin_lock(&dev->lock); - - for (;;) { - u32 intr_in = usb_read(USB_IN_INT); - u32 intr_out = usb_read(USB_OUT_INT); - u32 intr_int = usb_read(USB_INT); - - /* Test also against enable bits.. (lh7a40x errata).. Sigh.. */ - u32 in_en = usb_read(USB_IN_INT_EN); - u32 out_en = usb_read(USB_OUT_INT_EN); - - if (!intr_out && !intr_in && !intr_int) - break; - - DEBUG("%s (on state %s)\n", __func__, - state_names[dev->ep0state]); - DEBUG("intr_out = %x\n", intr_out); - DEBUG("intr_in = %x\n", intr_in); - DEBUG("intr_int = %x\n", intr_int); - - if (intr_in) { - usb_write(intr_in, USB_IN_INT); - - if ((intr_in & USB_IN_INT_EP1) - && (in_en & USB_IN_INT_EP1)) { - DEBUG("USB_IN_INT_EP1\n"); - lh7a40x_in_epn(dev, 1, intr_in); - } - if ((intr_in & USB_IN_INT_EP3) - && (in_en & USB_IN_INT_EP3)) { - DEBUG("USB_IN_INT_EP3\n"); - lh7a40x_in_epn(dev, 3, intr_in); - } - if (intr_in & USB_IN_INT_EP0) { - DEBUG("USB_IN_INT_EP0 (control)\n"); - lh7a40x_handle_ep0(dev, intr_in); - } - } - - if (intr_out) { - usb_write(intr_out, USB_OUT_INT); - - if ((intr_out & USB_OUT_INT_EP2) - && (out_en & USB_OUT_INT_EP2)) { - DEBUG("USB_OUT_INT_EP2\n"); - lh7a40x_out_epn(dev, 2, intr_out); - } - } - - if (intr_int) { - usb_write(intr_int, USB_INT); - - if (intr_int & USB_INT_RESET_INT) { - lh7a40x_reset_intr(dev); - } - - if (intr_int & USB_INT_RESUME_INT) { - DEBUG("USB resume\n"); - - if (dev->gadget.speed != USB_SPEED_UNKNOWN - && dev->driver - && dev->driver->resume - && is_usb_connected()) { - dev->driver->resume(&dev->gadget); - } - } - - if (intr_int & USB_INT_SUSPEND_INT) { - DEBUG("USB suspend%s\n", - is_usb_connected()? "" : "+disconnect"); - if (!is_usb_connected()) { - stop_activity(dev, dev->driver); - } else if (dev->gadget.speed != - USB_SPEED_UNKNOWN && dev->driver - && dev->driver->suspend) { - dev->driver->suspend(&dev->gadget); - } - } - - } - } - - spin_unlock(&dev->lock); - - return IRQ_HANDLED; -} - -static int lh7a40x_ep_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct lh7a40x_ep *ep; - struct lh7a40x_udc *dev; - unsigned long flags; - - DEBUG("%s, %p\n", __func__, _ep); - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name - || desc->bDescriptorType != USB_DT_ENDPOINT - || ep->bEndpointAddress != desc->bEndpointAddress - || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { - DEBUG("%s, bad ep or descriptor\n", __func__); - return -EINVAL; - } - - /* xfer types must match, except that interrupt ~= bulk */ - if (ep->bmAttributes != desc->bmAttributes - && ep->bmAttributes != USB_ENDPOINT_XFER_BULK - && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { - DEBUG("%s, %s type mismatch\n", __func__, _ep->name); - return -EINVAL; - } - - /* hardware _could_ do smaller, but driver doesn't */ - if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) - || !desc->wMaxPacketSize) { - DEBUG("%s, bad %s maxpacket\n", __func__, _ep->name); - return -ERANGE; - } - - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { - DEBUG("%s, bogus device state\n", __func__); - return -ESHUTDOWN; - } - - spin_lock_irqsave(&ep->dev->lock, flags); - - ep->stopped = 0; - ep->desc = desc; - ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); - - spin_unlock_irqrestore(&ep->dev->lock, flags); - - /* Reset halt state (does flush) */ - lh7a40x_set_halt(_ep, 0); - - DEBUG("%s: enabled %s\n", __func__, _ep->name); - return 0; -} - -/** Disable EP - * NOTE: Sets INDEX register - */ -static int lh7a40x_ep_disable(struct usb_ep *_ep) -{ - struct lh7a40x_ep *ep; - unsigned long flags; - - DEBUG("%s, %p\n", __func__, _ep); - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (!_ep || !ep->desc) { - DEBUG("%s, %s not enabled\n", __func__, - _ep ? ep->ep.name : NULL); - return -EINVAL; - } - - spin_lock_irqsave(&ep->dev->lock, flags); - - usb_set_index(ep_index(ep)); - - /* Nuke all pending requests (does flush) */ - nuke(ep, -ESHUTDOWN); - - /* Disable ep IRQ */ - pio_irq_disable(ep_index(ep)); - - ep->desc = 0; - ep->stopped = 1; - - spin_unlock_irqrestore(&ep->dev->lock, flags); - - DEBUG("%s: disabled %s\n", __func__, _ep->name); - return 0; -} - -static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, - gfp_t gfp_flags) -{ - struct lh7a40x_request *req; - - DEBUG("%s, %p\n", __func__, ep); - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return 0; - - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req) -{ - struct lh7a40x_request *req; - - DEBUG("%s, %p\n", __func__, ep); - - req = container_of(_req, struct lh7a40x_request, req); - WARN_ON(!list_empty(&req->queue)); - kfree(req); -} - -/** Queue one request - * Kickstart transfer if needed - * NOTE: Sets INDEX register - */ -static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req, - gfp_t gfp_flags) -{ - struct lh7a40x_request *req; - struct lh7a40x_ep *ep; - struct lh7a40x_udc *dev; - unsigned long flags; - - DEBUG("\n\n\n%s, %p\n", __func__, _ep); - - req = container_of(_req, struct lh7a40x_request, req); - if (unlikely - (!_req || !_req->complete || !_req->buf - || !list_empty(&req->queue))) { - DEBUG("%s, bad params\n", __func__); - return -EINVAL; - } - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { - DEBUG("%s, bad ep\n", __func__); - return -EINVAL; - } - - dev = ep->dev; - if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { - DEBUG("%s, bogus device state %p\n", __func__, dev->driver); - return -ESHUTDOWN; - } - - DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, - _req->buf); - - spin_lock_irqsave(&dev->lock, flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), - ep->stopped); - if (list_empty(&ep->queue) && likely(!ep->stopped)) { - u32 csr; - - if (unlikely(ep_index(ep) == 0)) { - /* EP0 */ - list_add_tail(&req->queue, &ep->queue); - lh7a40x_ep0_kick(dev, ep); - req = 0; - } else if (ep_is_in(ep)) { - /* EP1 & EP3 */ - usb_set_index(ep_index(ep)); - csr = usb_read(ep->csr1); - pio_irq_enable(ep_index(ep)); - if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) { - if (write_fifo(ep, req) == 1) - req = 0; - } - } else { - /* EP2 */ - usb_set_index(ep_index(ep)); - csr = usb_read(ep->csr1); - pio_irq_enable(ep_index(ep)); - if (!(csr & USB_OUT_CSR1_FIFO_FULL)) { - if (read_fifo(ep, req) == 1) - req = 0; - } - } - } - - /* pio or dma irq handler advances the queue. */ - if (likely(req != 0)) - list_add_tail(&req->queue, &ep->queue); - - spin_unlock_irqrestore(&dev->lock, flags); - - return 0; -} - -/* dequeue JUST ONE request */ -static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct lh7a40x_ep *ep; - struct lh7a40x_request *req; - unsigned long flags; - - DEBUG("%s, %p\n", __func__, _ep); - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (!_ep || ep->ep.name == ep0name) - return -EINVAL; - - spin_lock_irqsave(&ep->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) { - spin_unlock_irqrestore(&ep->dev->lock, flags); - return -EINVAL; - } - - done(ep, req, -ECONNRESET); - - spin_unlock_irqrestore(&ep->dev->lock, flags); - return 0; -} - -/** Halt specific EP - * Return 0 if success - * NOTE: Sets INDEX register to EP ! - */ -static int lh7a40x_set_halt(struct usb_ep *_ep, int value) -{ - struct lh7a40x_ep *ep; - unsigned long flags; - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { - DEBUG("%s, bad ep\n", __func__); - return -EINVAL; - } - - usb_set_index(ep_index(ep)); - - DEBUG("%s, ep %d, val %d\n", __func__, ep_index(ep), value); - - spin_lock_irqsave(&ep->dev->lock, flags); - - if (ep_index(ep) == 0) { - /* EP0 */ - usb_set(EP0_SEND_STALL, ep->csr1); - } else if (ep_is_in(ep)) { - u32 csr = usb_read(ep->csr1); - if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) - || !list_empty(&ep->queue))) { - /* - * Attempts to halt IN endpoints will fail (returning -EAGAIN) - * if any transfer requests are still queued, or if the controller - * FIFO still holds bytes that the host hasn't collected. - */ - spin_unlock_irqrestore(&ep->dev->lock, flags); - DEBUG - ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", - (csr & USB_IN_CSR1_FIFO_NOT_EMPTY), - !list_empty(&ep->queue)); - return -EAGAIN; - } - flush(ep); - if (value) - usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1); - else { - usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1); - usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1); - } - - } else { - - flush(ep); - if (value) - usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1); - else { - usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1); - usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1); - } - } - - if (value) { - ep->stopped = 1; - } else { - ep->stopped = 0; - } - - spin_unlock_irqrestore(&ep->dev->lock, flags); - - DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); - - return 0; -} - -/** Return bytes in EP FIFO - * NOTE: Sets INDEX register to EP - */ -static int lh7a40x_fifo_status(struct usb_ep *_ep) -{ - u32 csr; - int count = 0; - struct lh7a40x_ep *ep; - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (!_ep) { - DEBUG("%s, bad ep\n", __func__); - return -ENODEV; - } - - DEBUG("%s, %d\n", __func__, ep_index(ep)); - - /* LPD can't report unclaimed bytes from IN fifos */ - if (ep_is_in(ep)) - return -EOPNOTSUPP; - - usb_set_index(ep_index(ep)); - - csr = usb_read(ep->csr1); - if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || - csr & USB_OUT_CSR1_OUT_PKT_RDY) { - count = usb_read(USB_OUT_FIFO_WC1); - } - - return count; -} - -/** Flush EP FIFO - * NOTE: Sets INDEX register to EP - */ -static void lh7a40x_fifo_flush(struct usb_ep *_ep) -{ - struct lh7a40x_ep *ep; - - ep = container_of(_ep, struct lh7a40x_ep, ep); - if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { - DEBUG("%s, bad ep\n", __func__); - return; - } - - usb_set_index(ep_index(ep)); - flush(ep); -} - -/****************************************************************/ -/* End Point 0 related functions */ -/****************************************************************/ - -/* return: 0 = still running, 1 = completed, negative = errno */ -static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) -{ - u32 max; - unsigned count; - int is_last; - - max = ep_maxpacket(ep); - - DEBUG_EP0("%s\n", __func__); - - count = write_packet(ep, req, max); - - /* last packet is usually short (or a zlp) */ - if (unlikely(count != max)) - is_last = 1; - else { - if (likely(req->req.length != req->req.actual) || req->req.zero) - is_last = 0; - else - is_last = 1; - } - - DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, - ep->ep.name, count, - is_last ? "/L" : "", req->req.length - req->req.actual, req); - - /* requests complete when all IN data is in the FIFO */ - if (is_last) { - done(ep, req, 0); - return 1; - } - - return 0; -} - -static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep, - unsigned char *cp, int max) -{ - int bytes; - int count = usb_read(USB_OUT_FIFO_WC1); - volatile u32 *fifo = (volatile u32 *)ep->fifo; - - if (count > max) - count = max; - bytes = count; - while (count--) - *cp++ = *fifo & 0xFF; - return bytes; -} - -static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep, - unsigned char *cp, int count) -{ - volatile u32 *fifo = (volatile u32 *)ep->fifo; - DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); - while (count--) - *fifo = *cp++; -} - -static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) -{ - u32 csr; - u8 *buf; - unsigned bufferspace, count, is_short; - volatile u32 *fifo = (volatile u32 *)ep->fifo; - - DEBUG_EP0("%s\n", __func__); - - csr = usb_read(USB_EP0_CSR); - if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) - return 0; - - buf = req->req.buf + req->req.actual; - prefetchw(buf); - bufferspace = req->req.length - req->req.actual; - - /* read all bytes from this packet */ - if (likely(csr & EP0_OUT_PKT_RDY)) { - count = usb_read(USB_OUT_FIFO_WC1); - req->req.actual += min(count, bufferspace); - } else /* zlp */ - count = 0; - - is_short = (count < ep->ep.maxpacket); - DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", - ep->ep.name, csr, count, - is_short ? "/S" : "", req, req->req.actual, req->req.length); - - while (likely(count-- != 0)) { - u8 byte = (u8) (*fifo & 0xff); - - if (unlikely(bufferspace == 0)) { - /* this happens when the driver's buffer - * is smaller than what the host sent. - * discard the extra data. - */ - if (req->req.status != -EOVERFLOW) - DEBUG_EP0("%s overflow %d\n", ep->ep.name, - count); - req->req.status = -EOVERFLOW; - } else { - *buf++ = byte; - bufferspace--; - } - } - - /* completion */ - if (is_short || req->req.actual == req->req.length) { - done(ep, req, 0); - return 1; - } - - /* finished that packet. the next one may be waiting... */ - return 0; -} - -/** - * udc_set_address - set the USB address for this device - * @address: - * - * Called from control endpoint function after it decodes a set address setup packet. - */ -static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address) -{ - DEBUG_EP0("%s: %d\n", __func__, address); - /* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */ - dev->usb_address = address; - usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA); - usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA); - /* usb_read(USB_FA); */ -} - -/* - * DATA_STATE_RECV (OUT_PKT_RDY) - * - if error - * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits - * - else - * set EP0_CLR_OUT bit - if last set EP0_DATA_END bit - */ -static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr) -{ - struct lh7a40x_request *req; - struct lh7a40x_ep *ep = &dev->ep[0]; - int ret; - - DEBUG_EP0("%s: %x\n", __func__, csr); - - if (list_empty(&ep->queue)) - req = 0; - else - req = list_entry(ep->queue.next, struct lh7a40x_request, queue); - - if (req) { - - if (req->req.length == 0) { - DEBUG_EP0("ZERO LENGTH OUT!\n"); - usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); - dev->ep0state = WAIT_FOR_SETUP; - return; - } - ret = read_fifo_ep0(ep, req); - if (ret) { - /* Done! */ - DEBUG_EP0("%s: finished, waiting for status\n", - __func__); - - usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); - dev->ep0state = WAIT_FOR_SETUP; - } else { - /* Not done yet.. */ - DEBUG_EP0("%s: not finished\n", __func__); - usb_set(EP0_CLR_OUT, USB_EP0_CSR); - } - } else { - DEBUG_EP0("NO REQ??!\n"); - } -} - -/* - * DATA_STATE_XMIT - */ -static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr) -{ - struct lh7a40x_request *req; - struct lh7a40x_ep *ep = &dev->ep[0]; - int ret, need_zlp = 0; - - DEBUG_EP0("%s: %x\n", __func__, csr); - - if (list_empty(&ep->queue)) - req = 0; - else - req = list_entry(ep->queue.next, struct lh7a40x_request, queue); - - if (!req) { - DEBUG_EP0("%s: NULL REQ\n", __func__); - return 0; - } - - if (req->req.length == 0) { - - usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); - dev->ep0state = WAIT_FOR_SETUP; - return 1; - } - - if (req->req.length - req->req.actual == EP0_PACKETSIZE) { - /* Next write will end with the packet size, */ - /* so we need Zero-length-packet */ - need_zlp = 1; - } - - ret = write_fifo_ep0(ep, req); - - if (ret == 1 && !need_zlp) { - /* Last packet */ - DEBUG_EP0("%s: finished, waiting for status\n", __func__); - - usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); - dev->ep0state = WAIT_FOR_SETUP; - } else { - DEBUG_EP0("%s: not finished\n", __func__); - usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); - } - - if (need_zlp) { - DEBUG_EP0("%s: Need ZLP!\n", __func__); - usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); - dev->ep0state = DATA_STATE_NEED_ZLP; - } - - return 1; -} - -static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev, - struct usb_ctrlrequest *ctrl) -{ - struct lh7a40x_ep *ep0 = &dev->ep[0]; - struct lh7a40x_ep *qep; - int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); - u16 val = 0; - - if (reqtype == USB_RECIP_INTERFACE) { - /* This is not supported. - * And according to the USB spec, this one does nothing.. - * Just return 0 - */ - DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); - } else if (reqtype == USB_RECIP_DEVICE) { - DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); - val |= (1 << 0); /* Self powered */ - /*val |= (1<<1); *//* Remote wakeup */ - } else if (reqtype == USB_RECIP_ENDPOINT) { - int ep_num = (ctrl->wIndex & ~USB_DIR_IN); - - DEBUG_SETUP - ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", - ep_num, ctrl->wLength); - - if (ctrl->wLength > 2 || ep_num > 3) - return -EOPNOTSUPP; - - qep = &dev->ep[ep_num]; - if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) - && ep_index(qep) != 0) { - return -EOPNOTSUPP; - } - - usb_set_index(ep_index(qep)); - - /* Return status on next IN token */ - switch (qep->ep_type) { - case ep_control: - val = - (usb_read(qep->csr1) & EP0_SEND_STALL) == - EP0_SEND_STALL; - break; - case ep_bulk_in: - case ep_interrupt: - val = - (usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) == - USB_IN_CSR1_SEND_STALL; - break; - case ep_bulk_out: - val = - (usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) == - USB_OUT_CSR1_SEND_STALL; - break; - } - - /* Back to EP0 index */ - usb_set_index(0); - - DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, - ctrl->wIndex, val); - } else { - DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); - return -EOPNOTSUPP; - } - - /* Clear "out packet ready" */ - usb_set((EP0_CLR_OUT), USB_EP0_CSR); - /* Put status to FIFO */ - lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val)); - /* Issue "In packet ready" */ - usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); - - return 0; -} - -/* - * WAIT_FOR_SETUP (OUT_PKT_RDY) - * - read data packet from EP0 FIFO - * - decode command - * - if error - * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits - * - else - * set EP0_CLR_OUT | EP0_DATA_END bits - */ -static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr) -{ - struct lh7a40x_ep *ep = &dev->ep[0]; - struct usb_ctrlrequest ctrl; - int i, bytes, is_in; - - DEBUG_SETUP("%s: %x\n", __func__, csr); - - /* Nuke all previous transfers */ - nuke(ep, -EPROTO); - - /* read control req from fifo (8 bytes) */ - bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8); - - DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes); - DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType, - ctrl.bRequestType == USB_DIR_IN); - DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest); - DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength); - DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8); - DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex); - - /* Set direction of EP0 */ - if (likely(ctrl.bRequestType & USB_DIR_IN)) { - ep->bEndpointAddress |= USB_DIR_IN; - is_in = 1; - } else { - ep->bEndpointAddress &= ~USB_DIR_IN; - is_in = 0; - } - - dev->req_pending = 1; - - /* Handle some SETUP packets ourselves */ - switch (ctrl.bRequest) { - case USB_REQ_SET_ADDRESS: - if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) - break; - - DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); - udc_set_address(dev, ctrl.wValue); - usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); - return; - - case USB_REQ_GET_STATUS:{ - if (lh7a40x_handle_get_status(dev, &ctrl) == 0) - return; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { - struct lh7a40x_ep *qep; - int ep_num = (ctrl.wIndex & 0x0f); - - /* Support only HALT feature */ - if (ctrl.wValue != 0 || ctrl.wLength != 0 - || ep_num > 3 || ep_num < 1) - break; - - qep = &dev->ep[ep_num]; - spin_unlock(&dev->lock); - if (ctrl.bRequest == USB_REQ_SET_FEATURE) { - DEBUG_SETUP("SET_FEATURE (%d)\n", - ep_num); - lh7a40x_set_halt(&qep->ep, 1); - } else { - DEBUG_SETUP("CLR_FEATURE (%d)\n", - ep_num); - lh7a40x_set_halt(&qep->ep, 0); - } - spin_lock(&dev->lock); - usb_set_index(0); - - /* Reply with a ZLP on next IN token */ - usb_set((EP0_CLR_OUT | EP0_DATA_END), - USB_EP0_CSR); - return; - } - break; - } - - default: - break; - } - - if (likely(dev->driver)) { - /* device-2-host (IN) or no data setup command, process immediately */ - spin_unlock(&dev->lock); - i = dev->driver->setup(&dev->gadget, &ctrl); - spin_lock(&dev->lock); - - if (i < 0) { - /* setup processing failed, force stall */ - DEBUG_SETUP - (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", - i); - usb_set_index(0); - usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL), - USB_EP0_CSR); - - /* ep->stopped = 1; */ - dev->ep0state = WAIT_FOR_SETUP; - } - } -} - -/* - * DATA_STATE_NEED_ZLP - */ -static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr) -{ - DEBUG_EP0("%s: %x\n", __func__, csr); - - /* c.f. Table 15-14 */ - usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); - dev->ep0state = WAIT_FOR_SETUP; -} - -/* - * handle ep0 interrupt - */ -static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr) -{ - struct lh7a40x_ep *ep = &dev->ep[0]; - u32 csr; - - /* Set index 0 */ - usb_set_index(0); - csr = usb_read(USB_EP0_CSR); - - DEBUG_EP0("%s: csr = %x\n", __func__, csr); - - /* - * For overview of what we should be doing see c.f. Chapter 18.1.2.4 - * We will follow that outline here modified by our own global state - * indication which provides hints as to what we think should be - * happening.. - */ - - /* - * if SENT_STALL is set - * - clear the SENT_STALL bit - */ - if (csr & EP0_SENT_STALL) { - DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __func__, csr); - usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR); - nuke(ep, -ECONNABORTED); - dev->ep0state = WAIT_FOR_SETUP; - return; - } - - /* - * if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear - * - fill EP0 FIFO - * - if last packet - * - set IN_PKT_RDY | DATA_END - * - else - * set IN_PKT_RDY - */ - if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) { - DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n", - __func__); - - switch (dev->ep0state) { - case DATA_STATE_XMIT: - DEBUG_EP0("continue with DATA_STATE_XMIT\n"); - lh7a40x_ep0_in(dev, csr); - return; - case DATA_STATE_NEED_ZLP: - DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); - lh7a40x_ep0_in_zlp(dev, csr); - return; - default: - /* Stall? */ - DEBUG_EP0("Odd state!! state = %s\n", - state_names[dev->ep0state]); - dev->ep0state = WAIT_FOR_SETUP; - /* nuke(ep, 0); */ - /* usb_set(EP0_SEND_STALL, ep->csr1); */ - break; - } - } - - /* - * if SETUP_END is set - * - abort the last transfer - * - set SERVICED_SETUP_END_BIT - */ - if (csr & EP0_SETUP_END) { - DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __func__, csr); - - usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR); - - nuke(ep, 0); - dev->ep0state = WAIT_FOR_SETUP; - } - - /* - * if EP0_OUT_PKT_RDY is set - * - read data packet from EP0 FIFO - * - decode command - * - if error - * set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL - * - else - * set SERVICED_OUT_PKT_RDY | DATA_END bits - */ - if (csr & EP0_OUT_PKT_RDY) { - - DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __func__, - csr); - - switch (dev->ep0state) { - case WAIT_FOR_SETUP: - DEBUG_EP0("WAIT_FOR_SETUP\n"); - lh7a40x_ep0_setup(dev, csr); - break; - - case DATA_STATE_RECV: - DEBUG_EP0("DATA_STATE_RECV\n"); - lh7a40x_ep0_out(dev, csr); - break; - - default: - /* send stall? */ - DEBUG_EP0("strange state!! 2. send stall? state = %d\n", - dev->ep0state); - break; - } - } -} - -static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep) -{ - u32 csr; - - usb_set_index(0); - csr = usb_read(USB_EP0_CSR); - - DEBUG_EP0("%s: %x\n", __func__, csr); - - /* Clear "out packet ready" */ - usb_set(EP0_CLR_OUT, USB_EP0_CSR); - - if (ep_is_in(ep)) { - dev->ep0state = DATA_STATE_XMIT; - lh7a40x_ep0_in(dev, csr); - } else { - dev->ep0state = DATA_STATE_RECV; - lh7a40x_ep0_out(dev, csr); - } -} - -/* --------------------------------------------------------------------------- - * device-scoped parts of the api to the usb controller hardware - * --------------------------------------------------------------------------- - */ - -static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget) -{ - u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */ - u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */ - DEBUG("%s, %p\n", __func__, _gadget); - return ((frame2 & 0x07) << 8) | (frame1 & 0xff); -} - -static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget) -{ - /* host may not have enabled remote wakeup */ - /*if ((UDCCS0 & UDCCS0_DRWF) == 0) - return -EHOSTUNREACH; - udc_set_mask_UDCCR(UDCCR_RSM); */ - return -ENOTSUPP; -} - -static const struct usb_gadget_ops lh7a40x_udc_ops = { - .get_frame = lh7a40x_udc_get_frame, - .wakeup = lh7a40x_udc_wakeup, - /* current versions must always be self-powered */ -}; - -static void nop_release(struct device *dev) -{ - DEBUG("%s %s\n", __func__, dev_name(dev)); -} - -static struct lh7a40x_udc memory = { - .usb_address = 0, - - .gadget = { - .ops = &lh7a40x_udc_ops, - .ep0 = &memory.ep[0].ep, - .name = driver_name, - .dev = { - .init_name = "gadget", - .release = nop_release, - }, - }, - - /* control endpoint */ - .ep[0] = { - .ep = { - .name = ep0name, - .ops = &lh7a40x_ep_ops, - .maxpacket = EP0_PACKETSIZE, - }, - .dev = &memory, - - .bEndpointAddress = 0, - .bmAttributes = 0, - - .ep_type = ep_control, - .fifo = io_p2v(USB_EP0_FIFO), - .csr1 = USB_EP0_CSR, - .csr2 = USB_EP0_CSR, - }, - - /* first group of endpoints */ - .ep[1] = { - .ep = { - .name = "ep1in-bulk", - .ops = &lh7a40x_ep_ops, - .maxpacket = 64, - }, - .dev = &memory, - - .bEndpointAddress = USB_DIR_IN | 1, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - - .ep_type = ep_bulk_in, - .fifo = io_p2v(USB_EP1_FIFO), - .csr1 = USB_IN_CSR1, - .csr2 = USB_IN_CSR2, - }, - - .ep[2] = { - .ep = { - .name = "ep2out-bulk", - .ops = &lh7a40x_ep_ops, - .maxpacket = 64, - }, - .dev = &memory, - - .bEndpointAddress = 2, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - - .ep_type = ep_bulk_out, - .fifo = io_p2v(USB_EP2_FIFO), - .csr1 = USB_OUT_CSR1, - .csr2 = USB_OUT_CSR2, - }, - - .ep[3] = { - .ep = { - .name = "ep3in-int", - .ops = &lh7a40x_ep_ops, - .maxpacket = 64, - }, - .dev = &memory, - - .bEndpointAddress = USB_DIR_IN | 3, - .bmAttributes = USB_ENDPOINT_XFER_INT, - - .ep_type = ep_interrupt, - .fifo = io_p2v(USB_EP3_FIFO), - .csr1 = USB_IN_CSR1, - .csr2 = USB_IN_CSR2, - }, -}; - -/* - * probe - binds to the platform device - */ -static int lh7a40x_udc_probe(struct platform_device *pdev) -{ - struct lh7a40x_udc *dev = &memory; - int retval; - - DEBUG("%s: %p\n", __func__, pdev); - - spin_lock_init(&dev->lock); - dev->dev = &pdev->dev; - - device_initialize(&dev->gadget.dev); - dev->gadget.dev.parent = &pdev->dev; - - the_controller = dev; - platform_set_drvdata(pdev, dev); - - udc_disable(dev); - udc_reinit(dev); - - /* irq setup after old hardware state is cleaned up */ - retval = - request_irq(IRQ_USBINTR, lh7a40x_udc_irq, IRQF_DISABLED, driver_name, - dev); - if (retval != 0) { - DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, - IRQ_USBINTR, retval); - return -EBUSY; - } - - create_proc_files(); - - return retval; -} - -static int lh7a40x_udc_remove(struct platform_device *pdev) -{ - struct lh7a40x_udc *dev = platform_get_drvdata(pdev); - - DEBUG("%s: %p\n", __func__, pdev); - - if (dev->driver) - return -EBUSY; - - udc_disable(dev); - remove_proc_files(); - - free_irq(IRQ_USBINTR, dev); - - platform_set_drvdata(pdev, 0); - - the_controller = 0; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct platform_driver udc_driver = { - .probe = lh7a40x_udc_probe, - .remove = lh7a40x_udc_remove, - /* FIXME power management support */ - /* .suspend = ... disable UDC */ - /* .resume = ... re-enable UDC */ - .driver = { - .name = (char *)driver_name, - .owner = THIS_MODULE, - }, -}; - -static int __init udc_init(void) -{ - DEBUG("%s: %s version %s\n", __func__, driver_name, DRIVER_VERSION); - return platform_driver_register(&udc_driver); -} - -static void __exit udc_exit(void) -{ - platform_driver_unregister(&udc_driver); -} - -module_init(udc_init); -module_exit(udc_exit); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:lh7a40x_udc"); diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h deleted file mode 100644 index ca861203a30..00000000000 --- a/drivers/usb/gadget/lh7a40x_udc.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * linux/drivers/usb/gadget/lh7a40x_udc.h - * Sharp LH7A40x on-chip full speed USB device controllers - * - * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID - * Copyright (C) 2004 Bo Henriksen, Nordic ID - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __LH7A40X_H_ -#define __LH7A40X_H_ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/ioport.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/proc_fs.h> -#include <linux/mm.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> - -#include <asm/byteorder.h> -#include <asm/dma.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/unaligned.h> -#include <mach/hardware.h> - -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -/* - * Memory map - */ - -#define USB_FA 0x80000200 // function address register -#define USB_PM 0x80000204 // power management register - -#define USB_IN_INT 0x80000208 // IN interrupt register bank (EP0-EP3) -#define USB_OUT_INT 0x80000210 // OUT interrupt register bank (EP2) -#define USB_INT 0x80000218 // interrupt register bank - -#define USB_IN_INT_EN 0x8000021C // IN interrupt enable register bank -#define USB_OUT_INT_EN 0x80000224 // OUT interrupt enable register bank -#define USB_INT_EN 0x8000022C // USB interrupt enable register bank - -#define USB_FRM_NUM1 0x80000230 // Frame number1 register -#define USB_FRM_NUM2 0x80000234 // Frame number2 register -#define USB_INDEX 0x80000238 // index register - -#define USB_IN_MAXP 0x80000240 // IN MAXP register -#define USB_IN_CSR1 0x80000244 // IN CSR1 register/EP0 CSR register -#define USB_EP0_CSR 0x80000244 // IN CSR1 register/EP0 CSR register -#define USB_IN_CSR2 0x80000248 // IN CSR2 register -#define USB_OUT_MAXP 0x8000024C // OUT MAXP register - -#define USB_OUT_CSR1 0x80000250 // OUT CSR1 register -#define USB_OUT_CSR2 0x80000254 // OUT CSR2 register -#define USB_OUT_FIFO_WC1 0x80000258 // OUT FIFO write count1 register -#define USB_OUT_FIFO_WC2 0x8000025C // OUT FIFO write count2 register - -#define USB_RESET 0x8000044C // USB reset register - -#define USB_EP0_FIFO 0x80000280 -#define USB_EP1_FIFO 0x80000284 -#define USB_EP2_FIFO 0x80000288 -#define USB_EP3_FIFO 0x8000028c - -/* - * USB reset register - */ -#define USB_RESET_APB (1<<1) //resets USB APB control side WRITE -#define USB_RESET_IO (1<<0) //resets USB IO side WRITE - -/* - * USB function address register - */ -#define USB_FA_ADDR_UPDATE (1<<7) -#define USB_FA_FUNCTION_ADDR (0x7F) - -/* - * Power Management register - */ -#define PM_USB_DCP (1<<5) -#define PM_USB_ENABLE (1<<4) -#define PM_USB_RESET (1<<3) -#define PM_UC_RESUME (1<<2) -#define PM_SUSPEND_MODE (1<<1) -#define PM_ENABLE_SUSPEND (1<<0) - -/* - * IN interrupt register - */ -#define USB_IN_INT_EP3 (1<<3) -#define USB_IN_INT_EP1 (1<<1) -#define USB_IN_INT_EP0 (1<<0) - -/* - * OUT interrupt register - */ -#define USB_OUT_INT_EP2 (1<<2) - -/* - * USB interrupt register - */ -#define USB_INT_RESET_INT (1<<2) -#define USB_INT_RESUME_INT (1<<1) -#define USB_INT_SUSPEND_INT (1<<0) - -/* - * USB interrupt enable register - */ -#define USB_INT_EN_USB_RESET_INTER (1<<2) -#define USB_INT_EN_RESUME_INTER (1<<1) -#define USB_INT_EN_SUSPEND_INTER (1<<0) - -/* - * INCSR1 register - */ -#define USB_IN_CSR1_CLR_DATA_TOGGLE (1<<6) -#define USB_IN_CSR1_SENT_STALL (1<<5) -#define USB_IN_CSR1_SEND_STALL (1<<4) -#define USB_IN_CSR1_FIFO_FLUSH (1<<3) -#define USB_IN_CSR1_FIFO_NOT_EMPTY (1<<1) -#define USB_IN_CSR1_IN_PKT_RDY (1<<0) - -/* - * INCSR2 register - */ -#define USB_IN_CSR2_AUTO_SET (1<<7) -#define USB_IN_CSR2_USB_DMA_EN (1<<4) - -/* - * OUT CSR1 register - */ -#define USB_OUT_CSR1_CLR_DATA_REG (1<<7) -#define USB_OUT_CSR1_SENT_STALL (1<<6) -#define USB_OUT_CSR1_SEND_STALL (1<<5) -#define USB_OUT_CSR1_FIFO_FLUSH (1<<4) -#define USB_OUT_CSR1_FIFO_FULL (1<<1) -#define USB_OUT_CSR1_OUT_PKT_RDY (1<<0) - -/* - * OUT CSR2 register - */ -#define USB_OUT_CSR2_AUTO_CLR (1<<7) -#define USB_OUT_CSR2_USB_DMA_EN (1<<4) - -/* - * EP0 CSR - */ -#define EP0_CLR_SETUP_END (1<<7) /* Clear "Setup Ends" Bit (w) */ -#define EP0_CLR_OUT (1<<6) /* Clear "Out packet ready" Bit (w) */ -#define EP0_SEND_STALL (1<<5) /* Send STALL Handshake (rw) */ -#define EP0_SETUP_END (1<<4) /* Setup Ends (r) */ - -#define EP0_DATA_END (1<<3) /* Data end (rw) */ -#define EP0_SENT_STALL (1<<2) /* Sent Stall Handshake (r) */ -#define EP0_IN_PKT_RDY (1<<1) /* In packet ready (rw) */ -#define EP0_OUT_PKT_RDY (1<<0) /* Out packet ready (r) */ - -/* general CSR */ -#define OUT_PKT_RDY (1<<0) -#define IN_PKT_RDY (1<<0) - -/* - * IN/OUT MAXP register - */ -#define USB_OUT_MAXP_MAXP (0xF) -#define USB_IN_MAXP_MAXP (0xF) - -// Max packet size -//#define EP0_PACKETSIZE 0x10 -#define EP0_PACKETSIZE 0x8 -#define EP0_MAXPACKETSIZE 0x10 - -#define UDC_MAX_ENDPOINTS 4 - -#define WAIT_FOR_SETUP 0 -#define DATA_STATE_XMIT 1 -#define DATA_STATE_NEED_ZLP 2 -#define WAIT_FOR_OUT_STATUS 3 -#define DATA_STATE_RECV 4 - -/* ********************************************************************************************* */ -/* IO - */ - -typedef enum ep_type { - ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt -} ep_type_t; - -struct lh7a40x_ep { - struct usb_ep ep; - struct lh7a40x_udc *dev; - - const struct usb_endpoint_descriptor *desc; - struct list_head queue; - unsigned long pio_irqs; - - u8 stopped; - u8 bEndpointAddress; - u8 bmAttributes; - - ep_type_t ep_type; - u32 fifo; - u32 csr1; - u32 csr2; -}; - -struct lh7a40x_request { - struct usb_request req; - struct list_head queue; -}; - -struct lh7a40x_udc { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - struct device *dev; - spinlock_t lock; - - int ep0state; - struct lh7a40x_ep ep[UDC_MAX_ENDPOINTS]; - - unsigned char usb_address; - - unsigned req_pending:1, req_std:1, req_config:1; -}; - -extern struct lh7a40x_udc *the_controller; - -#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) -#define ep_index(EP) ((EP)->bEndpointAddress&0xF) -#define ep_maxpacket(EP) ((EP)->ep.maxpacket) - -#endif diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c new file mode 100644 index 00000000000..e471580a2a3 --- /dev/null +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -0,0 +1,3425 @@ +/* + * USB Gadget driver for LPC32xx + * + * Authors: + * Kevin Wells <kevin.wells@nxp.com> + * Mike James + * Roland Stigge <stigge@antcom.de> + * + * Copyright (C) 2006 Philips Semiconductors + * Copyright (C) 2009 NXP Semiconductors + * Copyright (C) 2012 Roland Stigge + * + * Note: This driver is based on original work done by Mike James for + * the LPC3180. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#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> +#include <linux/clk.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/i2c.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/workqueue.h> +#include <linux/of.h> +#include <linux/usb/isp1301.h> + +#include <asm/byteorder.h> +#include <mach/hardware.h> +#include <linux/io.h> +#include <asm/irq.h> + +#include <mach/platform.h> +#include <mach/irqs.h> +#include <mach/board.h> +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#endif + +/* + * USB device configuration structure + */ +typedef void (*usc_chg_event)(int); +struct lpc32xx_usbd_cfg { + int vbus_drv_pol; /* 0=active low drive for VBUS via ISP1301 */ + usc_chg_event conn_chgb; /* Connection change event (optional) */ + usc_chg_event susp_chgb; /* Suspend/resume event (optional) */ + usc_chg_event rmwk_chgb; /* Enable/disable remote wakeup */ +}; + +/* + * controller driver data structures + */ + +/* 16 endpoints (not to be confused with 32 hardware endpoints) */ +#define NUM_ENDPOINTS 16 + +/* + * IRQ indices make reading the code a little easier + */ +#define IRQ_USB_LP 0 +#define IRQ_USB_HP 1 +#define IRQ_USB_DEVDMA 2 +#define IRQ_USB_ATX 3 + +#define EP_OUT 0 /* RX (from host) */ +#define EP_IN 1 /* TX (to host) */ + +/* Returns the interrupt mask for the selected hardware endpoint */ +#define EP_MASK_SEL(ep, dir) (1 << (((ep) * 2) + dir)) + +#define EP_INT_TYPE 0 +#define EP_ISO_TYPE 1 +#define EP_BLK_TYPE 2 +#define EP_CTL_TYPE 3 + +/* EP0 states */ +#define WAIT_FOR_SETUP 0 /* Wait for setup packet */ +#define DATA_IN 1 /* Expect dev->host transfer */ +#define DATA_OUT 2 /* Expect host->dev transfer */ + +/* DD (DMA Descriptor) structure, requires word alignment, this is already + * defined in the LPC32XX USB device header file, but this version is slightly + * modified to tag some work data with each DMA descriptor. */ +struct lpc32xx_usbd_dd_gad { + u32 dd_next_phy; + u32 dd_setup; + u32 dd_buffer_addr; + u32 dd_status; + u32 dd_iso_ps_mem_addr; + u32 this_dma; + u32 iso_status[6]; /* 5 spare */ + u32 dd_next_v; +}; + +/* + * Logical endpoint structure + */ +struct lpc32xx_ep { + struct usb_ep ep; + struct list_head queue; + struct lpc32xx_udc *udc; + + u32 hwep_num_base; /* Physical hardware EP */ + u32 hwep_num; /* Maps to hardware endpoint */ + u32 maxpacket; + u32 lep; + + bool is_in; + bool req_pending; + u32 eptype; + + u32 totalints; + + bool wedge; +}; + +/* + * Common UDC structure + */ +struct lpc32xx_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct platform_device *pdev; + struct device *dev; + struct dentry *pde; + spinlock_t lock; + struct i2c_client *isp1301_i2c_client; + + /* Board and device specific */ + struct lpc32xx_usbd_cfg *board; + u32 io_p_start; + u32 io_p_size; + void __iomem *udp_baseaddr; + int udp_irq[4]; + struct clk *usb_pll_clk; + struct clk *usb_slv_clk; + struct clk *usb_otg_clk; + + /* DMA support */ + u32 *udca_v_base; + u32 udca_p_base; + struct dma_pool *dd_cache; + + /* Common EP and control data */ + u32 enabled_devints; + u32 enabled_hwepints; + u32 dev_status; + u32 realized_eps; + + /* VBUS detection, pullup, and power flags */ + u8 vbus; + u8 last_vbus; + int pullup; + int poweron; + + /* Work queues related to I2C support */ + struct work_struct pullup_job; + struct work_struct vbus_job; + struct work_struct power_job; + + /* USB device peripheral - various */ + struct lpc32xx_ep ep[NUM_ENDPOINTS]; + bool enabled; + bool clocked; + bool suspended; + bool selfpowered; + int ep0state; + atomic_t enabled_ep_cnt; + wait_queue_head_t ep_disable_wait_queue; +}; + +/* + * Endpoint request + */ +struct lpc32xx_request { + struct usb_request req; + struct list_head queue; + struct lpc32xx_usbd_dd_gad *dd_desc_ptr; + bool mapped; + bool send_zlp; +}; + +static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g) +{ + return container_of(g, struct lpc32xx_udc, gadget); +} + +#define ep_dbg(epp, fmt, arg...) \ + dev_dbg(epp->udc->dev, "%s: " fmt, __func__, ## arg) +#define ep_err(epp, fmt, arg...) \ + dev_err(epp->udc->dev, "%s: " fmt, __func__, ## arg) +#define ep_info(epp, fmt, arg...) \ + dev_info(epp->udc->dev, "%s: " fmt, __func__, ## arg) +#define ep_warn(epp, fmt, arg...) \ + dev_warn(epp->udc->dev, "%s:" fmt, __func__, ## arg) + +#define UDCA_BUFF_SIZE (128) + +/* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will + * be replaced with an inremap()ed pointer + * */ +#define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64) + +/* USB_CTRL bit defines */ +#define USB_SLAVE_HCLK_EN (1 << 24) +#define USB_HOST_NEED_CLK_EN (1 << 21) +#define USB_DEV_NEED_CLK_EN (1 << 22) + +/********************************************************************** + * USB device controller register offsets + **********************************************************************/ + +#define USBD_DEVINTST(x) ((x) + 0x200) +#define USBD_DEVINTEN(x) ((x) + 0x204) +#define USBD_DEVINTCLR(x) ((x) + 0x208) +#define USBD_DEVINTSET(x) ((x) + 0x20C) +#define USBD_CMDCODE(x) ((x) + 0x210) +#define USBD_CMDDATA(x) ((x) + 0x214) +#define USBD_RXDATA(x) ((x) + 0x218) +#define USBD_TXDATA(x) ((x) + 0x21C) +#define USBD_RXPLEN(x) ((x) + 0x220) +#define USBD_TXPLEN(x) ((x) + 0x224) +#define USBD_CTRL(x) ((x) + 0x228) +#define USBD_DEVINTPRI(x) ((x) + 0x22C) +#define USBD_EPINTST(x) ((x) + 0x230) +#define USBD_EPINTEN(x) ((x) + 0x234) +#define USBD_EPINTCLR(x) ((x) + 0x238) +#define USBD_EPINTSET(x) ((x) + 0x23C) +#define USBD_EPINTPRI(x) ((x) + 0x240) +#define USBD_REEP(x) ((x) + 0x244) +#define USBD_EPIND(x) ((x) + 0x248) +#define USBD_EPMAXPSIZE(x) ((x) + 0x24C) +/* DMA support registers only below */ +/* Set, clear, or get enabled state of the DMA request status. If + * enabled, an IN or OUT token will start a DMA transfer for the EP */ +#define USBD_DMARST(x) ((x) + 0x250) +#define USBD_DMARCLR(x) ((x) + 0x254) +#define USBD_DMARSET(x) ((x) + 0x258) +/* DMA UDCA head pointer */ +#define USBD_UDCAH(x) ((x) + 0x280) +/* EP DMA status, enable, and disable. This is used to specifically + * enabled or disable DMA for a specific EP */ +#define USBD_EPDMAST(x) ((x) + 0x284) +#define USBD_EPDMAEN(x) ((x) + 0x288) +#define USBD_EPDMADIS(x) ((x) + 0x28C) +/* DMA master interrupts enable and pending interrupts */ +#define USBD_DMAINTST(x) ((x) + 0x290) +#define USBD_DMAINTEN(x) ((x) + 0x294) +/* DMA end of transfer interrupt enable, disable, status */ +#define USBD_EOTINTST(x) ((x) + 0x2A0) +#define USBD_EOTINTCLR(x) ((x) + 0x2A4) +#define USBD_EOTINTSET(x) ((x) + 0x2A8) +/* New DD request interrupt enable, disable, status */ +#define USBD_NDDRTINTST(x) ((x) + 0x2AC) +#define USBD_NDDRTINTCLR(x) ((x) + 0x2B0) +#define USBD_NDDRTINTSET(x) ((x) + 0x2B4) +/* DMA error interrupt enable, disable, status */ +#define USBD_SYSERRTINTST(x) ((x) + 0x2B8) +#define USBD_SYSERRTINTCLR(x) ((x) + 0x2BC) +#define USBD_SYSERRTINTSET(x) ((x) + 0x2C0) + +/********************************************************************** + * USBD_DEVINTST/USBD_DEVINTEN/USBD_DEVINTCLR/USBD_DEVINTSET/ + * USBD_DEVINTPRI register definitions + **********************************************************************/ +#define USBD_ERR_INT (1 << 9) +#define USBD_EP_RLZED (1 << 8) +#define USBD_TXENDPKT (1 << 7) +#define USBD_RXENDPKT (1 << 6) +#define USBD_CDFULL (1 << 5) +#define USBD_CCEMPTY (1 << 4) +#define USBD_DEV_STAT (1 << 3) +#define USBD_EP_SLOW (1 << 2) +#define USBD_EP_FAST (1 << 1) +#define USBD_FRAME (1 << 0) + +/********************************************************************** + * USBD_EPINTST/USBD_EPINTEN/USBD_EPINTCLR/USBD_EPINTSET/ + * USBD_EPINTPRI register definitions + **********************************************************************/ +/* End point selection macro (RX) */ +#define USBD_RX_EP_SEL(e) (1 << ((e) << 1)) + +/* End point selection macro (TX) */ +#define USBD_TX_EP_SEL(e) (1 << (((e) << 1) + 1)) + +/********************************************************************** + * USBD_REEP/USBD_DMARST/USBD_DMARCLR/USBD_DMARSET/USBD_EPDMAST/ + * USBD_EPDMAEN/USBD_EPDMADIS/ + * USBD_NDDRTINTST/USBD_NDDRTINTCLR/USBD_NDDRTINTSET/ + * USBD_EOTINTST/USBD_EOTINTCLR/USBD_EOTINTSET/ + * USBD_SYSERRTINTST/USBD_SYSERRTINTCLR/USBD_SYSERRTINTSET + * register definitions + **********************************************************************/ +/* Endpoint selection macro */ +#define USBD_EP_SEL(e) (1 << (e)) + +/********************************************************************** + * SBD_DMAINTST/USBD_DMAINTEN + **********************************************************************/ +#define USBD_SYS_ERR_INT (1 << 2) +#define USBD_NEW_DD_INT (1 << 1) +#define USBD_EOT_INT (1 << 0) + +/********************************************************************** + * USBD_RXPLEN register definitions + **********************************************************************/ +#define USBD_PKT_RDY (1 << 11) +#define USBD_DV (1 << 10) +#define USBD_PK_LEN_MASK 0x3FF + +/********************************************************************** + * USBD_CTRL register definitions + **********************************************************************/ +#define USBD_LOG_ENDPOINT(e) ((e) << 2) +#define USBD_WR_EN (1 << 1) +#define USBD_RD_EN (1 << 0) + +/********************************************************************** + * USBD_CMDCODE register definitions + **********************************************************************/ +#define USBD_CMD_CODE(c) ((c) << 16) +#define USBD_CMD_PHASE(p) ((p) << 8) + +/********************************************************************** + * USBD_DMARST/USBD_DMARCLR/USBD_DMARSET register definitions + **********************************************************************/ +#define USBD_DMAEP(e) (1 << (e)) + +/* DD (DMA Descriptor) structure, requires word alignment */ +struct lpc32xx_usbd_dd { + u32 *dd_next; + u32 dd_setup; + u32 dd_buffer_addr; + u32 dd_status; + u32 dd_iso_ps_mem_addr; +}; + +/* dd_setup bit defines */ +#define DD_SETUP_ATLE_DMA_MODE 0x01 +#define DD_SETUP_NEXT_DD_VALID 0x04 +#define DD_SETUP_ISO_EP 0x10 +#define DD_SETUP_PACKETLEN(n) (((n) & 0x7FF) << 5) +#define DD_SETUP_DMALENBYTES(n) (((n) & 0xFFFF) << 16) + +/* dd_status bit defines */ +#define DD_STATUS_DD_RETIRED 0x01 +#define DD_STATUS_STS_MASK 0x1E +#define DD_STATUS_STS_NS 0x00 /* Not serviced */ +#define DD_STATUS_STS_BS 0x02 /* Being serviced */ +#define DD_STATUS_STS_NC 0x04 /* Normal completion */ +#define DD_STATUS_STS_DUR 0x06 /* Data underrun (short packet) */ +#define DD_STATUS_STS_DOR 0x08 /* Data overrun */ +#define DD_STATUS_STS_SE 0x12 /* System error */ +#define DD_STATUS_PKT_VAL 0x20 /* Packet valid */ +#define DD_STATUS_LSB_EX 0x40 /* LS byte extracted (ATLE) */ +#define DD_STATUS_MSB_EX 0x80 /* MS byte extracted (ATLE) */ +#define DD_STATUS_MLEN(n) (((n) >> 8) & 0x3F) +#define DD_STATUS_CURDMACNT(n) (((n) >> 16) & 0xFFFF) + +/* + * + * Protocol engine bits below + * + */ +/* Device Interrupt Bit Definitions */ +#define FRAME_INT 0x00000001 +#define EP_FAST_INT 0x00000002 +#define EP_SLOW_INT 0x00000004 +#define DEV_STAT_INT 0x00000008 +#define CCEMTY_INT 0x00000010 +#define CDFULL_INT 0x00000020 +#define RxENDPKT_INT 0x00000040 +#define TxENDPKT_INT 0x00000080 +#define EP_RLZED_INT 0x00000100 +#define ERR_INT 0x00000200 + +/* Rx & Tx Packet Length Definitions */ +#define PKT_LNGTH_MASK 0x000003FF +#define PKT_DV 0x00000400 +#define PKT_RDY 0x00000800 + +/* USB Control Definitions */ +#define CTRL_RD_EN 0x00000001 +#define CTRL_WR_EN 0x00000002 + +/* Command Codes */ +#define CMD_SET_ADDR 0x00D00500 +#define CMD_CFG_DEV 0x00D80500 +#define CMD_SET_MODE 0x00F30500 +#define CMD_RD_FRAME 0x00F50500 +#define DAT_RD_FRAME 0x00F50200 +#define CMD_RD_TEST 0x00FD0500 +#define DAT_RD_TEST 0x00FD0200 +#define CMD_SET_DEV_STAT 0x00FE0500 +#define CMD_GET_DEV_STAT 0x00FE0500 +#define DAT_GET_DEV_STAT 0x00FE0200 +#define CMD_GET_ERR_CODE 0x00FF0500 +#define DAT_GET_ERR_CODE 0x00FF0200 +#define CMD_RD_ERR_STAT 0x00FB0500 +#define DAT_RD_ERR_STAT 0x00FB0200 +#define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) +#define CMD_SEL_EP(x) (0x00000500 | ((x) << 16)) +#define DAT_SEL_EP(x) (0x00000200 | ((x) << 16)) +#define CMD_SEL_EP_CLRI(x) (0x00400500 | ((x) << 16)) +#define DAT_SEL_EP_CLRI(x) (0x00400200 | ((x) << 16)) +#define CMD_SET_EP_STAT(x) (0x00400500 | ((x) << 16)) +#define CMD_CLR_BUF 0x00F20500 +#define DAT_CLR_BUF 0x00F20200 +#define CMD_VALID_BUF 0x00FA0500 + +/* Device Address Register Definitions */ +#define DEV_ADDR_MASK 0x7F +#define DEV_EN 0x80 + +/* Device Configure Register Definitions */ +#define CONF_DVICE 0x01 + +/* Device Mode Register Definitions */ +#define AP_CLK 0x01 +#define INAK_CI 0x02 +#define INAK_CO 0x04 +#define INAK_II 0x08 +#define INAK_IO 0x10 +#define INAK_BI 0x20 +#define INAK_BO 0x40 + +/* Device Status Register Definitions */ +#define DEV_CON 0x01 +#define DEV_CON_CH 0x02 +#define DEV_SUS 0x04 +#define DEV_SUS_CH 0x08 +#define DEV_RST 0x10 + +/* Error Code Register Definitions */ +#define ERR_EC_MASK 0x0F +#define ERR_EA 0x10 + +/* Error Status Register Definitions */ +#define ERR_PID 0x01 +#define ERR_UEPKT 0x02 +#define ERR_DCRC 0x04 +#define ERR_TIMOUT 0x08 +#define ERR_EOP 0x10 +#define ERR_B_OVRN 0x20 +#define ERR_BTSTF 0x40 +#define ERR_TGL 0x80 + +/* Endpoint Select Register Definitions */ +#define EP_SEL_F 0x01 +#define EP_SEL_ST 0x02 +#define EP_SEL_STP 0x04 +#define EP_SEL_PO 0x08 +#define EP_SEL_EPN 0x10 +#define EP_SEL_B_1_FULL 0x20 +#define EP_SEL_B_2_FULL 0x40 + +/* Endpoint Status Register Definitions */ +#define EP_STAT_ST 0x01 +#define EP_STAT_DA 0x20 +#define EP_STAT_RF_MO 0x40 +#define EP_STAT_CND_ST 0x80 + +/* Clear Buffer Register Definitions */ +#define CLR_BUF_PO 0x01 + +/* DMA Interrupt Bit Definitions */ +#define EOT_INT 0x01 +#define NDD_REQ_INT 0x02 +#define SYS_ERR_INT 0x04 + +#define DRIVER_VERSION "1.03" +static const char driver_name[] = "lpc32xx_udc"; + +/* + * + * proc interface support + * + */ +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +static char *epnames[] = {"INT", "ISO", "BULK", "CTRL"}; +static const char debug_filename[] = "driver/udc"; + +static void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep) +{ + struct lpc32xx_request *req; + + seq_printf(s, "\n"); + seq_printf(s, "%12s, maxpacket %4d %3s", + ep->ep.name, ep->ep.maxpacket, + ep->is_in ? "in" : "out"); + seq_printf(s, " type %4s", epnames[ep->eptype]); + seq_printf(s, " ints: %12d", ep->totalints); + + if (list_empty(&ep->queue)) + seq_printf(s, "\t(queue empty)\n"); + else { + list_for_each_entry(req, &ep->queue, queue) { + u32 length = req->req.actual; + + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, length, + req->req.length, req->req.buf); + } + } +} + +static int proc_udc_show(struct seq_file *s, void *unused) +{ + struct lpc32xx_udc *udc = s->private; + struct lpc32xx_ep *ep; + unsigned long flags; + + seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION); + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n", + udc->vbus ? "present" : "off", + udc->enabled ? (udc->vbus ? "active" : "enabled") : + "disabled", + udc->selfpowered ? "self" : "VBUS", + udc->suspended ? ", suspended" : "", + udc->driver ? udc->driver->driver.name : "(none)"); + + if (udc->enabled && udc->vbus) { + proc_ep_show(s, &udc->ep[0]); + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) + proc_ep_show(s, ep); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int proc_udc_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_udc_show, PDE_DATA(inode)); +} + +static const struct file_operations proc_ops = { + .owner = THIS_MODULE, + .open = proc_udc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debug_file(struct lpc32xx_udc *udc) +{ + udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops); +} + +static void remove_debug_file(struct lpc32xx_udc *udc) +{ + if (udc->pde) + debugfs_remove(udc->pde); +} + +#else +static inline void create_debug_file(struct lpc32xx_udc *udc) {} +static inline void remove_debug_file(struct lpc32xx_udc *udc) {} +#endif + +/* Primary initialization sequence for the ISP1301 transceiver */ +static void isp1301_udc_configure(struct lpc32xx_udc *udc) +{ + /* LPC32XX only supports DAT_SE0 USB mode */ + /* This sequence is important */ + + /* Disable transparent UART mode first */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), + MC1_UART_EN); + + /* Set full speed and SE0 mode */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_MODE_CONTROL_1, (MC1_SPEED_REG | MC1_DAT_SE0)); + + /* + * The PSW_OE enable bit state is reversed in the ISP1301 User's Guide + */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + (ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_SPD_SUSP_CTRL)); + + /* Driver VBUS_DRV high or low depending on board setup */ + if (udc->board->vbus_drv_pol != 0) + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV); + else + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, + OTG1_VBUS_DRV); + + /* Bi-directional mode with suspend control + * Enable both pulldowns for now - the pullup will be enable when VBUS + * is detected */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1, + (0 | OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); + + /* Discharge VBUS (just in case) */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); + msleep(1); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), + OTG1_VBUS_DISCHRG); + + /* Clear and enable VBUS high edge interrupt */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_FALLING, INT_VBUS_VLD); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD); + + /* Enable usb_need_clk clock after transceiver is initialized */ + writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL); + + dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", + i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00)); + dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", + i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02)); + dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n", + i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14)); +} + +/* Enables or disables the USB device pullup via the ISP1301 transceiver */ +static void isp1301_pullup_set(struct lpc32xx_udc *udc) +{ + if (udc->pullup) + /* Enable pullup for bus signalling */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1, OTG1_DP_PULLUP); + else + /* Enable pullup for bus signalling */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, + OTG1_DP_PULLUP); +} + +static void pullup_work(struct work_struct *work) +{ + struct lpc32xx_udc *udc = + container_of(work, struct lpc32xx_udc, pullup_job); + + isp1301_pullup_set(udc); +} + +static void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup, + int block) +{ + if (en_pullup == udc->pullup) + return; + + udc->pullup = en_pullup; + if (block) + isp1301_pullup_set(udc); + else + /* defer slow i2c pull up setting */ + schedule_work(&udc->pullup_job); +} + +#ifdef CONFIG_PM +/* Powers up or down the ISP1301 transceiver */ +static void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable) +{ + if (enable != 0) + /* Power up ISP1301 - this ISP1301 will automatically wakeup + when VBUS is detected */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR, + MC2_GLOBAL_PWR_DN); + else + /* Power down ISP1301 */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); +} + +static void power_work(struct work_struct *work) +{ + struct lpc32xx_udc *udc = + container_of(work, struct lpc32xx_udc, power_job); + + isp1301_set_powerstate(udc, udc->poweron); +} +#endif + +/* + * + * USB protocol engine command/data read/write helper functions + * + */ +/* Issues a single command to the USB device state machine */ +static void udc_protocol_cmd_w(struct lpc32xx_udc *udc, u32 cmd) +{ + u32 pass = 0; + int to; + + /* EP may lock on CLRI if this read isn't done */ + u32 tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); + (void) tmp; + + while (pass == 0) { + writel(USBD_CCEMPTY, USBD_DEVINTCLR(udc->udp_baseaddr)); + + /* Write command code */ + writel(cmd, USBD_CMDCODE(udc->udp_baseaddr)); + to = 10000; + while (((readl(USBD_DEVINTST(udc->udp_baseaddr)) & + USBD_CCEMPTY) == 0) && (to > 0)) { + to--; + } + + if (to > 0) + pass = 1; + + cpu_relax(); + } +} + +/* Issues 2 commands (or command and data) to the USB device state machine */ +static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd, + u32 data) +{ + udc_protocol_cmd_w(udc, cmd); + udc_protocol_cmd_w(udc, data); +} + +/* Issues a single command to the USB device state machine and reads + * response data */ +static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) +{ + u32 tmp; + int to = 1000; + + /* Write a command and read data from the protocol engine */ + writel((USBD_CDFULL | USBD_CCEMPTY), + USBD_DEVINTCLR(udc->udp_baseaddr)); + + /* Write command code */ + udc_protocol_cmd_w(udc, cmd); + + tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); + while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL)) + && (to > 0)) + to--; + if (!to) + dev_dbg(udc->dev, + "Protocol engine didn't receive response (CDFULL)\n"); + + return readl(USBD_CMDDATA(udc->udp_baseaddr)); +} + +/* + * + * USB device interrupt mask support functions + * + */ +/* Enable one or more USB device interrupts */ +static inline void uda_enable_devint(struct lpc32xx_udc *udc, u32 devmask) +{ + udc->enabled_devints |= devmask; + writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); +} + +/* Disable one or more USB device interrupts */ +static inline void uda_disable_devint(struct lpc32xx_udc *udc, u32 mask) +{ + udc->enabled_devints &= ~mask; + writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); +} + +/* Clear one or more USB device interrupts */ +static inline void uda_clear_devint(struct lpc32xx_udc *udc, u32 mask) +{ + writel(mask, USBD_DEVINTCLR(udc->udp_baseaddr)); +} + +/* + * + * Endpoint interrupt disable/enable functions + * + */ +/* Enable one or more USB endpoint interrupts */ +static void uda_enable_hwepint(struct lpc32xx_udc *udc, u32 hwep) +{ + udc->enabled_hwepints |= (1 << hwep); + writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); +} + +/* Disable one or more USB endpoint interrupts */ +static void uda_disable_hwepint(struct lpc32xx_udc *udc, u32 hwep) +{ + udc->enabled_hwepints &= ~(1 << hwep); + writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); +} + +/* Clear one or more USB endpoint interrupts */ +static inline void uda_clear_hwepint(struct lpc32xx_udc *udc, u32 hwep) +{ + writel((1 << hwep), USBD_EPINTCLR(udc->udp_baseaddr)); +} + +/* Enable DMA for the HW channel */ +static inline void udc_ep_dma_enable(struct lpc32xx_udc *udc, u32 hwep) +{ + writel((1 << hwep), USBD_EPDMAEN(udc->udp_baseaddr)); +} + +/* Disable DMA for the HW channel */ +static inline void udc_ep_dma_disable(struct lpc32xx_udc *udc, u32 hwep) +{ + writel((1 << hwep), USBD_EPDMADIS(udc->udp_baseaddr)); +} + +/* + * + * Endpoint realize/unrealize functions + * + */ +/* Before an endpoint can be used, it needs to be realized + * in the USB protocol engine - this realizes the endpoint. + * The interrupt (FIFO or DMA) is not enabled with this function */ +static void udc_realize_hwep(struct lpc32xx_udc *udc, u32 hwep, + u32 maxpacket) +{ + int to = 1000; + + writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); + writel(hwep, USBD_EPIND(udc->udp_baseaddr)); + udc->realized_eps |= (1 << hwep); + writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); + writel(maxpacket, USBD_EPMAXPSIZE(udc->udp_baseaddr)); + + /* Wait until endpoint is realized in hardware */ + while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & + USBD_EP_RLZED)) && (to > 0)) + to--; + if (!to) + dev_dbg(udc->dev, "EP not correctly realized in hardware\n"); + + writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); +} + +/* Unrealize an EP */ +static void udc_unrealize_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc->realized_eps &= ~(1 << hwep); + writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); +} + +/* + * + * Endpoint support functions + * + */ +/* Select and clear endpoint interrupt */ +static u32 udc_selep_clrint(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_protocol_cmd_w(udc, CMD_SEL_EP_CLRI(hwep)); + return udc_protocol_cmd_r(udc, DAT_SEL_EP_CLRI(hwep)); +} + +/* Disables the endpoint in the USB protocol engine */ +static void udc_disable_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), + DAT_WR_BYTE(EP_STAT_DA)); +} + +/* Stalls the endpoint - endpoint will return STALL */ +static void udc_stall_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), + DAT_WR_BYTE(EP_STAT_ST)); +} + +/* Clear stall or reset endpoint */ +static void udc_clrstall_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), + DAT_WR_BYTE(0)); +} + +/* Select an endpoint for endpoint status, clear, validate */ +static void udc_select_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_protocol_cmd_w(udc, CMD_SEL_EP(hwep)); +} + +/* + * + * Endpoint buffer management functions + * + */ +/* Clear the current endpoint's buffer */ +static void udc_clr_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_select_hwep(udc, hwep); + udc_protocol_cmd_w(udc, CMD_CLR_BUF); +} + +/* Validate the current endpoint's buffer */ +static void udc_val_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) +{ + udc_select_hwep(udc, hwep); + udc_protocol_cmd_w(udc, CMD_VALID_BUF); +} + +static inline u32 udc_clearep_getsts(struct lpc32xx_udc *udc, u32 hwep) +{ + /* Clear EP interrupt */ + uda_clear_hwepint(udc, hwep); + return udc_selep_clrint(udc, hwep); +} + +/* + * + * USB EP DMA support + * + */ +/* Allocate a DMA Descriptor */ +static struct lpc32xx_usbd_dd_gad *udc_dd_alloc(struct lpc32xx_udc *udc) +{ + dma_addr_t dma; + struct lpc32xx_usbd_dd_gad *dd; + + dd = (struct lpc32xx_usbd_dd_gad *) dma_pool_alloc( + udc->dd_cache, (GFP_KERNEL | GFP_DMA), &dma); + if (dd) + dd->this_dma = dma; + + return dd; +} + +/* Free a DMA Descriptor */ +static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd) +{ + dma_pool_free(udc->dd_cache, dd, dd->this_dma); +} + +/* + * + * USB setup and shutdown functions + * + */ +/* Enables or disables most of the USB system clocks when low power mode is + * needed. Clocks are typically started on a connection event, and disabled + * when a cable is disconnected */ +static void udc_clk_set(struct lpc32xx_udc *udc, int enable) +{ + if (enable != 0) { + if (udc->clocked) + return; + + udc->clocked = 1; + + /* 48MHz PLL up */ + clk_enable(udc->usb_pll_clk); + + /* Enable the USB device clock */ + writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, + USB_CTRL); + + clk_enable(udc->usb_otg_clk); + } else { + if (!udc->clocked) + return; + + udc->clocked = 0; + + /* Never disable the USB_HCLK during normal operation */ + + /* 48MHz PLL dpwn */ + clk_disable(udc->usb_pll_clk); + + /* Disable the USB device clock */ + writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN, + USB_CTRL); + + clk_disable(udc->usb_otg_clk); + } +} + +/* Set/reset USB device address */ +static void udc_set_address(struct lpc32xx_udc *udc, u32 addr) +{ + /* Address will be latched at the end of the status phase, or + latched immediately if function is called twice */ + udc_protocol_cmd_data_w(udc, CMD_SET_ADDR, + DAT_WR_BYTE(DEV_EN | addr)); +} + +/* Setup up a IN request for DMA transfer - this consists of determining the + * list of DMA addresses for the transfer, allocating DMA Descriptors, + * installing the DD into the UDCA, and then enabling the DMA for that EP */ +static int udc_ep_in_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) +{ + struct lpc32xx_request *req; + u32 hwep = ep->hwep_num; + + ep->req_pending = 1; + + /* There will always be a request waiting here */ + req = list_entry(ep->queue.next, struct lpc32xx_request, queue); + + /* Place the DD Descriptor into the UDCA */ + udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; + + /* Enable DMA and interrupt for the HW EP */ + udc_ep_dma_enable(udc, hwep); + + /* Clear ZLP if last packet is not of MAXP size */ + if (req->req.length % ep->ep.maxpacket) + req->send_zlp = 0; + + return 0; +} + +/* Setup up a OUT request for DMA transfer - this consists of determining the + * list of DMA addresses for the transfer, allocating DMA Descriptors, + * installing the DD into the UDCA, and then enabling the DMA for that EP */ +static int udc_ep_out_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) +{ + struct lpc32xx_request *req; + u32 hwep = ep->hwep_num; + + ep->req_pending = 1; + + /* There will always be a request waiting here */ + req = list_entry(ep->queue.next, struct lpc32xx_request, queue); + + /* Place the DD Descriptor into the UDCA */ + udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; + + /* Enable DMA and interrupt for the HW EP */ + udc_ep_dma_enable(udc, hwep); + return 0; +} + +static void udc_disable(struct lpc32xx_udc *udc) +{ + u32 i; + + /* Disable device */ + udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); + udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(0)); + + /* Disable all device interrupts (including EP0) */ + uda_disable_devint(udc, 0x3FF); + + /* Disable and reset all endpoint interrupts */ + for (i = 0; i < 32; i++) { + uda_disable_hwepint(udc, i); + uda_clear_hwepint(udc, i); + udc_disable_hwep(udc, i); + udc_unrealize_hwep(udc, i); + udc->udca_v_base[i] = 0; + + /* Disable and clear all interrupts and DMA */ + udc_ep_dma_disable(udc, i); + writel((1 << i), USBD_EOTINTCLR(udc->udp_baseaddr)); + writel((1 << i), USBD_NDDRTINTCLR(udc->udp_baseaddr)); + writel((1 << i), USBD_SYSERRTINTCLR(udc->udp_baseaddr)); + writel((1 << i), USBD_DMARCLR(udc->udp_baseaddr)); + } + + /* Disable DMA interrupts */ + writel(0, USBD_DMAINTEN(udc->udp_baseaddr)); + + writel(0, USBD_UDCAH(udc->udp_baseaddr)); +} + +static void udc_enable(struct lpc32xx_udc *udc) +{ + u32 i; + struct lpc32xx_ep *ep = &udc->ep[0]; + + /* Start with known state */ + udc_disable(udc); + + /* Enable device */ + udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON)); + + /* EP interrupts on high priority, FRAME interrupt on low priority */ + writel(USBD_EP_FAST, USBD_DEVINTPRI(udc->udp_baseaddr)); + writel(0xFFFF, USBD_EPINTPRI(udc->udp_baseaddr)); + + /* Clear any pending device interrupts */ + writel(0x3FF, USBD_DEVINTCLR(udc->udp_baseaddr)); + + /* Setup UDCA - not yet used (DMA) */ + writel(udc->udca_p_base, USBD_UDCAH(udc->udp_baseaddr)); + + /* Only enable EP0 in and out for now, EP0 only works in FIFO mode */ + for (i = 0; i <= 1; i++) { + udc_realize_hwep(udc, i, ep->ep.maxpacket); + uda_enable_hwepint(udc, i); + udc_select_hwep(udc, i); + udc_clrstall_hwep(udc, i); + udc_clr_buffer_hwep(udc, i); + } + + /* Device interrupt setup */ + uda_clear_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | + USBD_EP_FAST)); + uda_enable_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | + USBD_EP_FAST)); + + /* Set device address to 0 - called twice to force a latch in the USB + engine without the need of a setup packet status closure */ + udc_set_address(udc, 0); + udc_set_address(udc, 0); + + /* Enable master DMA interrupts */ + writel((USBD_SYS_ERR_INT | USBD_EOT_INT), + USBD_DMAINTEN(udc->udp_baseaddr)); + + udc->dev_status = 0; +} + +/* + * + * USB device board specific events handled via callbacks + * + */ +/* Connection change event - notify board function of change */ +static void uda_power_event(struct lpc32xx_udc *udc, u32 conn) +{ + /* Just notify of a connection change event (optional) */ + if (udc->board->conn_chgb != NULL) + udc->board->conn_chgb(conn); +} + +/* Suspend/resume event - notify board function of change */ +static void uda_resm_susp_event(struct lpc32xx_udc *udc, u32 conn) +{ + /* Just notify of a Suspend/resume change event (optional) */ + if (udc->board->susp_chgb != NULL) + udc->board->susp_chgb(conn); + + if (conn) + udc->suspended = 0; + else + udc->suspended = 1; +} + +/* Remote wakeup enable/disable - notify board function of change */ +static void uda_remwkp_cgh(struct lpc32xx_udc *udc) +{ + if (udc->board->rmwk_chgb != NULL) + udc->board->rmwk_chgb(udc->dev_status & + (1 << USB_DEVICE_REMOTE_WAKEUP)); +} + +/* Reads data from FIFO, adjusts for alignment and data size */ +static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) +{ + int n, i, bl; + u16 *p16; + u32 *p32, tmp, cbytes; + + /* Use optimal data transfer method based on source address and size */ + switch (((u32) data) & 0x3) { + case 0: /* 32-bit aligned */ + p32 = (u32 *) data; + cbytes = (bytes & ~0x3); + + /* Copy 32-bit aligned data first */ + for (n = 0; n < cbytes; n += 4) + *p32++ = readl(USBD_RXDATA(udc->udp_baseaddr)); + + /* Handle any remaining bytes */ + bl = bytes - cbytes; + if (bl) { + tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); + for (n = 0; n < bl; n++) + data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); + + } + break; + + case 1: /* 8-bit aligned */ + case 3: + /* Each byte has to be handled independently */ + for (n = 0; n < bytes; n += 4) { + tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); + + bl = bytes - n; + if (bl > 3) + bl = 3; + + for (i = 0; i < bl; i++) + data[n + i] = (u8) ((tmp >> (n * 8)) & 0xFF); + } + break; + + case 2: /* 16-bit aligned */ + p16 = (u16 *) data; + cbytes = (bytes & ~0x3); + + /* Copy 32-bit sized objects first with 16-bit alignment */ + for (n = 0; n < cbytes; n += 4) { + tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); + *p16++ = (u16)(tmp & 0xFFFF); + *p16++ = (u16)((tmp >> 16) & 0xFFFF); + } + + /* Handle any remaining bytes */ + bl = bytes - cbytes; + if (bl) { + tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); + for (n = 0; n < bl; n++) + data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); + } + break; + } +} + +/* Read data from the FIFO for an endpoint. This function is for endpoints (such + * as EP0) that don't use DMA. This function should only be called if a packet + * is known to be ready to read for the endpoint. Note that the endpoint must + * be selected in the protocol engine prior to this call. */ +static u32 udc_read_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, + u32 bytes) +{ + u32 tmpv; + int to = 1000; + u32 tmp, hwrep = ((hwep & 0x1E) << 1) | CTRL_RD_EN; + + /* Setup read of endpoint */ + writel(hwrep, USBD_CTRL(udc->udp_baseaddr)); + + /* Wait until packet is ready */ + while ((((tmpv = readl(USBD_RXPLEN(udc->udp_baseaddr))) & + PKT_RDY) == 0) && (to > 0)) + to--; + if (!to) + dev_dbg(udc->dev, "No packet ready on FIFO EP read\n"); + + /* Mask out count */ + tmp = tmpv & PKT_LNGTH_MASK; + if (bytes < tmp) + tmp = bytes; + + if ((tmp > 0) && (data != NULL)) + udc_pop_fifo(udc, (u8 *) data, tmp); + + writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); + + /* Clear the buffer */ + udc_clr_buffer_hwep(udc, hwep); + + return tmp; +} + +/* Stuffs data into the FIFO, adjusts for alignment and data size */ +static void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) +{ + int n, i, bl; + u16 *p16; + u32 *p32, tmp, cbytes; + + /* Use optimal data transfer method based on source address and size */ + switch (((u32) data) & 0x3) { + case 0: /* 32-bit aligned */ + p32 = (u32 *) data; + cbytes = (bytes & ~0x3); + + /* Copy 32-bit aligned data first */ + for (n = 0; n < cbytes; n += 4) + writel(*p32++, USBD_TXDATA(udc->udp_baseaddr)); + + /* Handle any remaining bytes */ + bl = bytes - cbytes; + if (bl) { + tmp = 0; + for (n = 0; n < bl; n++) + tmp |= data[cbytes + n] << (n * 8); + + writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); + } + break; + + case 1: /* 8-bit aligned */ + case 3: + /* Each byte has to be handled independently */ + for (n = 0; n < bytes; n += 4) { + bl = bytes - n; + if (bl > 4) + bl = 4; + + tmp = 0; + for (i = 0; i < bl; i++) + tmp |= data[n + i] << (i * 8); + + writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); + } + break; + + case 2: /* 16-bit aligned */ + p16 = (u16 *) data; + cbytes = (bytes & ~0x3); + + /* Copy 32-bit aligned data first */ + for (n = 0; n < cbytes; n += 4) { + tmp = *p16++ & 0xFFFF; + tmp |= (*p16++ & 0xFFFF) << 16; + writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); + } + + /* Handle any remaining bytes */ + bl = bytes - cbytes; + if (bl) { + tmp = 0; + for (n = 0; n < bl; n++) + tmp |= data[cbytes + n] << (n * 8); + + writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); + } + break; + } +} + +/* Write data to the FIFO for an endpoint. This function is for endpoints (such + * as EP0) that don't use DMA. Note that the endpoint must be selected in the + * protocol engine prior to this call. */ +static void udc_write_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, + u32 bytes) +{ + u32 hwwep = ((hwep & 0x1E) << 1) | CTRL_WR_EN; + + if ((bytes > 0) && (data == NULL)) + return; + + /* Setup write of endpoint */ + writel(hwwep, USBD_CTRL(udc->udp_baseaddr)); + + writel(bytes, USBD_TXPLEN(udc->udp_baseaddr)); + + /* Need at least 1 byte to trigger TX */ + if (bytes == 0) + writel(0, USBD_TXDATA(udc->udp_baseaddr)); + else + udc_stuff_fifo(udc, (u8 *) data, bytes); + + writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); + + udc_val_buffer_hwep(udc, hwep); +} + +/* USB device reset - resets USB to a default state with just EP0 + enabled */ +static void uda_usb_reset(struct lpc32xx_udc *udc) +{ + u32 i = 0; + /* Re-init device controller and EP0 */ + udc_enable(udc); + udc->gadget.speed = USB_SPEED_FULL; + + for (i = 1; i < NUM_ENDPOINTS; i++) { + struct lpc32xx_ep *ep = &udc->ep[i]; + ep->req_pending = 0; + } +} + +/* Send a ZLP on EP0 */ +static void udc_ep0_send_zlp(struct lpc32xx_udc *udc) +{ + udc_write_hwep(udc, EP_IN, NULL, 0); +} + +/* Get current frame number */ +static u16 udc_get_current_frame(struct lpc32xx_udc *udc) +{ + u16 flo, fhi; + + udc_protocol_cmd_w(udc, CMD_RD_FRAME); + flo = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); + fhi = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); + + return (fhi << 8) | flo; +} + +/* Set the device as configured - enables all endpoints */ +static inline void udc_set_device_configured(struct lpc32xx_udc *udc) +{ + udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(CONF_DVICE)); +} + +/* Set the device as unconfigured - disables all endpoints */ +static inline void udc_set_device_unconfigured(struct lpc32xx_udc *udc) +{ + udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); +} + +/* reinit == restore initial software state */ +static void udc_reinit(struct lpc32xx_udc *udc) +{ + u32 i; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); + + for (i = 0; i < NUM_ENDPOINTS; i++) { + struct lpc32xx_ep *ep = &udc->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); + INIT_LIST_HEAD(&ep->queue); + ep->req_pending = 0; + } + + udc->ep0state = WAIT_FOR_SETUP; +} + +/* Must be called with lock */ +static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) +{ + struct lpc32xx_udc *udc = ep->udc; + + list_del_init(&req->queue); + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + if (ep->lep) { + usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); + + /* Free DDs */ + udc_dd_free(udc, req->dd_desc_ptr); + } + + if (status && status != -ESHUTDOWN) + ep_dbg(ep, "%s done %p, status %d\n", ep->ep.name, req, status); + + ep->req_pending = 0; + spin_unlock(&udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&udc->lock); +} + +/* Must be called with lock */ +static void nuke(struct lpc32xx_ep *ep, int status) +{ + struct lpc32xx_request *req; + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct lpc32xx_request, queue); + done(ep, req, status); + } + + if (status == -ESHUTDOWN) { + uda_disable_hwepint(ep->udc, ep->hwep_num); + udc_disable_hwep(ep->udc, ep->hwep_num); + } +} + +/* IN endpoint 0 transfer */ +static int udc_ep0_in_req(struct lpc32xx_udc *udc) +{ + struct lpc32xx_request *req; + struct lpc32xx_ep *ep0 = &udc->ep[0]; + u32 tsend, ts = 0; + + if (list_empty(&ep0->queue)) + /* Nothing to send */ + return 0; + else + req = list_entry(ep0->queue.next, struct lpc32xx_request, + queue); + + tsend = ts = req->req.length - req->req.actual; + if (ts == 0) { + /* Send a ZLP */ + udc_ep0_send_zlp(udc); + done(ep0, req, 0); + return 1; + } else if (ts > ep0->ep.maxpacket) + ts = ep0->ep.maxpacket; /* Just send what we can */ + + /* Write data to the EP0 FIFO and start transfer */ + udc_write_hwep(udc, EP_IN, (req->req.buf + req->req.actual), ts); + + /* Increment data pointer */ + req->req.actual += ts; + + if (tsend >= ep0->ep.maxpacket) + return 0; /* Stay in data transfer state */ + + /* Transfer request is complete */ + udc->ep0state = WAIT_FOR_SETUP; + done(ep0, req, 0); + return 1; +} + +/* OUT endpoint 0 transfer */ +static int udc_ep0_out_req(struct lpc32xx_udc *udc) +{ + struct lpc32xx_request *req; + struct lpc32xx_ep *ep0 = &udc->ep[0]; + u32 tr, bufferspace; + + if (list_empty(&ep0->queue)) + return 0; + else + req = list_entry(ep0->queue.next, struct lpc32xx_request, + queue); + + if (req) { + if (req->req.length == 0) { + /* Just dequeue request */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; + } + + /* Get data from FIFO */ + bufferspace = req->req.length - req->req.actual; + if (bufferspace > ep0->ep.maxpacket) + bufferspace = ep0->ep.maxpacket; + + /* Copy data to buffer */ + prefetchw(req->req.buf + req->req.actual); + tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, + bufferspace); + req->req.actual += bufferspace; + + if (tr < ep0->ep.maxpacket) { + /* This is the last packet */ + done(ep0, req, 0); + udc->ep0state = WAIT_FOR_SETUP; + return 1; + } + } + + return 0; +} + +/* Must be called with lock */ +static void stop_activity(struct lpc32xx_udc *udc) +{ + struct usb_gadget_driver *driver = udc->driver; + int i; + + if (udc->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->suspended = 0; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + struct lpc32xx_ep *ep = &udc->ep[i]; + nuke(ep, -ESHUTDOWN); + } + if (driver) { + spin_unlock(&udc->lock); + driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + + isp1301_pullup_enable(udc, 0, 0); + udc_disable(udc); + udc_reinit(udc); +} + +/* + * Activate or kill host pullup + * Can be called with or without lock + */ +static void pullup(struct lpc32xx_udc *udc, int is_on) +{ + if (!udc->clocked) + return; + + if (!udc->enabled || !udc->vbus) + is_on = 0; + + if (is_on != udc->pullup) + isp1301_pullup_enable(udc, is_on, 0); +} + +/* Must be called without lock */ +static int lpc32xx_ep_disable(struct usb_ep *_ep) +{ + struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); + struct lpc32xx_udc *udc = ep->udc; + unsigned long flags; + + if ((ep->hwep_num_base == 0) || (ep->hwep_num == 0)) + return -EINVAL; + spin_lock_irqsave(&udc->lock, flags); + + nuke(ep, -ESHUTDOWN); + + /* Clear all DMA statuses for this EP */ + udc_ep_dma_disable(udc, ep->hwep_num); + writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); + + /* Remove the DD pointer in the UDCA */ + udc->udca_v_base[ep->hwep_num] = 0; + + /* Disable and reset endpoint and interrupt */ + uda_clear_hwepint(udc, ep->hwep_num); + udc_unrealize_hwep(udc, ep->hwep_num); + + ep->hwep_num = 0; + + spin_unlock_irqrestore(&udc->lock, flags); + + atomic_dec(&udc->enabled_ep_cnt); + wake_up(&udc->ep_disable_wait_queue); + + return 0; +} + +/* Must be called without lock */ +static int lpc32xx_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); + struct lpc32xx_udc *udc = ep->udc; + u16 maxpacket; + u32 tmp; + unsigned long flags; + + /* Verify EP data */ + if ((!_ep) || (!ep) || (!desc) || + (desc->bDescriptorType != USB_DT_ENDPOINT)) { + dev_dbg(udc->dev, "bad ep or descriptor\n"); + return -EINVAL; + } + maxpacket = usb_endpoint_maxp(desc); + if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) { + dev_dbg(udc->dev, "bad ep descriptor's packet size\n"); + return -EINVAL; + } + + /* Don't touch EP0 */ + if (ep->hwep_num_base == 0) { + dev_dbg(udc->dev, "Can't re-enable EP0!!!\n"); + return -EINVAL; + } + + /* Is driver ready? */ + if ((!udc->driver) || (udc->gadget.speed == USB_SPEED_UNKNOWN)) { + dev_dbg(udc->dev, "bogus device state\n"); + return -ESHUTDOWN; + } + + tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + switch (tmp) { + case USB_ENDPOINT_XFER_CONTROL: + return -EINVAL; + + case USB_ENDPOINT_XFER_INT: + if (maxpacket > ep->maxpacket) { + dev_dbg(udc->dev, + "Bad INT endpoint maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + + case USB_ENDPOINT_XFER_BULK: + switch (maxpacket) { + case 8: + case 16: + case 32: + case 64: + break; + + default: + dev_dbg(udc->dev, + "Bad BULK endpoint maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + + case USB_ENDPOINT_XFER_ISOC: + break; + } + spin_lock_irqsave(&udc->lock, flags); + + /* Initialize endpoint to match the selected descriptor */ + ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep->ep.maxpacket = maxpacket; + + /* Map hardware endpoint from base and direction */ + if (ep->is_in) + /* IN endpoints are offset 1 from the OUT endpoint */ + ep->hwep_num = ep->hwep_num_base + EP_IN; + else + ep->hwep_num = ep->hwep_num_base; + + ep_dbg(ep, "EP enabled: %s, HW:%d, MP:%d IN:%d\n", ep->ep.name, + ep->hwep_num, maxpacket, (ep->is_in == 1)); + + /* Realize the endpoint, interrupt is enabled later when + * buffers are queued, IN EPs will NAK until buffers are ready */ + udc_realize_hwep(udc, ep->hwep_num, ep->ep.maxpacket); + udc_clr_buffer_hwep(udc, ep->hwep_num); + uda_disable_hwepint(udc, ep->hwep_num); + udc_clrstall_hwep(udc, ep->hwep_num); + + /* Clear all DMA statuses for this EP */ + udc_ep_dma_disable(udc, ep->hwep_num); + writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); + writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); + + spin_unlock_irqrestore(&udc->lock, flags); + + atomic_inc(&udc->enabled_ep_cnt); + return 0; +} + +/* + * Allocate a USB request list + * Can be called with or without lock + */ +static struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct lpc32xx_request *req; + + req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + +/* + * De-allocate a USB request list + * Can be called with or without lock + */ +static void lpc32xx_ep_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct lpc32xx_request *req; + + req = container_of(_req, struct lpc32xx_request, req); + BUG_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* Must be called without lock */ +static int lpc32xx_ep_queue(struct usb_ep *_ep, + struct usb_request *_req, gfp_t gfp_flags) +{ + struct lpc32xx_request *req; + struct lpc32xx_ep *ep; + struct lpc32xx_udc *udc; + unsigned long flags; + int status = 0; + + req = container_of(_req, struct lpc32xx_request, req); + ep = container_of(_ep, struct lpc32xx_ep, ep); + + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) + return -EINVAL; + + udc = ep->udc; + + if (!_ep) { + dev_dbg(udc->dev, "invalid ep\n"); + return -EINVAL; + } + + + if ((!udc) || (!udc->driver) || + (udc->gadget.speed == USB_SPEED_UNKNOWN)) { + dev_dbg(udc->dev, "invalid device\n"); + return -EINVAL; + } + + if (ep->lep) { + struct lpc32xx_usbd_dd_gad *dd; + + 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); + if (!dd) { + /* Error allocating DD */ + return -ENOMEM; + } + req->dd_desc_ptr = dd; + + /* Setup the DMA descriptor */ + dd->dd_next_phy = dd->dd_next_v = 0; + dd->dd_buffer_addr = req->req.dma; + dd->dd_status = 0; + + /* Special handling for ISO EPs */ + if (ep->eptype == EP_ISO_TYPE) { + dd->dd_setup = DD_SETUP_ISO_EP | + DD_SETUP_PACKETLEN(0) | + DD_SETUP_DMALENBYTES(1); + dd->dd_iso_ps_mem_addr = dd->this_dma + 24; + if (ep->is_in) + dd->iso_status[0] = req->req.length; + else + dd->iso_status[0] = 0; + } else + dd->dd_setup = DD_SETUP_PACKETLEN(ep->ep.maxpacket) | + DD_SETUP_DMALENBYTES(req->req.length); + } + + ep_dbg(ep, "%s queue req %p len %d buf %p (in=%d) z=%d\n", _ep->name, + _req, _req->length, _req->buf, ep->is_in, _req->zero); + + spin_lock_irqsave(&udc->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + req->send_zlp = _req->zero; + + /* Kickstart empty queues */ + if (list_empty(&ep->queue)) { + list_add_tail(&req->queue, &ep->queue); + + if (ep->hwep_num_base == 0) { + /* Handle expected data direction */ + if (ep->is_in) { + /* IN packet to host */ + udc->ep0state = DATA_IN; + status = udc_ep0_in_req(udc); + } else { + /* OUT packet from host */ + udc->ep0state = DATA_OUT; + status = udc_ep0_out_req(udc); + } + } else if (ep->is_in) { + /* IN packet to host and kick off transfer */ + if (!ep->req_pending) + udc_ep_in_req_dma(udc, ep); + } else + /* OUT packet from host and kick off list */ + if (!ep->req_pending) + udc_ep_out_req_dma(udc, ep); + } else + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&udc->lock, flags); + + return (status < 0) ? status : 0; +} + +/* Must be called without lock */ +static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct lpc32xx_ep *ep; + struct lpc32xx_request *req; + unsigned long flags; + + ep = container_of(_ep, struct lpc32xx_ep, ep); + if (!_ep || ep->hwep_num_base == 0) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->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) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + + return 0; +} + +/* Must be called without lock */ +static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); + struct lpc32xx_udc *udc = ep->udc; + unsigned long flags; + + if ((!ep) || (ep->hwep_num <= 1)) + return -EINVAL; + + /* Don't halt an IN EP */ + if (ep->is_in) + return -EAGAIN; + + spin_lock_irqsave(&udc->lock, flags); + + if (value == 1) { + /* stall */ + udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), + DAT_WR_BYTE(EP_STAT_ST)); + } else { + /* End stall */ + ep->wedge = 0; + udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), + DAT_WR_BYTE(0)); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/* set the halt feature and ignores clear requests */ +static int lpc32xx_ep_set_wedge(struct usb_ep *_ep) +{ + struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); + + if (!_ep || !ep->udc) + return -EINVAL; + + ep->wedge = 1; + + return usb_ep_set_halt(_ep); +} + +static const struct usb_ep_ops lpc32xx_ep_ops = { + .enable = lpc32xx_ep_enable, + .disable = lpc32xx_ep_disable, + .alloc_request = lpc32xx_ep_alloc_request, + .free_request = lpc32xx_ep_free_request, + .queue = lpc32xx_ep_queue, + .dequeue = lpc32xx_ep_dequeue, + .set_halt = lpc32xx_ep_set_halt, + .set_wedge = lpc32xx_ep_set_wedge, +}; + +/* Send a ZLP on a non-0 IN EP */ +void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) +{ + /* Clear EP status */ + udc_clearep_getsts(udc, ep->hwep_num); + + /* Send ZLP via FIFO mechanism */ + udc_write_hwep(udc, ep->hwep_num, NULL, 0); +} + +/* + * Handle EP completion for ZLP + * This function will only be called when a delayed ZLP needs to be sent out + * after a DMA transfer has filled both buffers. + */ +void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) +{ + u32 epstatus; + struct lpc32xx_request *req; + + if (ep->hwep_num <= 0) + return; + + uda_clear_hwepint(udc, ep->hwep_num); + + /* If this interrupt isn't enabled, return now */ + if (!(udc->enabled_hwepints & (1 << ep->hwep_num))) + return; + + /* Get endpoint status */ + epstatus = udc_clearep_getsts(udc, ep->hwep_num); + + /* + * This should never happen, but protect against writing to the + * buffer when full. + */ + if (epstatus & EP_SEL_F) + return; + + if (ep->is_in) { + udc_send_in_zlp(udc, ep); + uda_disable_hwepint(udc, ep->hwep_num); + } else + return; + + /* If there isn't a request waiting, something went wrong */ + req = list_entry(ep->queue.next, struct lpc32xx_request, queue); + if (req) { + done(ep, req, 0); + + /* Start another request if ready */ + if (!list_empty(&ep->queue)) { + if (ep->is_in) + udc_ep_in_req_dma(udc, ep); + else + udc_ep_out_req_dma(udc, ep); + } else + ep->req_pending = 0; + } +} + + +/* DMA end of transfer completion */ +static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) +{ + u32 status, epstatus; + struct lpc32xx_request *req; + struct lpc32xx_usbd_dd_gad *dd; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + ep->totalints++; +#endif + + req = list_entry(ep->queue.next, struct lpc32xx_request, queue); + if (!req) { + ep_err(ep, "DMA interrupt on no req!\n"); + return; + } + dd = req->dd_desc_ptr; + + /* DMA descriptor should always be retired for this call */ + if (!(dd->dd_status & DD_STATUS_DD_RETIRED)) + ep_warn(ep, "DMA descriptor did not retire\n"); + + /* Disable DMA */ + udc_ep_dma_disable(udc, ep->hwep_num); + writel((1 << ep->hwep_num), USBD_EOTINTCLR(udc->udp_baseaddr)); + writel((1 << ep->hwep_num), USBD_NDDRTINTCLR(udc->udp_baseaddr)); + + /* System error? */ + if (readl(USBD_SYSERRTINTST(udc->udp_baseaddr)) & + (1 << ep->hwep_num)) { + writel((1 << ep->hwep_num), + USBD_SYSERRTINTCLR(udc->udp_baseaddr)); + ep_err(ep, "AHB critical error!\n"); + ep->req_pending = 0; + + /* The error could have occurred on a packet of a multipacket + * transfer, so recovering the transfer is not possible. Close + * the request with an error */ + done(ep, req, -ECONNABORTED); + return; + } + + /* Handle the current DD's status */ + status = dd->dd_status; + switch (status & DD_STATUS_STS_MASK) { + case DD_STATUS_STS_NS: + /* DD not serviced? This shouldn't happen! */ + ep->req_pending = 0; + ep_err(ep, "DMA critical EP error: DD not serviced (0x%x)!\n", + status); + + done(ep, req, -ECONNABORTED); + return; + + case DD_STATUS_STS_BS: + /* Interrupt only fires on EOT - This shouldn't happen! */ + ep->req_pending = 0; + ep_err(ep, "DMA critical EP error: EOT prior to service completion (0x%x)!\n", + status); + done(ep, req, -ECONNABORTED); + return; + + case DD_STATUS_STS_NC: + case DD_STATUS_STS_DUR: + /* Really just a short packet, not an underrun */ + /* This is a good status and what we expect */ + break; + + default: + /* Data overrun, system error, or unknown */ + ep->req_pending = 0; + ep_err(ep, "DMA critical EP error: System error (0x%x)!\n", + status); + done(ep, req, -ECONNABORTED); + return; + } + + /* ISO endpoints are handled differently */ + if (ep->eptype == EP_ISO_TYPE) { + if (ep->is_in) + req->req.actual = req->req.length; + else + req->req.actual = dd->iso_status[0] & 0xFFFF; + } else + req->req.actual += DD_STATUS_CURDMACNT(status); + + /* Send a ZLP if necessary. This will be done for non-int + * packets which have a size that is a divisor of MAXP */ + if (req->send_zlp) { + /* + * If at least 1 buffer is available, send the ZLP now. + * Otherwise, the ZLP send needs to be deferred until a + * buffer is available. + */ + if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) { + udc_clearep_getsts(udc, ep->hwep_num); + uda_enable_hwepint(udc, ep->hwep_num); + epstatus = udc_clearep_getsts(udc, ep->hwep_num); + + /* Let the EP interrupt handle the ZLP */ + return; + } else + udc_send_in_zlp(udc, ep); + } + + /* Transfer request is complete */ + done(ep, req, 0); + + /* Start another request if ready */ + udc_clearep_getsts(udc, ep->hwep_num); + if (!list_empty((&ep->queue))) { + if (ep->is_in) + udc_ep_in_req_dma(udc, ep); + else + udc_ep_out_req_dma(udc, ep); + } else + ep->req_pending = 0; + +} + +/* + * + * Endpoint 0 functions + * + */ +static void udc_handle_dev(struct lpc32xx_udc *udc) +{ + u32 tmp; + + udc_protocol_cmd_w(udc, CMD_GET_DEV_STAT); + tmp = udc_protocol_cmd_r(udc, DAT_GET_DEV_STAT); + + if (tmp & DEV_RST) + uda_usb_reset(udc); + else if (tmp & DEV_CON_CH) + uda_power_event(udc, (tmp & DEV_CON)); + else if (tmp & DEV_SUS_CH) { + if (tmp & DEV_SUS) { + if (udc->vbus == 0) + stop_activity(udc); + else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && + udc->driver) { + /* Power down transceiver */ + udc->poweron = 0; + schedule_work(&udc->pullup_job); + uda_resm_susp_event(udc, 1); + } + } else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && + udc->driver && udc->vbus) { + uda_resm_susp_event(udc, 0); + /* Power up transceiver */ + udc->poweron = 1; + schedule_work(&udc->pullup_job); + } + } +} + +static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex) +{ + struct lpc32xx_ep *ep; + u32 ep0buff = 0, tmp; + + switch (reqtype & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + break; /* Not supported */ + + case USB_RECIP_DEVICE: + ep0buff = (udc->selfpowered << USB_DEVICE_SELF_POWERED); + if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP)) + ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP); + break; + + case USB_RECIP_ENDPOINT: + tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[tmp]; + if ((tmp == 0) || (tmp >= NUM_ENDPOINTS)) + return -EOPNOTSUPP; + + if (wIndex & USB_DIR_IN) { + if (!ep->is_in) + return -EOPNOTSUPP; /* Something's wrong */ + } else if (ep->is_in) + return -EOPNOTSUPP; /* Not an IN endpoint */ + + /* Get status of the endpoint */ + udc_protocol_cmd_w(udc, CMD_SEL_EP(ep->hwep_num)); + tmp = udc_protocol_cmd_r(udc, DAT_SEL_EP(ep->hwep_num)); + + if (tmp & EP_SEL_ST) + ep0buff = (1 << USB_ENDPOINT_HALT); + else + ep0buff = 0; + break; + + default: + break; + } + + /* Return data */ + udc_write_hwep(udc, EP_IN, &ep0buff, 2); + + return 0; +} + +static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) +{ + struct lpc32xx_ep *ep, *ep0 = &udc->ep[0]; + struct usb_ctrlrequest ctrlpkt; + int i, bytes; + u16 wIndex, wValue, wLength, reqtype, req, tmp; + + /* Nuke previous transfers */ + nuke(ep0, -EPROTO); + + /* Get setup packet */ + bytes = udc_read_hwep(udc, EP_OUT, (u32 *) &ctrlpkt, 8); + if (bytes != 8) { + ep_warn(ep0, "Incorrectly sized setup packet (s/b 8, is %d)!\n", + bytes); + return; + } + + /* Native endianness */ + wIndex = le16_to_cpu(ctrlpkt.wIndex); + wValue = le16_to_cpu(ctrlpkt.wValue); + wLength = le16_to_cpu(ctrlpkt.wLength); + reqtype = le16_to_cpu(ctrlpkt.bRequestType); + + /* Set direction of EP0 */ + if (likely(reqtype & USB_DIR_IN)) + ep0->is_in = 1; + else + ep0->is_in = 0; + + /* Handle SETUP packet */ + req = le16_to_cpu(ctrlpkt.bRequest); + switch (req) { + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + switch (reqtype) { + case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (wValue != USB_DEVICE_REMOTE_WAKEUP) + goto stall; /* Nothing else handled */ + + /* Tell board about event */ + if (req == USB_REQ_CLEAR_FEATURE) + udc->dev_status &= + ~(1 << USB_DEVICE_REMOTE_WAKEUP); + else + udc->dev_status |= + (1 << USB_DEVICE_REMOTE_WAKEUP); + uda_remwkp_cgh(udc); + goto zlp_send; + + case (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; + if ((wValue != USB_ENDPOINT_HALT) || + (tmp >= NUM_ENDPOINTS)) + break; + + /* Find hardware endpoint from logical endpoint */ + ep = &udc->ep[tmp]; + tmp = ep->hwep_num; + if (tmp == 0) + break; + + if (req == USB_REQ_SET_FEATURE) + udc_stall_hwep(udc, tmp); + else if (!ep->wedge) + udc_clrstall_hwep(udc, tmp); + + goto zlp_send; + + default: + break; + } + + + case USB_REQ_SET_ADDRESS: + if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { + udc_set_address(udc, wValue); + goto zlp_send; + } + break; + + case USB_REQ_GET_STATUS: + udc_get_status(udc, reqtype, wIndex); + return; + + default: + break; /* Let GadgetFS handle the descriptor instead */ + } + + if (likely(udc->driver)) { + /* device-2-host (IN) or no data setup command, process + * immediately */ + spin_unlock(&udc->lock); + i = udc->driver->setup(&udc->gadget, &ctrlpkt); + + spin_lock(&udc->lock); + if (req == USB_REQ_SET_CONFIGURATION) { + /* Configuration is set after endpoints are realized */ + if (wValue) { + /* Set configuration */ + udc_set_device_configured(udc); + + udc_protocol_cmd_data_w(udc, CMD_SET_MODE, + DAT_WR_BYTE(AP_CLK | + INAK_BI | INAK_II)); + } else { + /* Clear configuration */ + udc_set_device_unconfigured(udc); + + /* Disable NAK interrupts */ + udc_protocol_cmd_data_w(udc, CMD_SET_MODE, + DAT_WR_BYTE(AP_CLK)); + } + } + + if (i < 0) { + /* setup processing failed, force stall */ + dev_dbg(udc->dev, + "req %02x.%02x protocol STALL; stat %d\n", + reqtype, req, i); + udc->ep0state = WAIT_FOR_SETUP; + goto stall; + } + } + + if (!ep0->is_in) + udc_ep0_send_zlp(udc); /* ZLP IN packet on data phase */ + + return; + +stall: + udc_stall_hwep(udc, EP_IN); + return; + +zlp_send: + udc_ep0_send_zlp(udc); + return; +} + +/* IN endpoint 0 transfer */ +static void udc_handle_ep0_in(struct lpc32xx_udc *udc) +{ + struct lpc32xx_ep *ep0 = &udc->ep[0]; + u32 epstatus; + + /* Clear EP interrupt */ + epstatus = udc_clearep_getsts(udc, EP_IN); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + ep0->totalints++; +#endif + + /* Stalled? Clear stall and reset buffers */ + if (epstatus & EP_SEL_ST) { + udc_clrstall_hwep(udc, EP_IN); + nuke(ep0, -ECONNABORTED); + udc->ep0state = WAIT_FOR_SETUP; + return; + } + + /* Is a buffer available? */ + if (!(epstatus & EP_SEL_F)) { + /* Handle based on current state */ + if (udc->ep0state == DATA_IN) + udc_ep0_in_req(udc); + else { + /* Unknown state for EP0 oe end of DATA IN phase */ + nuke(ep0, -ECONNABORTED); + udc->ep0state = WAIT_FOR_SETUP; + } + } +} + +/* OUT endpoint 0 transfer */ +static void udc_handle_ep0_out(struct lpc32xx_udc *udc) +{ + struct lpc32xx_ep *ep0 = &udc->ep[0]; + u32 epstatus; + + /* Clear EP interrupt */ + epstatus = udc_clearep_getsts(udc, EP_OUT); + + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + ep0->totalints++; +#endif + + /* Stalled? */ + if (epstatus & EP_SEL_ST) { + udc_clrstall_hwep(udc, EP_OUT); + nuke(ep0, -ECONNABORTED); + udc->ep0state = WAIT_FOR_SETUP; + return; + } + + /* A NAK may occur if a packet couldn't be received yet */ + if (epstatus & EP_SEL_EPN) + return; + /* Setup packet incoming? */ + if (epstatus & EP_SEL_STP) { + nuke(ep0, 0); + udc->ep0state = WAIT_FOR_SETUP; + } + + /* Data available? */ + if (epstatus & EP_SEL_F) + /* Handle based on current state */ + switch (udc->ep0state) { + case WAIT_FOR_SETUP: + udc_handle_ep0_setup(udc); + break; + + case DATA_OUT: + udc_ep0_out_req(udc); + break; + + default: + /* Unknown state for EP0 */ + nuke(ep0, -ECONNABORTED); + udc->ep0state = WAIT_FOR_SETUP; + } +} + +/* Must be called without lock */ +static int lpc32xx_get_frame(struct usb_gadget *gadget) +{ + int frame; + unsigned long flags; + struct lpc32xx_udc *udc = to_udc(gadget); + + if (!udc->clocked) + return -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + + frame = (int) udc_get_current_frame(udc); + + spin_unlock_irqrestore(&udc->lock, flags); + + return frame; +} + +static int lpc32xx_wakeup(struct usb_gadget *gadget) +{ + return -ENOTSUPP; +} + +static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct lpc32xx_udc *udc = to_udc(gadget); + + /* Always self-powered */ + udc->selfpowered = (is_on != 0); + + return 0; +} + +/* + * vbus is here! turn everything on that's ready + * Must be called without lock + */ +static int lpc32xx_vbus_session(struct usb_gadget *gadget, int is_active) +{ + unsigned long flags; + struct lpc32xx_udc *udc = to_udc(gadget); + + spin_lock_irqsave(&udc->lock, flags); + + /* Doesn't need lock */ + if (udc->driver) { + udc_clk_set(udc, 1); + udc_enable(udc); + pullup(udc, is_active); + } else { + stop_activity(udc); + pullup(udc, 0); + + spin_unlock_irqrestore(&udc->lock, flags); + /* + * Wait for all the endpoints to disable, + * before disabling clocks. Don't wait if + * endpoints are not enabled. + */ + if (atomic_read(&udc->enabled_ep_cnt)) + wait_event_interruptible(udc->ep_disable_wait_queue, + (atomic_read(&udc->enabled_ep_cnt) == 0)); + + spin_lock_irqsave(&udc->lock, flags); + + udc_clk_set(udc, 0); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/* Can be called with or without lock */ +static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on) +{ + struct lpc32xx_udc *udc = to_udc(gadget); + + /* Doesn't need lock */ + pullup(udc, is_on); + + return 0; +} + +static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *); +static int lpc32xx_stop(struct usb_gadget *, struct usb_gadget_driver *); + +static const struct usb_gadget_ops lpc32xx_udc_ops = { + .get_frame = lpc32xx_get_frame, + .wakeup = lpc32xx_wakeup, + .set_selfpowered = lpc32xx_set_selfpowered, + .vbus_session = lpc32xx_vbus_session, + .pullup = lpc32xx_pullup, + .udc_start = lpc32xx_start, + .udc_stop = lpc32xx_stop, +}; + +static void nop_release(struct device *dev) +{ + /* nothing to free */ +} + +static const struct lpc32xx_udc controller_template = { + .gadget = { + .ops = &lpc32xx_udc_ops, + .name = driver_name, + .dev = { + .init_name = "gadget", + .release = nop_release, + } + }, + .ep[0] = { + .ep = { + .name = "ep0", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 0, + .hwep_num = 0, /* Can be 0 or 1, has special handling */ + .lep = 0, + .eptype = EP_CTL_TYPE, + }, + .ep[1] = { + .ep = { + .name = "ep1-int", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 2, + .hwep_num = 0, /* 2 or 3, will be set later */ + .lep = 1, + .eptype = EP_INT_TYPE, + }, + .ep[2] = { + .ep = { + .name = "ep2-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 4, + .hwep_num = 0, /* 4 or 5, will be set later */ + .lep = 2, + .eptype = EP_BLK_TYPE, + }, + .ep[3] = { + .ep = { + .name = "ep3-iso", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 1023, + .hwep_num_base = 6, + .hwep_num = 0, /* 6 or 7, will be set later */ + .lep = 3, + .eptype = EP_ISO_TYPE, + }, + .ep[4] = { + .ep = { + .name = "ep4-int", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 8, + .hwep_num = 0, /* 8 or 9, will be set later */ + .lep = 4, + .eptype = EP_INT_TYPE, + }, + .ep[5] = { + .ep = { + .name = "ep5-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 10, + .hwep_num = 0, /* 10 or 11, will be set later */ + .lep = 5, + .eptype = EP_BLK_TYPE, + }, + .ep[6] = { + .ep = { + .name = "ep6-iso", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 1023, + .hwep_num_base = 12, + .hwep_num = 0, /* 12 or 13, will be set later */ + .lep = 6, + .eptype = EP_ISO_TYPE, + }, + .ep[7] = { + .ep = { + .name = "ep7-int", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 14, + .hwep_num = 0, + .lep = 7, + .eptype = EP_INT_TYPE, + }, + .ep[8] = { + .ep = { + .name = "ep8-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 16, + .hwep_num = 0, + .lep = 8, + .eptype = EP_BLK_TYPE, + }, + .ep[9] = { + .ep = { + .name = "ep9-iso", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 1023, + .hwep_num_base = 18, + .hwep_num = 0, + .lep = 9, + .eptype = EP_ISO_TYPE, + }, + .ep[10] = { + .ep = { + .name = "ep10-int", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 20, + .hwep_num = 0, + .lep = 10, + .eptype = EP_INT_TYPE, + }, + .ep[11] = { + .ep = { + .name = "ep11-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 22, + .hwep_num = 0, + .lep = 11, + .eptype = EP_BLK_TYPE, + }, + .ep[12] = { + .ep = { + .name = "ep12-iso", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 1023, + .hwep_num_base = 24, + .hwep_num = 0, + .lep = 12, + .eptype = EP_ISO_TYPE, + }, + .ep[13] = { + .ep = { + .name = "ep13-int", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 26, + .hwep_num = 0, + .lep = 13, + .eptype = EP_INT_TYPE, + }, + .ep[14] = { + .ep = { + .name = "ep14-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 64, + .hwep_num_base = 28, + .hwep_num = 0, + .lep = 14, + .eptype = EP_BLK_TYPE, + }, + .ep[15] = { + .ep = { + .name = "ep15-bulk", + .ops = &lpc32xx_ep_ops, + }, + .maxpacket = 1023, + .hwep_num_base = 30, + .hwep_num = 0, + .lep = 15, + .eptype = EP_BLK_TYPE, + }, +}; + +/* ISO and status interrupts */ +static irqreturn_t lpc32xx_usb_lp_irq(int irq, void *_udc) +{ + u32 tmp, devstat; + struct lpc32xx_udc *udc = _udc; + + spin_lock(&udc->lock); + + /* Read the device status register */ + devstat = readl(USBD_DEVINTST(udc->udp_baseaddr)); + + devstat &= ~USBD_EP_FAST; + writel(devstat, USBD_DEVINTCLR(udc->udp_baseaddr)); + devstat = devstat & udc->enabled_devints; + + /* Device specific handling needed? */ + if (devstat & USBD_DEV_STAT) + udc_handle_dev(udc); + + /* Start of frame? (devstat & FRAME_INT): + * The frame interrupt isn't really needed for ISO support, + * as the driver will queue the necessary packets */ + + /* Error? */ + if (devstat & ERR_INT) { + /* All types of errors, from cable removal during transfer to + * misc protocol and bit errors. These are mostly for just info, + * as the USB hardware will work around these. If these errors + * happen alot, something is wrong. */ + udc_protocol_cmd_w(udc, CMD_RD_ERR_STAT); + tmp = udc_protocol_cmd_r(udc, DAT_RD_ERR_STAT); + dev_dbg(udc->dev, "Device error (0x%x)!\n", tmp); + } + + spin_unlock(&udc->lock); + + return IRQ_HANDLED; +} + +/* EP interrupts */ +static irqreturn_t lpc32xx_usb_hp_irq(int irq, void *_udc) +{ + u32 tmp; + struct lpc32xx_udc *udc = _udc; + + spin_lock(&udc->lock); + + /* Read the device status register */ + writel(USBD_EP_FAST, USBD_DEVINTCLR(udc->udp_baseaddr)); + + /* Endpoints */ + tmp = readl(USBD_EPINTST(udc->udp_baseaddr)); + + /* Special handling for EP0 */ + if (tmp & (EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { + /* Handle EP0 IN */ + if (tmp & (EP_MASK_SEL(0, EP_IN))) + udc_handle_ep0_in(udc); + + /* Handle EP0 OUT */ + if (tmp & (EP_MASK_SEL(0, EP_OUT))) + udc_handle_ep0_out(udc); + } + + /* All other EPs */ + if (tmp & ~(EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { + int i; + + /* Handle other EP interrupts */ + for (i = 1; i < NUM_ENDPOINTS; i++) { + if (tmp & (1 << udc->ep[i].hwep_num)) + udc_handle_eps(udc, &udc->ep[i]); + } + } + + spin_unlock(&udc->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc) +{ + struct lpc32xx_udc *udc = _udc; + + int i; + u32 tmp; + + spin_lock(&udc->lock); + + /* Handle EP DMA EOT interrupts */ + tmp = readl(USBD_EOTINTST(udc->udp_baseaddr)) | + (readl(USBD_EPDMAST(udc->udp_baseaddr)) & + readl(USBD_NDDRTINTST(udc->udp_baseaddr))) | + readl(USBD_SYSERRTINTST(udc->udp_baseaddr)); + for (i = 1; i < NUM_ENDPOINTS; i++) { + if (tmp & (1 << udc->ep[i].hwep_num)) + udc_handle_dma_ep(udc, &udc->ep[i]); + } + + spin_unlock(&udc->lock); + + return IRQ_HANDLED; +} + +/* + * + * VBUS detection, pullup handler, and Gadget cable state notification + * + */ +static void vbus_work(struct work_struct *work) +{ + u8 value; + struct lpc32xx_udc *udc = container_of(work, struct lpc32xx_udc, + vbus_job); + + if (udc->enabled != 0) { + /* Discharge VBUS real quick */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); + + /* Give VBUS some time (100mS) to discharge */ + msleep(100); + + /* Disable VBUS discharge resistor */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, + OTG1_VBUS_DISCHRG); + + /* Clear interrupt */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_LATCH | + ISP1301_I2C_REG_CLEAR_ADDR, ~0); + + /* Get the VBUS status from the transceiver */ + value = i2c_smbus_read_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_SOURCE); + + /* VBUS on or off? */ + if (value & INT_SESS_VLD) + udc->vbus = 1; + else + udc->vbus = 0; + + /* VBUS changed? */ + if (udc->last_vbus != udc->vbus) { + udc->last_vbus = udc->vbus; + lpc32xx_vbus_session(&udc->gadget, udc->vbus); + } + } + + /* Re-enable after completion */ + enable_irq(udc->udp_irq[IRQ_USB_ATX]); +} + +static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc) +{ + struct lpc32xx_udc *udc = _udc; + + /* Defer handling of VBUS IRQ to work queue */ + disable_irq_nosync(udc->udp_irq[IRQ_USB_ATX]); + schedule_work(&udc->vbus_job); + + return IRQ_HANDLED; +} + +static int lpc32xx_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct lpc32xx_udc *udc = to_udc(gadget); + int i; + + if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) { + dev_err(udc->dev, "bad parameter.\n"); + return -EINVAL; + } + + if (udc->driver) { + dev_err(udc->dev, "UDC already has a gadget driver\n"); + return -EBUSY; + } + + udc->driver = driver; + udc->gadget.dev.of_node = udc->dev->of_node; + udc->enabled = 1; + udc->selfpowered = 1; + udc->vbus = 0; + + /* Force VBUS process once to check for cable insertion */ + udc->last_vbus = udc->vbus = 0; + schedule_work(&udc->vbus_job); + + /* Do not re-enable ATX IRQ (3) */ + for (i = IRQ_USB_LP; i < IRQ_USB_ATX; i++) + enable_irq(udc->udp_irq[i]); + + return 0; +} + +static int lpc32xx_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + int i; + struct lpc32xx_udc *udc = to_udc(gadget); + + if (!driver || driver != udc->driver) + return -EINVAL; + + for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) + disable_irq(udc->udp_irq[i]); + + if (udc->clocked) { + spin_lock(&udc->lock); + stop_activity(udc); + spin_unlock(&udc->lock); + + /* + * Wait for all the endpoints to disable, + * before disabling clocks. Don't wait if + * endpoints are not enabled. + */ + if (atomic_read(&udc->enabled_ep_cnt)) + wait_event_interruptible(udc->ep_disable_wait_queue, + (atomic_read(&udc->enabled_ep_cnt) == 0)); + + spin_lock(&udc->lock); + udc_clk_set(udc, 0); + spin_unlock(&udc->lock); + } + + udc->enabled = 0; + udc->driver = NULL; + + return 0; +} + +static void lpc32xx_udc_shutdown(struct platform_device *dev) +{ + /* Force disconnect on reboot */ + struct lpc32xx_udc *udc = platform_get_drvdata(dev); + + pullup(udc, 0); +} + +/* + * Callbacks to be overridden by options passed via OF (TODO) + */ + +static void lpc32xx_usbd_conn_chg(int conn) +{ + /* Do nothing, it might be nice to enable an LED + * based on conn state being !0 */ +} + +static void lpc32xx_usbd_susp_chg(int susp) +{ + /* Device suspend if susp != 0 */ +} + +static void lpc32xx_rmwkup_chg(int remote_wakup_enable) +{ + /* Enable or disable USB remote wakeup */ +} + +struct lpc32xx_usbd_cfg lpc32xx_usbddata = { + .vbus_drv_pol = 0, + .conn_chgb = &lpc32xx_usbd_conn_chg, + .susp_chgb = &lpc32xx_usbd_susp_chg, + .rmwk_chgb = &lpc32xx_rmwkup_chg, +}; + + +static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; + +static int __init lpc32xx_udc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpc32xx_udc *udc; + int retval, i; + struct resource *res; + dma_addr_t dma_handle; + struct device_node *isp1301_node; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + memcpy(udc, &controller_template, sizeof(*udc)); + for (i = 0; i <= 15; i++) + udc->ep[i].udc = udc; + udc->gadget.ep0 = &udc->ep[0].ep; + + /* init software state */ + udc->gadget.dev.parent = dev; + udc->pdev = pdev; + udc->dev = &pdev->dev; + udc->enabled = 0; + + if (pdev->dev.of_node) { + isp1301_node = of_parse_phandle(pdev->dev.of_node, + "transceiver", 0); + } else { + isp1301_node = NULL; + } + + udc->isp1301_i2c_client = isp1301_get_client(isp1301_node); + if (!udc->isp1301_i2c_client) { + retval = -EPROBE_DEFER; + goto phy_fail; + } + + dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n", + udc->isp1301_i2c_client->addr); + + pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; + retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto resource_fail; + + udc->board = &lpc32xx_usbddata; + + /* + * Resources are mapped as follows: + * IORESOURCE_MEM, base address and size of USB space + * IORESOURCE_IRQ, USB device low priority interrupt number + * IORESOURCE_IRQ, USB device high priority interrupt number + * IORESOURCE_IRQ, USB device interrupt number + * IORESOURCE_IRQ, USB transceiver interrupt number + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENXIO; + goto resource_fail; + } + + spin_lock_init(&udc->lock); + + /* Get IRQs */ + for (i = 0; i < 4; i++) { + udc->udp_irq[i] = platform_get_irq(pdev, i); + if (udc->udp_irq[i] < 0) { + dev_err(udc->dev, + "irq resource %d not available!\n", i); + retval = udc->udp_irq[i]; + goto irq_fail; + } + } + + udc->io_p_start = res->start; + udc->io_p_size = resource_size(res); + if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) { + dev_err(udc->dev, "someone's using UDC memory\n"); + retval = -EBUSY; + goto request_mem_region_fail; + } + + udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size); + if (!udc->udp_baseaddr) { + retval = -ENOMEM; + dev_err(udc->dev, "IO map failure\n"); + goto io_map_fail; + } + + /* Enable AHB slave USB clock, needed for further USB clock control */ + writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL); + + /* Get required clocks */ + udc->usb_pll_clk = clk_get(&pdev->dev, "ck_pll5"); + if (IS_ERR(udc->usb_pll_clk)) { + dev_err(udc->dev, "failed to acquire USB PLL\n"); + retval = PTR_ERR(udc->usb_pll_clk); + goto pll_get_fail; + } + udc->usb_slv_clk = clk_get(&pdev->dev, "ck_usbd"); + if (IS_ERR(udc->usb_slv_clk)) { + dev_err(udc->dev, "failed to acquire USB device clock\n"); + retval = PTR_ERR(udc->usb_slv_clk); + goto usb_clk_get_fail; + } + udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); + if (IS_ERR(udc->usb_otg_clk)) { + dev_err(udc->dev, "failed to acquire USB otg clock\n"); + retval = PTR_ERR(udc->usb_otg_clk); + goto usb_otg_clk_get_fail; + } + + /* Setup PLL clock to 48MHz */ + retval = clk_enable(udc->usb_pll_clk); + if (retval < 0) { + dev_err(udc->dev, "failed to start USB PLL\n"); + goto pll_enable_fail; + } + + retval = clk_set_rate(udc->usb_pll_clk, 48000); + if (retval < 0) { + dev_err(udc->dev, "failed to set USB clock rate\n"); + goto pll_set_fail; + } + + writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, USB_CTRL); + + /* Enable USB device clock */ + retval = clk_enable(udc->usb_slv_clk); + if (retval < 0) { + dev_err(udc->dev, "failed to start USB device clock\n"); + goto usb_clk_enable_fail; + } + + /* Enable USB OTG clock */ + retval = clk_enable(udc->usb_otg_clk); + if (retval < 0) { + dev_err(udc->dev, "failed to start USB otg clock\n"); + goto usb_otg_clk_enable_fail; + } + + /* Setup deferred workqueue data */ + udc->poweron = udc->pullup = 0; + INIT_WORK(&udc->pullup_job, pullup_work); + INIT_WORK(&udc->vbus_job, vbus_work); +#ifdef CONFIG_PM + INIT_WORK(&udc->power_job, power_work); +#endif + + /* All clocks are now on */ + udc->clocked = 1; + + isp1301_udc_configure(udc); + /* Allocate memory for the UDCA */ + udc->udca_v_base = dma_alloc_coherent(&pdev->dev, UDCA_BUFF_SIZE, + &dma_handle, + (GFP_KERNEL | GFP_DMA)); + if (!udc->udca_v_base) { + dev_err(udc->dev, "error getting UDCA region\n"); + retval = -ENOMEM; + goto i2c_fail; + } + udc->udca_p_base = dma_handle; + dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n", + UDCA_BUFF_SIZE, udc->udca_p_base, udc->udca_v_base); + + /* Setup the DD DMA memory pool */ + udc->dd_cache = dma_pool_create("udc_dd", udc->dev, + sizeof(struct lpc32xx_usbd_dd_gad), + sizeof(u32), 0); + if (!udc->dd_cache) { + dev_err(udc->dev, "error getting DD DMA region\n"); + retval = -ENOMEM; + goto dma_alloc_fail; + } + + /* Clear USB peripheral and initialize gadget endpoints */ + udc_disable(udc); + udc_reinit(udc); + + /* 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, + 0, "udc_lp", udc); + if (retval < 0) { + dev_err(udc->dev, "LP request irq %d failed\n", + udc->udp_irq[IRQ_USB_LP]); + goto irq_lp_fail; + } + retval = request_irq(udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq, + 0, "udc_hp", udc); + if (retval < 0) { + dev_err(udc->dev, "HP request irq %d failed\n", + udc->udp_irq[IRQ_USB_HP]); + goto irq_hp_fail; + } + + retval = request_irq(udc->udp_irq[IRQ_USB_DEVDMA], + lpc32xx_usb_devdma_irq, 0, "udc_dma", udc); + if (retval < 0) { + dev_err(udc->dev, "DEV request irq %d failed\n", + udc->udp_irq[IRQ_USB_DEVDMA]); + goto irq_dev_fail; + } + + /* The transceiver interrupt is used for VBUS detection and will + kick off the VBUS handler function */ + retval = request_irq(udc->udp_irq[IRQ_USB_ATX], lpc32xx_usb_vbus_irq, + 0, "udc_otg", udc); + if (retval < 0) { + dev_err(udc->dev, "VBUS request irq %d failed\n", + udc->udp_irq[IRQ_USB_ATX]); + goto irq_xcvr_fail; + } + + /* Initialize wait queue */ + init_waitqueue_head(&udc->ep_disable_wait_queue); + atomic_set(&udc->enabled_ep_cnt, 0); + + /* Keep all IRQs disabled until GadgetFS starts up */ + for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) + disable_irq(udc->udp_irq[i]); + + retval = usb_add_gadget_udc(dev, &udc->gadget); + if (retval < 0) + goto add_gadget_fail; + + dev_set_drvdata(dev, udc); + device_init_wakeup(dev, 1); + create_debug_file(udc); + + /* Disable clocks for now */ + udc_clk_set(udc, 0); + + dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION); + return 0; + +add_gadget_fail: + free_irq(udc->udp_irq[IRQ_USB_ATX], udc); +irq_xcvr_fail: + free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); +irq_dev_fail: + free_irq(udc->udp_irq[IRQ_USB_HP], udc); +irq_hp_fail: + free_irq(udc->udp_irq[IRQ_USB_LP], udc); +irq_lp_fail: + dma_pool_destroy(udc->dd_cache); +dma_alloc_fail: + dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, + udc->udca_v_base, udc->udca_p_base); +i2c_fail: + clk_disable(udc->usb_otg_clk); +usb_otg_clk_enable_fail: + clk_disable(udc->usb_slv_clk); +usb_clk_enable_fail: +pll_set_fail: + clk_disable(udc->usb_pll_clk); +pll_enable_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: + iounmap(udc->udp_baseaddr); +io_map_fail: + release_mem_region(udc->io_p_start, udc->io_p_size); + dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); +request_mem_region_fail: +irq_fail: +resource_fail: +phy_fail: + kfree(udc); + return retval; +} + +static int lpc32xx_udc_remove(struct platform_device *pdev) +{ + struct lpc32xx_udc *udc = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&udc->gadget); + if (udc->driver) + return -EBUSY; + + udc_clk_set(udc, 1); + udc_disable(udc); + pullup(udc, 0); + + free_irq(udc->udp_irq[IRQ_USB_ATX], udc); + + device_init_wakeup(&pdev->dev, 0); + remove_debug_file(udc); + + dma_pool_destroy(udc->dd_cache); + dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, + udc->udca_v_base, udc->udca_p_base); + free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); + free_irq(udc->udp_irq[IRQ_USB_HP], udc); + free_irq(udc->udp_irq[IRQ_USB_LP], udc); + + clk_disable(udc->usb_otg_clk); + clk_put(udc->usb_otg_clk); + clk_disable(udc->usb_slv_clk); + clk_put(udc->usb_slv_clk); + clk_disable(udc->usb_pll_clk); + clk_put(udc->usb_pll_clk); + iounmap(udc->udp_baseaddr); + release_mem_region(udc->io_p_start, udc->io_p_size); + kfree(udc); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct lpc32xx_udc *udc = platform_get_drvdata(pdev); + + if (udc->clocked) { + /* Power down ISP */ + udc->poweron = 0; + isp1301_set_powerstate(udc, 0); + + /* Disable clocking */ + udc_clk_set(udc, 0); + + /* Keep clock flag on, so we know to re-enable clocks + on resume */ + udc->clocked = 1; + + /* Kill global USB clock */ + clk_disable(udc->usb_slv_clk); + } + + return 0; +} + +static int lpc32xx_udc_resume(struct platform_device *pdev) +{ + struct lpc32xx_udc *udc = platform_get_drvdata(pdev); + + if (udc->clocked) { + /* Enable global USB clock */ + clk_enable(udc->usb_slv_clk); + + /* Enable clocking */ + udc_clk_set(udc, 1); + + /* ISP back to normal power mode */ + udc->poweron = 1; + isp1301_set_powerstate(udc, 1); + } + + return 0; +} +#else +#define lpc32xx_udc_suspend NULL +#define lpc32xx_udc_resume NULL +#endif + +#ifdef CONFIG_OF +static struct of_device_id lpc32xx_udc_of_match[] = { + { .compatible = "nxp,lpc3220-udc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); +#endif + +static struct platform_driver lpc32xx_udc_driver = { + .remove = lpc32xx_udc_remove, + .shutdown = lpc32xx_udc_shutdown, + .suspend = lpc32xx_udc_suspend, + .resume = lpc32xx_udc_resume, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lpc32xx_udc_of_match), + }, +}; + +module_platform_driver_probe(lpc32xx_udc_driver, lpc32xx_udc_probe); + +MODULE_DESCRIPTION("LPC32XX udc driver"); +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lpc32xx_udc"); diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 43dcf9e1af6..0d17174b86f 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -3,21 +3,11 @@ * * Copyright (C) 2006-2007 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -25,44 +15,19 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/platform_device.h> - +#include <linux/slab.h> +#include <linux/err.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include "m66592-udc.h" - MODULE_DESCRIPTION("M66592 USB gadget driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_ALIAS("platform:m66592_udc"); -#define DRIVER_VERSION "18 Oct 2007" - -/* module parameters */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) -static unsigned short endian = M66592_LITTLE; -module_param(endian, ushort, 0644); -MODULE_PARM_DESC(endian, "data endian: big=0, little=0 (default=0)"); -#else -static unsigned short clock = M66592_XTAL24; -module_param(clock, ushort, 0644); -MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 " - "(default=16384)"); - -static unsigned short vif = M66592_LDRV; -module_param(vif, ushort, 0644); -MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0 (default=32768)"); - -static unsigned short endian; -module_param(endian, ushort, 0644); -MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)"); - -static unsigned short irq_sense = M66592_INTL; -module_param(irq_sense, ushort, 0644); -MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0 " - "(default=2)"); -#endif +#define DRIVER_VERSION "21 July 2009" static const char udc_name[] = "m66592_udc"; static const char *m66592_ep_name[] = { @@ -244,6 +209,7 @@ static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum) static inline void pipe_change(struct m66592 *m66592, u16 pipenum) { struct m66592_ep *ep = m66592->pipenum2ep[pipenum]; + unsigned short mbw; if (ep->use_dma) return; @@ -252,7 +218,12 @@ static inline void pipe_change(struct m66592 *m66592, u16 pipenum) ndelay(450); - m66592_bset(m66592, M66592_MBW, ep->fifosel); + if (m66592->pdata->on_chip) + mbw = M66592_MBW_32; + else + mbw = M66592_MBW_16; + + m66592_bset(m66592, mbw, ep->fifosel); } static int pipe_buffer_setting(struct m66592 *m66592, @@ -276,24 +247,27 @@ static int pipe_buffer_setting(struct m66592 *m66592, buf_bsize = 0; break; case M66592_BULK: - bufnum = m66592->bi_bufnum + - (info->pipe - M66592_BASE_PIPENUM_BULK) * 16; - m66592->bi_bufnum += 16; + /* isochronous pipes may be used as bulk pipes */ + if (info->pipe >= M66592_BASE_PIPENUM_BULK) + bufnum = info->pipe - M66592_BASE_PIPENUM_BULK; + else + bufnum = info->pipe - M66592_BASE_PIPENUM_ISOC; + + bufnum = M66592_BASE_BUFNUM + (bufnum * 16); buf_bsize = 7; pipecfg |= M66592_DBLB; if (!info->dir_in) pipecfg |= M66592_SHTNAK; break; case M66592_ISO: - bufnum = m66592->bi_bufnum + + bufnum = M66592_BASE_BUFNUM + (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16; - m66592->bi_bufnum += 16; buf_bsize = 7; break; } - if (m66592->bi_bufnum > M66592_MAX_BUFNUM) { - pr_err("m66592 pipe memory is insufficient(%d)\n", - m66592->bi_bufnum); + + if (buf_bsize && ((bufnum + 16) >= M66592_MAX_BUFNUM)) { + pr_err("m66592 pipe memory is insufficient\n"); return -ENOMEM; } @@ -313,17 +287,6 @@ static void pipe_buffer_release(struct m66592 *m66592, if (info->pipe == 0) return; - switch (info->type) { - case M66592_BULK: - if (is_bulk_pipe(info->pipe)) - m66592->bi_bufnum -= 16; - break; - case M66592_ISO: - if (is_isoc_pipe(info->pipe)) - m66592->bi_bufnum -= 16; - break; - } - if (is_bulk_pipe(info->pipe)) { m66592->bulk--; } else if (is_interrupt_pipe(info->pipe)) @@ -340,6 +303,7 @@ static void pipe_buffer_release(struct m66592 *m66592, static void pipe_initialize(struct m66592_ep *ep) { struct m66592 *m66592 = ep->m66592; + unsigned short mbw; m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel); @@ -351,7 +315,12 @@ static void pipe_initialize(struct m66592_ep *ep) ndelay(450); - m66592_bset(m66592, M66592_MBW, ep->fifosel); + if (m66592->pdata->on_chip) + mbw = M66592_MBW_32; + else + mbw = M66592_MBW_16; + + m66592_bset(m66592, mbw, ep->fifosel); } } @@ -367,15 +336,13 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->fifosel = M66592_D0FIFOSEL; ep->fifoctr = M66592_D0FIFOCTR; ep->fifotrn = M66592_D0FIFOTRN; -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) - } else if (m66592->num_dma == 1) { + } else if (!m66592->pdata->on_chip && m66592->num_dma == 1) { m66592->num_dma++; ep->use_dma = 1; ep->fifoaddr = M66592_D1FIFO; ep->fifosel = M66592_D1FIFOSEL; ep->fifoctr = M66592_D1FIFOCTR; ep->fifotrn = M66592_D1FIFOTRN; -#endif } else { ep->use_dma = 0; ep->fifoaddr = M66592_CFIFO; @@ -393,7 +360,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); m66592->pipenum2ep[pipenum] = ep; m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; INIT_LIST_HEAD(&ep->queue); @@ -423,7 +390,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, int *counter; int ret; - ep->desc = desc; + ep->ep.desc = desc; BUG_ON(ep->pipenum); @@ -470,7 +437,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; @@ -591,7 +558,7 @@ static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req) static void start_packet(struct m66592_ep *ep, struct m66592_request *req) { - if (ep->desc->bEndpointAddress & USB_DIR_IN) + if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) start_packet_write(ep, req); else start_packet_read(ep, req); @@ -620,76 +587,121 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req) } } -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) static void init_controller(struct m66592 *m66592) { - m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ - m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); - m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); - m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + unsigned int endian; - /* This is a workaound for SH7722 2nd cut */ - m66592_bset(m66592, 0x8000, M66592_DVSTCTR); - m66592_bset(m66592, 0x1000, M66592_TESTMODE); - m66592_bclr(m66592, 0x8000, M66592_DVSTCTR); + if (m66592->pdata->on_chip) { + if (m66592->pdata->endian) + endian = 0; /* big endian */ + else + endian = M66592_LITTLE; /* little endian */ - m66592_bset(m66592, M66592_INTL, M66592_INTENB1); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); - m66592_write(m66592, 0, M66592_CFBCFG); - m66592_write(m66592, 0, M66592_D0FBCFG); - m66592_bset(m66592, endian, M66592_CFBCFG); - m66592_bset(m66592, endian, M66592_D0FBCFG); -} -#else /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ -static void init_controller(struct m66592 *m66592) -{ - m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND), - M66592_PINCFG); - m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ - m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG); + /* This is a workaound for SH7722 2nd cut */ + m66592_bset(m66592, 0x8000, M66592_DVSTCTR); + m66592_bset(m66592, 0x1000, M66592_TESTMODE); + m66592_bclr(m66592, 0x8000, M66592_DVSTCTR); - m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); - m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); - m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bset(m66592, M66592_INTL, M66592_INTENB1); + + m66592_write(m66592, 0, M66592_CFBCFG); + m66592_write(m66592, 0, M66592_D0FBCFG); + m66592_bset(m66592, endian, M66592_CFBCFG); + m66592_bset(m66592, endian, M66592_D0FBCFG); + } else { + unsigned int clock, vif, irq_sense; - m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + if (m66592->pdata->endian) + endian = M66592_BIGEND; /* big endian */ + else + endian = 0; /* little endian */ + + if (m66592->pdata->vif) + vif = M66592_LDRV; /* 3.3v */ + else + vif = 0; /* 1.5v */ + + switch (m66592->pdata->xtal) { + case M66592_PLATDATA_XTAL_12MHZ: + clock = M66592_XTAL12; + break; + case M66592_PLATDATA_XTAL_24MHZ: + clock = M66592_XTAL24; + break; + case M66592_PLATDATA_XTAL_48MHZ: + clock = M66592_XTAL48; + break; + default: + pr_warning("m66592-udc: xtal configuration error\n"); + clock = 0; + } - msleep(3); + switch (m66592->irq_trigger) { + case IRQF_TRIGGER_LOW: + irq_sense = M66592_INTL; + break; + case IRQF_TRIGGER_FALLING: + irq_sense = 0; + break; + default: + pr_warning("m66592-udc: irq trigger config error\n"); + irq_sense = 0; + } - m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + m66592_bset(m66592, + (vif & M66592_LDRV) | (endian & M66592_BIGEND), + M66592_PINCFG); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, + M66592_SYSCFG); + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); - msleep(1); + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); - m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + msleep(3); - m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); - m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, - M66592_DMA0CFG); + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + + msleep(1); + + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + + m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); + m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, + M66592_DMA0CFG); + } } -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ static void disable_controller(struct m66592 *m66592) { -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) - m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); - udelay(1); - m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); -#endif + m66592_bclr(m66592, M66592_UTST, M66592_TESTMODE); + if (!m66592->pdata->on_chip) { + m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); + } } static void m66592_start_xclock(struct m66592 *m66592) { -#if !defined(CONFIG_SUPERH_BUILT_IN_M66592) u16 tmp; - tmp = m66592_read(m66592, M66592_SYSCFG); - if (!(tmp & M66592_XCKE)) - m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); -#endif + if (!m66592->pdata->on_chip) { + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_XCKE)) + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + } } /*-------------------------------------------------------------------------*/ @@ -722,7 +734,7 @@ __acquires(m66592->lock) if (restart) { req = list_entry(ep->queue.next, struct m66592_request, queue); - if (ep->desc) + if (ep->ep.desc) start_packet(ep, req); } } @@ -759,7 +771,7 @@ static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req) /* write fifo */ if (req->req.buf) { if (size > 0) - m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + m66592_write_fifo(m66592, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) m66592_bset(m66592, M66592_BVAL, ep->fifoctr); } @@ -805,7 +817,7 @@ static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req) /* write fifo */ if (req->req.buf) { - m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + m66592_write_fifo(m66592, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || ((bufsize != ep->ep.maxpacket) @@ -905,7 +917,7 @@ static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb) ep = m66592->pipenum2ep[pipenum]; req = list_entry(ep->queue.next, struct m66592_request, queue); - if (ep->desc->bEndpointAddress & USB_DIR_IN) + if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) irq_packet_write(ep, req); else irq_packet_read(ep, req); @@ -1027,10 +1039,30 @@ static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) { + u16 tmp; + int timeout = 3000; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - control_end(m66592, 1); + switch (le16_to_cpu(ctrl->wValue)) { + case USB_DEVICE_TEST_MODE: + control_end(m66592, 1); + /* Wait for the completion of status stage */ + do { + tmp = m66592_read(m66592, M66592_INTSTS0) & + M66592_CTSQ; + udelay(1); + } while (tmp != M66592_CS_IDST || timeout-- > 0); + + if (tmp == M66592_CS_IDST) + m66592_bset(m66592, + le16_to_cpu(ctrl->wIndex >> 8), + M66592_TESTMODE); + break; + default: + pipe_stall(m66592, 0); + break; + } break; case USB_RECIP_INTERFACE: control_end(m66592, 1); @@ -1177,8 +1209,7 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) intsts0 = m66592_read(m66592, M66592_INTSTS0); intenb0 = m66592_read(m66592, M66592_INTENB0); -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - if (!intsts0 && !intenb0) { + if (m66592->pdata->on_chip && !intsts0 && !intenb0) { /* * When USB clock stops, it cannot read register. Even if a * clock stops, the interrupt occurs. So this driver turn on @@ -1188,7 +1219,6 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) intsts0 = m66592_read(m66592, M66592_INTSTS0); intenb0 = m66592_read(m66592, M66592_INTENB0); } -#endif savepipe = m66592_read(m66592, M66592_CFIFOSEL); @@ -1347,7 +1377,7 @@ static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, req->req.actual = 0; req->req.status = -EINPROGRESS; - if (ep->desc == NULL) /* control */ + if (ep->ep.desc == NULL) /* control */ start_ep0(ep, req); else { if (request && !ep->busy) @@ -1433,40 +1463,14 @@ static struct usb_ep_ops m66592_ep_ops = { }; /*-------------------------------------------------------------------------*/ -static struct m66592 *the_controller; - -int usb_gadget_register_driver(struct usb_gadget_driver *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->speed != USB_SPEED_HIGH - || !driver->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 = driver->bind (&m66592->gadget); - 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) { @@ -1479,41 +1483,24 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) } return 0; - -error: - m66592->driver = NULL; - m66592->gadget.dev.driver = NULL; - - return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); -int usb_gadget_unregister_driver(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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ static int m66592_get_frame(struct usb_gadget *_gadget) @@ -1522,22 +1509,42 @@ static int m66592_get_frame(struct usb_gadget *_gadget) return m66592_read(m66592, M66592_FRMNUM) & 0x03FF; } -static struct usb_gadget_ops m66592_gadget_ops = { +static int m66592_pullup(struct usb_gadget *gadget, int is_on) +{ + struct m66592 *m66592 = gadget_to_m66592(gadget); + unsigned long flags; + + spin_lock_irqsave(&m66592->lock, flags); + if (is_on) + m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG); + else + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + spin_unlock_irqrestore(&m66592->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops m66592_gadget_ops = { .get_frame = m66592_get_frame, + .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); del_timer_sync(&m66592->timer); iounmap(m66592->reg); free_irq(platform_get_irq(pdev, 0), m66592); m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - clk_disable(m66592->clk); - clk_put(m66592->clk); -#endif + if (m66592->pdata->on_chip) { + clk_disable(m66592->clk); + clk_put(m66592->clk); + } kfree(m66592); return 0; } @@ -1548,13 +1555,10 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) static int __init m66592_probe(struct platform_device *pdev) { - struct resource *res; - int irq; + struct resource *res, *ires; void __iomem *reg = NULL; struct m66592 *m66592 = NULL; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) char clk_name[8]; -#endif int ret = 0; int i; @@ -1565,10 +1569,11 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { ret = -ENODEV; - pr_err("platform_get_irq error.\n"); + dev_err(&pdev->dev, + "platform_get_resource IORESOURCE_IRQ error.\n"); goto clean_up; } @@ -1579,23 +1584,27 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } + if (dev_get_platdata(&pdev->dev) == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + ret = -ENODEV; + goto clean_up; + } + /* initialize ucd */ m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); if (m66592 == NULL) { - pr_err("kzalloc error\n"); + ret = -ENOMEM; goto clean_up; } + 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.is_dualspeed = 1; - m66592->gadget.dev.parent = &pdev->dev; - m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; - m66592->gadget.dev.release = pdev->dev.release; + m66592->gadget.max_speed = USB_SPEED_HIGH; m66592->gadget.name = udc_name; init_timer(&m66592->timer); @@ -1603,25 +1612,25 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->timer.data = (unsigned long)m66592; m66592->reg = reg; - m66592->bi_bufnum = M66592_BASE_BUFNUM; - - ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, udc_name, m66592); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); goto clean_up; } -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); - m66592->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(m66592->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - ret = PTR_ERR(m66592->clk); - goto clean_up2; + if (m66592->pdata->on_chip) { + snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id); + m66592->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(m66592->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", + clk_name); + ret = PTR_ERR(m66592->clk); + goto clean_up2; + } + clk_enable(m66592->clk); } - clk_enable(m66592->clk); -#endif + INIT_LIST_HEAD(&m66592->gadget.ep_list); m66592->gadget.ep0 = &m66592->ep[0].ep; INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); @@ -1637,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; @@ -1649,25 +1658,32 @@ 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); + ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget); + if (ret) + goto err_add_udc; + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; +err_add_udc: + m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); + clean_up3: -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) - clk_disable(m66592->clk); - clk_put(m66592->clk); + if (m66592->pdata->on_chip) { + clk_disable(m66592->clk); + clk_put(m66592->clk); + } clean_up2: -#endif - free_irq(irq, m66592); + free_irq(ires->start, m66592); clean_up: if (m66592) { if (m66592->ep0_req) @@ -1689,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 286ce07e796..96d49d7bfb6 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -3,29 +3,18 @@ * * Copyright (C) 2006-2007 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __M66592_UDC_H__ #define __M66592_UDC_H__ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) #include <linux/clk.h> -#endif +#include <linux/usb/m66592.h> #define M66592_SYSCFG 0x00 #define M66592_XTAL 0xC000 /* b15-14: Crystal selection */ @@ -76,11 +65,11 @@ #define M66592_P_TST_J 0x0001 /* PERI TEST J */ #define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) +/* built-in registers */ #define M66592_CFBCFG 0x0A #define M66592_D0FBCFG 0x0C #define M66592_LITTLE 0x0100 /* b8: Little endian mode */ -#else +/* external chip case */ #define M66592_PINCFG 0x0A #define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */ #define M66592_BIGEND 0x0100 /* b8: Big endian mode */ @@ -100,8 +89,8 @@ #define M66592_PKTM 0x0020 /* b5: Packet mode */ #define M66592_DENDE 0x0010 /* b4: Dend enable */ #define M66592_OBUS 0x0004 /* b2: OUTbus mode */ -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ +/* common case */ #define M66592_CFIFO 0x10 #define M66592_D0FIFO 0x14 #define M66592_D1FIFO 0x18 @@ -113,13 +102,9 @@ #define M66592_REW 0x4000 /* b14: Buffer rewind */ #define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */ #define M66592_DREQE 0x1000 /* b12: DREQ output enable */ -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) -#define M66592_MBW 0x0800 /* b11: Maximum bit width for FIFO */ -#else -#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO */ -#define M66592_MBW_8 0x0000 /* 8bit */ -#define M66592_MBW_16 0x0400 /* 16bit */ -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ +#define M66592_MBW_8 0x0000 /* 8bit */ +#define M66592_MBW_16 0x0400 /* 16bit */ +#define M66592_MBW_32 0x0800 /* 32bit */ #define M66592_TRENB 0x0200 /* b9: Transaction counter enable */ #define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */ #define M66592_DEZPM 0x0080 /* b7: Zero-length packet mode */ @@ -468,7 +453,7 @@ struct m66592_ep { unsigned use_dma:1; u16 pipenum; u16 type; - const struct usb_endpoint_descriptor *desc; + /* register address */ unsigned long fifoaddr; unsigned long fifosel; @@ -480,9 +465,9 @@ struct m66592_ep { struct m66592 { spinlock_t lock; void __iomem *reg; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK) struct clk *clk; -#endif + struct m66592_platdata *pdata; + unsigned long irq_trigger; struct usb_gadget gadget; struct usb_gadget_driver *driver; @@ -506,8 +491,8 @@ struct m66592 { int interrupt; int isochronous; int num_dma; - int bi_bufnum; /* bulk and isochronous's bufnum */ }; +#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) @@ -538,62 +523,28 @@ struct m66592 { /*-------------------------------------------------------------------------*/ static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset) { - return inw((unsigned long)m66592->reg + offset); + return ioread16(m66592->reg + offset); } static inline void m66592_read_fifo(struct m66592 *m66592, unsigned long offset, void *buf, unsigned long len) { - unsigned long fifoaddr = (unsigned long)m66592->reg + offset; - -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - len = (len + 3) / 4; - insl(fifoaddr, buf, len); -#else - len = (len + 1) / 2; - insw(fifoaddr, buf, len); -#endif + void __iomem *fifoaddr = m66592->reg + offset; + + if (m66592->pdata->on_chip) { + len = (len + 3) / 4; + ioread32_rep(fifoaddr, buf, len); + } else { + len = (len + 1) / 2; + ioread16_rep(fifoaddr, buf, len); + } } static inline void m66592_write(struct m66592 *m66592, u16 val, unsigned long offset) { - outw(val, (unsigned long)m66592->reg + offset); -} - -static inline void m66592_write_fifo(struct m66592 *m66592, - unsigned long offset, - void *buf, unsigned long len) -{ - unsigned long fifoaddr = (unsigned long)m66592->reg + offset; -#if defined(CONFIG_SUPERH_BUILT_IN_M66592) - unsigned long count; - unsigned char *pb; - int i; - - count = len / 4; - outsl(fifoaddr, buf, count); - - if (len & 0x00000003) { - pb = buf + count * 4; - for (i = 0; i < (len & 0x00000003); i++) { - if (m66592_read(m66592, M66592_CFBCFG)) /* little */ - outb(pb[i], fifoaddr + (3 - i)); - else - outb(pb[i], fifoaddr + i); - } - } -#else - unsigned long odd = len & 0x0001; - - len = len / 2; - outsw(fifoaddr, buf, len); - if (odd) { - unsigned char *p = buf + len*2; - outb(*p, fifoaddr); - } -#endif /* #if defined(CONFIG_SUPERH_BUILT_IN_M66592) */ + iowrite16(val, m66592->reg + offset); } static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, @@ -611,6 +562,45 @@ static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, #define m66592_bset(m66592, val, offset) \ m66592_mdfy(m66592, val, 0, offset) +static inline void m66592_write_fifo(struct m66592 *m66592, + struct m66592_ep *ep, + void *buf, unsigned long len) +{ + void __iomem *fifoaddr = m66592->reg + ep->fifoaddr; + + if (m66592->pdata->on_chip) { + unsigned long count; + unsigned char *pb; + int i; + + count = len / 4; + iowrite32_rep(fifoaddr, buf, count); + + if (len & 0x00000003) { + pb = buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (m66592_read(m66592, M66592_CFBCFG)) /* le */ + iowrite8(pb[i], fifoaddr + (3 - i)); + else + iowrite8(pb[i], fifoaddr + i); + } + } + } else { + unsigned long odd = len & 0x0001; + + len = len / 2; + iowrite16_rep(fifoaddr, buf, len); + if (odd) { + unsigned char *p = buf + len*2; + if (m66592->pdata->wr0_shorted_to_wr1) + m66592_bclr(m66592, M66592_MBW_16, ep->fifosel); + iowrite8(*p, fifoaddr); + if (m66592->pdata->wr0_shorted_to_wr1) + m66592_bset(m66592, M66592_MBW_16, ep->fifosel); + } + } +} + #endif /* ifndef __M66592_UDC_H__ */ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c new file mode 100644 index 00000000000..8e27a8c9644 --- /dev/null +++ b/drivers/usb/gadget/mass_storage.c @@ -0,0 +1,276 @@ +/* + * mass_storage.c -- Mass Storage USB Gadget + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz <mina86@mina86.com> + * All rights reserved. + * + * 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. + */ + + +/* + * The Mass Storage Gadget acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful gadget + * driver for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. Last but not least, it + * gives an easy way to probe the behavior of the Mass Storage drivers + * in a USB host. + * + * Since this file serves only administrative purposes and all the + * business logic is implemented in f_mass_storage.* file. Read + * comments in this file for more detailed description. + */ + + +#include <linux/kernel.h> +#include <linux/usb/ch9.h> +#include <linux/module.h> + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_DESC "Mass Storage Gadget" +#define DRIVER_VERSION "2009/09/11" + +/* + * 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 */ + +#include "f_mass_storage.h" + +/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct usb_device_descriptor msg_device_desc = { + .bLength = sizeof msg_device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(FSG_VENDOR_ID), + .idProduct = cpu_to_le16(FSG_PRODUCT_ID), + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static 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; +static void msg_cleanup(void); + +static int msg_thread_exits(struct fsg_common *common) +{ + msg_cleanup(); + return 0; +} + +static int __init msg_do_config(struct usb_configuration *c) +{ + struct fsg_opts *opts; + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + 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; + + return 0; + +put_func: + usb_put_function(f_msg); + return ret; +} + +static struct usb_configuration msg_config_driver = { + .label = "Linux File-Backed Storage", + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + + +/****************************** Gadget Bind ******************************/ + +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) + 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) + 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 ******************************/ + +static __refdata struct usb_composite_driver msg_driver = { + .name = "g_mass_storage", + .dev = &msg_device_desc, + .max_speed = USB_SPEED_SUPER, + .needs_serial = 1, + .strings = dev_strings, + .bind = msg_bind, + .unbind = msg_unbind, +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + +static int __init msg_init(void) +{ + return usb_composite_probe(&msg_driver); +} +module_init(msg_init); + +static void msg_cleanup(void) +{ + if (test_and_clear_bit(0, &msg_registered)) + usb_composite_unregister(&msg_driver); +} +module_exit(msg_cleanup); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c new file mode 100644 index 00000000000..940f6cde8e8 --- /dev/null +++ b/drivers/usb/gadget/multi.c @@ -0,0 +1,521 @@ +/* + * multi.c -- Multifunction Composite driver + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (mina86@mina86.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> +#include <linux/module.h> +#include <linux/netdevice.h> + +#include "u_serial.h" +#if defined USB_ETH_RNDIS +# undef USB_ETH_RNDIS +#endif +#ifdef CONFIG_USB_G_MULTI_RNDIS +# define USB_ETH_RNDIS y +#endif + + +#define DRIVER_DESC "Multifunction Composite Gadget" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); + + +#include "f_mass_storage.h" + +#include "u_ecm.h" +#ifdef USB_ETH_RNDIS +# include "u_rndis.h" +# include "rndis.h" +#endif +#include "u_ether.h" + +USB_GADGET_COMPOSITE_OPTIONS(); + +USB_ETHERNET_MODULE_PARAMETERS(); + +/***************************** Device Descriptor ****************************/ + +#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define MULTI_PRODUCT_NUM 0x0104 /* Multifunction Composite Gadget */ + + +enum { + __MULTI_NO_CONFIG, +#ifdef CONFIG_USB_G_MULTI_RNDIS + MULTI_RNDIS_CONFIG_NUM, +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + MULTI_CDC_CONFIG_NUM, +#endif +}; + + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), + .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), +}; + + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }, + NULL, +}; + + +enum { + MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX, + MULTI_STRING_CDC_CONFIG_IDX, +}; + +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS", + [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM", + { } /* end of list */ +}; + +static struct usb_gadget_strings *dev_strings[] = { + &(struct usb_gadget_strings){ + .language = 0x0409, /* en-us */ + .strings = strings_dev, + }, + NULL, +}; + + + + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_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 */, 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)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); + + ret = usb_add_function(c, f_rndis); + if (ret < 0) + goto err_func_rndis; + + 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 __ref int rndis_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + }; + + config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s; + config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id; + + return usb_add_config(cdev, &config, rndis_do_config); +} + +#else + +static __ref int rndis_config_register(struct usb_composite_dev *cdev) +{ + return 0; +} + +#endif + + +/********** 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)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) + return PTR_ERR(f_ecm); + + ret = usb_add_function(c, f_ecm); + if (ret < 0) + goto err_func_ecm; + + /* 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 __ref int cdc_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .bConfigurationValue = MULTI_CDC_CONFIG_NUM, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + }; + + config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s; + config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id; + + return usb_add_config(cdev, &config, cdc_do_config); +} + +#else + +static __ref int cdc_config_register(struct usb_composite_dev *cdev) +{ + return 0; +} + +#endif + + + +/****************************** 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); + return -EINVAL; + } + +#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 */ + fi_acm = usb_get_function_instance("acm"); + if (IS_ERR(fi_acm)) { + status = PTR_ERR(fi_acm); + goto fail0; + } + + /* set up mass storage function */ + 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 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 fail_string_ids; + + status = cdc_config_register(cdev); + if (unlikely(status < 0)) + goto fail_string_ids; + usb_composite_overwrite_options(cdev, &coverwrite); + + /* we're done */ + dev_info(&gadget->dev, DRIVER_DESC "\n"); + 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: + usb_put_function_instance(fi_msg); +fail1: + usb_put_function_instance(fi_acm); +fail0: +#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) +{ +#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; +} + + +/****************************** Some noise ******************************/ + + +static __refdata struct usb_composite_driver multi_driver = { + .name = "g_multi", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = multi_bind, + .unbind = __exit_p(multi_unbind), + .needs_serial = 1, +}; + + +static int __init multi_init(void) +{ + return usb_composite_probe(&multi_driver); +} +module_init(multi_init); + +static void __exit multi_exit(void) +{ + usb_composite_unregister(&multi_driver); +} +module_exit(multi_exit); diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/mv_u3d.h new file mode 100644 index 00000000000..e32a787ac37 --- /dev/null +++ b/drivers/usb/gadget/mv_u3d.h @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __MV_U3D_H +#define __MV_U3D_H + +#define MV_U3D_EP_CONTEXT_ALIGNMENT 32 +#define MV_U3D_TRB_ALIGNMENT 16 +#define MV_U3D_DMA_BOUNDARY 4096 +#define MV_U3D_EP0_MAX_PKT_SIZE 512 + +/* ep0 transfer state */ +#define MV_U3D_WAIT_FOR_SETUP 0 +#define MV_U3D_DATA_STATE_XMIT 1 +#define MV_U3D_DATA_STATE_NEED_ZLP 2 +#define MV_U3D_WAIT_FOR_OUT_STATUS 3 +#define MV_U3D_DATA_STATE_RECV 4 +#define MV_U3D_STATUS_STAGE 5 + +#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000 + +/* USB3 Interrupt Status */ +#define MV_U3D_USBINT_SETUP 0x00000001 +#define MV_U3D_USBINT_RX_COMPLETE 0x00000002 +#define MV_U3D_USBINT_TX_COMPLETE 0x00000004 +#define MV_U3D_USBINT_UNDER_RUN 0x00000008 +#define MV_U3D_USBINT_RXDESC_ERR 0x00000010 +#define MV_U3D_USBINT_TXDESC_ERR 0x00000020 +#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040 +#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080 +#define MV_U3D_USBINT_VBUS_VALID 0x00010000 +#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000 +#define MV_U3D_USBINT_LINK_CHG 0x01000000 + +/* USB3 Interrupt Enable */ +#define MV_U3D_INTR_ENABLE_SETUP 0x00000001 +#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002 +#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004 +#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008 +#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010 +#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020 +#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040 +#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080 +#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100 +#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000 +#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000 +#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000 +#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000 + +/* USB3 Link Change */ +#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001 +#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002 +#define MV_U3D_LINK_CHANGE_RESUME 0x00000004 +#define MV_U3D_LINK_CHANGE_WRESET 0x00000008 +#define MV_U3D_LINK_CHANGE_HRESET 0x00000010 +#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020 +#define MV_U3D_LINK_CHANGE_INACT 0x00000040 +#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080 +#define MV_U3D_LINK_CHANGE_U1 0x00000100 +#define MV_U3D_LINK_CHANGE_U2 0x00000200 +#define MV_U3D_LINK_CHANGE_U3 0x00000400 + +/* bridge setting */ +#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16) + +/* Command Register Bit Masks */ +#define MV_U3D_CMD_RUN_STOP 0x00000001 +#define MV_U3D_CMD_CTRL_RESET 0x00000002 + +/* ep control register */ +#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0 +#define MV_U3D_EPXCR_EP_TYPE_ISOC 1 +#define MV_U3D_EPXCR_EP_TYPE_BULK 2 +#define MV_U3D_EPXCR_EP_TYPE_INT 3 +#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4 +#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12 +#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16 +#define MV_U3D_USB_BULK_BURST_OUT 6 +#define MV_U3D_USB_BULK_BURST_IN 14 + +#define MV_U3D_EPXCR_EP_FLUSH (1 << 7) +#define MV_U3D_EPXCR_EP_HALT (1 << 1) +#define MV_U3D_EPXCR_EP_INIT (1) + +/* TX/RX Status Register */ +#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24 +#define MV_U3D_COMPLETE_INVALID 0 +#define MV_U3D_COMPLETE_SUCCESS 1 +#define MV_U3D_COMPLETE_BUFF_ERR 2 +#define MV_U3D_COMPLETE_SHORT_PACKET 3 +#define MV_U3D_COMPLETE_TRB_ERR 5 +#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF) + +#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8 + +#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000 +#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000 + +#define MV_U3D_USB3_OP_REGS_OFFSET 0x100 +#define MV_U3D_USB3_PHY_OFFSET 0xB800 + +#define DCS_ENABLE 0x1 + +/* timeout */ +#define MV_U3D_RESET_TIMEOUT 10000 +#define MV_U3D_FLUSH_TIMEOUT 100000 +#define MV_U3D_OWN_TIMEOUT 10000 +#define LOOPS_USEC_SHIFT 4 +#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) +#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) + +/* ep direction */ +#define MV_U3D_EP_DIR_IN 1 +#define MV_U3D_EP_DIR_OUT 0 +#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \ + ((ep)->u3d->ep0_dir) : ((ep)->direction)) + +/* usb capability registers */ +struct mv_u3d_cap_regs { + u32 rsvd[5]; + u32 dboff; /* doorbell register offset */ + u32 rtsoff; /* runtime register offset */ + u32 vuoff; /* vendor unique register offset */ +}; + +/* operation registers */ +struct mv_u3d_op_regs { + u32 usbcmd; /* Command register */ + u32 rsvd1[11]; + u32 dcbaapl; /* Device Context Base Address low register */ + u32 dcbaaph; /* Device Context Base Address high register */ + u32 rsvd2[243]; + u32 portsc; /* port status and control register*/ + u32 portlinkinfo; /* port link info register*/ + u32 rsvd3[9917]; + u32 doorbell; /* doorbell register */ +}; + +/* control enpoint enable registers */ +struct epxcr { + u32 epxoutcr0; /* ep out control 0 register */ + u32 epxoutcr1; /* ep out control 1 register */ + u32 epxincr0; /* ep in control 0 register */ + u32 epxincr1; /* ep in control 1 register */ +}; + +/* transfer status registers */ +struct xferstatus { + u32 curdeqlo; /* current TRB pointer low */ + u32 curdeqhi; /* current TRB pointer high */ + u32 statuslo; /* transfer status low */ + u32 statushi; /* transfer status high */ +}; + +/* vendor unique control registers */ +struct mv_u3d_vuc_regs { + u32 ctrlepenable; /* control endpoint enable register */ + u32 setuplock; /* setup lock register */ + u32 endcomplete; /* endpoint transfer complete register */ + u32 intrcause; /* interrupt cause register */ + u32 intrenable; /* interrupt enable register */ + u32 trbcomplete; /* TRB complete register */ + u32 linkchange; /* link change register */ + u32 rsvd1[5]; + u32 trbunderrun; /* TRB underrun register */ + u32 rsvd2[43]; + u32 bridgesetting; /* bridge setting register */ + u32 rsvd3[7]; + struct xferstatus txst[16]; /* TX status register */ + struct xferstatus rxst[16]; /* RX status register */ + u32 ltssm; /* LTSSM control register */ + u32 pipe; /* PIPE control register */ + u32 linkcr0; /* link control 0 register */ + u32 linkcr1; /* link control 1 register */ + u32 rsvd6[60]; + u32 mib0; /* MIB0 counter register */ + u32 usblink; /* usb link control register */ + u32 ltssmstate; /* LTSSM state register */ + u32 linkerrorcause; /* link error cause register */ + u32 rsvd7[60]; + u32 devaddrtiebrkr; /* device address and tie breaker */ + u32 itpinfo0; /* ITP info 0 register */ + u32 itpinfo1; /* ITP info 1 register */ + u32 rsvd8[61]; + struct epxcr epcr[16]; /* ep control register */ + u32 rsvd9[64]; + u32 phyaddr; /* PHY address register */ + u32 phydata; /* PHY data register */ +}; + +/* Endpoint context structure */ +struct mv_u3d_ep_context { + u32 rsvd0; + u32 rsvd1; + u32 trb_addr_lo; /* TRB address low 32 bit */ + u32 trb_addr_hi; /* TRB address high 32 bit */ + u32 rsvd2; + u32 rsvd3; + struct usb_ctrlrequest setup_buffer; /* setup data buffer */ +}; + +/* TRB control data structure */ +struct mv_u3d_trb_ctrl { + u32 own:1; /* owner of TRB */ + u32 rsvd1:3; + u32 chain:1; /* associate this TRB with the + next TRB on the Ring */ + u32 ioc:1; /* interrupt on complete */ + u32 rsvd2:4; + u32 type:6; /* TRB type */ +#define TYPE_NORMAL 1 +#define TYPE_DATA 3 +#define TYPE_LINK 6 + u32 dir:1; /* Working at data stage of control endpoint + operation. 0 is OUT and 1 is IN. */ + u32 rsvd3:15; +}; + +/* TRB data structure + * For multiple TRB, all the TRBs' physical address should be continuous. + */ +struct mv_u3d_trb_hw { + u32 buf_addr_lo; /* data buffer address low 32 bit */ + u32 buf_addr_hi; /* data buffer address high 32 bit */ + u32 trb_len; /* transfer length */ + struct mv_u3d_trb_ctrl ctrl; /* TRB control data */ +}; + +/* TRB structure */ +struct mv_u3d_trb { + struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */ + dma_addr_t trb_dma; /* dma address for this trb_hw */ + struct list_head trb_list; /* trb list */ +}; + +/* device data structure */ +struct mv_u3d { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; /* device lock */ + struct completion *done; + struct device *dev; + int irq; + + /* usb controller registers */ + struct mv_u3d_cap_regs __iomem *cap_regs; + struct mv_u3d_op_regs __iomem *op_regs; + struct mv_u3d_vuc_regs __iomem *vuc_regs; + void __iomem *phy_regs; + + unsigned int max_eps; + struct mv_u3d_ep_context *ep_context; + size_t ep_context_size; + dma_addr_t ep_context_dma; + + struct dma_pool *trb_pool; /* for TRB data structure */ + struct mv_u3d_ep *eps; + + struct mv_u3d_req *status_req; /* ep0 status request */ + struct usb_ctrlrequest local_setup_buff; /* store setup data*/ + + unsigned int resume_state; /* USB state to resume */ + unsigned int usb_state; /* USB current state */ + unsigned int ep0_state; /* Endpoint zero state */ + unsigned int ep0_dir; + + unsigned int dev_addr; /* device address */ + + unsigned int errors; + + unsigned softconnect:1; + unsigned vbus_active:1; /* vbus is active or not */ + unsigned remote_wakeup:1; /* support remote wakeup */ + unsigned clock_gating:1; /* clock gating or not */ + unsigned active:1; /* udc is active or not */ + unsigned vbus_valid_detect:1; /* udc vbus detection */ + + struct mv_usb_addon_irq *vbus; + unsigned int power; + + struct clk *clk; +}; + +/* endpoint data structure */ +struct mv_u3d_ep { + struct usb_ep ep; + struct mv_u3d *u3d; + struct list_head queue; /* ep request queued hardware */ + struct list_head req_list; /* list of ep request */ + struct mv_u3d_ep_context *ep_context; /* ep context */ + u32 direction; + char name[14]; + u32 processing; /* there is ep request + queued on haredware */ + spinlock_t req_lock; /* ep lock */ + unsigned wedge:1; + unsigned enabled:1; + unsigned ep_type:2; + unsigned ep_num:8; +}; + +/* request data structure */ +struct mv_u3d_req { + struct usb_request req; + struct mv_u3d_ep *ep; + struct list_head queue; /* ep requst queued on hardware */ + struct list_head list; /* ep request list */ + struct list_head trb_list; /* trb list of a request */ + + struct mv_u3d_trb *trb_head; /* point to first trb of a request */ + unsigned trb_count; /* TRB number in the chain */ + unsigned chain; /* TRB chain or not */ +}; + +#endif diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c new file mode 100644 index 00000000000..16248711c15 --- /dev/null +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -0,0 +1,2070 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/notifier.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/pm.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/platform_data/mv_usb.h> +#include <linux/clk.h> + +#include "mv_u3d.h" + +#define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver" + +static const char driver_name[] = "mv_u3d"; +static const char driver_desc[] = DRIVER_DESC; + +static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status); +static void mv_u3d_stop_activity(struct mv_u3d *u3d, + struct usb_gadget_driver *driver); + +/* for endpoint 0 operations */ +static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = MV_U3D_EP0_MAX_PKT_SIZE, +}; + +static void mv_u3d_ep0_reset(struct mv_u3d *u3d) +{ + struct mv_u3d_ep *ep; + u32 epxcr; + int i; + + for (i = 0; i < 2; i++) { + ep = &u3d->eps[i]; + ep->u3d = u3d; + + /* ep0 ep context, ep0 in and out share the same ep context */ + ep->ep_context = &u3d->ep_context[1]; + } + + /* reset ep state machine */ + /* reset ep0 out */ + epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); + + epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE + << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | MV_U3D_EPXCR_EP_TYPE_CONTROL); + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1); + + /* reset ep0 in */ + epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); + + epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE + << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | MV_U3D_EPXCR_EP_TYPE_CONTROL); + iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1); +} + +static void mv_u3d_ep0_stall(struct mv_u3d *u3d) +{ + u32 tmp; + dev_dbg(u3d->dev, "%s\n", __func__); + + /* set TX and RX to stall */ + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + tmp |= MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + + /* update ep0 state */ + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; +} + +static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, + struct mv_u3d_req *curr_req) +{ + 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 = 0; + int direction, ep_num; + int retval = 0; + u32 tmp, status, length; + + curr_ep_context = &u3d->ep_context[index]; + direction = index % 2; + ep_num = index / 2; + + trb_complete = 0; + actual = curr_req->req.length; + + while (!list_empty(&curr_req->trb_list)) { + curr_trb = list_entry(curr_req->trb_list.next, + struct mv_u3d_trb, trb_list); + if (!curr_trb->trb_hw->ctrl.own) { + dev_err(u3d->dev, "%s, TRB own error!\n", + u3d->eps[index].name); + return 1; + } + + curr_trb->trb_hw->ctrl.own = 0; + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo); + cur_deq_lo = + ioread32(&u3d->vuc_regs->rxst[ep_num].curdeqlo); + } else { + tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo); + cur_deq_lo = + ioread32(&u3d->vuc_regs->txst[ep_num].curdeqlo); + } + + status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT; + length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK; + + if (status == MV_U3D_COMPLETE_SUCCESS || + (status == MV_U3D_COMPLETE_SHORT_PACKET && + direction == MV_U3D_EP_DIR_OUT)) { + remaining_length += length; + actual -= remaining_length; + } else { + dev_err(u3d->dev, + "complete_tr error: ep=%d %s: error = 0x%x\n", + index >> 1, direction ? "SEND" : "RECV", + status); + retval = -EPROTO; + } + + list_del_init(&curr_trb->trb_list); + } + if (retval) + return retval; + + curr_req->req.actual = actual; + return 0; +} + +/* + * mv_u3d_done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + */ +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; + + dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n"); + /* Removed the req from ep queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free trb for the request */ + if (!req->chain) + dma_pool_free(u3d->trb_pool, + req->trb_head->trb_hw, req->trb_head->trb_dma); + else { + dma_unmap_single(ep->u3d->gadget.dev.parent, + (dma_addr_t)req->trb_head->trb_dma, + req->trb_count * sizeof(struct mv_u3d_trb_hw), + DMA_BIDIRECTIONAL); + kfree(req->trb_head->trb_hw); + } + kfree(req->trb_head); + + usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep)); + + if (status && (status != -ESHUTDOWN)) { + dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + spin_unlock(&ep->u3d->lock); + /* + * complete() is from gadget layer, + * eg fsg->bulk_in_complete() + */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->u3d->lock); +} + +static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req) +{ + u32 tmp, direction; + struct mv_u3d *u3d; + struct mv_u3d_ep_context *ep_context; + int retval = 0; + + u3d = ep->u3d; + direction = mv_u3d_ep_dir(ep); + + /* ep0 in and out share the same ep context slot 1*/ + if (ep->ep_num == 0) + ep_context = &(u3d->ep_context[1]); + else + ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]); + + /* check if the pipe is empty or not */ + if (!list_empty(&ep->queue)) { + dev_err(u3d->dev, "add trb to non-empty queue!\n"); + retval = -ENOMEM; + WARN_ON(1); + } else { + ep_context->rsvd0 = cpu_to_le32(1); + ep_context->rsvd1 = 0; + + /* Configure the trb address and set the DCS bit. + * Both DCS bit and own bit in trb should be set. + */ + ep_context->trb_addr_lo = + cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE); + ep_context->trb_addr_hi = 0; + + /* Ensure that updates to the EP Context will + * occure before Ring Bell. + */ + wmb(); + + /* ring bell the ep */ + if (ep->ep_num == 0) + tmp = 0x1; + else + tmp = ep->ep_num * 2 + + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1); + + iowrite32(tmp, &u3d->op_regs->doorbell); + } + return retval; +} + +static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, + unsigned *length, dma_addr_t *dma) +{ + u32 temp; + unsigned int direction; + struct mv_u3d_trb *trb; + struct mv_u3d_trb_hw *trb_hw; + struct mv_u3d *u3d; + + /* how big will this transfer be? */ + *length = req->req.length - req->req.actual; + BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); + + u3d = req->ep->u3d; + + trb = kzalloc(sizeof(*trb), GFP_ATOMIC); + if (!trb) + return NULL; + + /* + * Be careful that no _GFP_HIGHMEM is set, + * or we can not use dma_to_virt + * cannot use GFP_KERNEL in spin lock + */ + 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; + } + trb->trb_dma = *dma; + trb->trb_hw = trb_hw; + + /* initialize buffer page pointers */ + temp = (u32)(req->req.dma + req->req.actual); + + trb_hw->buf_addr_lo = cpu_to_le32(temp); + trb_hw->buf_addr_hi = 0; + trb_hw->trb_len = cpu_to_le32(*length); + trb_hw->ctrl.own = 1; + + if (req->ep->ep_num == 0) + trb_hw->ctrl.type = TYPE_DATA; + else + trb_hw->ctrl.type = TYPE_NORMAL; + + req->req.actual += *length; + + direction = mv_u3d_ep_dir(req->ep); + if (direction == MV_U3D_EP_DIR_IN) + trb_hw->ctrl.dir = 1; + else + trb_hw->ctrl.dir = 0; + + /* Enable interrupt for the last trb of a request */ + if (!req->req.no_interrupt) + trb_hw->ctrl.ioc = 1; + + trb_hw->ctrl.chain = 0; + + wmb(); + return trb; +} + +static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length, + struct mv_u3d_trb *trb, int *is_last) +{ + u32 temp; + unsigned int direction; + struct mv_u3d *u3d; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); + + u3d = req->ep->u3d; + + trb->trb_dma = 0; + + /* initialize buffer page pointers */ + temp = (u32)(req->req.dma + req->req.actual); + + trb->trb_hw->buf_addr_lo = cpu_to_le32(temp); + trb->trb_hw->buf_addr_hi = 0; + trb->trb_hw->trb_len = cpu_to_le32(*length); + trb->trb_hw->ctrl.own = 1; + + if (req->ep->ep_num == 0) + trb->trb_hw->ctrl.type = TYPE_DATA; + else + trb->trb_hw->ctrl.type = TYPE_NORMAL; + + req->req.actual += *length; + + direction = mv_u3d_ep_dir(req->ep); + if (direction == MV_U3D_EP_DIR_IN) + trb->trb_hw->ctrl.dir = 1; + else + trb->trb_hw->ctrl.dir = 0; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + /* Enable interrupt for the last trb of a request */ + if (*is_last && !req->req.no_interrupt) + trb->trb_hw->ctrl.ioc = 1; + + if (*is_last) + trb->trb_hw->ctrl.chain = 0; + else { + trb->trb_hw->ctrl.chain = 1; + dev_dbg(u3d->dev, "chain trb\n"); + } + + wmb(); + + return 0; +} + +/* generate TRB linked list for a request + * usb controller only supports continous trb chain, + * that trb structure physical address should be continous. + */ +static int mv_u3d_req_to_trb(struct mv_u3d_req *req) +{ + unsigned count; + int is_last; + struct mv_u3d_trb *trb; + struct mv_u3d_trb_hw *trb_hw; + struct mv_u3d *u3d; + dma_addr_t dma; + unsigned length; + unsigned trb_num; + + u3d = req->ep->u3d; + + INIT_LIST_HEAD(&req->trb_list); + + length = req->req.length - req->req.actual; + /* normally the request transfer length is less than 16KB. + * we use buil_trb_one() to optimize it. + */ + if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) { + trb = mv_u3d_build_trb_one(req, &count, &dma); + list_add_tail(&trb->trb_list, &req->trb_list); + req->trb_head = trb; + req->trb_count = 1; + req->chain = 0; + } else { + trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER; + if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER) + trb_num++; + + trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC); + if (!trb) + return -ENOMEM; + + trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); + if (!trb_hw) { + kfree(trb); + return -ENOMEM; + } + + do { + trb->trb_hw = trb_hw; + if (mv_u3d_build_trb_chain(req, &count, + trb, &is_last)) { + dev_err(u3d->dev, + "%s, mv_u3d_build_trb_chain fail\n", + __func__); + return -EIO; + } + + list_add_tail(&trb->trb_list, &req->trb_list); + req->trb_count++; + trb++; + trb_hw++; + } while (!is_last); + + req->trb_head = list_entry(req->trb_list.next, + struct mv_u3d_trb, trb_list); + req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent, + req->trb_head->trb_hw, + trb_num * sizeof(*trb_hw), + DMA_BIDIRECTIONAL); + + req->chain = 1; + } + + return 0; +} + +static int +mv_u3d_start_queue(struct mv_u3d_ep *ep) +{ + struct mv_u3d *u3d = ep->u3d; + struct mv_u3d_req *req; + int ret; + + if (!list_empty(&ep->req_list) && !ep->processing) + req = list_entry(ep->req_list.next, struct mv_u3d_req, list); + else + return 0; + + ep->processing = 1; + + /* set up dma mapping */ + ret = usb_gadget_map_request(&u3d->gadget, &req->req, + mv_u3d_ep_dir(ep)); + if (ret) + return ret; + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->trb_count = 0; + + /* build trbs and push them to device queue */ + if (!mv_u3d_req_to_trb(req)) { + ret = mv_u3d_queue_trb(ep, req); + if (ret) { + ep->processing = 0; + return ret; + } + } else { + ep->processing = 0; + dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__); + return -ENOMEM; + } + + /* irq handler advances the queue */ + if (req) + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static int mv_u3d_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct mv_u3d *u3d; + struct mv_u3d_ep *ep; + struct mv_u3d_ep_context *ep_context; + u16 max = 0; + unsigned maxburst = 0; + u32 epxcr, direction; + + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + direction = mv_u3d_ep_dir(ep); + max = le16_to_cpu(desc->wMaxPacketSize); + + if (!_ep->maxburst) + _ep->maxburst = 1; + maxburst = _ep->maxburst; + + /* Get the endpoint context address */ + ep_context = (struct mv_u3d_ep_context *)ep->ep_context; + + /* Set the max burst size */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (maxburst > 16) { + dev_dbg(u3d->dev, + "max burst should not be greater " + "than 16 on bulk ep\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + dev_dbg(u3d->dev, + "maxburst: %d on bulk %s\n", maxburst, ep->name); + break; + case USB_ENDPOINT_XFER_CONTROL: + /* control transfer only supports maxburst as one */ + maxburst = 1; + _ep->maxburst = maxburst; + break; + case USB_ENDPOINT_XFER_INT: + if (maxburst != 1) { + dev_dbg(u3d->dev, + "max burst should be 1 on int ep " + "if transfer size is not 1024\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (maxburst != 1) { + dev_dbg(u3d->dev, + "max burst should be 1 on isoc ep " + "if transfer size is not 1024\n"); + maxburst = 1; + _ep->maxburst = maxburst; + } + break; + default: + goto en_done; + } + + ep->ep.maxpacket = max; + ep->ep.desc = desc; + ep->enabled = 1; + + /* Enable the endpoint for Rx or Tx and set the endpoint type */ + if (direction == MV_U3D_EP_DIR_OUT) { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + + epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + } else { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + epxcr |= MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + udelay(5); + epxcr &= ~MV_U3D_EPXCR_EP_INIT; + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + + epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) + | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) + | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + } + + return 0; +en_done: + return -EINVAL; +} + +static int mv_u3d_ep_disable(struct usb_ep *_ep) +{ + struct mv_u3d *u3d; + struct mv_u3d_ep *ep; + struct mv_u3d_ep_context *ep_context; + u32 epxcr, direction; + unsigned long flags; + + if (!_ep) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + if (!ep->ep.desc) + return -EINVAL; + + u3d = ep->u3d; + + /* Get the endpoint context address */ + ep_context = ep->ep_context; + + 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) { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | USB_ENDPOINT_XFERTYPE_MASK); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); + } else { + epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) + | USB_ENDPOINT_XFERTYPE_MASK); + iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); + } + + ep->enabled = 0; + + ep->ep.desc = NULL; + return 0; +} + +static struct usb_request * +mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct mv_u3d_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req); + + kfree(req); +} + +static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep) +{ + struct mv_u3d *u3d; + u32 direction; + struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep); + unsigned int loops; + u32 tmp; + + /* if endpoint is not enabled, cannot flush endpoint */ + if (!ep->enabled) + return; + + u3d = ep->u3d; + direction = mv_u3d_ep_dir(ep); + + /* ep0 need clear bit after flushing fifo. */ + if (!ep->ep_num) { + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + udelay(10); + tmp &= ~MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); + } else { + tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + udelay(10); + tmp &= ~MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); + } + return; + } + + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + + /* Wait until flushing completed */ + loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); + while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) & + MV_U3D_EPXCR_EP_FLUSH) { + /* + * EP_FLUSH bit should be cleared to indicate this + * operation is complete + */ + if (loops == 0) { + dev_dbg(u3d->dev, + "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, + direction ? "in" : "out"); + return; + } + loops--; + udelay(LOOPS_USEC); + } + } else { /* EP_DIR_IN */ + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + tmp |= MV_U3D_EPXCR_EP_FLUSH; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + + /* Wait until flushing completed */ + loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); + while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) & + MV_U3D_EPXCR_EP_FLUSH) { + /* + * EP_FLUSH bit should be cleared to indicate this + * operation is complete + */ + if (loops == 0) { + dev_dbg(u3d->dev, + "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, + direction ? "in" : "out"); + return; + } + loops--; + udelay(LOOPS_USEC); + } + } +} + +/* queues (submits) an I/O request to an endpoint */ +static int +mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct mv_u3d_ep *ep; + struct mv_u3d_req *req; + struct mv_u3d *u3d; + unsigned long flags; + int is_first_req = 0; + + if (unlikely(!_ep || !_req)) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + req = container_of(_req, struct mv_u3d_req, req); + + if (!ep->ep_num + && u3d->ep0_state == MV_U3D_STATUS_STAGE + && !_req->length) { + dev_dbg(u3d->dev, "ep0 status stage\n"); + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + return 0; + } + + 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%p," + "req->req.complete: 0x%p, req->req.buf: 0x%p," + "list_empty: 0x%x\n", + __func__, _req, + req->req.complete, req->req.buf, + list_empty(&req->queue)); + return -EINVAL; + } + if (unlikely(!ep->ep.desc)) { + dev_err(u3d->dev, "%s, bad ep\n", __func__); + return -EINVAL; + } + if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + } + + if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(u3d->dev, + "bad params of driver/speed\n"); + return -ESHUTDOWN; + } + + req->ep = ep; + + /* Software list handles usb request. */ + spin_lock_irqsave(&ep->req_lock, flags); + is_first_req = list_empty(&ep->req_list); + list_add_tail(&req->list, &ep->req_list); + spin_unlock_irqrestore(&ep->req_lock, flags); + if (!is_first_req) { + dev_dbg(u3d->dev, "list is not empty\n"); + return 0; + } + + dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n"); + spin_lock_irqsave(&u3d->lock, flags); + mv_u3d_start_queue(ep); + spin_unlock_irqrestore(&u3d->lock, flags); + return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_u3d_ep *ep; + struct mv_u3d_req *req; + struct mv_u3d *u3d; + struct mv_u3d_ep_context *ep_context; + struct mv_u3d_req *next_req; + + unsigned long flags; + int ret = 0; + + if (!_ep || !_req) + return -EINVAL; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + + spin_lock_irqsave(&ep->u3d->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; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + mv_u3d_ep_fifo_flush(_ep); + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + dev_dbg(u3d->dev, + "it is the last request in this ep queue\n"); + ep_context = ep->ep_context; + next_req = list_entry(req->queue.next, + struct mv_u3d_req, queue); + + /* Point first TRB of next request to the EP context. */ + iowrite32((unsigned long) next_req->trb_head, + &ep_context->trb_addr_lo); + } else { + struct mv_u3d_ep_context *ep_context; + ep_context = ep->ep_context; + ep_context->trb_addr_lo = 0; + ep_context->trb_addr_hi = 0; + } + + } else + WARN_ON(1); + + mv_u3d_done(ep, req, -ECONNRESET); + + /* remove the req from the ep req list */ + if (!list_empty(&ep->req_list)) { + struct mv_u3d_req *curr_req; + curr_req = list_entry(ep->req_list.next, + struct mv_u3d_req, list); + if (curr_req == req) { + list_del_init(&req->list); + ep->processing = 0; + } + } + +out: + spin_unlock_irqrestore(&ep->u3d->lock, flags); + return ret; +} + +static void +mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall) +{ + u32 tmp; + struct mv_u3d_ep *ep = u3d->eps; + + dev_dbg(u3d->dev, "%s\n", __func__); + if (direction == MV_U3D_EP_DIR_OUT) { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + if (stall) + tmp |= MV_U3D_EPXCR_EP_HALT; + else + tmp &= ~MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); + } else { + tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + if (stall) + tmp |= MV_U3D_EPXCR_EP_HALT; + else + tmp &= ~MV_U3D_EPXCR_EP_HALT; + iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); + } +} + +static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) +{ + struct mv_u3d_ep *ep; + unsigned long flags = 0; + int status = 0; + struct mv_u3d *u3d; + + ep = container_of(_ep, struct mv_u3d_ep, ep); + u3d = ep->u3d; + if (!ep->ep.desc) { + status = -EINVAL; + goto out; + } + + if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* + * Attempt to halt IN ep will fail if any transfer requests + * are still queue + */ + if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN) + && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + spin_lock_irqsave(&ep->u3d->lock, flags); + mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt); + if (halt && wedge) + ep->wedge = 1; + else if (!halt) + ep->wedge = 0; + spin_unlock_irqrestore(&ep->u3d->lock, flags); + + if (ep->ep_num == 0) + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; +out: + return status; +} + +static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt) +{ + return mv_u3d_ep_set_halt_wedge(_ep, halt, 0); +} + +static int mv_u3d_ep_set_wedge(struct usb_ep *_ep) +{ + return mv_u3d_ep_set_halt_wedge(_ep, 1, 1); +} + +static struct usb_ep_ops mv_u3d_ep_ops = { + .enable = mv_u3d_ep_enable, + .disable = mv_u3d_ep_disable, + + .alloc_request = mv_u3d_alloc_request, + .free_request = mv_u3d_free_request, + + .queue = mv_u3d_ep_queue, + .dequeue = mv_u3d_ep_dequeue, + + .set_wedge = mv_u3d_ep_set_wedge, + .set_halt = mv_u3d_ep_set_halt, + .fifo_flush = mv_u3d_ep_fifo_flush, +}; + +static void mv_u3d_controller_stop(struct mv_u3d *u3d) +{ + u32 tmp; + + if (!u3d->clock_gating && u3d->vbus_valid_detect) + iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID, + &u3d->vuc_regs->intrenable); + else + iowrite32(0, &u3d->vuc_regs->intrenable); + iowrite32(~0x0, &u3d->vuc_regs->endcomplete); + iowrite32(~0x0, &u3d->vuc_regs->trbunderrun); + iowrite32(~0x0, &u3d->vuc_regs->trbcomplete); + iowrite32(~0x0, &u3d->vuc_regs->linkchange); + iowrite32(0x1, &u3d->vuc_regs->setuplock); + + /* Reset the RUN bit in the command register to stop USB */ + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); + dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n", + ioread32(&u3d->op_regs->usbcmd)); +} + +static void mv_u3d_controller_start(struct mv_u3d *u3d) +{ + u32 usbintr; + u32 temp; + + /* enable link LTSSM state machine */ + temp = ioread32(&u3d->vuc_regs->ltssm); + temp |= MV_U3D_LTSSM_PHY_INIT_DONE; + iowrite32(temp, &u3d->vuc_regs->ltssm); + + /* Enable interrupts */ + usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR | + MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE | + MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP | + (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0); + iowrite32(usbintr, &u3d->vuc_regs->intrenable); + + /* Enable ctrl ep */ + iowrite32(0x1, &u3d->vuc_regs->ctrlepenable); + + /* Set the Run bit in the command register */ + iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd); + dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n", + ioread32(&u3d->op_regs->usbcmd)); +} + +static int mv_u3d_controller_reset(struct mv_u3d *u3d) +{ + unsigned int loops; + u32 tmp; + + /* Stop the controller */ + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); + + /* Reset the controller to get default values */ + iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd); + + /* wait for reset to complete */ + loops = LOOPS(MV_U3D_RESET_TIMEOUT); + while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) { + if (loops == 0) { + dev_err(u3d->dev, + "Wait for RESET completed TIMEOUT\n"); + return -ETIMEDOUT; + } + loops--; + udelay(LOOPS_USEC); + } + + /* Configure the Endpoint Context Address */ + iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl); + iowrite32(0, &u3d->op_regs->dcbaaph); + + return 0; +} + +static int mv_u3d_enable(struct mv_u3d *u3d) +{ + struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); + int retval; + + if (u3d->active) + return 0; + + if (!u3d->clock_gating) { + u3d->active = 1; + return 0; + } + + dev_dbg(u3d->dev, "enable u3d\n"); + clk_enable(u3d->clk); + if (pdata->phy_init) { + retval = pdata->phy_init(u3d->phy_regs); + if (retval) { + dev_err(u3d->dev, + "init phy error %d\n", retval); + clk_disable(u3d->clk); + return retval; + } + } + u3d->active = 1; + + return 0; +} + +static void mv_u3d_disable(struct mv_u3d *u3d) +{ + 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) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + u3d->active = 0; + } +} + +static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_u3d *u3d; + unsigned long flags; + int retval = 0; + + u3d = container_of(gadget, struct mv_u3d, gadget); + + spin_lock_irqsave(&u3d->lock, flags); + + u3d->vbus_active = (is_active != 0); + dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, u3d->softconnect, u3d->vbus_active); + /* + * 1. external VBUS detect: we can disable/enable clock on demand. + * 2. UDC VBUS detect: we have to enable clock all the time. + * 3. No VBUS detect: we have to enable clock all the time. + */ + if (u3d->driver && u3d->softconnect && u3d->vbus_active) { + retval = mv_u3d_enable(u3d); + if (retval == 0) { + /* + * after clock is disabled, we lost all the register + * context. We have to re-init registers + */ + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } else if (u3d->driver && u3d->softconnect) { + if (!u3d->active) + goto out; + + /* stop all the transfer in queue*/ + mv_u3d_stop_activity(u3d, u3d->driver); + mv_u3d_controller_stop(u3d); + mv_u3d_disable(u3d); + } + +out: + spin_unlock_irqrestore(&u3d->lock, flags); + return retval; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); + + u3d->power = mA; + + return 0; +} + +static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on) +{ + struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&u3d->lock, flags); + + dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, u3d->softconnect, u3d->vbus_active); + u3d->softconnect = (is_on != 0); + if (u3d->driver && u3d->softconnect && u3d->vbus_active) { + retval = mv_u3d_enable(u3d); + if (retval == 0) { + /* + * after clock is disabled, we lost all the register + * context. We have to re-init registers + */ + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } else if (u3d->driver && u3d->vbus_active) { + /* stop all the transfer in queue*/ + mv_u3d_stop_activity(u3d, u3d->driver); + mv_u3d_controller_stop(u3d); + mv_u3d_disable(u3d); + } + + spin_unlock_irqrestore(&u3d->lock, flags); + + return retval; +} + +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 = dev_get_platdata(u3d->dev); + unsigned long flags; + + if (u3d->driver) + return -EBUSY; + + spin_lock_irqsave(&u3d->lock, flags); + + if (!u3d->clock_gating) { + clk_enable(u3d->clk); + if (pdata->phy_init) + pdata->phy_init(u3d->phy_regs); + } + + /* hook up the driver ... */ + driver->driver.bus = NULL; + u3d->driver = driver; + + u3d->ep0_dir = USB_DIR_OUT; + + spin_unlock_irqrestore(&u3d->lock, flags); + + u3d->vbus_valid_detect = 1; + + return 0; +} + +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 = dev_get_platdata(u3d->dev); + unsigned long flags; + + u3d->vbus_valid_detect = 0; + spin_lock_irqsave(&u3d->lock, flags); + + /* enable clock to access controller register */ + clk_enable(u3d->clk); + if (pdata->phy_init) + pdata->phy_init(u3d->phy_regs); + + mv_u3d_controller_stop(u3d); + /* stop all usb activities */ + u3d->gadget.speed = USB_SPEED_UNKNOWN; + mv_u3d_stop_activity(u3d, driver); + mv_u3d_disable(u3d); + + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + + spin_unlock_irqrestore(&u3d->lock, flags); + + u3d->driver = NULL; + + return 0; +} + +/* device controller usb_gadget_ops structure */ +static const struct usb_gadget_ops mv_u3d_ops = { + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_u3d_vbus_session, + + /* constrain controller's VBUS power usage */ + .vbus_draw = mv_u3d_vbus_draw, + + .pullup = mv_u3d_pullup, + .udc_start = mv_u3d_start, + .udc_stop = mv_u3d_stop, +}; + +static int mv_u3d_eps_init(struct mv_u3d *u3d) +{ + struct mv_u3d_ep *ep; + char name[14]; + int i; + + /* initialize ep0, ep0 in/out use eps[1] */ + ep = &u3d->eps[1]; + ep->u3d = u3d; + strncpy(ep->name, "ep0", sizeof(ep->name)); + ep->ep.name = ep->name; + ep->ep.ops = &mv_u3d_ep_ops; + ep->wedge = 0; + 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); + INIT_LIST_HEAD(&ep->req_list); + ep->ep_type = USB_ENDPOINT_XFER_CONTROL; + + /* add ep0 ep_context */ + ep->ep_context = &u3d->ep_context[1]; + + /* initialize other endpoints */ + for (i = 2; i < u3d->max_eps * 2; i++) { + ep = &u3d->eps[i]; + if (i & 1) { + snprintf(name, sizeof(name), "ep%din", i >> 1); + ep->direction = MV_U3D_EP_DIR_IN; + } else { + snprintf(name, sizeof(name), "ep%dout", i >> 1); + ep->direction = MV_U3D_EP_DIR_OUT; + } + ep->u3d = u3d; + strncpy(ep->name, name, sizeof(ep->name)); + ep->ep.name = ep->name; + + ep->ep.ops = &mv_u3d_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); + ep->ep_num = i / 2; + + INIT_LIST_HEAD(&ep->queue); + list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list); + + INIT_LIST_HEAD(&ep->req_list); + spin_lock_init(&ep->req_lock); + ep->ep_context = &u3d->ep_context[i]; + } + + return 0; +} + +/* delete all endpoint requests, called with spinlock held */ +static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status) +{ + /* endpoint fifo flush */ + mv_u3d_ep_fifo_flush(&ep->ep); + + while (!list_empty(&ep->queue)) { + struct mv_u3d_req *req = NULL; + req = list_entry(ep->queue.next, struct mv_u3d_req, queue); + mv_u3d_done(ep, req, status); + } +} + +/* stop all USB activities */ +static +void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver) +{ + struct mv_u3d_ep *ep; + + mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN); + + list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) { + mv_u3d_nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&u3d->lock); + driver->disconnect(&u3d->gadget); + spin_lock(&u3d->lock); + } +} + +static void mv_u3d_irq_process_error(struct mv_u3d *u3d) +{ + /* Increment the error count */ + u3d->errors++; + dev_err(u3d->dev, "%s\n", __func__); +} + +static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d) +{ + u32 linkchange; + + linkchange = ioread32(&u3d->vuc_regs->linkchange); + iowrite32(linkchange, &u3d->vuc_regs->linkchange); + + dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange); + + if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) { + dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n", + ioread32(&u3d->vuc_regs->ltssmstate)); + + u3d->usb_state = USB_STATE_DEFAULT; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; + u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; + + /* set speed */ + u3d->gadget.speed = USB_SPEED_SUPER; + } + + if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) { + dev_dbg(u3d->dev, "link suspend\n"); + u3d->resume_state = u3d->usb_state; + u3d->usb_state = USB_STATE_SUSPENDED; + } + + if (linkchange & MV_U3D_LINK_CHANGE_RESUME) { + dev_dbg(u3d->dev, "link resume\n"); + u3d->usb_state = u3d->resume_state; + u3d->resume_state = 0; + } + + if (linkchange & MV_U3D_LINK_CHANGE_WRESET) { + dev_dbg(u3d->dev, "warm reset\n"); + u3d->usb_state = USB_STATE_POWERED; + } + + if (linkchange & MV_U3D_LINK_CHANGE_HRESET) { + dev_dbg(u3d->dev, "hot reset\n"); + u3d->usb_state = USB_STATE_DEFAULT; + } + + if (linkchange & MV_U3D_LINK_CHANGE_INACT) + dev_dbg(u3d->dev, "inactive\n"); + + if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0) + dev_dbg(u3d->dev, "ss.disabled\n"); + + if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) { + dev_dbg(u3d->dev, "vbus invalid\n"); + u3d->usb_state = USB_STATE_ATTACHED; + u3d->vbus_valid_detect = 1; + /* if external vbus detect is not supported, + * we handle it here. + */ + if (!u3d->vbus) { + spin_unlock(&u3d->lock); + mv_u3d_vbus_session(&u3d->gadget, 0); + spin_lock(&u3d->lock); + } + } +} + +static void mv_u3d_ch9setaddress(struct mv_u3d *u3d, + struct usb_ctrlrequest *setup) +{ + u32 tmp; + + if (u3d->usb_state != USB_STATE_DEFAULT) { + dev_err(u3d->dev, + "%s, cannot setaddr in this state (%d)\n", + __func__, u3d->usb_state); + goto err; + } + + u3d->dev_addr = (u8)setup->wValue; + + dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr); + + if (u3d->dev_addr > 127) { + dev_err(u3d->dev, + "%s, u3d address is wrong (out of range)\n", __func__); + u3d->dev_addr = 0; + goto err; + } + + /* update usb state */ + u3d->usb_state = USB_STATE_ADDRESS; + + /* set the new address */ + tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr); + tmp &= ~0x7F; + tmp |= (u32)u3d->dev_addr; + iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr); + + return; +err: + mv_u3d_ep0_stall(u3d); +} + +static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) +{ + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + if (setup->bRequest == USB_REQ_SET_CONFIGURATION) + return 1; + + return 0; +} + +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; + + mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN); + + dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", + setup->bRequestType, setup->bRequest, + setup->wValue, setup->wIndex, setup->wLength); + + /* We process some stardard setup requests here */ + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + delegate = true; + break; + + case USB_REQ_SET_ADDRESS: + mv_u3d_ch9setaddress(u3d, setup); + break; + + case USB_REQ_CLEAR_FEATURE: + delegate = true; + break; + + case USB_REQ_SET_FEATURE: + delegate = true; + break; + + default: + delegate = true; + } + } else + delegate = true; + + /* delegate USB standard requests to the gadget driver */ + if (delegate == true) { + /* USB requests handled by gadget */ + if (setup->wLength) { + /* DATA phase from gadget, STATUS phase from u3d */ + u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT; + spin_unlock(&u3d->lock); + if (u3d->driver->setup(&u3d->gadget, + &u3d->local_setup_buff) < 0) { + dev_err(u3d->dev, "setup error!\n"); + mv_u3d_ep0_stall(u3d); + } + spin_lock(&u3d->lock); + } else { + /* no DATA phase, STATUS phase from gadget */ + u3d->ep0_dir = MV_U3D_EP_DIR_IN; + u3d->ep0_state = MV_U3D_STATUS_STAGE; + spin_unlock(&u3d->lock); + if (u3d->driver->setup(&u3d->gadget, + &u3d->local_setup_buff) < 0) + mv_u3d_ep0_stall(u3d); + spin_lock(&u3d->lock); + } + + if (mv_u3d_is_set_configuration(setup)) { + dev_dbg(u3d->dev, "u3d configured\n"); + u3d->usb_state = USB_STATE_CONFIGURED; + } + } +} + +static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr) +{ + struct mv_u3d_ep_context *epcontext; + + epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN]; + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8); +} + +static void mv_u3d_irq_process_setup(struct mv_u3d *u3d) +{ + u32 tmp, i; + /* Process all Setup packet received interrupts */ + tmp = ioread32(&u3d->vuc_regs->setuplock); + if (tmp) { + for (i = 0; i < u3d->max_eps; i++) { + if (tmp & (1 << i)) { + mv_u3d_get_setup_data(u3d, i, + (u8 *)(&u3d->local_setup_buff)); + mv_u3d_handle_setup_packet(u3d, i, + &u3d->local_setup_buff); + } + } + } + + iowrite32(tmp, &u3d->vuc_regs->setuplock); +} + +static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d) +{ + u32 tmp, bit_pos; + int i, ep_num = 0, direction = 0; + struct mv_u3d_ep *curr_ep; + struct mv_u3d_req *curr_req, *temp_req; + int status; + + tmp = ioread32(&u3d->vuc_regs->endcomplete); + + dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp); + if (!tmp) + return; + iowrite32(tmp, &u3d->vuc_regs->endcomplete); + + for (i = 0; i < u3d->max_eps * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_pos = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & tmp)) + continue; + + if (i == 0) + curr_ep = &u3d->eps[1]; + else + curr_ep = &u3d->eps[i]; + + /* remove req out of ep request list after completion */ + dev_dbg(u3d->dev, "tr comp: check req_list\n"); + spin_lock(&curr_ep->req_lock); + if (!list_empty(&curr_ep->req_list)) { + struct mv_u3d_req *req; + req = list_entry(curr_ep->req_list.next, + struct mv_u3d_req, list); + list_del_init(&req->list); + curr_ep->processing = 0; + } + spin_unlock(&curr_ep->req_lock); + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, + &curr_ep->queue, queue) { + status = mv_u3d_process_ep_req(u3d, i, curr_req); + if (status) + break; + /* write back status to req */ + curr_req->req.status = status; + + /* ep0 request completion */ + if (ep_num == 0) { + mv_u3d_done(curr_ep, curr_req, 0); + break; + } else { + mv_u3d_done(curr_ep, curr_req, status); + } + } + + dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n"); + mv_u3d_start_queue(curr_ep); + } +} + +static irqreturn_t mv_u3d_irq(int irq, void *dev) +{ + struct mv_u3d *u3d = (struct mv_u3d *)dev; + u32 status, intr; + u32 bridgesetting; + u32 trbunderrun; + + spin_lock(&u3d->lock); + + status = ioread32(&u3d->vuc_regs->intrcause); + intr = ioread32(&u3d->vuc_regs->intrenable); + status &= intr; + + if (status == 0) { + spin_unlock(&u3d->lock); + dev_err(u3d->dev, "irq error!\n"); + return IRQ_NONE; + } + + if (status & MV_U3D_USBINT_VBUS_VALID) { + bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting); + if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) { + /* write vbus valid bit of bridge setting to clear */ + bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID; + iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting); + dev_dbg(u3d->dev, "vbus valid\n"); + + u3d->usb_state = USB_STATE_POWERED; + u3d->vbus_valid_detect = 0; + /* if external vbus detect is not supported, + * we handle it here. + */ + if (!u3d->vbus) { + spin_unlock(&u3d->lock); + mv_u3d_vbus_session(&u3d->gadget, 1); + spin_lock(&u3d->lock); + } + } else + dev_err(u3d->dev, "vbus bit is not set\n"); + } + + /* RX data is already in the 16KB FIFO.*/ + if (status & MV_U3D_USBINT_UNDER_RUN) { + trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun); + dev_err(u3d->dev, "under run, ep%d\n", trbunderrun); + iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun); + mv_u3d_irq_process_error(u3d); + } + + if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) { + /* write one to clear */ + iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR + | MV_U3D_USBINT_TXDESC_ERR), + &u3d->vuc_regs->intrcause); + dev_err(u3d->dev, "desc err 0x%x\n", status); + mv_u3d_irq_process_error(u3d); + } + + if (status & MV_U3D_USBINT_LINK_CHG) + mv_u3d_irq_process_link_change(u3d); + + if (status & MV_U3D_USBINT_TX_COMPLETE) + mv_u3d_irq_process_tr_complete(u3d); + + if (status & MV_U3D_USBINT_RX_COMPLETE) + mv_u3d_irq_process_tr_complete(u3d); + + if (status & MV_U3D_USBINT_SETUP) + mv_u3d_irq_process_setup(u3d); + + spin_unlock(&u3d->lock); + return IRQ_HANDLED; +} + +static int mv_u3d_remove(struct platform_device *dev) +{ + struct mv_u3d *u3d = platform_get_drvdata(dev); + + BUG_ON(u3d == NULL); + + usb_del_gadget_udc(&u3d->gadget); + + /* free memory allocated in probe */ + if (u3d->trb_pool) + dma_pool_destroy(u3d->trb_pool); + + if (u3d->ep_context) + dma_free_coherent(&dev->dev, u3d->ep_context_size, + u3d->ep_context, u3d->ep_context_dma); + + kfree(u3d->eps); + + if (u3d->irq) + free_irq(u3d->irq, u3d); + + if (u3d->cap_regs) + iounmap(u3d->cap_regs); + u3d->cap_regs = NULL; + + kfree(u3d->status_req); + + clk_put(u3d->clk); + + kfree(u3d); + + return 0; +} + +static int mv_u3d_probe(struct platform_device *dev) +{ + struct mv_u3d *u3d = NULL; + struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); + int retval = 0; + struct resource *r; + size_t size; + + if (!dev_get_platdata(&dev->dev)) { + dev_err(&dev->dev, "missing platform_data\n"); + retval = -ENODEV; + goto err_pdata; + } + + u3d = kzalloc(sizeof(*u3d), GFP_KERNEL); + if (!u3d) { + retval = -ENOMEM; + goto err_alloc_private; + } + + spin_lock_init(&u3d->lock); + + platform_set_drvdata(dev, u3d); + + u3d->dev = &dev->dev; + u3d->vbus = pdata->vbus; + + u3d->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(u3d->clk)) { + retval = PTR_ERR(u3d->clk); + goto err_get_clk; + } + + r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs"); + if (!r) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + retval = -ENODEV; + goto err_get_cap_regs; + } + + u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *) + ioremap(r->start, resource_size(r)); + if (!u3d->cap_regs) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + retval = -EBUSY; + goto err_map_cap_regs; + } else { + 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 */ + clk_enable(u3d->clk); + + if (pdata->phy_init) { + retval = pdata->phy_init(u3d->phy_regs); + if (retval) { + dev_err(&dev->dev, "init phy error %d\n", retval); + goto err_u3d_enable; + } + } + + 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 *)(u3d->cap_regs + + ioread32(&u3d->cap_regs->vuoff)); + + u3d->max_eps = 16; + + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop u3d here. + */ + mv_u3d_controller_stop(u3d); + iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause); + + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); + + size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2; + size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1) + & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1); + u3d->ep_context = dma_alloc_coherent(&dev->dev, size, + &u3d->ep_context_dma, GFP_KERNEL); + if (!u3d->ep_context) { + dev_err(&dev->dev, "allocate ep context memory failed\n"); + retval = -ENOMEM; + goto err_alloc_ep_context; + } + u3d->ep_context_size = size; + + /* create TRB dma_pool resource */ + u3d->trb_pool = dma_pool_create("u3d_trb", + &dev->dev, + sizeof(struct mv_u3d_trb_hw), + MV_U3D_TRB_ALIGNMENT, + MV_U3D_DMA_BOUNDARY); + + if (!u3d->trb_pool) { + retval = -ENOMEM; + goto err_alloc_trb_pool; + } + + size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2; + u3d->eps = kzalloc(size, GFP_KERNEL); + if (!u3d->eps) { + retval = -ENOMEM; + goto err_alloc_eps; + } + + /* initialize ep0 status request structure */ + u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL); + if (!u3d->status_req) { + retval = -ENOMEM; + goto err_alloc_status_req; + } + INIT_LIST_HEAD(&u3d->status_req->queue); + + /* allocate a small amount of memory to get valid address */ + u3d->status_req->req.buf = (char *)u3d->status_req + + sizeof(struct mv_u3d_req); + u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf); + + u3d->resume_state = USB_STATE_NOTATTACHED; + u3d->usb_state = USB_STATE_ATTACHED; + u3d->ep0_dir = MV_U3D_EP_DIR_OUT; + u3d->remote_wakeup = 0; + + r = platform_get_resource(dev, IORESOURCE_IRQ, 0); + if (!r) { + dev_err(&dev->dev, "no IRQ resource defined\n"); + retval = -ENODEV; + goto err_get_irq; + } + u3d->irq = r->start; + if (request_irq(u3d->irq, mv_u3d_irq, + IRQF_SHARED, driver_name, u3d)) { + u3d->irq = 0; + dev_err(&dev->dev, "Request irq %d for u3d failed\n", + u3d->irq); + retval = -ENODEV; + goto err_request_irq; + } + + /* initialize gadget structure */ + u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */ + u3d->gadget.ep0 = &u3d->eps[1].ep; /* gadget ep0 */ + INIT_LIST_HEAD(&u3d->gadget.ep_list); /* ep_list */ + u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ + + /* the "gadget" abstracts/virtualizes the controller */ + u3d->gadget.name = driver_name; /* gadget name */ + + mv_u3d_eps_init(u3d); + + /* external vbus detection */ + if (u3d->vbus) { + u3d->clock_gating = 1; + dev_err(&dev->dev, "external vbus detection\n"); + } + + if (!u3d->clock_gating) + u3d->vbus_active = 1; + + /* enable usb3 controller vbus detection */ + u3d->vbus_valid_detect = 1; + + retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget); + if (retval) + goto err_unregister; + + dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n", + u3d->clock_gating ? "with" : "without"); + + return 0; + +err_unregister: + free_irq(u3d->irq, u3d); +err_request_irq: +err_get_irq: + kfree(u3d->status_req); +err_alloc_status_req: + kfree(u3d->eps); +err_alloc_eps: + dma_pool_destroy(u3d->trb_pool); +err_alloc_trb_pool: + dma_free_coherent(&dev->dev, u3d->ep_context_size, + u3d->ep_context, u3d->ep_context_dma); +err_alloc_ep_context: + if (pdata->phy_deinit) + pdata->phy_deinit(u3d->phy_regs); + clk_disable(u3d->clk); +err_u3d_enable: + iounmap(u3d->cap_regs); +err_map_cap_regs: +err_get_cap_regs: +err_get_clk: + clk_put(u3d->clk); + kfree(u3d); +err_alloc_private: +err_pdata: + return retval; +} + +#ifdef CONFIG_PM_SLEEP +static int mv_u3d_suspend(struct device *dev) +{ + struct mv_u3d *u3d = dev_get_drvdata(dev); + + /* + * only cable is unplugged, usb can suspend. + * So do not care about clock_gating == 1, it is handled by + * vbus session. + */ + if (!u3d->clock_gating) { + mv_u3d_controller_stop(u3d); + + spin_lock_irq(&u3d->lock); + /* stop all usb activities */ + mv_u3d_stop_activity(u3d, u3d->driver); + spin_unlock_irq(&u3d->lock); + + mv_u3d_disable(u3d); + } + + return 0; +} + +static int mv_u3d_resume(struct device *dev) +{ + struct mv_u3d *u3d = dev_get_drvdata(dev); + int retval; + + if (!u3d->clock_gating) { + retval = mv_u3d_enable(u3d); + if (retval) + return retval; + + if (u3d->driver && u3d->softconnect) { + mv_u3d_controller_reset(u3d); + mv_u3d_ep0_reset(u3d); + mv_u3d_controller_start(u3d); + } + } + + return 0; +} +#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 = platform_get_drvdata(dev); + u32 tmp; + + tmp = ioread32(&u3d->op_regs->usbcmd); + tmp &= ~MV_U3D_CMD_RUN_STOP; + iowrite32(tmp, &u3d->op_regs->usbcmd); +} + +static struct platform_driver mv_u3d_driver = { + .probe = mv_u3d_probe, + .remove = mv_u3d_remove, + .shutdown = mv_u3d_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "mv-u3d", + .pm = &mv_u3d_pm_ops, + }, +}; + +module_platform_driver(mv_u3d_driver); +MODULE_ALIAS("platform:mv-u3d"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h new file mode 100644 index 00000000000..be77f207dba --- /dev/null +++ b/drivers/usb/gadget/mv_udc.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * 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 __MV_UDC_H +#define __MV_UDC_H + +#define VUSBHS_MAX_PORTS 8 + +#define DQH_ALIGNMENT 2048 +#define DTD_ALIGNMENT 64 +#define DMA_BOUNDARY 4096 + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#define EP0_MAX_PKT_SIZE 64 +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +#define CAPLENGTH_MASK (0xff) +#define DCCPARAMS_DEN_MASK (0x1f) + +#define HCSPARAMS_PPC (0x10) + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_MASKS 0x3fff + +/* Command Register Bit Masks */ +#define USBCMD_RUN_STOP (0x00000001) +#define USBCMD_CTRL_RESET (0x00000002) +#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000) +#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET) + +#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000) +#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET) + +/* bit 15,3,2 are for frame list size */ +#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */ +#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */ +#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */ +#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */ +#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */ +#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */ +#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */ +#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */ + +#define EPCTRL_TX_ALL_MASK (0xFFFF0000) +#define EPCTRL_RX_ALL_MASK (0x0000FFFF) + +#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000) +#define EPCTRL_TX_EP_STALL (0x00010000) +#define EPCTRL_RX_EP_STALL (0x00000001) +#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040) +#define EPCTRL_RX_ENABLE (0x00000080) +#define EPCTRL_TX_ENABLE (0x00800000) +#define EPCTRL_CONTROL (0x00000000) +#define EPCTRL_ISOCHRONOUS (0x00040000) +#define EPCTRL_BULK (0x00080000) +#define EPCTRL_INT (0x000C0000) +#define EPCTRL_TX_TYPE (0x000C0000) +#define EPCTRL_RX_TYPE (0x0000000C) +#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020) +#define EPCTRL_TX_EP_TYPE_SHIFT (18) +#define EPCTRL_RX_EP_TYPE_SHIFT (2) + +#define EPCOMPLETE_MAX_ENDPOINTS (16) + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 + +#define PORTSCX_W1C_BITS 0x2a +#define PORTSCX_PORT_RESET 0x00000100 +#define PORTSCX_PORT_POWER 0x00001000 +#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000 +#define PORTSCX_PAR_XCVR_SELECT 0xC0000000 +#define PORTSCX_PORT_FORCE_RESUME 0x00000040 +#define PORTSCX_PORT_SUSPEND 0x00000080 +#define PORTSCX_PORT_SPEED_FULL 0x00000000 +#define PORTSCX_PORT_SPEED_LOW 0x04000000 +#define PORTSCX_PORT_SPEED_HIGH 0x08000000 +#define PORTSCX_PORT_SPEED_MASK 0x0C000000 + +/* USB MODE Register Bit Masks */ +#define USBMODE_CTRL_MODE_IDLE 0x00000000 +#define USBMODE_CTRL_MODE_DEVICE 0x00000002 +#define USBMODE_CTRL_MODE_HOST 0x00000003 +#define USBMODE_CTRL_MODE_RSV 0x00000001 +#define USBMODE_SETUP_LOCK_OFF 0x00000008 +#define USBMODE_STREAM_DISABLE 0x00000010 + +/* USB STS Register Bit Masks */ +#define USBSTS_INT 0x00000001 +#define USBSTS_ERR 0x00000002 +#define USBSTS_PORT_CHANGE 0x00000004 +#define USBSTS_FRM_LST_ROLL 0x00000008 +#define USBSTS_SYS_ERR 0x00000010 +#define USBSTS_IAA 0x00000020 +#define USBSTS_RESET 0x00000040 +#define USBSTS_SOF 0x00000080 +#define USBSTS_SUSPEND 0x00000100 +#define USBSTS_HC_HALTED 0x00001000 +#define USBSTS_RCL 0x00002000 +#define USBSTS_PERIODIC_SCHEDULE 0x00004000 +#define USBSTS_ASYNC_SCHEDULE 0x00008000 + + +/* Interrupt Enable Register Bit Masks */ +#define USBINTR_INT_EN (0x00000001) +#define USBINTR_ERR_INT_EN (0x00000002) +#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004) + +#define USBINTR_ASYNC_ADV_AAE (0x00000020) +#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020) +#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF) + +#define USBINTR_RESET_EN (0x00000040) +#define USBINTR_SOF_UFRAME_EN (0x00000080) +#define USBINTR_DEVICE_SUSPEND (0x00000100) + +#define USB_DEVICE_ADDRESS_MASK (0xfe000000) +#define USB_DEVICE_ADDRESS_BIT_SHIFT (25) + +struct mv_cap_regs { + u32 caplength_hciversion; + u32 hcsparams; /* HC structural parameters */ + u32 hccparams; /* HC Capability Parameters*/ + u32 reserved[5]; + u32 dciversion; /* DC version number and reserved 16 bits */ + u32 dccparams; /* DC Capability Parameters */ +}; + +struct mv_op_regs { + u32 usbcmd; /* Command register */ + u32 usbsts; /* Status register */ + u32 usbintr; /* Interrupt enable */ + u32 frindex; /* Frame index */ + u32 reserved1[1]; + u32 deviceaddr; /* Device Address */ + u32 eplistaddr; /* Endpoint List Address */ + u32 ttctrl; /* HOST TT status and control */ + u32 burstsize; /* Programmable Burst Size */ + u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ + u32 reserved[4]; + u32 epnak; /* Endpoint NAK */ + u32 epnaken; /* Endpoint NAK Enable */ + u32 configflag; /* Configured Flag register */ + u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ + u32 otgsc; + u32 usbmode; /* USB Host/Device mode */ + u32 epsetupstat; /* Endpoint Setup Status */ + u32 epprime; /* Endpoint Initialize */ + u32 epflush; /* Endpoint De-initialize */ + u32 epstatus; /* Endpoint Status */ + u32 epcomplete; /* Endpoint Interrupt On Complete */ + u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ + u32 mcr; /* Mux Control */ + u32 isr; /* Interrupt Status */ + u32 ier; /* Interrupt Enable */ +}; + +struct mv_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; + struct completion *done; + struct platform_device *dev; + int irq; + + struct mv_cap_regs __iomem *cap_regs; + struct mv_op_regs __iomem *op_regs; + void __iomem *phy_regs; + unsigned int max_eps; + struct mv_dqh *ep_dqh; + size_t ep_dqh_size; + dma_addr_t ep_dqh_dma; + + struct dma_pool *dtd_pool; + struct mv_ep *eps; + + struct mv_dtd *dtd_head; + struct mv_dtd *dtd_tail; + unsigned int dtd_entries; + + struct mv_req *status_req; + struct usb_ctrlrequest local_setup_buff; + + unsigned int resume_state; /* USB state to resume */ + unsigned int usb_state; /* USB current state */ + unsigned int ep0_state; /* Endpoint zero state */ + unsigned int ep0_dir; + + unsigned int dev_addr; + unsigned int test_mode; + + int errors; + unsigned softconnect:1, + vbus_active:1, + remote_wakeup:1, + softconnected:1, + force_fs:1, + clock_gating:1, + active:1, + stopped:1; /* stop bit is setted */ + + struct work_struct vbus_work; + struct workqueue_struct *qwork; + + struct usb_phy *transceiver; + + struct mv_usb_platform_data *pdata; + + /* some SOC has mutiple clock sources for USB*/ + struct clk *clk; +}; + +/* endpoint data structure */ +struct mv_ep { + struct usb_ep ep; + struct mv_udc *udc; + struct list_head queue; + struct mv_dqh *dqh; + u32 direction; + char name[14]; + unsigned stopped:1, + wedge:1, + ep_type:2, + ep_num:8; +}; + +/* request data structure */ +struct mv_req { + struct usb_request req; + struct mv_dtd *dtd, *head, *tail; + struct mv_ep *ep; + struct list_head queue; + unsigned int test_mode; + unsigned dtd_count; + unsigned mapped:1; +}; + +#define EP_QUEUE_HEAD_MULT_POS 30 +#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS 0x00008000 +#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 +#define EP_QUEUE_HEAD_IOC 0x00008000 +#define EP_QUEUE_HEAD_MULTO 0x00000C00 +#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 +#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 +#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 +#define EP_QUEUE_FRINDEX_MASK 0x000007FF +#define EP_MAX_LENGTH_TRANSFER 0x4000 + +struct mv_dqh { + /* Bits 16..26 Bit 15 is Interrupt On Setup */ + u32 max_packet_length; + u32 curr_dtd_ptr; /* Current dTD Pointer */ + u32 next_dtd_ptr; /* Next dTD Pointer */ + /* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */ + u32 size_ioc_int_sts; + u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */ + u32 reserved1; + /* 8 bytes of setup data that follows the Setup PID */ + u8 setup_buffer[8]; + u32 reserved2[4]; +}; + + +#define DTD_NEXT_TERMINATE (0x00000001) +#define DTD_IOC (0x00008000) +#define DTD_STATUS_ACTIVE (0x00000080) +#define DTD_STATUS_HALTED (0x00000040) +#define DTD_STATUS_DATA_BUFF_ERR (0x00000020) +#define DTD_STATUS_TRANSACTION_ERR (0x00000008) +#define DTD_RESERVED_FIELDS (0x00007F00) +#define DTD_ERROR_MASK (0x68) +#define DTD_ADDR_MASK (0xFFFFFFE0) +#define DTD_PACKET_SIZE 0x7FFF0000 +#define DTD_LENGTH_BIT_POS (16) + +struct mv_dtd { + u32 dtd_next; + u32 size_ioc_sts; + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 scratch_ptr; + /* 32 bytes */ + dma_addr_t td_dma; /* dma address for this td */ + struct mv_dtd *next_dtd_virt; +}; + +#endif diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c new file mode 100644 index 00000000000..fcff3a571b4 --- /dev/null +++ b/drivers/usb/gadget/mv_udc_core.c @@ -0,0 +1,2423 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie <chao.xie@marvell.com> + * Neil Zhang <zhangwm@marvell.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/module.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/pm.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/platform_data/mv_usb.h> +#include <asm/unaligned.h> + +#include "mv_udc.h" + +#define DRIVER_DESC "Marvell PXA USB Device Controller driver" +#define DRIVER_VERSION "8 Nov 2010" + +#define ep_dir(ep) (((ep)->ep_num == 0) ? \ + ((ep)->udc->ep0_dir) : ((ep)->direction)) + +/* timeout value -- usec */ +#define RESET_TIMEOUT 10000 +#define FLUSH_TIMEOUT 10000 +#define EPSTATUS_TIMEOUT 10000 +#define PRIME_TIMEOUT 10000 +#define READSAFE_TIMEOUT 1000 + +#define LOOPS_USEC_SHIFT 1 +#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) +#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) + +static DECLARE_COMPLETION(release_done); + +static const char driver_name[] = "mv_udc"; +static const char driver_desc[] = DRIVER_DESC; + +static void nuke(struct mv_ep *ep, int status); +static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); + +/* for endpoint 0 operations */ +static const struct usb_endpoint_descriptor mv_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = EP0_MAX_PKT_SIZE, +}; + +static void ep0_reset(struct mv_udc *udc) +{ + struct mv_ep *ep; + u32 epctrlx; + int i = 0; + + /* ep0 in and out */ + for (i = 0; i < 2; i++) { + ep = &udc->eps[i]; + ep->udc = udc; + + /* ep0 dQH */ + ep->dqh = &udc->ep_dqh[i]; + + /* configure ep0 endpoint capabilities in dQH */ + ep->dqh->max_packet_length = + (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + + ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; + + epctrlx = readl(&udc->op_regs->epctrlx[0]); + if (i) { /* TX */ + epctrlx |= EPCTRL_TX_ENABLE + | (USB_ENDPOINT_XFER_CONTROL + << EPCTRL_TX_EP_TYPE_SHIFT); + + } else { /* RX */ + epctrlx |= EPCTRL_RX_ENABLE + | (USB_ENDPOINT_XFER_CONTROL + << EPCTRL_RX_EP_TYPE_SHIFT); + } + + writel(epctrlx, &udc->op_regs->epctrlx[0]); + } +} + +/* protocol ep0 stall, will automatically be cleared on new transaction */ +static void ep0_stall(struct mv_udc *udc) +{ + u32 epctrlx; + + /* set TX and RX to stall */ + epctrlx = readl(&udc->op_regs->epctrlx[0]); + epctrlx |= EPCTRL_RX_EP_STALL | EPCTRL_TX_EP_STALL; + writel(epctrlx, &udc->op_regs->epctrlx[0]); + + /* update ep0 state */ + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = EP_DIR_OUT; +} + +static int process_ep_req(struct mv_udc *udc, int index, + struct mv_req *curr_req) +{ + struct mv_dtd *curr_dtd; + struct mv_dqh *curr_dqh; + int td_complete, actual, remaining_length; + int i, direction; + int retval = 0; + u32 errors; + u32 bit_pos; + + curr_dqh = &udc->ep_dqh[index]; + direction = index % 2; + + curr_dtd = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + for (i = 0; i < curr_req->dtd_count; i++) { + if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) { + dev_dbg(&udc->dev->dev, "%s, dTD not completed\n", + udc->eps[index].name); + return 1; + } + + errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; + if (!errors) { + remaining_length = + (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + + if (remaining_length) { + if (direction) { + dev_dbg(&udc->dev->dev, + "TX dTD remains data\n"); + retval = -EPROTO; + break; + } else + break; + } + } else { + dev_info(&udc->dev->dev, + "complete_tr error: ep=%d %s: error = 0x%x\n", + index >> 1, direction ? "SEND" : "RECV", + errors); + if (errors & DTD_STATUS_HALTED) { + /* Clear the errors and Halt condition */ + curr_dqh->size_ioc_int_sts &= ~errors; + retval = -EPIPE; + } else if (errors & DTD_STATUS_DATA_BUFF_ERR) { + retval = -EPROTO; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + retval = -EILSEQ; + } + } + if (i != curr_req->dtd_count - 1) + curr_dtd = (struct mv_dtd *)curr_dtd->next_dtd_virt; + } + if (retval) + return retval; + + if (direction == EP_DIR_OUT) + bit_pos = 1 << curr_req->ep->ep_num; + else + bit_pos = 1 << (16 + curr_req->ep->ep_num); + + while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) { + if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { + while (readl(&udc->op_regs->epstatus) & bit_pos) + udelay(1); + break; + } + udelay(1); + } + + curr_req->req.actual = actual; + + return 0; +} + +/* + * done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * 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; + struct mv_dtd *curr_td, *next_td; + int j; + + udc = (struct mv_udc *)ep->udc; + /* Removed the req from fsl_ep->queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free dtd for the request */ + next_td = req->head; + for (j = 0; j < req->dtd_count; j++) { + curr_td = next_td; + if (j != req->dtd_count - 1) + next_td = curr_td->next_dtd_virt; + dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma); + } + + 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", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + ep->stopped = 1; + + spin_unlock(&ep->udc->lock); + /* + * complete() is from gadget layer, + * eg fsg->bulk_in_complete() + */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +static int queue_dtd(struct mv_ep *ep, struct mv_req *req) +{ + struct mv_udc *udc; + struct mv_dqh *dqh; + u32 bit_pos, direction; + u32 usbcmd, epstatus; + unsigned int loops; + int retval = 0; + + udc = ep->udc; + direction = ep_dir(ep); + dqh = &(udc->ep_dqh[ep->ep_num * 2 + direction]); + bit_pos = 1 << (((direction == EP_DIR_OUT) ? 0 : 16) + ep->ep_num); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + struct mv_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct mv_req, queue); + lastreq->tail->dtd_next = + req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + + wmb(); + + if (readl(&udc->op_regs->epprime) & bit_pos) + goto done; + + loops = LOOPS(READSAFE_TIMEOUT); + while (1) { + /* start with setting the semaphores */ + usbcmd = readl(&udc->op_regs->usbcmd); + usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET; + writel(usbcmd, &udc->op_regs->usbcmd); + + /* read the endpoint status */ + epstatus = readl(&udc->op_regs->epstatus) & bit_pos; + + /* + * Reread the ATDTW semaphore bit to check if it is + * cleared. When hardware see a hazard, it will clear + * the bit or else we remain set to 1 and we can + * proceed with priming of endpoint if not already + * primed. + */ + if (readl(&udc->op_regs->usbcmd) + & USBCMD_ATDTW_TRIPWIRE_SET) + break; + + loops--; + if (loops == 0) { + dev_err(&udc->dev->dev, + "Timeout for ATDTW_TRIPWIRE...\n"); + retval = -ETIME; + goto done; + } + udelay(LOOPS_USEC); + } + + /* Clear the semaphore */ + usbcmd = readl(&udc->op_regs->usbcmd); + usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR; + writel(usbcmd, &udc->op_regs->usbcmd); + + if (epstatus) + goto done; + } + + /* Write dQH next pointer and terminate bit to 0 */ + dqh->next_dtd_ptr = req->head->td_dma + & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + + /* clear active and halt bit, in case set from a previous error */ + dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); + + /* Ensure that updates to the QH will occure before priming. */ + wmb(); + + /* Prime the Endpoint */ + writel(bit_pos, &udc->op_regs->epprime); + +done: + return retval; +} + +static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length, + dma_addr_t *dma, int *is_last) +{ + struct mv_dtd *dtd; + struct mv_udc *udc; + struct mv_dqh *dqh; + u32 temp, mult = 0; + + /* how big will this transfer be? */ + if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) { + dqh = req->ep->dqh; + mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS) + & 0x3; + *length = min(req->req.length - req->req.actual, + (unsigned)(mult * req->ep->ep.maxpacket)); + } else + *length = min(req->req.length - req->req.actual, + (unsigned)EP_MAX_LENGTH_TRANSFER); + + udc = req->ep->udc; + + /* + * Be careful that no _GFP_HIGHMEM is set, + * or we can not use dma_to_virt + */ + dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma); + if (dtd == NULL) + return dtd; + + dtd->td_dma = *dma; + /* initialize buffer page pointers */ + temp = (u32)(req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(temp); + temp &= ~0xFFF; + dtd->buff_ptr1 = cpu_to_le32(temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(temp + 0x4000); + + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + /* Fill in the transfer size; set active bit */ + temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + temp |= DTD_IOC; + + temp |= mult << 10; + + dtd->size_ioc_sts = temp; + + mb(); + + return dtd; +} + +/* generate dTD linked list for a request */ +static int req_to_dtd(struct mv_req *req) +{ + unsigned count; + int is_last, is_first = 1; + struct mv_dtd *dtd, *last_dtd = NULL; + struct mv_udc *udc; + dma_addr_t dma; + + udc = req->ep->udc; + + do { + dtd = build_dtd(req, &count, &dma, &is_last); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->dtd_next = dma; + last_dtd->next_dtd_virt = dtd; + } + last_dtd = dtd; + req->dtd_count++; + } while (!is_last); + + /* set terminate bit to 1 for the last dTD */ + dtd->dtd_next = DTD_NEXT_TERMINATE; + + req->tail = dtd; + + return 0; +} + +static int mv_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct mv_udc *udc; + struct mv_ep *ep; + struct mv_dqh *dqh; + u16 max = 0; + u32 bit_pos, epctrlx, direction; + unsigned char zlt = 0, ios = 0, mult = 0; + unsigned long flags; + + ep = container_of(_ep, struct mv_ep, ep); + udc = ep->udc; + + if (!_ep || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + direction = ep_dir(ep); + max = usb_endpoint_maxp(desc); + + /* + * disable HW zero length termination select + * driver handles zero length packet through req->req.zero + */ + zlt = 1; + + bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); + + /* Check if the Endpoint is Primed */ + if ((readl(&udc->op_regs->epprime) & bit_pos) + || (readl(&udc->op_regs->epstatus) & bit_pos)) { + dev_info(&udc->dev->dev, + "ep=%d %s: Init ERROR: ENDPTPRIME=0x%x," + " ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)ep->ep_num, direction ? "SEND" : "RECV", + (unsigned)readl(&udc->op_regs->epprime), + (unsigned)readl(&udc->op_regs->epstatus), + (unsigned)bit_pos); + goto en_done; + } + /* Set the max packet length, interrupt on Setup and Mult fields */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + zlt = 1; + mult = 0; + break; + case USB_ENDPOINT_XFER_CONTROL: + ios = 1; + case USB_ENDPOINT_XFER_INT: + mult = 0; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Calculate transactions needed for high bandwidth iso */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x7ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto en_done; + break; + default: + goto en_done; + } + + spin_lock_irqsave(&udc->lock, flags); + /* Get the endpoint queue head address */ + dqh = ep->dqh; + dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | (mult << EP_QUEUE_HEAD_MULT_POS) + | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) + | (ios ? EP_QUEUE_HEAD_IOS : 0); + dqh->next_dtd_ptr = 1; + dqh->size_ioc_int_sts = 0; + + ep->ep.maxpacket = max; + ep->ep.desc = desc; + ep->stopped = 0; + + /* Enable the endpoint for Rx or Tx and set the endpoint type */ + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + if (direction == EP_DIR_IN) { + epctrlx &= ~EPCTRL_TX_ALL_MASK; + epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST + | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + epctrlx &= ~EPCTRL_RX_ALL_MASK; + epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST + | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + << EPCTRL_RX_EP_TYPE_SHIFT); + } + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + + /* + * Implement Guideline (GL# USB-7) The unused endpoint type must + * be programmed to bulk. + */ + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + if ((epctrlx & EPCTRL_RX_ENABLE) == 0) { + epctrlx |= (USB_ENDPOINT_XFER_BULK + << EPCTRL_RX_EP_TYPE_SHIFT); + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + } + + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + if ((epctrlx & EPCTRL_TX_ENABLE) == 0) { + epctrlx |= (USB_ENDPOINT_XFER_BULK + << EPCTRL_TX_EP_TYPE_SHIFT); + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +en_done: + return -EINVAL; +} + +static int mv_ep_disable(struct usb_ep *_ep) +{ + struct mv_udc *udc; + struct mv_ep *ep; + struct mv_dqh *dqh; + u32 bit_pos, epctrlx, direction; + unsigned long flags; + + ep = container_of(_ep, struct mv_ep, ep); + if ((_ep == NULL) || !ep->ep.desc) + return -EINVAL; + + udc = ep->udc; + + /* Get the endpoint queue head address */ + dqh = ep->dqh; + + spin_lock_irqsave(&udc->lock, flags); + + direction = ep_dir(ep); + bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); + + /* Reset the max packet length and the interrupt on Setup */ + dqh->max_packet_length = 0; + + /* Disable the endpoint for Rx or Tx and reset the endpoint type */ + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + epctrlx &= ~((direction == EP_DIR_IN) + ? (EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE) + : (EPCTRL_RX_ENABLE | EPCTRL_RX_TYPE)); + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->ep.desc = NULL; + ep->stopped = 1; + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static struct usb_request * +mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct mv_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void mv_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_req *req = NULL; + + req = container_of(_req, struct mv_req, req); + + if (_req) + kfree(req); +} + +static void mv_ep_fifo_flush(struct usb_ep *_ep) +{ + struct mv_udc *udc; + u32 bit_pos, direction; + struct mv_ep *ep; + unsigned int loops; + + if (!_ep) + return; + + ep = container_of(_ep, struct mv_ep, ep); + if (!ep->ep.desc) + return; + + udc = ep->udc; + direction = ep_dir(ep); + + if (ep->ep_num == 0) + bit_pos = (1 << 16) | 1; + else if (direction == EP_DIR_OUT) + bit_pos = 1 << ep->ep_num; + else + bit_pos = 1 << (16 + ep->ep_num); + + loops = LOOPS(EPSTATUS_TIMEOUT); + do { + unsigned int inter_loops; + + if (loops == 0) { + dev_err(&udc->dev->dev, + "TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)readl(&udc->op_regs->epstatus), + (unsigned)bit_pos); + return; + } + /* Write 1 to the Flush register */ + writel(bit_pos, &udc->op_regs->epflush); + + /* Wait until flushing completed */ + inter_loops = LOOPS(FLUSH_TIMEOUT); + while (readl(&udc->op_regs->epflush)) { + /* + * ENDPTFLUSH bit should be cleared to indicate this + * operation is complete + */ + if (inter_loops == 0) { + dev_err(&udc->dev->dev, + "TIMEOUT for ENDPTFLUSH=0x%x," + "bit_pos=0x%x\n", + (unsigned)readl(&udc->op_regs->epflush), + (unsigned)bit_pos); + return; + } + inter_loops--; + udelay(LOOPS_USEC); + } + loops--; + } while (readl(&udc->op_regs->epstatus) & bit_pos); +} + +/* queues (submits) an I/O request to an endpoint */ +static int +mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); + struct mv_req *req = container_of(_req, struct mv_req, req); + struct mv_udc *udc = ep->udc; + unsigned long flags; + int retval; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + dev_err(&udc->dev->dev, "%s, bad params", __func__); + return -EINVAL; + } + if (unlikely(!_ep || !ep->ep.desc)) { + dev_err(&udc->dev->dev, "%s, bad ep", __func__); + return -EINVAL; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep)); + if (retval) + return retval; + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* build dtds and push them to device queue */ + if (!req_to_dtd(req)) { + retval = queue_dtd(ep, req); + if (retval) { + spin_unlock_irqrestore(&udc->lock, flags); + dev_err(&udc->dev->dev, "Failed to queue dtd\n"); + goto err_unmap_dma; + } + } else { + spin_unlock_irqrestore(&udc->lock, flags); + dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n"); + retval = -ENOMEM; + goto err_unmap_dma; + } + + /* Update ep0 state */ + if (ep->ep_num == 0) + udc->ep0_state = DATA_STATE_XMIT; + + /* irq handler advances the queue */ + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; + +err_unmap_dma: + usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep)); + + return retval; +} + +static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req) +{ + struct mv_dqh *dqh = ep->dqh; + u32 bit_pos; + + /* Write dQH next pointer and terminate bit to 0 */ + dqh->next_dtd_ptr = req->head->td_dma + & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + + /* clear active and halt bit, in case set from a previous error */ + dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); + + /* Ensure that updates to the QH will occure before priming. */ + wmb(); + + bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num); + + /* Prime the Endpoint */ + writel(bit_pos, &ep->udc->op_regs->epprime); +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); + struct mv_req *req; + struct mv_udc *udc = ep->udc; + unsigned long flags; + int stopped, ret = 0; + u32 epctrlx; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + stopped = ep->stopped; + + /* Stop the ep before we deal with the queue */ + ep->stopped = 1; + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + if (ep_dir(ep) == EP_DIR_IN) + epctrlx &= ~EPCTRL_TX_ENABLE; + else + epctrlx &= ~EPCTRL_RX_ENABLE; + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + + /* 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; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + mv_ep_fifo_flush(_ep); /* flush current transfer */ + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + struct mv_req *next_req; + + next_req = list_entry(req->queue.next, + struct mv_req, queue); + + /* Point the QH to the first TD of next request */ + mv_prime_ep(ep, next_req); + } else { + struct mv_dqh *qh; + + qh = ep->dqh; + qh->next_dtd_ptr = 1; + qh->size_ioc_int_sts = 0; + } + + /* The request hasn't been processed, patch up the TD chain */ + } else { + struct mv_req *prev_req; + + prev_req = list_entry(req->queue.prev, struct mv_req, queue); + writel(readl(&req->tail->dtd_next), + &prev_req->tail->dtd_next); + + } + + done(ep, req, -ECONNRESET); + + /* Enable EP */ +out: + epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); + if (ep_dir(ep) == EP_DIR_IN) + epctrlx |= EPCTRL_TX_ENABLE; + else + epctrlx |= EPCTRL_RX_ENABLE; + writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return ret; +} + +static void ep_set_stall(struct mv_udc *udc, u8 ep_num, u8 direction, int stall) +{ + u32 epctrlx; + + epctrlx = readl(&udc->op_regs->epctrlx[ep_num]); + + if (stall) { + if (direction == EP_DIR_IN) + epctrlx |= EPCTRL_TX_EP_STALL; + else + epctrlx |= EPCTRL_RX_EP_STALL; + } else { + if (direction == EP_DIR_IN) { + epctrlx &= ~EPCTRL_TX_EP_STALL; + epctrlx |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + epctrlx &= ~EPCTRL_RX_EP_STALL; + epctrlx |= EPCTRL_RX_DATA_TOGGLE_RST; + } + } + writel(epctrlx, &udc->op_regs->epctrlx[ep_num]); +} + +static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction) +{ + u32 epctrlx; + + epctrlx = readl(&udc->op_regs->epctrlx[ep_num]); + + if (direction == EP_DIR_OUT) + return (epctrlx & EPCTRL_RX_EP_STALL) ? 1 : 0; + else + return (epctrlx & EPCTRL_TX_EP_STALL) ? 1 : 0; +} + +static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) +{ + struct mv_ep *ep; + unsigned long flags = 0; + int status = 0; + struct mv_udc *udc; + + ep = container_of(_ep, struct mv_ep, ep); + udc = ep->udc; + if (!_ep || !ep->ep.desc) { + status = -EINVAL; + goto out; + } + + if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* + * Attempt to halt IN ep will fail if any transfer requests + * are still queue + */ + if (halt && (ep_dir(ep) == EP_DIR_IN) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + spin_lock_irqsave(&ep->udc->lock, flags); + ep_set_stall(udc, ep->ep_num, ep_dir(ep), halt); + if (halt && wedge) + ep->wedge = 1; + else if (!halt) + ep->wedge = 0; + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep->ep_num == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = EP_DIR_OUT; + } +out: + return status; +} + +static int mv_ep_set_halt(struct usb_ep *_ep, int halt) +{ + return mv_ep_set_halt_wedge(_ep, halt, 0); +} + +static int mv_ep_set_wedge(struct usb_ep *_ep) +{ + return mv_ep_set_halt_wedge(_ep, 1, 1); +} + +static struct usb_ep_ops mv_ep_ops = { + .enable = mv_ep_enable, + .disable = mv_ep_disable, + + .alloc_request = mv_alloc_request, + .free_request = mv_free_request, + + .queue = mv_ep_queue, + .dequeue = mv_ep_dequeue, + + .set_wedge = mv_ep_set_wedge, + .set_halt = mv_ep_set_halt, + .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ +}; + +static void udc_clock_enable(struct mv_udc *udc) +{ + clk_prepare_enable(udc->clk); +} + +static void udc_clock_disable(struct mv_udc *udc) +{ + clk_disable_unprepare(udc->clk); +} + +static void udc_stop(struct mv_udc *udc) +{ + u32 tmp; + + /* Disable interrupts */ + tmp = readl(&udc->op_regs->usbintr); + tmp &= ~(USBINTR_INT_EN | USBINTR_ERR_INT_EN | + USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN); + writel(tmp, &udc->op_regs->usbintr); + + udc->stopped = 1; + + /* Reset the Run the bit in the command register to stop VUSB */ + tmp = readl(&udc->op_regs->usbcmd); + tmp &= ~USBCMD_RUN_STOP; + writel(tmp, &udc->op_regs->usbcmd); +} + +static void udc_start(struct mv_udc *udc) +{ + u32 usbintr; + + usbintr = USBINTR_INT_EN | USBINTR_ERR_INT_EN + | USBINTR_PORT_CHANGE_DETECT_EN + | USBINTR_RESET_EN | USBINTR_DEVICE_SUSPEND; + /* Enable interrupts */ + writel(usbintr, &udc->op_regs->usbintr); + + udc->stopped = 0; + + /* Set the Run bit in the command register */ + writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd); +} + +static int udc_reset(struct mv_udc *udc) +{ + unsigned int loops; + u32 tmp, portsc; + + /* Stop the controller */ + tmp = readl(&udc->op_regs->usbcmd); + tmp &= ~USBCMD_RUN_STOP; + writel(tmp, &udc->op_regs->usbcmd); + + /* Reset the controller to get default values */ + writel(USBCMD_CTRL_RESET, &udc->op_regs->usbcmd); + + /* wait for reset to complete */ + loops = LOOPS(RESET_TIMEOUT); + while (readl(&udc->op_regs->usbcmd) & USBCMD_CTRL_RESET) { + if (loops == 0) { + dev_err(&udc->dev->dev, + "Wait for RESET completed TIMEOUT\n"); + return -ETIMEDOUT; + } + loops--; + udelay(LOOPS_USEC); + } + + /* set controller to device mode */ + tmp = readl(&udc->op_regs->usbmode); + tmp |= USBMODE_CTRL_MODE_DEVICE; + + /* turn setup lockout off, require setup tripwire in usbcmd */ + tmp |= USBMODE_SETUP_LOCK_OFF; + + writel(tmp, &udc->op_regs->usbmode); + + writel(0x0, &udc->op_regs->epsetupstat); + + /* Configure the Endpoint List Address */ + writel(udc->ep_dqh_dma & USB_EP_LIST_ADDRESS_MASK, + &udc->op_regs->eplistaddr); + + portsc = readl(&udc->op_regs->portsc[0]); + if (readl(&udc->cap_regs->hcsparams) & HCSPARAMS_PPC) + portsc &= (~PORTSCX_W1C_BITS | ~PORTSCX_PORT_POWER); + + if (udc->force_fs) + portsc |= PORTSCX_FORCE_FULL_SPEED_CONNECT; + else + portsc &= (~PORTSCX_FORCE_FULL_SPEED_CONNECT); + + writel(portsc, &udc->op_regs->portsc[0]); + + tmp = readl(&udc->op_regs->epctrlx[0]); + tmp &= ~(EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL); + writel(tmp, &udc->op_regs->epctrlx[0]); + + return 0; +} + +static int mv_udc_enable_internal(struct mv_udc *udc) +{ + int retval; + + if (udc->active) + return 0; + + dev_dbg(&udc->dev->dev, "enable udc\n"); + udc_clock_enable(udc); + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d\n", retval); + udc_clock_disable(udc); + return retval; + } + } + udc->active = 1; + + return 0; +} + +static int mv_udc_enable(struct mv_udc *udc) +{ + if (udc->clock_gating) + return mv_udc_enable_internal(udc); + + return 0; +} + +static void mv_udc_disable_internal(struct mv_udc *udc) +{ + if (udc->active) { + dev_dbg(&udc->dev->dev, "disable udc\n"); + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + udc->active = 0; + } +} + +static void mv_udc_disable(struct mv_udc *udc) +{ + if (udc->clock_gating) + mv_udc_disable_internal(udc); +} + +static int mv_udc_get_frame(struct usb_gadget *gadget) +{ + struct mv_udc *udc; + u16 retval; + + if (!gadget) + return -ENODEV; + + udc = container_of(gadget, struct mv_udc, gadget); + + retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS; + + return retval; +} + +/* Tries to wake up the host connected to this gadget */ +static int mv_udc_wakeup(struct usb_gadget *gadget) +{ + struct mv_udc *udc = container_of(gadget, struct mv_udc, gadget); + u32 portsc; + + /* Remote wakeup feature not enabled by host */ + if (!udc->remote_wakeup) + return -ENOTSUPP; + + portsc = readl(&udc->op_regs->portsc); + /* not suspended? */ + if (!(portsc & PORTSCX_PORT_SUSPEND)) + return 0; + /* trigger force resume */ + portsc |= PORTSCX_PORT_FORCE_RESUME; + writel(portsc, &udc->op_regs->portsc[0]); + return 0; +} + +static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_udc *udc; + unsigned long flags; + int retval = 0; + + udc = container_of(gadget, struct mv_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + udc->vbus_active = (is_active != 0); + + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->softconnect) { + if (!udc->active) + goto out; + + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); + udc_stop(udc); + mv_udc_disable(udc); + } + +out: + spin_unlock_irqrestore(&udc->lock, flags); + return retval; +} + +static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct mv_udc *udc; + unsigned long flags; + int retval = 0; + + udc = container_of(gadget, struct mv_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + udc->softconnect = (is_on != 0); + + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->vbus_active) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); + udc_stop(udc); + mv_udc_disable(udc); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return retval; +} + +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 = { + + /* returns the current frame number */ + .get_frame = mv_udc_get_frame, + + /* tries to wake up the host connected to this gadget */ + .wakeup = mv_udc_wakeup, + + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_udc_vbus_session, + + /* D+ pullup, software-controlled connect/disconnect to USB host */ + .pullup = mv_udc_pullup, + .udc_start = mv_udc_start, + .udc_stop = mv_udc_stop, +}; + +static int eps_init(struct mv_udc *udc) +{ + struct mv_ep *ep; + char name[14]; + int i; + + /* initialize ep0 */ + ep = &udc->eps[0]; + ep->udc = udc; + strncpy(ep->name, "ep0", sizeof(ep->name)); + ep->ep.name = ep->name; + ep->ep.ops = &mv_ep_ops; + ep->wedge = 0; + ep->stopped = 0; + 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); + + ep->ep_type = USB_ENDPOINT_XFER_CONTROL; + + /* initialize other endpoints */ + for (i = 2; i < udc->max_eps * 2; i++) { + ep = &udc->eps[i]; + if (i % 2) { + snprintf(name, sizeof(name), "ep%din", i / 2); + ep->direction = EP_DIR_IN; + } else { + snprintf(name, sizeof(name), "ep%dout", i / 2); + ep->direction = EP_DIR_OUT; + } + ep->udc = udc; + strncpy(ep->name, name, sizeof(ep->name)); + ep->ep.name = ep->name; + + ep->ep.ops = &mv_ep_ops; + ep->stopped = 0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); + ep->ep_num = i / 2; + + INIT_LIST_HEAD(&ep->queue); + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + + ep->dqh = &udc->ep_dqh[i]; + } + + return 0; +} + +/* delete all endpoint requests, called with spinlock held */ +static void nuke(struct mv_ep *ep, int status) +{ + /* called with spinlock held */ + ep->stopped = 1; + + /* endpoint fifo flush */ + mv_ep_fifo_flush(&ep->ep); + + while (!list_empty(&ep->queue)) { + struct mv_req *req = NULL; + req = list_entry(ep->queue.next, struct mv_req, queue); + done(ep, req, status); + } +} + +/* stop all USB activities */ +static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver) +{ + struct mv_ep *ep; + + nuke(&udc->eps[0], -ESHUTDOWN); + + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&udc->lock); + driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } +} + +static int mv_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct mv_udc *udc; + int retval = 0; + unsigned long flags; + + udc = container_of(gadget, struct mv_udc, gadget); + + if (udc->driver) + return -EBUSY; + + spin_lock_irqsave(&udc->lock, flags); + + /* hook up the driver ... */ + driver->driver.bus = NULL; + udc->driver = driver; + + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = EP_DIR_OUT; + + spin_unlock_irqrestore(&udc->lock, flags); + + 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"); + udc->driver = NULL; + return retval; + } + } + + /* pullup is always on */ + mv_udc_pullup(&udc->gadget, 1); + + /* When boot with cable attached, there will be no vbus irq occurred */ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); + + return 0; +} + +static int mv_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct mv_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct mv_udc, gadget); + + spin_lock_irqsave(&udc->lock, flags); + + mv_udc_enable(udc); + udc_stop(udc); + + /* stop all usb activities */ + udc->gadget.speed = USB_SPEED_UNKNOWN; + stop_activity(udc, driver); + mv_udc_disable(udc); + + spin_unlock_irqrestore(&udc->lock, flags); + + /* unbind gadget driver */ + udc->driver = NULL; + + return 0; +} + +static void mv_set_ptc(struct mv_udc *udc, u32 mode) +{ + u32 portsc; + + portsc = readl(&udc->op_regs->portsc[0]); + portsc |= mode << 16; + writel(portsc, &udc->op_regs->portsc[0]); +} + +static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) +{ + 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); + if (req->test_mode) { + mv_set_ptc(udc, req->test_mode); + req->test_mode = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); +} + +static int +udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) +{ + int retval = 0; + struct mv_req *req; + struct mv_ep *ep; + + ep = &udc->eps[0]; + udc->ep0_dir = direction; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + + req = udc->status_req; + + /* fill in the reqest structure */ + if (empty == false) { + *((u16 *) req->req.buf) = cpu_to_le16(status); + req->req.length = 2; + } else + req->req.length = 0; + + req->ep = ep; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + if (udc->test_mode) { + req->req.complete = prime_status_complete; + req->test_mode = udc->test_mode; + udc->test_mode = 0; + } else + req->req.complete = NULL; + req->dtd_count = 0; + + 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; + } + + /* prime the data phase */ + if (!req_to_dtd(req)) { + retval = queue_dtd(ep, req); + if (retval) { + dev_err(&udc->dev->dev, + "Failed to queue dtd when prime status\n"); + goto out; + } + } else{ /* no mem */ + retval = -ENOMEM; + dev_err(&udc->dev->dev, + "Failed to dma_pool_alloc when prime status\n"); + goto out; + } + + list_add_tail(&req->queue, &ep->queue); + + return 0; +out: + usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); + + return retval; +} + +static void mv_udc_testmode(struct mv_udc *udc, u16 index) +{ + if (index <= TEST_FORCE_EN) { + udc->test_mode = index; + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); + } else + dev_err(&udc->dev->dev, + "This test mode(%d) is not supported\n", index); +} + +static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) +{ + udc->dev_addr = (u8)setup->wValue; + + /* update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); +} + +static void ch9getstatus(struct mv_udc *udc, u8 ep_num, + struct usb_ctrlrequest *setup) +{ + u16 status = 0; + int retval; + + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + return; + + if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + status = 1 << USB_DEVICE_SELF_POWERED; + status |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + /* get interface status */ + status = 0; + } else if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_ENDPOINT) { + u8 ep_num, direction; + + ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; + direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) + ? EP_DIR_IN : EP_DIR_OUT; + status = ep_is_stall(udc, ep_num, direction) + << USB_ENDPOINT_HALT; + } + + retval = udc_prime_status(udc, EP_DIR_IN, status, false); + if (retval) + ep0_stall(udc); + else + udc->ep0_state = DATA_STATE_XMIT; +} + +static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) +{ + u8 ep_num; + u8 direction; + struct mv_ep *ep; + + if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) + == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { + switch (setup->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + udc->remote_wakeup = 0; + break; + default: + goto out; + } + } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) + == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { + switch (setup->wValue) { + case USB_ENDPOINT_HALT: + ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; + direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) + ? EP_DIR_IN : EP_DIR_OUT; + if (setup->wValue != 0 || setup->wLength != 0 + || ep_num > udc->max_eps) + goto out; + ep = &udc->eps[ep_num * 2 + direction]; + if (ep->wedge == 1) + break; + spin_unlock(&udc->lock); + ep_set_stall(udc, ep_num, direction, 0); + spin_lock(&udc->lock); + break; + default: + goto out; + } + } else + goto out; + + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); +out: + return; +} + +static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) +{ + u8 ep_num; + u8 direction; + + if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) + == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { + switch (setup->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + udc->remote_wakeup = 1; + break; + case USB_DEVICE_TEST_MODE: + if (setup->wIndex & 0xFF + || udc->gadget.speed != USB_SPEED_HIGH) + ep0_stall(udc); + + if (udc->usb_state != USB_STATE_CONFIGURED + && udc->usb_state != USB_STATE_ADDRESS + && udc->usb_state != USB_STATE_DEFAULT) + ep0_stall(udc); + + mv_udc_testmode(udc, (setup->wIndex >> 8)); + goto out; + default: + goto out; + } + } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) + == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { + switch (setup->wValue) { + case USB_ENDPOINT_HALT: + ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; + direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) + ? EP_DIR_IN : EP_DIR_OUT; + if (setup->wValue != 0 || setup->wLength != 0 + || ep_num > udc->max_eps) + goto out; + spin_unlock(&udc->lock); + ep_set_stall(udc, ep_num, direction, 1); + spin_lock(&udc->lock); + break; + default: + goto out; + } + } else + goto out; + + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); +out: + return; +} + +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; + + nuke(&udc->eps[ep_num * 2 + EP_DIR_OUT], -ESHUTDOWN); + + dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", + setup->bRequestType, setup->bRequest, + setup->wValue, setup->wIndex, setup->wLength); + /* We process some stardard setup requests here */ + if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + ch9getstatus(udc, ep_num, setup); + break; + + case USB_REQ_SET_ADDRESS: + ch9setaddress(udc, setup); + break; + + case USB_REQ_CLEAR_FEATURE: + ch9clearfeature(udc, setup); + break; + + case USB_REQ_SET_FEATURE: + ch9setfeature(udc, setup); + break; + + default: + delegate = true; + } + } else + delegate = true; + + /* delegate USB standard requests to the gadget driver */ + if (delegate == true) { + /* USB requests handled by gadget */ + if (setup->wLength) { + /* DATA phase from gadget, STATUS phase from udc */ + udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? EP_DIR_IN : EP_DIR_OUT; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0_stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + } else { + /* no DATA phase, IN STATUS phase from gadget */ + udc->ep0_dir = EP_DIR_IN; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0_stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = WAIT_FOR_OUT_STATUS; + } + } +} + +/* complete DATA or STATUS phase of ep0 prime status phase if needed */ +static void ep0_req_complete(struct mv_udc *udc, + struct mv_ep *ep0, struct mv_req *req) +{ + u32 new_addr; + + if (udc->usb_state == USB_STATE_ADDRESS) { + /* set the new address */ + new_addr = (u32)udc->dev_addr; + writel(new_addr << USB_DEVICE_ADDRESS_BIT_SHIFT, + &udc->op_regs->deviceaddr); + } + + done(ep0, req, 0); + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (udc_prime_status(udc, EP_DIR_OUT, 0, true)) + ep0_stall(udc); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (udc_prime_status(udc, EP_DIR_IN, 0 , true)) + ep0_stall(udc); + break; + case WAIT_FOR_OUT_STATUS: + udc->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + dev_err(&udc->dev->dev, "unexpect ep0 packets\n"); + break; + default: + ep0_stall(udc); + break; + } +} + +static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr) +{ + u32 temp; + struct mv_dqh *dqh; + + dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ + writel((1 << ep_num), &udc->op_regs->epsetupstat); + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = readl(&udc->op_regs->usbcmd); + writel(temp | USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd); + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) dqh->setup_buffer, 8); + } while (!(readl(&udc->op_regs->usbcmd) & USBCMD_SETUP_TRIPWIRE_SET)); + + /* Clear Setup Tripwire */ + temp = readl(&udc->op_regs->usbcmd); + writel(temp & ~USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd); +} + +static void irq_process_tr_complete(struct mv_udc *udc) +{ + u32 tmp, bit_pos; + int i, ep_num = 0, direction = 0; + struct mv_ep *curr_ep; + struct mv_req *curr_req, *temp_req; + int status; + + /* + * We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE + * because the setup packets are to be read ASAP + */ + + /* Process all Setup packet received interrupts */ + tmp = readl(&udc->op_regs->epsetupstat); + + if (tmp) { + for (i = 0; i < udc->max_eps; i++) { + if (tmp & (1 << i)) { + get_setup_data(udc, i, + (u8 *)(&udc->local_setup_buff)); + handle_setup_packet(udc, i, + &udc->local_setup_buff); + } + } + } + + /* Don't clear the endpoint setup status register here. + * It is cleared as a setup packet is read out of the buffer + */ + + /* Process non-setup transaction complete interrupts */ + tmp = readl(&udc->op_regs->epcomplete); + + if (!tmp) + return; + + writel(tmp, &udc->op_regs->epcomplete); + + for (i = 0; i < udc->max_eps * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_pos = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & tmp)) + continue; + + if (i == 1) + curr_ep = &udc->eps[0]; + else + curr_ep = &udc->eps[i]; + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, + &curr_ep->queue, queue) { + status = process_ep_req(udc, i, curr_req); + if (status) + break; + + /* write back status to req */ + curr_req->req.status = status; + + /* ep0 request completion */ + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else { + done(curr_ep, curr_req, status); + } + } + } +} + +static void irq_process_reset(struct mv_udc *udc) +{ + u32 tmp; + unsigned int loops; + + udc->ep0_dir = EP_DIR_OUT; + udc->ep0_state = WAIT_FOR_SETUP; + udc->remote_wakeup = 0; /* default to 0 on reset */ + + /* The address bits are past bit 25-31. Set the address */ + tmp = readl(&udc->op_regs->deviceaddr); + tmp &= ~(USB_DEVICE_ADDRESS_MASK); + writel(tmp, &udc->op_regs->deviceaddr); + + /* Clear all the setup token semaphores */ + tmp = readl(&udc->op_regs->epsetupstat); + writel(tmp, &udc->op_regs->epsetupstat); + + /* Clear all the endpoint complete status bits */ + tmp = readl(&udc->op_regs->epcomplete); + writel(tmp, &udc->op_regs->epcomplete); + + /* wait until all endptprime bits cleared */ + loops = LOOPS(PRIME_TIMEOUT); + while (readl(&udc->op_regs->epprime) & 0xFFFFFFFF) { + if (loops == 0) { + dev_err(&udc->dev->dev, + "Timeout for ENDPTPRIME = 0x%x\n", + readl(&udc->op_regs->epprime)); + break; + } + loops--; + udelay(LOOPS_USEC); + } + + /* Write 1s to the Flush register */ + writel((u32)~0, &udc->op_regs->epflush); + + if (readl(&udc->op_regs->portsc[0]) & PORTSCX_PORT_RESET) { + dev_info(&udc->dev->dev, "usb bus reset\n"); + udc->usb_state = USB_STATE_DEFAULT; + /* reset all the queues, stop all USB activities */ + stop_activity(udc, udc->driver); + } else { + dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n", + readl(&udc->op_regs->portsc)); + + /* + * re-initialize + * controller reset + */ + udc_reset(udc); + + /* reset all the queues, stop all USB activities */ + stop_activity(udc, udc->driver); + + /* reset ep0 dQH and endptctrl */ + ep0_reset(udc); + + /* enable interrupt and set controller to run state */ + udc_start(udc); + + udc->usb_state = USB_STATE_ATTACHED; + } +} + +static void handle_bus_resume(struct mv_udc *udc) +{ + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver */ + if (udc->driver) { + if (udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } +} + +static void irq_process_suspend(struct mv_udc *udc) +{ + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + if (udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } +} + +static void irq_process_port_change(struct mv_udc *udc) +{ + u32 portsc; + + portsc = readl(&udc->op_regs->portsc[0]); + if (!(portsc & PORTSCX_PORT_RESET)) { + /* Get the speed */ + u32 speed = portsc & PORTSCX_PORT_SPEED_MASK; + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + udc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSCX_PORT_SPEED_FULL: + udc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSCX_PORT_SPEED_LOW: + udc->gadget.speed = USB_SPEED_LOW; + break; + default: + udc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + } + + if (portsc & PORTSCX_PORT_SUSPEND) { + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + if (udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (!(portsc & PORTSCX_PORT_SUSPEND) + && udc->usb_state == USB_STATE_SUSPENDED) { + handle_bus_resume(udc); + } + + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +static void irq_process_error(struct mv_udc *udc) +{ + /* Increment the error count */ + udc->errors++; +} + +static irqreturn_t mv_udc_irq(int irq, void *dev) +{ + struct mv_udc *udc = (struct mv_udc *)dev; + u32 status, intr; + + /* Disable ISR when stopped bit is set */ + if (udc->stopped) + return IRQ_NONE; + + spin_lock(&udc->lock); + + status = readl(&udc->op_regs->usbsts); + intr = readl(&udc->op_regs->usbintr); + status &= intr; + + if (status == 0) { + spin_unlock(&udc->lock); + return IRQ_NONE; + } + + /* Clear all the interrupts occurred */ + writel(status, &udc->op_regs->usbsts); + + if (status & USBSTS_ERR) + irq_process_error(udc); + + if (status & USBSTS_RESET) + irq_process_reset(udc); + + if (status & USBSTS_PORT_CHANGE) + irq_process_port_change(udc); + + if (status & USBSTS_INT) + irq_process_tr_complete(udc); + + if (status & USBSTS_SUSPEND) + irq_process_suspend(udc); + + spin_unlock(&udc->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t mv_udc_vbus_irq(int irq, void *dev) +{ + struct mv_udc *udc = (struct mv_udc *)dev; + + /* polling VBUS and init phy may cause too much time*/ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); + + return IRQ_HANDLED; +} + +static void mv_udc_vbus_work(struct work_struct *work) +{ + struct mv_udc *udc; + unsigned int vbus; + + udc = container_of(work, struct mv_udc, vbus_work); + if (!udc->pdata->vbus) + return; + + vbus = udc->pdata->vbus->poll(); + dev_info(&udc->dev->dev, "vbus is %d\n", vbus); + + if (vbus == VBUS_HIGH) + mv_udc_vbus_session(&udc->gadget, 1); + else if (vbus == VBUS_LOW) + mv_udc_vbus_session(&udc->gadget, 0); +} + +/* release device structure */ +static void gadget_release(struct device *_dev) +{ + struct mv_udc *udc; + + udc = dev_get_drvdata(_dev); + + complete(udc->done); +} + +static int mv_udc_remove(struct platform_device *pdev) +{ + struct mv_udc *udc; + + udc = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&udc->gadget); + + if (udc->qwork) { + flush_workqueue(udc->qwork); + destroy_workqueue(udc->qwork); + } + + /* free memory allocated in probe */ + if (udc->dtd_pool) + dma_pool_destroy(udc->dtd_pool); + + if (udc->ep_dqh) + dma_free_coherent(&pdev->dev, udc->ep_dqh_size, + udc->ep_dqh, udc->ep_dqh_dma); + + mv_udc_disable(udc); + + /* free dev, wait for the release() finished */ + wait_for_completion(udc->done); + + return 0; +} + +static int mv_udc_probe(struct platform_device *pdev) +{ + struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mv_udc *udc; + int retval = 0; + struct resource *r; + size_t size; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform_data\n"); + return -ENODEV; + } + + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); + if (udc == NULL) { + dev_err(&pdev->dev, "failed to allocate memory for udc\n"); + return -ENOMEM; + } + + udc->done = &release_done; + udc->pdata = dev_get_platdata(&pdev->dev); + spin_lock_init(&udc->lock); + + udc->dev = pdev; + + 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->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(&pdev->dev, "no I/O memory resource defined\n"); + return -ENODEV; + } + + udc->cap_regs = (struct mv_cap_regs __iomem *) + devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (udc->cap_regs == NULL) { + 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(&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(&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) + return retval; + + udc->op_regs = + (struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs + + (readl(&udc->cap_regs->caplength_hciversion) + & CAPLENGTH_MASK)); + udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK; + + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop udc here. + */ + udc_stop(udc); + writel(0xFFFFFFFF, &udc->op_regs->usbsts); + + size = udc->max_eps * sizeof(struct mv_dqh) *2; + size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); + udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size, + &udc->ep_dqh_dma, GFP_KERNEL); + + if (udc->ep_dqh == NULL) { + dev_err(&pdev->dev, "allocate dQH memory failed\n"); + retval = -ENOMEM; + goto err_disable_clock; + } + udc->ep_dqh_size = size; + + /* create dTD dma_pool resource */ + udc->dtd_pool = dma_pool_create("mv_dtd", + &pdev->dev, + sizeof(struct mv_dtd), + DTD_ALIGNMENT, + DMA_BOUNDARY); + + if (!udc->dtd_pool) { + retval = -ENOMEM; + goto err_free_dma; + } + + size = udc->max_eps * sizeof(struct mv_ep) *2; + udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (udc->eps == NULL) { + dev_err(&pdev->dev, "allocate ep memory failed\n"); + retval = -ENOMEM; + goto err_destroy_dma; + } + + /* initialize ep0 status request structure */ + udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req), + GFP_KERNEL); + if (!udc->status_req) { + dev_err(&pdev->dev, "allocate status_req memory failed\n"); + retval = -ENOMEM; + goto err_destroy_dma; + } + INIT_LIST_HEAD(&udc->status_req->queue); + + /* allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = kzalloc(8, GFP_KERNEL); + udc->status_req->req.dma = DMA_ADDR_INVALID; + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = EP_DIR_OUT; + udc->remote_wakeup = 0; + + r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + retval = -ENODEV; + goto err_destroy_dma; + } + udc->irq = r->start; + if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq, + IRQF_SHARED, driver_name, udc)) { + dev_err(&pdev->dev, "Request irq %d for UDC failed\n", + udc->irq); + retval = -ENODEV; + goto err_destroy_dma; + } + + /* initialize gadget structure */ + udc->gadget.ops = &mv_ops; /* usb_gadget_ops */ + udc->gadget.ep0 = &udc->eps[0].ep; /* gadget ep0 */ + INIT_LIST_HEAD(&udc->gadget.ep_list); /* ep_list */ + udc->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ + udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ + + /* the "gadget" abstracts/virtualizes the controller */ + udc->gadget.name = driver_name; /* gadget name */ + + eps_init(udc); + + /* VBUS detect: we can disable/enable clock on demand.*/ + if (udc->transceiver) + udc->clock_gating = 1; + else if (pdata->vbus) { + udc->clock_gating = 1; + retval = devm_request_threaded_irq(&pdev->dev, + pdata->vbus->irq, NULL, + mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); + if (retval) { + dev_info(&pdev->dev, + "Can not request irq for VBUS, " + "disable clock gating\n"); + udc->clock_gating = 0; + } + + udc->qwork = create_singlethread_workqueue("mv_udc_queue"); + if (!udc->qwork) { + dev_err(&pdev->dev, "cannot create workqueue\n"); + retval = -ENOMEM; + goto err_destroy_dma; + } + + INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); + } + + /* + * When clock gating is supported, we can disable clk and phy. + * If not, it means that VBUS detection is not supported, we + * have to enable vbus active all the time to let controller work. + */ + if (udc->clock_gating) + mv_udc_disable_internal(udc); + else + udc->vbus_active = 1; + + retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + gadget_release); + if (retval) + goto err_create_workqueue; + + 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_create_workqueue: + destroy_workqueue(udc->qwork); +err_destroy_dma: + dma_pool_destroy(udc->dtd_pool); +err_free_dma: + dma_free_coherent(&pdev->dev, udc->ep_dqh_size, + udc->ep_dqh, udc->ep_dqh_dma); +err_disable_clock: + mv_udc_disable_internal(udc); + + return retval; +} + +#ifdef CONFIG_PM +static int mv_udc_suspend(struct device *dev) +{ + struct mv_udc *udc; + + udc = dev_get_drvdata(dev); + + /* if OTG is enabled, the following will be done in OTG driver*/ + if (udc->transceiver) + return 0; + + if (udc->pdata->vbus && udc->pdata->vbus->poll) + if (udc->pdata->vbus->poll() == VBUS_HIGH) { + dev_info(&udc->dev->dev, "USB cable is connected!\n"); + return -EAGAIN; + } + + /* + * only cable is unplugged, udc can suspend. + * So do not care about clock_gating == 1. + */ + if (!udc->clock_gating) { + udc_stop(udc); + + spin_lock_irq(&udc->lock); + /* stop all usb activities */ + stop_activity(udc, udc->driver); + spin_unlock_irq(&udc->lock); + + mv_udc_disable_internal(udc); + } + + return 0; +} + +static int mv_udc_resume(struct device *dev) +{ + struct mv_udc *udc; + int retval; + + udc = dev_get_drvdata(dev); + + /* if OTG is enabled, the following will be done in OTG driver*/ + if (udc->transceiver) + return 0; + + if (!udc->clock_gating) { + retval = mv_udc_enable_internal(udc); + if (retval) + return retval; + + if (udc->driver && udc->softconnect) { + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } + + return 0; +} + +static const struct dev_pm_ops mv_udc_pm_ops = { + .suspend = mv_udc_suspend, + .resume = mv_udc_resume, +}; +#endif + +static void mv_udc_shutdown(struct platform_device *pdev) +{ + 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); + mode &= ~3; + writel(mode, &udc->op_regs->usbmode); + mv_udc_disable(udc); +} + +static struct platform_driver udc_driver = { + .probe = mv_udc_probe, + .remove = mv_udc_remove, + .shutdown = mv_udc_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "mv-udc", +#ifdef CONFIG_PM + .pm = &mv_udc_pm_ops, +#endif + }, +}; + +module_platform_driver(udc_driver); +MODULE_ALIAS("platform:mv-udc"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c new file mode 100644 index 00000000000..81956feca1b --- /dev/null +++ b/drivers/usb/gadget/ncm.c @@ -0,0 +1,221 @@ +/* + * ncm.c -- NCM gadget driver + * + * Copyright (C) 2010 Nokia Corporation + * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com> + * + * The driver borrows from ether.c which is: + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/composite.h> + +#include "u_ether.h" +#include "u_ncm.h" + +#define DRIVER_DESC "NCM Gadget" + +/*-------------------------------------------------------------------------*/ + +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ + +/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +USB_ETHERNET_MODULE_PARAMETERS(); + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16 (0x0200), + + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id defaults change according to what configs + * we support. (As does bNumConfigurations.) These values can + * also be overridden by module parameters. + */ + .idVendor = cpu_to_le16 (CDC_VENDOR_NUM), + .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + +/* string IDs are assigned dynamically */ +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static 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)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + 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 = { + /* .label = f(hardware) */ + .label = "CDC Ethernet (NCM)", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init gncm_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct f_ncm_opts *ncm_opts; + int 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. + */ + + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + goto fail; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + + status = usb_add_config(cdev, &ncm_config_driver, + ncm_do_config); + if (status < 0) + goto fail; + + usb_composite_overwrite_options(cdev, &coverwrite); + dev_info(&gadget->dev, "%s\n", DRIVER_DESC); + + return 0; + +fail: + usb_put_function_instance(f_ncm_inst); + return status; +} + +static int __exit gncm_unbind(struct usb_composite_dev *cdev) +{ + 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; +} + +static __refdata struct usb_composite_driver ncm_driver = { + .name = "g_ncm", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = gncm_bind, + .unbind = __exit_p(gncm_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Yauheni Kaliuta"); +MODULE_LICENSE("GPL"); + +static int __init init(void) +{ + return usb_composite_probe(&ncm_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&ncm_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index df886cec5ef..a19f72dec0c 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -10,22 +10,11 @@ * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * */ #ifndef _LINUX_NDIS_H #define _LINUX_NDIS_H - -#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 -#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A -#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B - enum NDIS_DEVICE_POWER_STATE { NdisDeviceStateUnspecified = 0, NdisDeviceStateD0, @@ -41,11 +30,6 @@ struct NDIS_PM_WAKE_UP_CAPABILITIES { enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; }; -/* NDIS_PNP_CAPABILITIES.Flags constants */ -#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 -#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 -#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 - struct NDIS_PNP_CAPABILITIES { __le32 Flags; struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; @@ -60,158 +44,4 @@ struct NDIS_PM_PACKET_PATTERN { __le32 PatternFlags; }; - -/* Required Object IDs (OIDs) */ -#define OID_GEN_SUPPORTED_LIST 0x00010101 -#define OID_GEN_HARDWARE_STATUS 0x00010102 -#define OID_GEN_MEDIA_SUPPORTED 0x00010103 -#define OID_GEN_MEDIA_IN_USE 0x00010104 -#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 -#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 -#define OID_GEN_LINK_SPEED 0x00010107 -#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 -#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 -#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A -#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B -#define OID_GEN_VENDOR_ID 0x0001010C -#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D -#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E -#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F -#define OID_GEN_DRIVER_VERSION 0x00010110 -#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 -#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 -#define OID_GEN_MAC_OPTIONS 0x00010113 -#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 -#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 -#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 -#define OID_GEN_SUPPORTED_GUIDS 0x00010117 -#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 -#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 -#define OID_GEN_MACHINE_NAME 0x0001021A -#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B -#define OID_GEN_VLAN_ID 0x0001021C - -/* Optional OIDs */ -#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 -#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 - -/* Required statistics OIDs */ -#define OID_GEN_XMIT_OK 0x00020101 -#define OID_GEN_RCV_OK 0x00020102 -#define OID_GEN_XMIT_ERROR 0x00020103 -#define OID_GEN_RCV_ERROR 0x00020104 -#define OID_GEN_RCV_NO_BUFFER 0x00020105 - -/* Optional statistics OIDs */ -#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 -#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 -#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 -#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 -#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 -#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 -#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 -#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 -#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 -#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A -#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B -#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C -#define OID_GEN_RCV_CRC_ERROR 0x0002020D -#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E -#define OID_GEN_GET_TIME_CAPS 0x0002020F -#define OID_GEN_GET_NETCARD_TIME 0x00020210 -#define OID_GEN_NETCARD_LOAD 0x00020211 -#define OID_GEN_DEVICE_PROFILE 0x00020212 -#define OID_GEN_INIT_TIME_MS 0x00020213 -#define OID_GEN_RESET_COUNTS 0x00020214 -#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 -#define OID_GEN_FRIENDLY_NAME 0x00020216 -#define OID_GEN_MINIPORT_INFO 0x00020217 -#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 - -/* IEEE 802.3 (Ethernet) OIDs */ -#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 - -#define OID_802_3_PERMANENT_ADDRESS 0x01010101 -#define OID_802_3_CURRENT_ADDRESS 0x01010102 -#define OID_802_3_MULTICAST_LIST 0x01010103 -#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 -#define OID_802_3_MAC_OPTIONS 0x01010105 -#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 -#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 -#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 -#define OID_802_3_XMIT_DEFERRED 0x01020201 -#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 -#define OID_802_3_RCV_OVERRUN 0x01020203 -#define OID_802_3_XMIT_UNDERRUN 0x01020204 -#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 -#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 -#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 - -/* OID_GEN_MINIPORT_INFO constants */ -#define NDIS_MINIPORT_BUS_MASTER 0x00000001 -#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 -#define NDIS_MINIPORT_SG_LIST 0x00000004 -#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 -#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 -#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 -#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 -#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 -#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 -#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 -#define NDIS_MINIPORT_IS_CO 0x00000400 -#define NDIS_MINIPORT_DESERIALIZE 0x00000800 -#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 -#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 -#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 -#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 -#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 -#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 -#define NDIS_MINIPORT_HIDDEN 0x00040000 -#define NDIS_MINIPORT_SWENUM 0x00080000 -#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 -#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 -#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 -#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 -#define NDIS_MINIPORT_64BITS_DMA 0x01000000 - -#define NDIS_MEDIUM_802_3 0x00000000 -#define NDIS_MEDIUM_802_5 0x00000001 -#define NDIS_MEDIUM_FDDI 0x00000002 -#define NDIS_MEDIUM_WAN 0x00000003 -#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 -#define NDIS_MEDIUM_DIX 0x00000005 -#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 -#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 -#define NDIS_MEDIUM_ATM 0x00000008 -#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 -#define NDIS_MEDIUM_IRDA 0x0000000A -#define NDIS_MEDIUM_BPC 0x0000000B -#define NDIS_MEDIUM_CO_WAN 0x0000000C -#define NDIS_MEDIUM_1394 0x0000000D - -#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 -#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 -#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 -#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 -#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 -#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 -#define NDIS_PACKET_TYPE_SMT 0x00000040 -#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 -#define NDIS_PACKET_TYPE_GROUP 0x00000100 -#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 -#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 -#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 - -#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 -#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 - -#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 -#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 -#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 -#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 -#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 -#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 -#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 -#define NDIS_MAC_OPTION_RESERVED 0x80000000 - #endif /* _LINUX_NDIS_H */ diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c new file mode 100644 index 00000000000..ca15405583e --- /dev/null +++ b/drivers/usb/gadget/net2272.c @@ -0,0 +1,2710 @@ +/* + * Driver for PLX NET2272 USB device controller + * + * Copyright (C) 2005-2006 PLX Technology, Inc. + * Copyright (C) 2006-2011 Analog Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/prefetch.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/usb.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +#include "net2272.h" + +#define DRIVER_DESC "PLX NET2272 USB Peripheral Controller" + +static const char driver_name[] = "net2272"; +static const char driver_vers[] = "2006 October 17/mainline"; +static const char driver_desc[] = DRIVER_DESC; + +static const char ep0name[] = "ep0"; +static const char * const ep_name[] = { + ep0name, + "ep-a", "ep-b", "ep-c", +}; + +#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, + * notably request_dma, start_dma, and cancel_dma will need to be + * modified for your platform's particular dma controller. + * + * If use_dma is disabled, pio will be used instead. + */ +static bool use_dma = 0; +module_param(use_dma, bool, 0644); + +/* + * dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b) + * The NET2272 can only use dma for a single endpoint at a time. + * At some point this could be modified to allow either endpoint + * to take control of dma as it becomes available. + * + * Note that DMA should not be used on OUT endpoints unless it can + * be guaranteed that no short packets will arrive on an IN endpoint + * while the DMA operation is pending. Otherwise the OUT DMA will + * terminate prematurely (See NET2272 Errata 630-0213-0101) + */ +static ushort dma_ep = 1; +module_param(dma_ep, ushort, 0644); + +/* + * dma_mode: net2272 dma mode setting (see LOCCTL1 definiton): + * mode 0 == Slow DREQ mode + * mode 1 == Fast DREQ mode + * mode 2 == Burst mode + */ +static ushort dma_mode = 2; +module_param(dma_mode, ushort, 0644); +#else +#define use_dma 0 +#define dma_ep 1 +#define dma_mode 2 +#endif + +/* + * fifo_mode: net2272 buffer configuration: + * mode 0 == ep-{a,b,c} 512db each + * mode 1 == ep-a 1k, ep-{b,c} 512db + * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db + * mode 3 == ep-a 1k, ep-b disabled, ep-c 512db + */ +static ushort fifo_mode = 0; +module_param(fifo_mode, ushort, 0644); + +/* + * enable_suspend: When enabled, the driver will respond to + * USB suspend requests by powering down the NET2272. Otherwise, + * USB suspend requests will be ignored. This is acceptible for + * self-powered devices. For bus powered devices set this to 1. + */ +static ushort enable_suspend = 0; +module_param(enable_suspend, ushort, 0644); + +static void assert_out_naking(struct net2272_ep *ep, const char *where) +{ + u8 tmp; + +#ifndef DEBUG + return; +#endif + + tmp = net2272_ep_read(ep, EP_STAT0); + if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { + dev_dbg(ep->dev->dev, "%s %s %02x !NAK\n", + ep->ep.name, where, tmp); + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + } +} +#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) + +static void stop_out_naking(struct net2272_ep *ep) +{ + u8 tmp = net2272_ep_read(ep, EP_STAT0); + + if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); +} + +#define PIPEDIR(bAddress) (usb_pipein(bAddress) ? "in" : "out") + +static char *type_string(u8 bmAttributes) +{ + switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: return "bulk"; + case USB_ENDPOINT_XFER_ISOC: return "iso"; + case USB_ENDPOINT_XFER_INT: return "intr"; + default: return "control"; + } +} + +static char *buf_state_string(unsigned state) +{ + switch (state) { + case BUFF_FREE: return "free"; + case BUFF_VALID: return "valid"; + case BUFF_LCL: return "local"; + case BUFF_USB: return "usb"; + default: return "unknown"; + } +} + +static char *dma_mode_string(void) +{ + if (!use_dma) + return "PIO"; + switch (dma_mode) { + case 0: return "SLOW DREQ"; + case 1: return "FAST DREQ"; + case 2: return "BURST"; + default: return "invalid"; + } +} + +static void net2272_dequeue_all(struct net2272_ep *); +static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *); +static int net2272_fifo_status(struct usb_ep *); + +static struct usb_ep_ops net2272_ep_ops; + +/*---------------------------------------------------------------------------*/ + +static int +net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct net2272 *dev; + struct net2272_ep *ep; + u32 max; + u8 tmp; + unsigned long flags; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = usb_endpoint_maxp(desc) & 0x1fff; + + spin_lock_irqsave(&dev->lock, flags); + _ep->maxpacket = max & 0x7fff; + ep->desc = desc; + + /* net2272_ep_reset() has already been called */ + ep->stopped = 0; + ep->wedged = 0; + + /* set speed-dependent max packet */ + net2272_ep_write(ep, EP_MAXPKT0, max & 0xff); + net2272_ep_write(ep, EP_MAXPKT1, (max & 0xff00) >> 8); + + /* set type, direction, address; reset fifo counters */ + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); + tmp = usb_endpoint_type(desc); + if (usb_endpoint_xfer_bulk(desc)) { + /* catch some particularly blatant driver bugs */ + if ((dev->gadget.speed == USB_SPEED_HIGH && max != 512) || + (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ERANGE; + } + } + ep->is_iso = usb_endpoint_xfer_isoc(desc) ? 1 : 0; + tmp <<= ENDPOINT_TYPE; + tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER); + tmp |= usb_endpoint_dir_in(desc) << ENDPOINT_DIRECTION; + tmp |= (1 << ENDPOINT_ENABLE); + + /* for OUT transfers, block the rx fifo until a read is posted */ + ep->is_in = usb_endpoint_dir_in(desc); + if (!ep->is_in) + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + + net2272_ep_write(ep, EP_CFG, tmp); + + /* enable irqs */ + tmp = (1 << ep->num) | net2272_read(dev, IRQENB0); + net2272_write(dev, IRQENB0, tmp); + + tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | net2272_ep_read(ep, EP_IRQENB); + net2272_ep_write(ep, EP_IRQENB, tmp); + + tmp = desc->bEndpointAddress; + dev_dbg(dev->dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n", + _ep->name, tmp & 0x0f, PIPEDIR(tmp), + type_string(desc->bmAttributes), max, + net2272_ep_read(ep, EP_CFG)); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static void net2272_ep_reset(struct net2272_ep *ep) +{ + u8 tmp; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2272_ep_ops; + + /* disable irqs, endpoint */ + net2272_ep_write(ep, EP_IRQENB, 0); + + /* init to our chosen defaults, notably so that we NAK OUT + * packets until the driver queues a read. + */ + tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS); + net2272_ep_write(ep, EP_RSPSET, tmp); + + tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE); + if (ep->num != 0) + tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT); + + net2272_ep_write(ep, EP_RSPCLR, tmp); + + /* scrub most status bits, and flush any fifo state */ + net2272_ep_write(ep, EP_STAT0, + (1 << DATA_IN_TOKEN_INTERRUPT) + | (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); + + net2272_ep_write(ep, EP_STAT1, + (1 << TIMEOUT) + | (1 << USB_OUT_ACK_SENT) + | (1 << USB_OUT_NAK_SENT) + | (1 << USB_IN_ACK_RCVD) + | (1 << USB_IN_NAK_SENT) + | (1 << USB_STALL_SENT) + | (1 << LOCAL_OUT_ZLP) + | (1 << BUFFER_FLUSH)); + + /* fifo size is handled seperately */ +} + +static int net2272_disable(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + net2272_dequeue_all(ep); + net2272_ep_reset(ep); + + dev_vdbg(ep->dev->dev, "disabled %s\n", _ep->name); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static struct usb_request * +net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct net2272_ep *ep; + struct net2272_request *req; + + if (!_ep) + return NULL; + ep = container_of(_ep, struct net2272_ep, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void +net2272_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2272_ep *ep; + struct net2272_request *req; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !_req) + return; + + req = container_of(_req, struct net2272_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void +net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status) +{ + struct net2272 *dev; + unsigned stopped = ep->stopped; + + if (ep->num == 0) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + allow_status(ep); + } + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (use_dma && ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, + ep->is_in); + + if (status && status != -ESHUTDOWN) + dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length, req->req.buf); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +static int +net2272_write_packet(struct net2272_ep *ep, u8 *buf, + struct net2272_request *req, unsigned max) +{ + u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); + u16 *bufp; + unsigned length, count; + u8 tmp; + + length = min(req->req.length - req->req.actual, max); + req->req.actual += length; + + dev_vdbg(ep->dev->dev, "write packet %s req %p max %u len %u avail %u\n", + ep->ep.name, req, max, length, + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); + + count = length; + bufp = (u16 *)buf; + + while (likely(count >= 2)) { + /* no byte-swap required; chip endian set during init */ + writew(*bufp++, ep_data); + count -= 2; + } + buf = (u8 *)bufp; + + /* write final byte by placing the NET2272 into 8-bit mode */ + if (unlikely(count)) { + tmp = net2272_read(ep->dev, LOCCTL); + net2272_write(ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH)); + writeb(*buf, ep_data); + net2272_write(ep->dev, LOCCTL, tmp); + } + return length; +} + +/* returns: 0: still running, 1: completed, negative: errno */ +static int +net2272_write_fifo(struct net2272_ep *ep, struct net2272_request *req) +{ + u8 *buf; + unsigned count, max; + int status; + + dev_vdbg(ep->dev->dev, "write_fifo %s actual %d len %d\n", + ep->ep.name, req->req.actual, req->req.length); + + /* + * Keep loading the endpoint until the final packet is loaded, + * or the endpoint buffer is full. + */ + top: + /* + * Clear interrupt status + * - Packet Transmitted interrupt will become set again when the + * host successfully takes another packet + */ + net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); + while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_FULL))) { + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* force pagesel */ + net2272_ep_read(ep, EP_STAT0); + + max = (net2272_ep_read(ep, EP_AVAIL1) << 8) | + (net2272_ep_read(ep, EP_AVAIL0)); + + if (max < ep->ep.maxpacket) + max = (net2272_ep_read(ep, EP_AVAIL1) << 8) + | (net2272_ep_read(ep, EP_AVAIL0)); + + count = net2272_write_packet(ep, buf, req, max); + /* see if we are done */ + if (req->req.length == req->req.actual) { + /* validate short or zlp packet */ + if (count < ep->ep.maxpacket) + set_fifo_bytecount(ep, 0); + net2272_done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, + queue); + status = net2272_kick_dma(ep, req); + + if (status < 0) + if ((net2272_ep_read(ep, EP_STAT0) + & (1 << BUFFER_EMPTY))) + goto top; + } + return 1; + } + net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); + } + return 0; +} + +static void +net2272_out_flush(struct net2272_ep *ep) +{ + ASSERT_OUT_NAKING(ep); + + net2272_ep_write(ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT)); + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); +} + +static int +net2272_read_packet(struct net2272_ep *ep, u8 *buf, + struct net2272_request *req, unsigned avail) +{ + u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); + unsigned is_short; + u16 *bufp; + + req->req.actual += avail; + + dev_vdbg(ep->dev->dev, "read packet %s req %p len %u avail %u\n", + ep->ep.name, req, avail, + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); + + is_short = (avail < ep->ep.maxpacket); + + if (unlikely(avail == 0)) { + /* remove any zlp from the buffer */ + (void)readw(ep_data); + return is_short; + } + + /* Ensure we get the final byte */ + if (unlikely(avail % 2)) + avail++; + bufp = (u16 *)buf; + + do { + *bufp++ = readw(ep_data); + avail -= 2; + } while (avail); + + /* + * To avoid false endpoint available race condition must read + * ep stat0 twice in the case of a short transfer + */ + if (net2272_ep_read(ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) + net2272_ep_read(ep, EP_STAT0); + + return is_short; +} + +static int +net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) +{ + u8 *buf; + unsigned is_short; + int count; + int tmp; + int cleanup = 0; + int status = -1; + + dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n", + ep->ep.name, req->req.actual, req->req.length); + + top: + do { + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + count = (net2272_ep_read(ep, EP_AVAIL1) << 8) + | net2272_ep_read(ep, EP_AVAIL0); + + net2272_ep_write(ep, EP_STAT0, + (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | + (1 << DATA_PACKET_RECEIVED_INTERRUPT)); + + tmp = req->req.length - req->req.actual; + + if (count > tmp) { + if ((tmp % ep->ep.maxpacket) != 0) { + dev_err(ep->dev->dev, + "%s out fifo %d bytes, expected %d\n", + ep->ep.name, count, tmp); + cleanup = 1; + } + count = (tmp > 0) ? tmp : 0; + } + + is_short = net2272_read_packet(ep, buf, req, count); + + /* completion */ + if (unlikely(cleanup || is_short || + ((req->req.actual == req->req.length) + && !req->req.zero))) { + + if (cleanup) { + net2272_out_flush(ep); + net2272_done(ep, req, -EOVERFLOW); + } else + net2272_done(ep, req, 0); + + /* re-initialize endpoint transfer registers + * otherwise they may result in erroneous pre-validation + * for subsequent control reads + */ + if (unlikely(ep->num == 0)) { + net2272_ep_write(ep, EP_TRANSFER2, 0); + net2272_ep_write(ep, EP_TRANSFER1, 0); + net2272_ep_write(ep, EP_TRANSFER0, 0); + } + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + status = net2272_kick_dma(ep, req); + if ((status < 0) && + !(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))) + goto top; + } + return 1; + } + } while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))); + + return 0; +} + +static void +net2272_pio_advance(struct net2272_ep *ep) +{ + struct net2272_request *req; + + if (unlikely(list_empty(&ep->queue))) + return; + + req = list_entry(ep->queue.next, struct net2272_request, queue); + (ep->is_in ? net2272_write_fifo : net2272_read_fifo)(ep, req); +} + +/* returns 0 on success, else negative errno */ +static int +net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf, + unsigned len, unsigned dir) +{ + dev_vdbg(dev->dev, "request_dma ep %d buf %08x len %d dir %d\n", + ep, buf, len, dir); + + /* The NET2272 only supports a single dma channel */ + if (dev->dma_busy) + return -EBUSY; + /* + * EP_TRANSFER (used to determine the number of bytes received + * in an OUT transfer) is 24 bits wide; don't ask for more than that. + */ + if ((dir == 1) && (len > 0x1000000)) + return -EINVAL; + + dev->dma_busy = 1; + + /* initialize platform's dma */ +#ifdef CONFIG_PCI + /* NET2272 addr, buffer addr, length, etc. */ + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + /* Setup PLX 9054 DMA mode */ + writel((1 << LOCAL_BUS_WIDTH) | + (1 << TA_READY_INPUT_ENABLE) | + (0 << LOCAL_BURST_ENABLE) | + (1 << DONE_INTERRUPT_ENABLE) | + (1 << LOCAL_ADDRESSING_MODE) | + (1 << DEMAND_MODE) | + (1 << DMA_EOT_ENABLE) | + (1 << FAST_SLOW_TERMINATE_MODE_SELECT) | + (1 << DMA_CHANNEL_INTERRUPT_SELECT), + dev->rdk1.plx9054_base_addr + DMAMODE0); + + writel(0x100000, dev->rdk1.plx9054_base_addr + DMALADR0); + writel(buf, dev->rdk1.plx9054_base_addr + DMAPADR0); + writel(len, dev->rdk1.plx9054_base_addr + DMASIZ0); + writel((dir << DIRECTION_OF_TRANSFER) | + (1 << INTERRUPT_AFTER_TERMINAL_COUNT), + dev->rdk1.plx9054_base_addr + DMADPR0); + writel((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) | + readl(dev->rdk1.plx9054_base_addr + INTCSR), + dev->rdk1.plx9054_base_addr + INTCSR); + + break; + } +#endif + + net2272_write(dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (1 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (dev->dma_eot_polarity << EOT_POLARITY) | + (dev->dma_dack_polarity << DACK_POLARITY) | + (dev->dma_dreq_polarity << DREQ_POLARITY) | + ((ep >> 1) << DMA_ENDPOINT_SELECT)); + + (void) net2272_read(dev, SCRATCH); + + return 0; +} + +static void +net2272_start_dma(struct net2272 *dev) +{ + /* start platform's dma controller */ +#ifdef CONFIG_PCI + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START), + dev->rdk1.plx9054_base_addr + DMACSR0); + break; + } +#endif +} + +/* returns 0 on success, else negative errno */ +static int +net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req) +{ + unsigned size; + u8 tmp; + + if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma) + return -EINVAL; + + /* don't use dma for odd-length transfers + * otherwise, we'd need to deal with the last byte with pio + */ + if (req->req.length & 1) + return -EINVAL; + + dev_vdbg(ep->dev->dev, "kick_dma %s req %p dma %08llx\n", + ep->ep.name, req, (unsigned long long) req->req.dma); + + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + + /* The NET2272 can only use DMA on one endpoint at a time */ + if (ep->dev->dma_busy) + return -EBUSY; + + /* Make sure we only DMA an even number of bytes (we'll use + * pio to complete the transfer) + */ + size = req->req.length; + size &= ~1; + + /* device-to-host transfer */ + if (ep->is_in) { + /* initialize platform's dma controller */ + if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 0)) + /* unable to obtain DMA channel; return error and use pio mode */ + return -EBUSY; + req->req.actual += size; + + /* host-to-device transfer */ + } else { + tmp = net2272_ep_read(ep, EP_STAT0); + + /* initialize platform's dma controller */ + if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 1)) + /* unable to obtain DMA channel; return error and use pio mode */ + return -EBUSY; + + if (!(tmp & (1 << BUFFER_EMPTY))) + ep->not_empty = 1; + else + ep->not_empty = 0; + + + /* allow the endpoint's buffer to fill */ + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); + + /* this transfer completed and data's already in the fifo + * return error so pio gets used. + */ + if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { + + /* deassert dreq */ + net2272_write(ep->dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (0 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (ep->dev->dma_eot_polarity << EOT_POLARITY) | + (ep->dev->dma_dack_polarity << DACK_POLARITY) | + (ep->dev->dma_dreq_polarity << DREQ_POLARITY) | + ((ep->num >> 1) << DMA_ENDPOINT_SELECT)); + + return -EBUSY; + } + } + + /* Don't use per-packet interrupts: use dma interrupts only */ + net2272_ep_write(ep, EP_IRQENB, 0); + + net2272_start_dma(ep->dev); + + return 0; +} + +static void net2272_cancel_dma(struct net2272 *dev) +{ +#ifdef CONFIG_PCI + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0); + writeb(1 << CHANNEL_ABORT, dev->rdk1.plx9054_base_addr + DMACSR0); + while (!(readb(dev->rdk1.plx9054_base_addr + DMACSR0) & + (1 << CHANNEL_DONE))) + continue; /* wait for dma to stabalize */ + + /* dma abort generates an interrupt */ + writeb(1 << CHANNEL_CLEAR_INTERRUPT, + dev->rdk1.plx9054_base_addr + DMACSR0); + break; + } +#endif + + dev->dma_busy = 0; +} + +/*---------------------------------------------------------------------------*/ + +static int +net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct net2272_request *req; + struct net2272_ep *ep; + struct net2272 *dev; + unsigned long flags; + int status = -1; + u8 s; + + req = container_of(_req, struct net2272_request, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue)) + return -EINVAL; + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* set up dma mapping in case the caller didn't */ + if (use_dma && ep->dma) { + status = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (status) + return status; + } + + dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n", + _ep->name, _req, _req->length, _req->buf, + (unsigned long long) _req->dma, _req->zero ? "zero" : "!zero"); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + /* maybe there's no control data, just status ack */ + if (ep->num == 0 && _req->length == 0) { + net2272_done(ep, req, 0); + dev_vdbg(dev->dev, "%s status ack\n", ep->ep.name); + goto done; + } + + /* Return zlp, don't let it block subsequent packets */ + s = net2272_ep_read(ep, EP_STAT0); + if (s & (1 << BUFFER_EMPTY)) { + /* Buffer is empty check for a blocking zlp, handle it */ + if ((s & (1 << NAK_OUT_PACKETS)) && + net2272_ep_read(ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP)) { + dev_dbg(dev->dev, "WARNING: returning ZLP short packet termination!\n"); + /* + * Request is going to terminate with a short packet ... + * hope the client is ready for it! + */ + status = net2272_read_fifo(ep, req); + /* clear short packet naking */ + net2272_ep_write(ep, EP_STAT0, (1 << NAK_OUT_PACKETS)); + goto done; + } + } + + /* try dma first */ + status = net2272_kick_dma(ep, req); + + if (status < 0) { + /* dma failed (most likely in use by another endpoint) + * fallback to pio + */ + status = 0; + + if (ep->is_in) + status = net2272_write_fifo(ep, req); + else { + s = net2272_ep_read(ep, EP_STAT0); + if ((s & (1 << BUFFER_EMPTY)) == 0) + status = net2272_read_fifo(ep, req); + } + + if (unlikely(status != 0)) { + if (status > 0) + status = 0; + req = NULL; + } + } + } + if (likely(req)) + list_add_tail(&req->queue, &ep->queue); + + if (likely(!list_empty(&ep->queue))) + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); + done: + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue ALL requests */ +static void +net2272_dequeue_all(struct net2272_ep *ep) +{ + struct net2272_request *req; + + /* called with spinlock held */ + ep->stopped = 1; + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, + queue); + net2272_done(ep, req, -ESHUTDOWN); + } +} + +/* dequeue JUST ONE request */ +static int +net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2272_ep *ep; + struct net2272_request *req; + unsigned long flags; + int stopped; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0) || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + stopped = ep->stopped; + ep->stopped = 1; + + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete */ + if (ep->queue.next == &req->queue) { + dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name); + net2272_done(ep, req, -ECONNRESET); + } + req = NULL; + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int +net2272_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) +{ + struct net2272_ep *ep; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc)) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + if (!list_empty(&ep->queue)) + ret = -EAGAIN; + else if (ep->is_in && value && net2272_fifo_status(_ep) != 0) + ret = -EAGAIN; + else { + dev_vdbg(ep->dev->dev, "%s %s %s\n", _ep->name, + value ? "set" : "clear", + wedged ? "wedge" : "halt"); + /* set/clear */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt(ep); + if (wedged) + ep->wedged = 1; + } else { + clear_halt(ep); + ep->wedged = 0; + } + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + + return ret; +} + +static int +net2272_set_halt(struct usb_ep *_ep, int value) +{ + return net2272_set_halt_and_wedge(_ep, value, 0); +} + +static int +net2272_set_wedge(struct usb_ep *_ep) +{ + if (!_ep || _ep->name == ep0name) + return -EINVAL; + return net2272_set_halt_and_wedge(_ep, 1, 1); +} + +static int +net2272_fifo_status(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + u16 avail; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -ENODEV; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + avail = net2272_ep_read(ep, EP_AVAIL1) << 8; + avail |= net2272_ep_read(ep, EP_AVAIL0); + if (avail > ep->fifo_size) + return -EOVERFLOW; + if (ep->is_in) + avail = ep->fifo_size - avail; + return avail; +} + +static void +net2272_fifo_flush(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return; + + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); +} + +static struct usb_ep_ops net2272_ep_ops = { + .enable = net2272_enable, + .disable = net2272_disable, + + .alloc_request = net2272_alloc_request, + .free_request = net2272_free_request, + + .queue = net2272_queue, + .dequeue = net2272_dequeue, + + .set_halt = net2272_set_halt, + .set_wedge = net2272_set_wedge, + .fifo_status = net2272_fifo_status, + .fifo_flush = net2272_fifo_flush, +}; + +/*---------------------------------------------------------------------------*/ + +static int +net2272_get_frame(struct usb_gadget *_gadget) +{ + struct net2272 *dev; + unsigned long flags; + u16 ret; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + spin_lock_irqsave(&dev->lock, flags); + + ret = net2272_read(dev, FRAME1) << 8; + ret |= net2272_read(dev, FRAME0); + + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int +net2272_wakeup(struct usb_gadget *_gadget) +{ + struct net2272 *dev; + u8 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2272, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = net2272_read(dev, USBCTL0); + if (tmp & (1 << IO_WAKEUP_ENABLE)) + net2272_write(dev, USBCTL1, (1 << GENERATE_RESUME)); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int +net2272_set_selfpowered(struct usb_gadget *_gadget, int value) +{ + struct net2272 *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + + dev->is_selfpowered = value; + + return 0; +} + +static int +net2272_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct net2272 *dev; + u8 tmp; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = net2272_read(dev, USBCTL0); + dev->softconnect = (is_on != 0); + if (is_on) + tmp |= (1 << USB_DETECT_ENABLE); + else + tmp &= ~(1 << USB_DETECT_ENABLE); + net2272_write(dev, USBCTL0, tmp); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); + +static const struct usb_gadget_ops net2272_ops = { + .get_frame = net2272_get_frame, + .wakeup = net2272_wakeup, + .set_selfpowered = net2272_set_selfpowered, + .pullup = net2272_pullup, + .udc_start = net2272_start, + .udc_stop = net2272_stop, +}; + +/*---------------------------------------------------------------------------*/ + +static ssize_t +registers_show(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct net2272 *dev; + char *next; + unsigned size, t; + unsigned long flags; + u8 t1, t2; + int i; + const char *s; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + if (dev->driver) + s = dev->driver->driver.name; + else + s = "(none)"; + + /* Main Control Registers */ + t = scnprintf(next, size, "%s version %s," + "chiprev %02x, locctl %02x\n" + "irqenb0 %02x irqenb1 %02x " + "irqstat0 %02x irqstat1 %02x\n", + driver_name, driver_vers, dev->chiprev, + net2272_read(dev, LOCCTL), + net2272_read(dev, IRQENB0), + net2272_read(dev, IRQENB1), + net2272_read(dev, IRQSTAT0), + net2272_read(dev, IRQSTAT1)); + size -= t; + next += t; + + /* DMA */ + t1 = net2272_read(dev, DMAREQ); + t = scnprintf(next, size, "\ndmareq %02x: %s %s%s%s%s\n", + t1, ep_name[(t1 & 0x01) + 1], + t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "", + t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "", + t1 & (1 << DMA_REQUEST) ? "req " : "", + t1 & (1 << DMA_BUFFER_VALID) ? "valid " : ""); + size -= t; + next += t; + + /* USB Control Registers */ + t1 = net2272_read(dev, USBCTL1); + if (t1 & (1 << VBUS_PIN)) { + if (t1 & (1 << USB_HIGH_SPEED)) + s = "high speed"; + else if (dev->gadget.speed == USB_SPEED_UNKNOWN) + s = "powered"; + else + s = "full speed"; + } else + s = "not attached"; + t = scnprintf(next, size, + "usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n", + net2272_read(dev, USBCTL0), t1, + net2272_read(dev, OURADDR), s); + size -= t; + next += t; + + /* Endpoint Registers */ + for (i = 0; i < 4; ++i) { + struct net2272_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->desc) + continue; + + t1 = net2272_ep_read(ep, EP_CFG); + t2 = net2272_ep_read(ep, EP_RSPSET); + t = scnprintf(next, size, + "\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s" + "irqenb %02x\n", + ep->ep.name, t1, t2, + (t2 & (1 << ALT_NAK_OUT_PACKETS)) ? "NAK " : "", + (t2 & (1 << HIDE_STATUS_PHASE)) ? "hide " : "", + (t2 & (1 << AUTOVALIDATE)) ? "auto " : "", + (t2 & (1 << INTERRUPT_MODE)) ? "interrupt " : "", + (t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "", + (t2 & (1 << NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "", + (t2 & (1 << ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ", + (t2 & (1 << ENDPOINT_HALT)) ? "HALT " : "", + net2272_ep_read(ep, EP_IRQENB)); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tstat0 %02x stat1 %02x avail %04x " + "(ep%d%s-%s)%s\n", + net2272_ep_read(ep, EP_STAT0), + net2272_ep_read(ep, EP_STAT1), + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0), + t1 & 0x0f, + ep->is_in ? "in" : "out", + type_string(t1 >> 5), + ep->stopped ? "*" : ""); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tep_transfer %06x\n", + ((net2272_ep_read(ep, EP_TRANSFER2) & 0xff) << 16) | + ((net2272_ep_read(ep, EP_TRANSFER1) & 0xff) << 8) | + ((net2272_ep_read(ep, EP_TRANSFER0) & 0xff))); + size -= t; + next += t; + + t1 = net2272_ep_read(ep, EP_BUFF_STATES) & 0x03; + t2 = (net2272_ep_read(ep, EP_BUFF_STATES) >> 2) & 0x03; + t = scnprintf(next, size, + "\tbuf-a %s buf-b %s\n", + buf_state_string(t1), + buf_state_string(t2)); + size -= t; + next += t; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(registers); + +/*---------------------------------------------------------------------------*/ + +static void +net2272_set_fifo_mode(struct net2272 *dev, int mode) +{ + u8 tmp; + + tmp = net2272_read(dev, LOCCTL) & 0x3f; + tmp |= (mode << 6); + net2272_write(dev, LOCCTL, tmp); + + INIT_LIST_HEAD(&dev->gadget.ep_list); + + /* always ep-a, ep-c ... maybe not ep-b */ + list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); + + switch (mode) { + case 0: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512; + break; + case 1: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = 1024; + dev->ep[2].fifo_size = 512; + break; + case 2: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; + break; + case 3: + dev->ep[1].fifo_size = 1024; + break; + } + + /* ep-c is always 2 512 byte buffers */ + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + dev->ep[3].fifo_size = 512; +} + +/*---------------------------------------------------------------------------*/ + +static void +net2272_usb_reset(struct net2272 *dev) +{ + dev->gadget.speed = USB_SPEED_UNKNOWN; + + net2272_cancel_dma(dev); + + net2272_write(dev, IRQENB0, 0); + net2272_write(dev, IRQENB1, 0); + + /* clear irq state */ + net2272_write(dev, IRQSTAT0, 0xff); + net2272_write(dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT)); + + net2272_write(dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (0 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (dev->dma_eot_polarity << EOT_POLARITY) | + (dev->dma_dack_polarity << DACK_POLARITY) | + (dev->dma_dreq_polarity << DREQ_POLARITY) | + ((dma_ep >> 1) << DMA_ENDPOINT_SELECT)); + + net2272_cancel_dma(dev); + net2272_set_fifo_mode(dev, (fifo_mode <= 3) ? fifo_mode : 0); + + /* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping + * note that the higher level gadget drivers are expected to convert data to little endian. + * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here + */ + net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) | (1 << DATA_WIDTH)); + net2272_write(dev, LOCCTL1, (dma_mode << DMA_MODE)); +} + +static void +net2272_usb_reinit(struct net2272 *dev) +{ + int i; + + /* basic endpoint init */ + for (i = 0; i < 4; ++i) { + struct net2272_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + ep->not_empty = 0; + + if (use_dma && ep->num == dma_ep) + ep->dma = 1; + + if (i > 0 && i <= 3) + ep->fifo_size = 512; + else + ep->fifo_size = 64; + net2272_ep_reset(ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); +} + +static void +net2272_ep0_start(struct net2272 *dev) +{ + struct net2272_ep *ep0 = &dev->ep[0]; + + net2272_ep_write(ep0, EP_RSPSET, + (1 << NAK_OUT_PACKETS_MODE) | + (1 << ALT_NAK_OUT_PACKETS)); + net2272_ep_write(ep0, EP_RSPCLR, + (1 << HIDE_STATUS_PHASE) | + (1 << CONTROL_STATUS_PHASE_HANDSHAKE)); + net2272_write(dev, USBCTL0, + (dev->softconnect << USB_DETECT_ENABLE) | + (1 << USB_ROOT_PORT_WAKEUP_ENABLE) | + (1 << IO_WAKEUP_ENABLE)); + net2272_write(dev, IRQENB0, + (1 << SETUP_PACKET_INTERRUPT_ENABLE) | + (1 << ENDPOINT_0_INTERRUPT_ENABLE) | + (1 << DMA_DONE_INTERRUPT_ENABLE)); + net2272_write(dev, IRQENB1, + (1 << VBUS_INTERRUPT_ENABLE) | + (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) | + (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)); +} + +/* 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 net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2272 *dev; + unsigned i; + + if (!driver || !driver->unbind || !driver->setup || + driver->max_speed != USB_SPEED_HIGH) + return -EINVAL; + + dev = container_of(_gadget, struct net2272, gadget); + + for (i = 0; i < 4; ++i) + dev->ep[i].irqs = 0; + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + net2272_ep0_start(dev); + + dev_dbg(dev->dev, "%s ready\n", driver->driver.name); + + return 0; +} + +static void +stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + net2272_usb_reset(dev); + 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); +} + +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2272 *dev; + unsigned long flags; + + dev = container_of(_gadget, struct net2272, gadget); + + spin_lock_irqsave(&dev->lock, flags); + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + dev->driver = NULL; + + dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name); + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* handle ep-a/ep-b dma completions */ +static void +net2272_handle_dma(struct net2272_ep *ep) +{ + struct net2272_request *req; + unsigned len; + int status; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2272_request, queue); + else + req = NULL; + + dev_vdbg(ep->dev->dev, "handle_dma %s req %p\n", ep->ep.name, req); + + /* Ensure DREQ is de-asserted */ + net2272_write(ep->dev, DMAREQ, + (0 << DMA_BUFFER_VALID) + | (0 << DMA_REQUEST_ENABLE) + | (1 << DMA_CONTROL_DACK) + | (ep->dev->dma_eot_polarity << EOT_POLARITY) + | (ep->dev->dma_dack_polarity << DACK_POLARITY) + | (ep->dev->dma_dreq_polarity << DREQ_POLARITY) + | (ep->dma << DMA_ENDPOINT_SELECT)); + + ep->dev->dma_busy = 0; + + net2272_ep_write(ep, EP_IRQENB, + (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | net2272_ep_read(ep, EP_IRQENB)); + + /* device-to-host transfer completed */ + if (ep->is_in) { + /* validate a short packet or zlp if necessary */ + if ((req->req.length % ep->ep.maxpacket != 0) || + req->req.zero) + set_fifo_bytecount(ep, 0); + + net2272_done(ep, req, 0); + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + status = net2272_kick_dma(ep, req); + if (status < 0) + net2272_pio_advance(ep); + } + + /* host-to-device transfer completed */ + } else { + /* terminated with a short packet? */ + if (net2272_read(ep->dev, IRQSTAT0) & + (1 << DMA_DONE_INTERRUPT)) { + /* abort system dma */ + net2272_cancel_dma(ep->dev); + } + + /* EP_TRANSFER will contain the number of bytes + * actually received. + * NOTE: There is no overflow detection on EP_TRANSFER: + * We can't deal with transfers larger than 2^24 bytes! + */ + len = (net2272_ep_read(ep, EP_TRANSFER2) << 16) + | (net2272_ep_read(ep, EP_TRANSFER1) << 8) + | (net2272_ep_read(ep, EP_TRANSFER0)); + + if (ep->not_empty) + len += 4; + + req->req.actual += len; + + /* get any remaining data */ + net2272_pio_advance(ep); + } +} + +/*---------------------------------------------------------------------------*/ + +static void +net2272_handle_ep(struct net2272_ep *ep) +{ + struct net2272_request *req; + u8 stat0, stat1; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2272_request, queue); + else + req = NULL; + + /* ack all, and handle what we care about */ + stat0 = net2272_ep_read(ep, EP_STAT0); + stat1 = net2272_ep_read(ep, EP_STAT1); + 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 : NULL); + + net2272_ep_write(ep, EP_STAT0, stat0 & + ~((1 << NAK_OUT_PACKETS) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))); + net2272_ep_write(ep, EP_STAT1, stat1); + + /* data packet(s) received (in the fifo, OUT) + * direction must be validated, otherwise control read status phase + * could be interpreted as a valid packet + */ + if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT))) + net2272_pio_advance(ep); + /* data packet(s) transmitted (IN) */ + else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) + net2272_pio_advance(ep); +} + +static struct net2272_ep * +net2272_get_ep_by_addr(struct net2272 *dev, u16 wIndex) +{ + struct net2272_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return &dev->ep[0]; + + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) + return ep; + } + return NULL; +} + +/* + * USB Test Packet: + * JKJKJKJK * 9 + * JJKKJJKK * 8 + * JJJJKKKK * 8 + * JJJJJJJKKKKKKK * 8 + * JJJJJJJK * 8 + * {JKKKKKKK * 10}, JK + */ +static const u8 net2272_test_packet[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, + 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E +}; + +static void +net2272_set_test_mode(struct net2272 *dev, int mode) +{ + int i; + + /* Disable all net2272 interrupts: + * Nothing but a power cycle should stop the test. + */ + net2272_write(dev, IRQENB0, 0x00); + net2272_write(dev, IRQENB1, 0x00); + + /* Force tranceiver to high-speed */ + net2272_write(dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED); + + net2272_write(dev, PAGESEL, 0); + net2272_write(dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT); + net2272_write(dev, EP_RSPCLR, + (1 << CONTROL_STATUS_PHASE_HANDSHAKE) + | (1 << HIDE_STATUS_PHASE)); + net2272_write(dev, EP_CFG, 1 << ENDPOINT_DIRECTION); + net2272_write(dev, EP_STAT1, 1 << BUFFER_FLUSH); + + /* wait for status phase to complete */ + while (!(net2272_read(dev, EP_STAT0) & + (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))) + ; + + /* Enable test mode */ + net2272_write(dev, USBTEST, mode); + + /* load test packet */ + if (mode == TEST_PACKET) { + /* switch to 8 bit mode */ + net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) & + ~(1 << DATA_WIDTH)); + + for (i = 0; i < sizeof(net2272_test_packet); ++i) + net2272_write(dev, EP_DATA, net2272_test_packet[i]); + + /* Validate test packet */ + net2272_write(dev, EP_TRANSFER0, 0); + } +} + +static void +net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) +{ + struct net2272_ep *ep; + u8 num, scratch; + + /* starting a control request? */ + if (unlikely(stat & (1 << SETUP_PACKET_INTERRUPT))) { + union { + u8 raw[8]; + struct usb_ctrlrequest r; + } u; + int tmp = 0; + struct net2272_request *req; + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + if (net2272_read(dev, USBCTL1) & (1 << USB_HIGH_SPEED)) + dev->gadget.speed = USB_SPEED_HIGH; + else + dev->gadget.speed = USB_SPEED_FULL; + dev_dbg(dev->dev, "%s\n", + usb_speed_string(dev->gadget.speed)); + } + + ep = &dev->ep[0]; + ep->irqs++; + + /* make sure any leftover interrupt state is cleared */ + stat &= ~(1 << ENDPOINT_0_INTERRUPT); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + net2272_done(ep, req, + (req->req.actual == req->req.length) ? 0 : -EPROTO); + } + ep->stopped = 0; + dev->protocol_stall = 0; + net2272_ep_write(ep, EP_STAT0, + (1 << DATA_IN_TOKEN_INTERRUPT) + | (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); + net2272_ep_write(ep, EP_STAT1, + (1 << TIMEOUT) + | (1 << USB_OUT_ACK_SENT) + | (1 << USB_OUT_NAK_SENT) + | (1 << USB_IN_ACK_RCVD) + | (1 << USB_IN_NAK_SENT) + | (1 << USB_STALL_SENT) + | (1 << LOCAL_OUT_ZLP)); + + /* + * Ensure Control Read pre-validation setting is beyond maximum size + * - Control Writes can leave non-zero values in EP_TRANSFER. If + * an EP0 transfer following the Control Write is a Control Read, + * the NET2272 sees the non-zero EP_TRANSFER as an unexpected + * pre-validation count. + * - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures + * the pre-validation count cannot cause an unexpected validatation + */ + net2272_write(dev, PAGESEL, 0); + net2272_write(dev, EP_TRANSFER2, 0xff); + net2272_write(dev, EP_TRANSFER1, 0xff); + net2272_write(dev, EP_TRANSFER0, 0xff); + + u.raw[0] = net2272_read(dev, SETUP0); + u.raw[1] = net2272_read(dev, SETUP1); + u.raw[2] = net2272_read(dev, SETUP2); + u.raw[3] = net2272_read(dev, SETUP3); + u.raw[4] = net2272_read(dev, SETUP4); + u.raw[5] = net2272_read(dev, SETUP5); + u.raw[6] = net2272_read(dev, SETUP6); + u.raw[7] = net2272_read(dev, SETUP7); + /* + * If you have a big endian cpu make sure le16_to_cpus + * performs the proper byte swapping here... + */ + le16_to_cpus(&u.r.wValue); + le16_to_cpus(&u.r.wIndex); + le16_to_cpus(&u.r.wLength); + + /* ack the irq */ + net2272_write(dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT); + stat ^= (1 << SETUP_PACKET_INTERRUPT); + + /* watch control traffic at the token level, and force + * synchronization before letting the status phase happen. + */ + ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; + if (ep->is_in) { + scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) + | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); + stop_out_naking(ep); + } else + scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) + | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); + net2272_ep_write(ep, EP_IRQENB, scratch); + + if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto delegate; + switch (u.r.bRequest) { + case USB_REQ_GET_STATUS: { + struct net2272_ep *e; + u16 status = 0; + + switch (u.r.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_ENDPOINT: + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e || u.r.wLength > 2) + goto do_stall; + if (net2272_ep_read(e, EP_RSPSET) & (1 << ENDPOINT_HALT)) + status = __constant_cpu_to_le16(1); + else + status = __constant_cpu_to_le16(0); + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "%s stat %02x\n", + ep->ep.name, status); + goto next_endpoints; + case USB_RECIP_DEVICE: + if (u.r.wLength > 2) + goto do_stall; + if (dev->is_selfpowered) + status = (1 << USB_DEVICE_SELF_POWERED); + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "device stat %02x\n", status); + goto next_endpoints; + case USB_RECIP_INTERFACE: + if (u.r.wLength > 2) + goto do_stall; + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "interface status %02x\n", status); + goto next_endpoints; + } + + break; + } + case USB_REQ_CLEAR_FEATURE: { + struct net2272_ep *e; + + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT || + u.r.wLength != 0) + goto do_stall; + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e) + goto do_stall; + if (e->wedged) { + dev_vdbg(dev->dev, "%s wedged, halt not cleared\n", + ep->ep.name); + } else { + dev_vdbg(dev->dev, "%s clear halt\n", ep->ep.name); + clear_halt(e); + } + allow_status(ep); + goto next_endpoints; + } + case USB_REQ_SET_FEATURE: { + struct net2272_ep *e; + + if (u.r.bRequestType == USB_RECIP_DEVICE) { + if (u.r.wIndex != NORMAL_OPERATION) + net2272_set_test_mode(dev, (u.r.wIndex >> 8)); + allow_status(ep); + dev_vdbg(dev->dev, "test mode: %d\n", u.r.wIndex); + goto next_endpoints; + } else if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT || + u.r.wLength != 0) + goto do_stall; + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e) + goto do_stall; + set_halt(e); + allow_status(ep); + dev_vdbg(dev->dev, "%s set halt\n", ep->ep.name); + goto next_endpoints; + } + case USB_REQ_SET_ADDRESS: { + net2272_write(dev, OURADDR, u.r.wValue & 0xff); + allow_status(ep); + break; + } + default: + delegate: + dev_vdbg(dev->dev, "setup %02x.%02x v%04x i%04x " + "ep_cfg %08x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, + net2272_ep_read(ep, EP_CFG)); + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &u.r); + spin_lock(&dev->lock); + } + + /* stall ep0 on error */ + if (tmp < 0) { + do_stall: + dev_vdbg(dev->dev, "req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, tmp); + dev->protocol_stall = 1; + } + /* endpoint dma irq? */ + } else if (stat & (1 << DMA_DONE_INTERRUPT)) { + net2272_cancel_dma(dev); + net2272_write(dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT); + stat &= ~(1 << DMA_DONE_INTERRUPT); + num = (net2272_read(dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT)) + ? 2 : 1; + + ep = &dev->ep[num]; + net2272_handle_dma(ep); + } + + next_endpoints: + /* endpoint data irq? */ + scratch = stat & 0x0f; + stat &= ~0x0f; + for (num = 0; scratch; num++) { + u8 t; + + /* does this endpoint's FIFO and queue need tending? */ + t = 1 << num; + if ((scratch & t) == 0) + continue; + scratch ^= t; + + ep = &dev->ep[num]; + net2272_handle_ep(ep); + } + + /* some interrupts we can just ignore */ + stat &= ~(1 << SOF_INTERRUPT); + + if (stat) + dev_dbg(dev->dev, "unhandled irqstat0 %02x\n", stat); +} + +static void +net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat) +{ + u8 tmp, mask; + + /* after disconnect there's nothing else to do! */ + tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); + mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED); + + if (stat & tmp) { + net2272_write(dev, IRQSTAT1, tmp); + if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && + ((net2272_read(dev, USBCTL1) & mask) == 0)) + || ((net2272_read(dev, USBCTL1) & (1 << VBUS_PIN)) + == 0)) + && (dev->gadget.speed != USB_SPEED_UNKNOWN)) { + dev_dbg(dev->dev, "disconnect %s\n", + dev->driver->driver.name); + stop_activity(dev, dev->driver); + net2272_ep0_start(dev); + return; + } + stat &= ~tmp; + + if (!stat) + return; + } + + tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); + if (stat & tmp) { + net2272_write(dev, IRQSTAT1, tmp); + if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + if (!enable_suspend) { + stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); + dev_dbg(dev->dev, "Suspend disabled, ignoring\n"); + } + } else { + if (dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + stat &= ~tmp; + } + + /* clear any other status/irqs */ + if (stat) + net2272_write(dev, IRQSTAT1, stat); + + /* some status we can just ignore */ + stat &= ~((1 << CONTROL_STATUS_INTERRUPT) + | (1 << SUSPEND_REQUEST_INTERRUPT) + | (1 << RESUME_INTERRUPT)); + if (!stat) + return; + else + dev_dbg(dev->dev, "unhandled irqstat1 %02x\n", stat); +} + +static irqreturn_t net2272_irq(int irq, void *_dev) +{ + struct net2272 *dev = _dev; +#if defined(PLX_PCI_RDK) || defined(PLX_PCI_RDK2) + u32 intcsr; +#endif +#if defined(PLX_PCI_RDK) + u8 dmareq; +#endif + spin_lock(&dev->lock); +#if defined(PLX_PCI_RDK) + intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); + + if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) { + writel(intcsr & ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); + net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); + intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); + writel(intcsr | (1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + } + if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) { + writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), + dev->rdk1.plx9054_base_addr + DMACSR0); + + dmareq = net2272_read(dev, DMAREQ); + if (dmareq & 0x01) + net2272_handle_dma(&dev->ep[2]); + else + net2272_handle_dma(&dev->ep[1]); + } +#endif +#if defined(PLX_PCI_RDK2) + /* see if PCI int for us by checking irqstat */ + intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT); + if (!intcsr & (1 << NET2272_PCI_IRQ)) { + spin_unlock(&dev->lock); + return IRQ_NONE; + } + /* check dma interrupts */ +#endif + /* Platform/devcice interrupt handler */ +#if !defined(PLX_PCI_RDK) + net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); + net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); +#endif + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +static int net2272_present(struct net2272 *dev) +{ + /* + * Quick test to see if CPU can communicate properly with the NET2272. + * Verifies connection using writes and reads to write/read and + * read-only registers. + * + * This routine is strongly recommended especially during early bring-up + * of new hardware, however for designs that do not apply Power On System + * Tests (POST) it may discarded (or perhaps minimized). + */ + unsigned int ii; + u8 val, refval; + + /* Verify NET2272 write/read SCRATCH register can write and read */ + refval = net2272_read(dev, SCRATCH); + for (ii = 0; ii < 0x100; ii += 7) { + net2272_write(dev, SCRATCH, ii); + val = net2272_read(dev, SCRATCH); + if (val != ii) { + dev_dbg(dev->dev, + "%s: write/read SCRATCH register test failed: " + "wrote:0x%2.2x, read:0x%2.2x\n", + __func__, ii, val); + return -EINVAL; + } + } + /* To be nice, we write the original SCRATCH value back: */ + net2272_write(dev, SCRATCH, refval); + + /* Verify NET2272 CHIPREV register is read-only: */ + refval = net2272_read(dev, CHIPREV_2272); + for (ii = 0; ii < 0x100; ii += 7) { + net2272_write(dev, CHIPREV_2272, ii); + val = net2272_read(dev, CHIPREV_2272); + if (val != refval) { + dev_dbg(dev->dev, + "%s: write/read CHIPREV register test failed: " + "wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n", + __func__, ii, val, refval); + return -EINVAL; + } + } + + /* + * Verify NET2272's "NET2270 legacy revision" register + * - NET2272 has two revision registers. The NET2270 legacy revision + * register should read the same value, regardless of the NET2272 + * silicon revision. The legacy register applies to NET2270 + * firmware being applied to the NET2272. + */ + val = net2272_read(dev, CHIPREV_LEGACY); + if (val != NET2270_LEGACY_REV) { + /* + * Unexpected legacy revision value + * - Perhaps the chip is a NET2270? + */ + dev_dbg(dev->dev, + "%s: WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n" + " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n", + __func__, NET2270_LEGACY_REV, val); + return -EINVAL; + } + + /* + * Verify NET2272 silicon revision + * - This revision register is appropriate for the silicon version + * of the NET2272 + */ + val = net2272_read(dev, CHIPREV_2272); + switch (val) { + case CHIPREV_NET2272_R1: + /* + * NET2272 Rev 1 has DMA related errata: + * - Newer silicon (Rev 1A or better) required + */ + dev_dbg(dev->dev, + "%s: Rev 1 detected: newer silicon recommended for DMA support\n", + __func__); + break; + case CHIPREV_NET2272_R1A: + break; + default: + /* NET2272 silicon version *may* not work with this firmware */ + dev_dbg(dev->dev, + "%s: unexpected silicon revision register value: " + " CHIPREV_2272: 0x%2.2x\n", + __func__, val); + /* + * Return Success, even though the chip rev is not an expected value + * - Older, pre-built firmware can attempt to operate on newer silicon + * - Often, new silicon is perfectly compatible + */ + } + + /* Success: NET2272 checks out OK */ + return 0; +} + +static void +net2272_gadget_release(struct device *_dev) +{ + struct net2272 *dev = dev_get_drvdata(_dev); + kfree(dev); +} + +/*---------------------------------------------------------------------------*/ + +static void +net2272_remove(struct net2272 *dev) +{ + usb_del_gadget_udc(&dev->gadget); + + /* start with the driver above us */ + if (dev->driver) { + /* should have been done already by driver model core */ + dev_warn(dev->dev, "pci remove, driver '%s' is still registered\n", + dev->driver->driver.name); + usb_gadget_unregister_driver(dev->driver); + } + + free_irq(dev->irq, dev); + iounmap(dev->base_addr); + + device_remove_file(dev->dev, &dev_attr_registers); + + dev_info(dev->dev, "unbind\n"); +} + +static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) +{ + struct net2272 *ret; + + if (!irq) { + dev_dbg(dev, "No IRQ!\n"); + return ERR_PTR(-ENODEV); + } + + /* alloc, and start init */ + ret = kzalloc(sizeof(*ret), GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ret->lock); + ret->irq = irq; + ret->dev = dev; + ret->gadget.ops = &net2272_ops; + ret->gadget.max_speed = USB_SPEED_HIGH; + + /* the "gadget" abstracts/virtualizes the controller */ + ret->gadget.name = driver_name; + + return ret; +} + +static int +net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) +{ + int ret; + + /* See if there... */ + if (net2272_present(dev)) { + dev_warn(dev->dev, "2272 not found!\n"); + ret = -ENODEV; + goto err; + } + + net2272_usb_reset(dev); + net2272_usb_reinit(dev); + + ret = request_irq(dev->irq, net2272_irq, irqflags, driver_name, dev); + if (ret) { + dev_err(dev->dev, "request interrupt %i failed\n", dev->irq); + goto err; + } + + dev->chiprev = net2272_read(dev, CHIPREV_2272); + + /* done */ + dev_info(dev->dev, "%s\n", driver_desc); + dev_info(dev->dev, "irq %i, mem %p, chip rev %04x, dma %s\n", + dev->irq, dev->base_addr, dev->chiprev, + dma_mode_string()); + dev_info(dev->dev, "version: %s\n", driver_vers); + + ret = device_create_file(dev->dev, &dev_attr_registers); + if (ret) + goto err_irq; + + ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget, + net2272_gadget_release); + if (ret) + goto err_add_udc; + + return 0; + +err_add_udc: + device_remove_file(dev->dev, &dev_attr_registers); + err_irq: + free_irq(dev->irq, dev); + err: + return ret; +} + +#ifdef CONFIG_PCI + +/* + * wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us + */ + +static int +net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev) +{ + unsigned long resource, len, tmp; + void __iomem *mem_mapped_addr[4]; + int ret, i; + + /* + * BAR 0 holds PLX 9054 config registers + * BAR 1 is i/o memory; unused here + * BAR 2 holds EPLD config registers + * BAR 3 holds NET2272 registers + */ + + /* Find and map all address spaces */ + for (i = 0; i < 4; ++i) { + if (i == 1) + continue; /* BAR1 unused */ + + resource = pci_resource_start(pdev, i); + len = pci_resource_len(pdev, i); + + if (!request_mem_region(resource, len, driver_name)) { + dev_dbg(dev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err; + } + + mem_mapped_addr[i] = ioremap_nocache(resource, len); + if (mem_mapped_addr[i] == NULL) { + release_mem_region(resource, len); + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err; + } + } + + dev->rdk1.plx9054_base_addr = mem_mapped_addr[0]; + dev->rdk1.epld_base_addr = mem_mapped_addr[2]; + dev->base_addr = mem_mapped_addr[3]; + + /* Set PLX 9054 bus width (16 bits) */ + tmp = readl(dev->rdk1.plx9054_base_addr + LBRD1); + writel((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT, + dev->rdk1.plx9054_base_addr + LBRD1); + + /* Enable PLX 9054 Interrupts */ + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) | + (1 << PCI_INTERRUPT_ENABLE) | + (1 << LOCAL_INTERRUPT_INPUT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + + writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), + dev->rdk1.plx9054_base_addr + DMACSR0); + + /* reset */ + writeb((1 << EPLD_DMA_ENABLE) | + (1 << DMA_CTL_DACK) | + (1 << DMA_TIMEOUT_ENABLE) | + (1 << USER) | + (0 << MPX_MODE) | + (1 << BUSWIDTH) | + (1 << NET2272_RESET), + dev->base_addr + EPLD_IO_CONTROL_REGISTER); + + mb(); + writeb(readb(dev->base_addr + EPLD_IO_CONTROL_REGISTER) & + ~(1 << NET2272_RESET), + dev->base_addr + EPLD_IO_CONTROL_REGISTER); + udelay(200); + + return 0; + + err: + while (--i >= 0) { + iounmap(mem_mapped_addr[i]); + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } + + return ret; +} + +static int +net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev) +{ + unsigned long resource, len; + void __iomem *mem_mapped_addr[2]; + int ret, i; + + /* + * BAR 0 holds FGPA config registers + * BAR 1 holds NET2272 registers + */ + + /* Find and map all address spaces, bar2-3 unused in rdk 2 */ + for (i = 0; i < 2; ++i) { + resource = pci_resource_start(pdev, i); + len = pci_resource_len(pdev, i); + + if (!request_mem_region(resource, len, driver_name)) { + dev_dbg(dev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err; + } + + mem_mapped_addr[i] = ioremap_nocache(resource, len); + if (mem_mapped_addr[i] == NULL) { + release_mem_region(resource, len); + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err; + } + } + + dev->rdk2.fpga_base_addr = mem_mapped_addr[0]; + dev->base_addr = mem_mapped_addr[1]; + + mb(); + /* Set 2272 bus width (16 bits) and reset */ + writel((1 << CHIP_RESET), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); + udelay(200); + writel((1 << BUS_WIDTH), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); + /* Print fpga version number */ + dev_info(dev->dev, "RDK2 FPGA version %08x\n", + readl(dev->rdk2.fpga_base_addr + RDK2_FPGAREV)); + /* Enable FPGA Interrupts */ + writel((1 << NET2272_PCI_IRQ), dev->rdk2.fpga_base_addr + RDK2_IRQENB); + + return 0; + + err: + while (--i >= 0) { + iounmap(mem_mapped_addr[i]); + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } + + return ret; +} + +static int +net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net2272 *dev; + int ret; + + dev = net2272_probe_init(&pdev->dev, pdev->irq); + if (IS_ERR(dev)) + return PTR_ERR(dev); + dev->dev_id = pdev->device; + + if (pci_enable_device(pdev) < 0) { + ret = -ENODEV; + goto err_free; + } + + pci_set_master(pdev); + + switch (pdev->device) { + case PCI_DEVICE_ID_RDK1: ret = net2272_rdk1_probe(pdev, dev); break; + case PCI_DEVICE_ID_RDK2: ret = net2272_rdk2_probe(pdev, dev); break; + default: BUG(); + } + if (ret) + goto err_pci; + + ret = net2272_probe_fin(dev, 0); + if (ret) + goto err_pci; + + pci_set_drvdata(pdev, dev); + + return 0; + + err_pci: + pci_disable_device(pdev); + err_free: + kfree(dev); + + return ret; +} + +static void +net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev) +{ + int i; + + /* disable PLX 9054 interrupts */ + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & + ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + + /* clean up resources allocated during probe() */ + iounmap(dev->rdk1.plx9054_base_addr); + iounmap(dev->rdk1.epld_base_addr); + + for (i = 0; i < 4; ++i) { + if (i == 1) + continue; /* BAR1 unused */ + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } +} + +static void +net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev) +{ + int i; + + /* disable fpga interrupts + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & + ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + */ + + /* clean up resources allocated during probe() */ + iounmap(dev->rdk2.fpga_base_addr); + + for (i = 0; i < 2; ++i) + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); +} + +static void +net2272_pci_remove(struct pci_dev *pdev) +{ + struct net2272 *dev = pci_get_drvdata(pdev); + + net2272_remove(dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_RDK1: net2272_rdk1_remove(pdev, dev); break; + case PCI_DEVICE_ID_RDK2: net2272_rdk2_remove(pdev, dev); break; + default: BUG(); + } + + pci_disable_device(pdev); + + kfree(dev); +} + +/* Table of matching PCI IDs */ +static struct pci_device_id pci_ids[] = { + { /* RDK 1 card */ + .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), + .class_mask = 0, + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_RDK1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { /* RDK 2 card */ + .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), + .class_mask = 0, + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_RDK2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver net2272_pci_driver = { + .name = driver_name, + .id_table = pci_ids, + + .probe = net2272_pci_probe, + .remove = net2272_pci_remove, +}; + +static int net2272_pci_register(void) +{ + return pci_register_driver(&net2272_pci_driver); +} + +static void net2272_pci_unregister(void) +{ + pci_unregister_driver(&net2272_pci_driver); +} + +#else +static inline int net2272_pci_register(void) { return 0; } +static inline void net2272_pci_unregister(void) { } +#endif + +/*---------------------------------------------------------------------------*/ + +static int +net2272_plat_probe(struct platform_device *pdev) +{ + struct net2272 *dev; + int ret; + unsigned int irqflags; + resource_size_t base, len; + struct resource *iomem, *iomem_bus, *irq_res; + + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem_bus = platform_get_resource(pdev, IORESOURCE_BUS, 0); + if (!irq_res || !iomem) { + dev_err(&pdev->dev, "must provide irq/base addr"); + return -EINVAL; + } + + dev = net2272_probe_init(&pdev->dev, irq_res->start); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + irqflags = 0; + if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) + irqflags |= IRQF_TRIGGER_RISING; + if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) + irqflags |= IRQF_TRIGGER_FALLING; + if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) + irqflags |= IRQF_TRIGGER_HIGH; + if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) + irqflags |= IRQF_TRIGGER_LOW; + + base = iomem->start; + len = resource_size(iomem); + if (iomem_bus) + dev->base_shift = iomem_bus->start; + + if (!request_mem_region(base, len, driver_name)) { + dev_dbg(dev->dev, "get request memory region!\n"); + ret = -EBUSY; + goto err; + } + dev->base_addr = ioremap_nocache(base, len); + if (!dev->base_addr) { + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err_req; + } + + ret = net2272_probe_fin(dev, IRQF_TRIGGER_LOW); + if (ret) + goto err_io; + + platform_set_drvdata(pdev, dev); + dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", + (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); + + return 0; + + err_io: + iounmap(dev->base_addr); + err_req: + release_mem_region(base, len); + err: + return ret; +} + +static int +net2272_plat_remove(struct platform_device *pdev) +{ + struct net2272 *dev = platform_get_drvdata(pdev); + + net2272_remove(dev); + + release_mem_region(pdev->resource[0].start, + resource_size(&pdev->resource[0])); + + kfree(dev); + + return 0; +} + +static struct platform_driver net2272_plat_driver = { + .probe = net2272_plat_probe, + .remove = net2272_plat_remove, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, + /* FIXME .suspend, .resume */ +}; +MODULE_ALIAS("platform:net2272"); + +static int __init net2272_init(void) +{ + int ret; + + ret = net2272_pci_register(); + if (ret) + return ret; + ret = platform_driver_register(&net2272_plat_driver); + if (ret) + goto err_pci; + return ret; + +err_pci: + net2272_pci_unregister(); + return ret; +} +module_init(net2272_init); + +static void __exit net2272_cleanup(void) +{ + net2272_pci_unregister(); + platform_driver_unregister(&net2272_plat_driver); +} +module_exit(net2272_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("PLX Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/net2272.h new file mode 100644 index 00000000000..e5950578935 --- /dev/null +++ b/drivers/usb/gadget/net2272.h @@ -0,0 +1,601 @@ +/* + * PLX NET2272 high/full speed USB device controller + * + * Copyright (C) 2005-2006 PLX Technology, Inc. + * Copyright (C) 2006-2011 Analog Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __NET2272_H__ +#define __NET2272_H__ + +/* Main Registers */ +#define REGADDRPTR 0x00 +#define REGDATA 0x01 +#define IRQSTAT0 0x02 +#define ENDPOINT_0_INTERRUPT 0 +#define ENDPOINT_A_INTERRUPT 1 +#define ENDPOINT_B_INTERRUPT 2 +#define ENDPOINT_C_INTERRUPT 3 +#define VIRTUALIZED_ENDPOINT_INTERRUPT 4 +#define SETUP_PACKET_INTERRUPT 5 +#define DMA_DONE_INTERRUPT 6 +#define SOF_INTERRUPT 7 +#define IRQSTAT1 0x03 +#define CONTROL_STATUS_INTERRUPT 1 +#define VBUS_INTERRUPT 2 +#define SUSPEND_REQUEST_INTERRUPT 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4 +#define RESUME_INTERRUPT 5 +#define ROOT_PORT_RESET_INTERRUPT 6 +#define RESET_STATUS 7 +#define PAGESEL 0x04 +#define DMAREQ 0x1c +#define DMA_ENDPOINT_SELECT 0 +#define DREQ_POLARITY 1 +#define DACK_POLARITY 2 +#define EOT_POLARITY 3 +#define DMA_CONTROL_DACK 4 +#define DMA_REQUEST_ENABLE 5 +#define DMA_REQUEST 6 +#define DMA_BUFFER_VALID 7 +#define SCRATCH 0x1d +#define IRQENB0 0x20 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4 +#define SETUP_PACKET_INTERRUPT_ENABLE 5 +#define DMA_DONE_INTERRUPT_ENABLE 6 +#define SOF_INTERRUPT_ENABLE 7 +#define IRQENB1 0x21 +#define VBUS_INTERRUPT_ENABLE 2 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4 +#define RESUME_INTERRUPT_ENABLE 5 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6 +#define LOCCTL 0x22 +#define DATA_WIDTH 0 +#define LOCAL_CLOCK_OUTPUT 1 +#define LOCAL_CLOCK_OUTPUT_OFF 0 +#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1 +#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2 +#define LOCAL_CLOCK_OUTPUT_15MHZ 3 +#define LOCAL_CLOCK_OUTPUT_30MHZ 4 +#define LOCAL_CLOCK_OUTPUT_60MHZ 5 +#define DMA_SPLIT_BUS_MODE 4 +#define BYTE_SWAP 5 +#define BUFFER_CONFIGURATION 6 +#define BUFFER_CONFIGURATION_EPA512_EPB512 0 +#define BUFFER_CONFIGURATION_EPA1024_EPB512 1 +#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2 +#define BUFFER_CONFIGURATION_EPA1024DB 3 +#define CHIPREV_LEGACY 0x23 +#define NET2270_LEGACY_REV 0x40 +#define LOCCTL1 0x24 +#define DMA_MODE 0 +#define SLOW_DREQ 0 +#define FAST_DREQ 1 +#define BURST_MODE 2 +#define DMA_DACK_ENABLE 2 +#define CHIPREV_2272 0x25 +#define CHIPREV_NET2272_R1 0x10 +#define CHIPREV_NET2272_R1A 0x11 +/* USB Registers */ +#define USBCTL0 0x18 +#define IO_WAKEUP_ENABLE 1 +#define USB_DETECT_ENABLE 3 +#define USB_ROOT_PORT_WAKEUP_ENABLE 5 +#define USBCTL1 0x19 +#define VBUS_PIN 0 +#define USB_FULL_SPEED 1 +#define USB_HIGH_SPEED 2 +#define GENERATE_RESUME 3 +#define VIRTUAL_ENDPOINT_ENABLE 4 +#define FRAME0 0x1a +#define FRAME1 0x1b +#define OURADDR 0x30 +#define FORCE_IMMEDIATE 7 +#define USBDIAG 0x31 +#define FORCE_TRANSMIT_CRC_ERROR 0 +#define PREVENT_TRANSMIT_BIT_STUFF 1 +#define FORCE_RECEIVE_ERROR 2 +#define FAST_TIMES 4 +#define USBTEST 0x32 +#define TEST_MODE_SELECT 0 +#define NORMAL_OPERATION 0 +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_ENABLE 5 +#define XCVRDIAG 0x33 +#define FORCE_FULL_SPEED 2 +#define FORCE_HIGH_SPEED 3 +#define OPMODE 4 +#define NORMAL_OPERATION 0 +#define NON_DRIVING 1 +#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2 +#define LINESTATE 6 +#define SE0_STATE 0 +#define J_STATE 1 +#define K_STATE 2 +#define SE1_STATE 3 +#define VIRTOUT0 0x34 +#define VIRTOUT1 0x35 +#define VIRTIN0 0x36 +#define VIRTIN1 0x37 +#define SETUP0 0x40 +#define SETUP1 0x41 +#define SETUP2 0x42 +#define SETUP3 0x43 +#define SETUP4 0x44 +#define SETUP5 0x45 +#define SETUP6 0x46 +#define SETUP7 0x47 +/* Endpoint Registers (Paged via PAGESEL) */ +#define EP_DATA 0x05 +#define EP_STAT0 0x06 +#define DATA_IN_TOKEN_INTERRUPT 0 +#define DATA_OUT_TOKEN_INTERRUPT 1 +#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 +#define DATA_PACKET_RECEIVED_INTERRUPT 3 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4 +#define NAK_OUT_PACKETS 5 +#define BUFFER_EMPTY 6 +#define BUFFER_FULL 7 +#define EP_STAT1 0x07 +#define TIMEOUT 0 +#define USB_OUT_ACK_SENT 1 +#define USB_OUT_NAK_SENT 2 +#define USB_IN_ACK_RCVD 3 +#define USB_IN_NAK_SENT 4 +#define USB_STALL_SENT 5 +#define LOCAL_OUT_ZLP 6 +#define BUFFER_FLUSH 7 +#define EP_TRANSFER0 0x08 +#define EP_TRANSFER1 0x09 +#define EP_TRANSFER2 0x0a +#define EP_IRQENB 0x0b +#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 +#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1 +#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 +#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4 +#define EP_AVAIL0 0x0c +#define EP_AVAIL1 0x0d +#define EP_RSPCLR 0x0e +#define EP_RSPSET 0x0f +#define ENDPOINT_HALT 0 +#define ENDPOINT_TOGGLE 1 +#define NAK_OUT_PACKETS_MODE 2 +#define CONTROL_STATUS_PHASE_HANDSHAKE 3 +#define INTERRUPT_MODE 4 +#define AUTOVALIDATE 5 +#define HIDE_STATUS_PHASE 6 +#define ALT_NAK_OUT_PACKETS 7 +#define EP_MAXPKT0 0x28 +#define EP_MAXPKT1 0x29 +#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3 +#define NONE_ADDITIONAL_TRANSACTION 0 +#define ONE_ADDITIONAL_TRANSACTION 1 +#define TWO_ADDITIONAL_TRANSACTION 2 +#define EP_CFG 0x2a +#define ENDPOINT_NUMBER 0 +#define ENDPOINT_DIRECTION 4 +#define ENDPOINT_TYPE 5 +#define ENDPOINT_ENABLE 7 +#define EP_HBW 0x2b +#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0 +#define DATA0_PID 0 +#define DATA1_PID 1 +#define DATA2_PID 2 +#define MDATA_PID 3 +#define EP_BUFF_STATES 0x2c +#define BUFFER_A_STATE 0 +#define BUFFER_B_STATE 2 +#define BUFF_FREE 0 +#define BUFF_VALID 1 +#define BUFF_LCL 2 +#define BUFF_USB 3 + +/*---------------------------------------------------------------------------*/ + +#define PCI_DEVICE_ID_RDK1 0x9054 + +/* PCI-RDK EPLD Registers */ +#define RDK_EPLD_IO_REGISTER1 0x00000000 +#define RDK_EPLD_USB_RESET 0 +#define RDK_EPLD_USB_POWERDOWN 1 +#define RDK_EPLD_USB_WAKEUP 2 +#define RDK_EPLD_USB_EOT 3 +#define RDK_EPLD_DPPULL 4 +#define RDK_EPLD_IO_REGISTER2 0x00000004 +#define RDK_EPLD_BUSWIDTH 0 +#define RDK_EPLD_USER 2 +#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 +#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 +#define RDK_EPLD_STATUS_REGISTER 0x00000008 +#define RDK_EPLD_USB_LRESET 0 +#define RDK_EPLD_REVISION_REGISTER 0x0000000c + +/* PCI-RDK PLX 9054 Registers */ +#define INTCSR 0x68 +#define PCI_INTERRUPT_ENABLE 8 +#define LOCAL_INTERRUPT_INPUT_ENABLE 11 +#define LOCAL_INPUT_INTERRUPT_ACTIVE 15 +#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18 +#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19 +#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21 +#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22 +#define CNTRL 0x6C +#define RELOAD_CONFIGURATION_REGISTERS 29 +#define PCI_ADAPTER_SOFTWARE_RESET 30 +#define DMAMODE0 0x80 +#define LOCAL_BUS_WIDTH 0 +#define INTERNAL_WAIT_STATES 2 +#define TA_READY_INPUT_ENABLE 6 +#define LOCAL_BURST_ENABLE 8 +#define SCATTER_GATHER_MODE 9 +#define DONE_INTERRUPT_ENABLE 10 +#define LOCAL_ADDRESSING_MODE 11 +#define DEMAND_MODE 12 +#define DMA_EOT_ENABLE 14 +#define FAST_SLOW_TERMINATE_MODE_SELECT 15 +#define DMA_CHANNEL_INTERRUPT_SELECT 17 +#define DMAPADR0 0x84 +#define DMALADR0 0x88 +#define DMASIZ0 0x8c +#define DMADPR0 0x90 +#define DESCRIPTOR_LOCATION 0 +#define END_OF_CHAIN 1 +#define INTERRUPT_AFTER_TERMINAL_COUNT 2 +#define DIRECTION_OF_TRANSFER 3 +#define DMACSR0 0xa8 +#define CHANNEL_ENABLE 0 +#define CHANNEL_START 1 +#define CHANNEL_ABORT 2 +#define CHANNEL_CLEAR_INTERRUPT 3 +#define CHANNEL_DONE 4 +#define DMATHR 0xb0 +#define LBRD1 0xf8 +#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0 +#define W8_BIT 0 +#define W16_BIT 1 + +/* Special OR'ing of INTCSR bits */ +#define LOCAL_INTERRUPT_TEST \ + ((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_INTERRUPT_INPUT_ENABLE)) + +#define DMA_CHANNEL_0_TEST \ + ((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE)) + +#define DMA_CHANNEL_1_TEST \ + ((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE)) + +/* EPLD Registers */ +#define RDK_EPLD_IO_REGISTER1 0x00000000 +#define RDK_EPLD_USB_RESET 0 +#define RDK_EPLD_USB_POWERDOWN 1 +#define RDK_EPLD_USB_WAKEUP 2 +#define RDK_EPLD_USB_EOT 3 +#define RDK_EPLD_DPPULL 4 +#define RDK_EPLD_IO_REGISTER2 0x00000004 +#define RDK_EPLD_BUSWIDTH 0 +#define RDK_EPLD_USER 2 +#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 +#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 +#define RDK_EPLD_STATUS_REGISTER 0x00000008 +#define RDK_EPLD_USB_LRESET 0 +#define RDK_EPLD_REVISION_REGISTER 0x0000000c + +#define EPLD_IO_CONTROL_REGISTER 0x400 +#define NET2272_RESET 0 +#define BUSWIDTH 1 +#define MPX_MODE 3 +#define USER 4 +#define DMA_TIMEOUT_ENABLE 5 +#define DMA_CTL_DACK 6 +#define EPLD_DMA_ENABLE 7 +#define EPLD_DMA_CONTROL_REGISTER 0x800 +#define SPLIT_DMA_MODE 0 +#define SPLIT_DMA_DIRECTION 1 +#define SPLIT_DMA_ENABLE 2 +#define SPLIT_DMA_INTERRUPT_ENABLE 3 +#define SPLIT_DMA_INTERRUPT 4 +#define EPLD_DMA_MODE 5 +#define EPLD_DMA_CONTROLLER_ENABLE 7 +#define SPLIT_DMA_ADDRESS_LOW 0xc00 +#define SPLIT_DMA_ADDRESS_HIGH 0x1000 +#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400 +#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800 +#define EPLD_REVISION_REGISTER 0x1c00 +#define SPLIT_DMA_RAM 0x4000 +#define DMA_RAM_SIZE 0x1000 + +/*---------------------------------------------------------------------------*/ + +#define PCI_DEVICE_ID_RDK2 0x3272 + +/* PCI-RDK version 2 registers */ + +/* Main Control Registers */ + +#define RDK2_IRQENB 0x00 +#define RDK2_IRQSTAT 0x04 +#define PB7 23 +#define PB6 22 +#define PB5 21 +#define PB4 20 +#define PB3 19 +#define PB2 18 +#define PB1 17 +#define PB0 16 +#define GP3 23 +#define GP2 23 +#define GP1 23 +#define GP0 23 +#define DMA_RETRY_ABORT 6 +#define DMA_PAUSE_DONE 5 +#define DMA_ABORT_DONE 4 +#define DMA_OUT_FIFO_TRANSFER_DONE 3 +#define DMA_LOCAL_DONE 2 +#define DMA_PCI_DONE 1 +#define NET2272_PCI_IRQ 0 + +#define RDK2_LOCCTLRDK 0x08 +#define CHIP_RESET 3 +#define SPLIT_DMA 2 +#define MULTIPLEX_MODE 1 +#define BUS_WIDTH 0 + +#define RDK2_GPIOCTL 0x10 +#define GP3_OUT_ENABLE 7 +#define GP2_OUT_ENABLE 6 +#define GP1_OUT_ENABLE 5 +#define GP0_OUT_ENABLE 4 +#define GP3_DATA 3 +#define GP2_DATA 2 +#define GP1_DATA 1 +#define GP0_DATA 0 + +#define RDK2_LEDSW 0x14 +#define LED3 27 +#define LED2 26 +#define LED1 25 +#define LED0 24 +#define PBUTTON 16 +#define DIPSW 0 + +#define RDK2_DIAG 0x18 +#define RDK2_FAST_TIMES 2 +#define FORCE_PCI_SERR 1 +#define FORCE_PCI_INT 0 +#define RDK2_FPGAREV 0x1C + +/* Dma Control registers */ +#define RDK2_DMACTL 0x80 +#define ADDR_HOLD 24 +#define RETRY_COUNT 16 /* 23:16 */ +#define FIFO_THRESHOLD 11 /* 15:11 */ +#define MEM_WRITE_INVALIDATE 10 +#define READ_MULTIPLE 9 +#define READ_LINE 8 +#define RDK2_DMA_MODE 6 /* 7:6 */ +#define CONTROL_DACK 5 +#define EOT_ENABLE 4 +#define EOT_POLARITY 3 +#define DACK_POLARITY 2 +#define DREQ_POLARITY 1 +#define DMA_ENABLE 0 + +#define RDK2_DMASTAT 0x84 +#define GATHER_COUNT 12 /* 14:12 */ +#define FIFO_COUNT 6 /* 11:6 */ +#define FIFO_FLUSH 5 +#define FIFO_TRANSFER 4 +#define PAUSE_DONE 3 +#define ABORT_DONE 2 +#define DMA_ABORT 1 +#define DMA_START 0 + +#define RDK2_DMAPCICOUNT 0x88 +#define DMA_DIRECTION 31 +#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */ + +#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */ + +#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */ + +/*---------------------------------------------------------------------------*/ + +#define REG_INDEXED_THRESHOLD (1 << 5) + +/* DRIVER DATA STRUCTURES and UTILITIES */ +struct net2272_ep { + struct usb_ep ep; + struct net2272 *dev; + unsigned long irqs; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num:8, + fifo_size:12, + stopped:1, + wedged:1, + is_in:1, + is_iso:1, + dma:1, + not_empty:1; +}; + +struct net2272 { + /* each device provides one gadget, several endpoints */ + struct usb_gadget gadget; + struct device *dev; + unsigned short dev_id; + + spinlock_t lock; + struct net2272_ep ep[4]; + struct usb_gadget_driver *driver; + unsigned protocol_stall:1, + softconnect:1, + is_selfpowered:1, + wakeup:1, + dma_eot_polarity:1, + dma_dack_polarity:1, + dma_dreq_polarity:1, + dma_busy:1; + u16 chiprev; + u8 pagesel; + + unsigned int irq; + unsigned short fifo_mode; + + unsigned int base_shift; + u16 __iomem *base_addr; + union { +#ifdef CONFIG_PCI + struct { + void __iomem *plx9054_base_addr; + void __iomem *epld_base_addr; + } rdk1; + struct { + /* Bar0, Bar1 is base_addr both mem-mapped */ + void __iomem *fpga_base_addr; + } rdk2; +#endif + }; +}; + +static void __iomem * +net2272_reg_addr(struct net2272 *dev, unsigned int reg) +{ + return dev->base_addr + (reg << dev->base_shift); +} + +static void +net2272_write(struct net2272 *dev, unsigned int reg, u8 value) +{ + if (reg >= REG_INDEXED_THRESHOLD) { + /* + * Indexed register; use REGADDRPTR/REGDATA + * - Save and restore REGADDRPTR. This prevents REGADDRPTR from + * changes between other code sections, but it is time consuming. + * - Performance tips: either do not save and restore REGADDRPTR (if it + * is safe) or do save/restore operations only in critical sections. + u8 tmp = readb(dev->base_addr + REGADDRPTR); + */ + writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); + writeb(value, net2272_reg_addr(dev, REGDATA)); + /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ + } else + writeb(value, net2272_reg_addr(dev, reg)); +} + +static u8 +net2272_read(struct net2272 *dev, unsigned int reg) +{ + u8 ret; + + if (reg >= REG_INDEXED_THRESHOLD) { + /* + * Indexed register; use REGADDRPTR/REGDATA + * - Save and restore REGADDRPTR. This prevents REGADDRPTR from + * changes between other code sections, but it is time consuming. + * - Performance tips: either do not save and restore REGADDRPTR (if it + * is safe) or do save/restore operations only in critical sections. + u8 tmp = readb(dev->base_addr + REGADDRPTR); + */ + writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); + ret = readb(net2272_reg_addr(dev, REGDATA)); + /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ + } else + ret = readb(net2272_reg_addr(dev, reg)); + + return ret; +} + +static void +net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value) +{ + struct net2272 *dev = ep->dev; + + if (dev->pagesel != ep->num) { + net2272_write(dev, PAGESEL, ep->num); + dev->pagesel = ep->num; + } + net2272_write(dev, reg, value); +} + +static u8 +net2272_ep_read(struct net2272_ep *ep, unsigned int reg) +{ + struct net2272 *dev = ep->dev; + + if (dev->pagesel != ep->num) { + net2272_write(dev, PAGESEL, ep->num); + dev->pagesel = ep->num; + } + return net2272_read(dev, reg); +} + +static void allow_status(struct net2272_ep *ep) +{ + /* ep0 only */ + net2272_ep_write(ep, EP_RSPCLR, + (1 << CONTROL_STATUS_PHASE_HANDSHAKE) | + (1 << ALT_NAK_OUT_PACKETS) | + (1 << NAK_OUT_PACKETS_MODE)); + ep->stopped = 1; +} + +static void set_halt(struct net2272_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE); + net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT); +} + +static void clear_halt(struct net2272_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + net2272_ep_write(ep, EP_RSPCLR, + (1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE)); +} + +/* count (<= 4) bytes in the next fifo write will be valid */ +static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count) +{ + /* net2272_ep_write will truncate to u8 for us */ + net2272_ep_write(ep, EP_TRANSFER2, count >> 16); + net2272_ep_write(ep, EP_TRANSFER1, count >> 8); + net2272_ep_write(ep, EP_TRANSFER0, count); +} + +struct net2272_request { + struct usb_request req; + struct list_head queue; + unsigned mapped:1, + valid:1; +}; + +#endif diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 9498be87a72..300b3a71383 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -9,7 +9,7 @@ * CODE STATUS HIGHLIGHTS * * This driver should work well with most "gadget" drivers, including - * the File Storage, Serial, and Ethernet/RNDIS gadget drivers + * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers * as well as Gadget Zero and Gadgetfs. * * DMA is enabled by default. Drivers using transfer queues might use @@ -33,15 +33,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG /* messages on error and most fault paths */ @@ -63,18 +54,17 @@ #include <linux/device.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/prefetch.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/unaligned.h> #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 */ @@ -98,8 +88,8 @@ static const char *const ep_name [] = { * Some gadget drivers work better with the dma support here than others. * These two parameters let you use PIO or more aggressive DMA. */ -static int use_dma = 1; -static int use_dma_chaining = 0; +static bool use_dma = 1; +static bool use_dma_chaining = 0; /* "modprobe net2280 use_dma=n" etc */ module_param (use_dma, bool, S_IRUGO); @@ -117,14 +107,18 @@ module_param (fifo_mode, ushort, 0644); /* enable_suspend -- When enabled, the driver will respond to * USB suspend requests by powering down the NET2280. Otherwise, - * USB suspend requests will be ignored. This is acceptible for + * USB suspend requests will be ignored. This is acceptable for * self-powered devices */ -static int enable_suspend = 0; +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") @@ -135,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 @@ -168,7 +162,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EDOM; /* sanity check ep-e/ep-f since their fifos are small */ - max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp (desc) & 0x1fff; if (ep->num > 4 && max > 64) return -ERANGE; @@ -299,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... */ @@ -411,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 */ @@ -425,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; } @@ -814,12 +806,8 @@ done (struct net2280_ep *ep, struct net2280_request *req, int status) status = req->req.status; dev = ep->dev; - if (req->mapped) { - pci_unmap_single (dev->pdev, req->req.dma, req->req.length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); if (status && status != -ESHUTDOWN) VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", @@ -865,10 +853,13 @@ net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -EOPNOTSUPP; /* set up dma mapping in case the caller didn't */ - if (ep->dma && _req->dma == DMA_ADDR_INVALID) { - _req->dma = pci_map_single (dev->pdev, _req->buf, _req->length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->mapped = 1; + if (ep->dma) { + int ret; + + ret = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (ret) + return ret; } #if 0 @@ -1409,11 +1400,18 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) return 0; } +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); + static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, + .udc_start = net2280_start, + .udc_stop = net2280_stop, }; /*-------------------------------------------------------------------------*/ @@ -1426,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); @@ -1437,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; @@ -1592,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; @@ -1632,8 +1630,8 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) val = "intr"; break; default: val = "iso"; break; - }; val; }), - le16_to_cpu (d->wMaxPacketSize) & 0x1fff, + } val; }), + usb_endpoint_maxp (d) & 0x1fff, ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ @@ -1692,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 @@ -1737,62 +1735,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode) list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); } -/* just declare this in any driver that really need it */ -extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); - -/** - * net2280_set_fifo_mode - change allocation of fifo buffers - * @gadget: access to the net2280 device that will be updated - * @mode: 0 for default, four 1kB buffers (ep-a through ep-d); - * 1 for two 2kB buffers (ep-a and ep-b only); - * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c). - * - * returns zero on success, else negative errno. when this succeeds, - * the contents of gadget->ep_list may have changed. - * - * you may only call this function when endpoints a-d are all disabled. - * use it whenever extra hardware buffering can help performance, such - * as before enabling "high bandwidth" interrupt endpoints that use - * maxpacket bigger than 512 (when double buffering would otherwise - * be unavailable). - */ -int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) -{ - int i; - struct net2280 *dev; - int status = 0; - unsigned long flags; - - if (!gadget) - return -ENODEV; - dev = container_of (gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - - for (i = 1; i <= 4; i++) - if (dev->ep [i].desc) { - status = -EINVAL; - break; - } - if (mode < 0 || mode > 2) - status = -EINVAL; - if (status == 0) - set_fifo_mode (dev, mode); - spin_unlock_irqrestore (&dev->lock, flags); - - if (status == 0) { - if (mode == 1) - DEBUG (dev, "fifo: ep-a 2K, ep-b 2K\n"); - else if (mode == 2) - DEBUG (dev, "fifo: ep-a 2K, ep-b 1K, ep-c 1K\n"); - /* else all are 1K */ - } - return status; -} -EXPORT_SYMBOL (net2280_set_fifo_mode); - -/*-------------------------------------------------------------------------*/ - /* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second @@ -1802,8 +1744,6 @@ EXPORT_SYMBOL (net2280_set_fifo_mode); * perhaps to bind specific drivers to specific devices. */ -static struct net2280 *the_controller; - static void usb_reset (struct net2280 *dev) { u32 tmp; @@ -1865,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; @@ -1929,9 +1869,10 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_register_driver (struct usb_gadget_driver *driver) +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; int retval; unsigned i; @@ -1939,15 +1880,11 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) * "must not be used in normal operation" */ - if (!driver - || driver->speed != USB_SPEED_HIGH - || !driver->bind + if (!driver || driver->max_speed < USB_SPEED_HIGH || !driver->setup) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of (_gadget, struct net2280, gadget); for (i = 0; i < 7; i++) dev->ep [i].irqs = 0; @@ -1956,21 +1893,16 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; - retval = driver->bind (&dev->gadget); - if (retval) { - DEBUG (dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } 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. */ @@ -1988,12 +1920,9 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) 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; } -EXPORT_SYMBOL (usb_gadget_register_driver); static void stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) @@ -2013,43 +1942,41 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) /* report disconnect; the driver is already quiesced */ if (driver) { - spin_unlock (&dev->lock); - driver->disconnect (&dev->gadget); - spin_lock (&dev->lock); + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); } usb_reinit (dev); } -int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; + dev = container_of (_gadget, struct net2280, gadget); spin_lock_irqsave (&dev->lock, flags); stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - net2280_pullup (&dev->gadget, 0); - - driver->unbind (&dev->gadget); - 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; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -2141,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; @@ -2318,9 +2245,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) else dev->gadget.speed = USB_SPEED_FULL; net2280_led_speed (dev, dev->gadget.speed); - DEBUG (dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) - ? "high" : "full"); + DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } ep = &dev->ep [0]; @@ -2401,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; @@ -2429,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", @@ -2451,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; @@ -2533,7 +2458,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. - * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT * only indicates a change in the reset state). */ @@ -2731,6 +2656,8 @@ static void net2280_remove (struct pci_dev *pdev) { struct net2280 *dev = pci_get_drvdata (pdev); + usb_del_gadget_udc(&dev->gadget); + BUG_ON(dev->driver); /* then clean up the resources we allocated during probe() */ @@ -2754,13 +2681,9 @@ 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"); - - the_controller = NULL; } /* wrap this driver around the specified device, but @@ -2774,14 +2697,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval, i; - /* if you want to support more than one controller in a system, - * usb_gadget_driver_{register,unregister}() must change. - */ - if (the_controller) { - dev_warn (&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc (sizeof *dev, GFP_KERNEL); if (dev == NULL){ @@ -2793,13 +2708,9 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init (&dev->lock); dev->pdev = pdev; dev->gadget.ops = &net2280_ops; - dev->gadget.is_dualspeed = 1; + 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 ... */ @@ -2881,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; } @@ -2908,13 +2818,13 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - the_controller = dev; - - 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_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto done; return 0; done: @@ -2937,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/net2280.h b/drivers/usb/gadget/net2280.h index c36852263d9..a844be0d683 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -11,15 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/usb/net2280.h> diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c new file mode 100644 index 00000000000..3ab38616751 --- /dev/null +++ b/drivers/usb/gadget/nokia.c @@ -0,0 +1,360 @@ +/* + * nokia.c -- Nokia Composite Gadget Driver + * + * Copyright (C) 2008-2010 Nokia Corporation + * Contact: Felipe Balbi <felipe.balbi@nokia.com> + * + * This gadget driver borrows from serial.c which is: + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * version 2 of that License. + */ + +#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 */ + +#define NOKIA_VERSION_NUM 0x0211 +#define NOKIA_LONG_NAME "N900 (PC-Suite Mode)" + +USB_GADGET_COMPOSITE_OPTIONS(); + +USB_ETHERNET_MODULE_PARAMETERS(); + +#define NOKIA_VENDOR_ID 0x0421 /* Nokia */ +#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */ + +/* string IDs are assigned dynamically */ + +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX + +static char manufacturer_nokia[] = "Nokia"; +static const char product_nokia[] = NOKIA_LONG_NAME; +static const char description_nokia[] = "PC-Suite Configuration"; + +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia, + [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME, + [USB_GADGET_SERIAL_IDX].s = "", + [STRING_DESCRIPTION_IDX].s = description_nokia, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static struct usb_device_descriptor device_desc = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_COMM, + .idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID), + .idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID), + .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM), + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + .bNumConfigurations = 1, +}; + +/*-------------------------------------------------------------------------*/ + +/* Module */ +MODULE_DESCRIPTION("Nokia composite gadget driver for N900"); +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 struct usb_configuration nokia_config_500ma_driver = { + .label = "Bus Powered", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .MaxPower = 500, +}; + +static struct usb_configuration nokia_config_100ma_driver = { + .label = "Self Powered", + .bConfigurationValue = 2, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .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 = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + goto err_usb; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + status = strings_dev[STRING_DESCRIPTION_IDX].id; + nokia_config_500ma_driver.iConfiguration = status; + nokia_config_100ma_driver.iConfiguration = status; + + 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_ecm_inst; + + status = usb_add_config(cdev, &nokia_config_100ma_driver, + nokia_bind_config); + if (status < 0) + 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: + return status; +} + +static int __exit nokia_unbind(struct usb_composite_dev *cdev) +{ + 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; +} + +static __refdata struct usb_composite_driver nokia_driver = { + .name = "g_nokia", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = nokia_bind, + .unbind = __exit_p(nokia_unbind), +}; + +static int __init nokia_init(void) +{ + return usb_composite_probe(&nokia_driver); +} +module_init(nokia_init); + +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 a2db0e174f2..2ae4f6d69f7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG @@ -31,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> @@ -44,17 +34,18 @@ #include <linux/usb/otg.h> #include <linux/dma-mapping.h> #include <linux/clk.h> +#include <linux/err.h> +#include <linux/prefetch.h> +#include <linux/io.h> #include <asm/byteorder.h> -#include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/unaligned.h> #include <asm/mach-types.h> -#include <mach/dma.h> +#include <linux/omap-dma.h> + #include <mach/usb.h> -#include <mach/control.h> #include "omap_udc.h" @@ -69,10 +60,8 @@ #define DRIVER_DESC "OMAP UDC driver" #define DRIVER_VERSION "4 October 2004" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - -#define OMAP2_DMA_CH(ch) (((ch) - 1) << 1) -#define OMAP24XX_DMA(name, ch) (OMAP24XX_DMA_##name + OMAP2_DMA_CH(ch)) +#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 @@ -97,23 +86,23 @@ #ifdef USE_ISO static unsigned fifo_mode = 3; #else -static unsigned fifo_mode = 0; +static unsigned fifo_mode; #endif /* "modprobe omap_udc fifo_mode=42", or else as a kernel * boot parameter "omap_udc:fifo_mode=42" */ -module_param (fifo_mode, uint, 0); -MODULE_PARM_DESC (fifo_mode, "endpoint configuration"); +module_param(fifo_mode, uint, 0); +MODULE_PARM_DESC(fifo_mode, "endpoint configuration"); #ifdef USE_DMA -static unsigned use_dma = 1; +static bool use_dma = 1; /* "modprobe omap_udc use_dma=y", or else as a kernel * boot parameter "omap_udc:use_dma=y" */ -module_param (use_dma, bool, 0); -MODULE_PARM_DESC (use_dma, "enable/disable DMA"); +module_param(use_dma, bool, 0); +MODULE_PARM_DESC(use_dma, "enable/disable DMA"); #else /* !USE_DMA */ /* save a bit of code */ @@ -121,8 +110,8 @@ MODULE_PARM_DESC (use_dma, "enable/disable DMA"); #endif /* !USE_DMA */ -static const char driver_name [] = "omap_udc"; -static const char driver_desc [] = DRIVER_DESC; +static const char driver_name[] = "omap_udc"; +static const char driver_desc[] = DRIVER_DESC; /*-------------------------------------------------------------------------*/ @@ -163,18 +152,17 @@ static int omap_ep_enable(struct usb_ep *_ep, u16 maxp; /* catch various bogus parameters */ - if (!_ep || !desc || ep->desc + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->maxpacket < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->maxpacket < usb_endpoint_maxp(desc)) { DBG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } - maxp = le16_to_cpu (desc->wMaxPacketSize); + maxp = usb_endpoint_maxp(desc); if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && maxp != ep->maxpacket) - || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket + || usb_endpoint_maxp(desc) > ep->maxpacket || !desc->wMaxPacketSize) { DBG("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; @@ -211,7 +199,7 @@ static int omap_ep_enable(struct usb_ep *_ep, spin_lock_irqsave(&udc->lock, flags); - ep->desc = desc; + ep->ep.desc = desc; ep->irqs = 0; ep->stopped = 0; ep->ep.maxpacket = maxp; @@ -253,15 +241,15 @@ static int omap_ep_disable(struct usb_ep *_ep) struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); unsigned long flags; - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { DBG("%s, %s not enabled\n", __func__, _ep ? ep->ep.name : NULL); return -EINVAL; } spin_lock_irqsave(&ep->udc->lock, flags); - ep->desc = NULL; - nuke (ep, -ESHUTDOWN); + ep->ep.desc = NULL; + nuke(ep, -ESHUTDOWN); ep->ep.maxpacket = ep->maxpacket; ep->has_dma = 0; omap_writew(UDC_SET_HALT, UDC_CTRL); @@ -282,10 +270,11 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) struct omap_req *req; req = kzalloc(sizeof(*req), gfp_flags); - if (req) { - req->req.dma = DMA_ADDR_INVALID; - INIT_LIST_HEAD (&req->queue); - } + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + return &req->req; } @@ -294,8 +283,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) { struct omap_req *req = container_of(_req, struct omap_req, req); - if (_req) - kfree (req); + kfree(req); } /*-------------------------------------------------------------------------*/ @@ -303,6 +291,7 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) static void done(struct omap_ep *ep, struct omap_req *req, int status) { + struct omap_udc *udc = ep->udc; unsigned stopped = ep->stopped; list_del_init(&req->queue); @@ -312,22 +301,9 @@ done(struct omap_ep *ep, struct omap_req *req, int status) else status = req->req.status; - if (use_dma && ep->has_dma) { - if (req->mapped) { - dma_unmap_single(ep->udc->gadget.dev.parent, - req->req.dma, req->req.length, - (ep->bEndpointAddress & USB_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->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - } + if (use_dma && ep->has_dma) + usb_gadget_unmap_request(&udc->gadget, &req->req, + (ep->bEndpointAddress & USB_DIR_IN)); #ifndef USB_TRACE if (status && status != -ESHUTDOWN) @@ -375,10 +351,10 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max) return len; } -// FIXME change r/w fifo calling convention +/* FIXME change r/w fifo calling convention */ -// return: 0 = still running, 1 = completed, negative = errno +/* return: 0 = still running, 1 = completed, negative = errno */ static int write_fifo(struct omap_ep *ep, struct omap_req *req) { u8 *buf; @@ -440,7 +416,7 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail) return len; } -// return: 0 = still running, 1 = queue empty, negative = errno +/* return: 0 = still running, 1 = queue empty, negative = errno */ static int read_fifo(struct omap_ep *ep, struct omap_req *req) { u8 *buf; @@ -547,12 +523,8 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) : OMAP_DMA_SYNC_ELEMENT; int dma_trigger = 0; - if (cpu_is_omap24xx()) - dma_trigger = OMAP24XX_DMA(USB_W2FC_TX0, ep->dma_channel); - /* measure length in either bytes or packets */ if ((cpu_is_omap16xx() && length <= UDC_TXN_TSC) - || (cpu_is_omap24xx() && length < ep->maxpacket) || (cpu_is_omap15xx() && length < ep->maxpacket)) { txdma_ctrl = UDC_TXN_EOT | length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, @@ -611,28 +583,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) int dma_trigger = 0; u16 w; - if (cpu_is_omap24xx()) - dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel); - - /* NOTE: we filtered out "short reads" before, so we know - * the buffer has only whole numbers of packets. - * except MODE SELECT(6) sent the 24 bytes data in OMAP24XX DMA mode - */ - if (cpu_is_omap24xx() && packets < ep->maxpacket) { - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - packets, 1, OMAP_DMA_SYNC_ELEMENT, - dma_trigger, 0); - req->dma_bytes = packets; - } else { - /* set up this DMA transfer, enable the fifo, start */ - packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); - req->dma_bytes = packets * ep->ep.maxpacket; - omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, - ep->ep.maxpacket >> 1, packets, - OMAP_DMA_SYNC_ELEMENT, - dma_trigger, 0); - } + /* set up this DMA transfer, enable the fifo, start */ + packets /= ep->ep.maxpacket; + packets = min(packets, (unsigned)UDC_RXN_TC + 1); + req->dma_bytes = packets * ep->ep.maxpacket; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, + ep->ep.maxpacket >> 1, packets, + OMAP_DMA_SYNC_ELEMENT, + dma_trigger, 0); omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 0, 0); @@ -694,7 +652,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) } omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC); - if (!list_empty (&ep->queue)) { + if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_in_dma(ep, req); @@ -713,7 +671,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src) } omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC); - if (!list_empty (&ep->queue)) { + if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_out_dma(ep, req); @@ -771,10 +729,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) ep->dma_channel = channel; if (is_in) { - if (cpu_is_omap24xx()) - dma_channel = OMAP24XX_DMA(USB_W2FC_TX0, channel); - else - dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel; + dma_channel = OMAP_DMA_USB_W2FC_TX0 - 1 + channel; status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { @@ -791,11 +746,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) 0, 0); } } else { - if (cpu_is_omap24xx()) - dma_channel = OMAP24XX_DMA(USB_W2FC_RX0, channel); - else - dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; - + dma_channel = OMAP_DMA_USB_W2FC_RX0 - 1 + channel; status = omap_request_dma(dma_channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { @@ -819,7 +770,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); /* channel type P: hw synch (fifo) */ - if (cpu_class_is_omap1() && !cpu_is_omap15xx()) + if (!cpu_is_omap15xx()) omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P); } @@ -927,7 +878,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) DBG("%s, bad params\n", __func__); return -EINVAL; } - if (!_ep || (!ep->desc && ep->bEndpointAddress)) { + if (!_ep || (!ep->ep.desc && ep->bEndpointAddress)) { DBG("%s, bad ep\n", __func__); return -EINVAL; } @@ -939,13 +890,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* this isn't bogus, but OMAP DMA isn't the only hardware to * have a hard time with partial packet reads... reject it. - * Except OMAP2 can handle the small packets. */ if (use_dma && ep->has_dma && ep->bEndpointAddress != 0 && (ep->bEndpointAddress & USB_DIR_IN) == 0 - && !cpu_class_is_omap2() && (req->req.length % ep->ep.maxpacket) != 0) { DBG("%s, no partial packet OUT reads\n", __func__); return -EMSGSIZE; @@ -955,26 +904,9 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - if (use_dma && ep->has_dma) { - 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->bEndpointAddress & USB_DIR_IN) - ? 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->bEndpointAddress & USB_DIR_IN) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - req->mapped = 0; - } - } + if (use_dma && ep->has_dma) + usb_gadget_map_request(&udc->gadget, &req->req, + (ep->bEndpointAddress & USB_DIR_IN)); VDBG("%s queue req %p, len %d buf %p\n", ep->ep.name, _req, _req->length, _req->buf); @@ -995,7 +927,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) int is_in; if (ep->bEndpointAddress == 0) { - if (!udc->ep0_pending || !list_empty (&ep->queue)) { + if (!udc->ep0_pending || !list_empty(&ep->queue)) { spin_unlock_irqrestore(&udc->lock, flags); return -EL2HLT; } @@ -1022,7 +954,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * always an IN ... even for IN transfers, * a weird case which seem to stall OMAP. */ - omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_EP_SEL | UDC_EP_DIR, + UDC_EP_NUM); omap_writew(UDC_CLR_EP, UDC_CTRL); omap_writew(UDC_SET_FIFO_EN, UDC_CTRL); omap_writew(UDC_EP_DIR, UDC_EP_NUM); @@ -1034,7 +967,8 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* non-empty DATA stage */ } else if (is_in) { - omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM); + omap_writew(UDC_EP_SEL | UDC_EP_DIR, + UDC_EP_NUM); } else { if (udc->ep0_setup) goto irq_wait; @@ -1082,7 +1016,7 @@ static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) spin_lock_irqsave(&ep->udc->lock, flags); /* make sure it's actually queued on this endpoint */ - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) break; } @@ -1131,7 +1065,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) status = 0; /* otherwise, all active non-ISO endpoints can halt */ - } else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->desc) { + } else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->ep.desc) { /* IN endpoints must already be idle */ if ((ep->bEndpointAddress & USB_DIR_IN) @@ -1189,8 +1123,8 @@ static struct usb_ep_ops omap_ep_ops = { .dequeue = omap_ep_dequeue, .set_halt = omap_ep_set_halt, - // fifo_status ... report bytes in fifo - // fifo_flush ... flush fifo + /* fifo_status ... report bytes in fifo */ + /* fifo_flush ... flush fifo */ }; /*-------------------------------------------------------------------------*/ @@ -1222,8 +1156,8 @@ static int omap_wakeup(struct usb_gadget *gadget) /* NOTE: non-OTG systems may use SRP TOO... */ } else if (!(udc->devstat & UDC_ATT)) { - if (udc->transceiver) - retval = otg_start_srp(udc->transceiver); + if (!IS_ERR_OR_NULL(udc->transceiver)) + retval = otg_start_srp(udc->transceiver->otg); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1354,8 +1288,8 @@ static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) struct omap_udc *udc; udc = container_of(gadget, struct omap_udc, gadget); - if (udc->transceiver) - return otg_set_power(udc->transceiver, mA); + if (!IS_ERR_OR_NULL(udc->transceiver)) + return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } @@ -1375,13 +1309,20 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on) return 0; } -static struct usb_gadget_ops omap_gadget_ops = { +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 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, + .udc_start = omap_udc_start, + .udc_stop = omap_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -1414,7 +1355,7 @@ static void udc_quiesce(struct omap_udc *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; nuke(&udc->ep[0], -ESHUTDOWN); - list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) nuke(ep, -ESHUTDOWN); } @@ -1530,7 +1471,8 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) /* read next OUT packet of request, maybe * reactiviting the fifo; stall on errors. */ - if (!req || (stat = read_fifo(ep0, req)) < 0) { + stat = read_fifo(ep0, req); + if (!req || stat < 0) { omap_writew(UDC_STALL_CMD, UDC_SYSCON2); udc->ep0_pending = 0; stat = 0; @@ -1629,7 +1571,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (w_index & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - || !ep->desc) + || !ep->ep.desc) goto do_stall; use_ep(ep, 0); omap_writew(udc->clr_halt, UDC_CTRL); @@ -1657,13 +1599,13 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src) if (w_index & USB_DIR_IN) ep += 16; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - || ep == ep0 || !ep->desc) + || ep == ep0 || !ep->ep.desc) goto do_stall; if (use_dma && ep->has_dma) { /* this has rude side-effects (aborts) and * can't really work if DMA-IN is active */ - DBG("%s host set_halt, NYET \n", ep->name); + DBG("%s host set_halt, NYET\n", ep->name); goto do_stall; } use_ep(ep, 0); @@ -1692,7 +1634,7 @@ ep0out_status_stage: ep = &udc->ep[w_index & 0xf]; if (w_index & USB_DIR_IN) ep += 16; - if (!ep->desc) + if (!ep->ep.desc) goto do_stall; /* iso never stalls */ @@ -1754,7 +1696,7 @@ delegate: */ udc->ep0_setup = 1; spin_unlock(&udc->lock); - status = udc->driver->setup (&udc->gadget, &u.r); + status = udc->driver->setup(&udc->gadget, &u.r); spin_lock(&udc->lock); udc->ep0_setup = 0; } @@ -1797,12 +1739,12 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) if (devstat & UDC_ATT) { udc->gadget.speed = USB_SPEED_FULL; VDBG("connect\n"); - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_enable(udc); - // if (driver->connect) call it + /* if (driver->connect) call it */ } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { udc->gadget.speed = USB_SPEED_UNKNOWN; - if (!udc->transceiver) + if (IS_ERR_OR_NULL(udc->transceiver)) pullup_disable(udc); DBG("disconnect, gadget %s\n", udc->driver->driver.name); @@ -1831,7 +1773,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) } if (change & UDC_SUS) { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { - // FIXME tell isp1301 to suspend/resume (?) + /* FIXME tell isp1301 to suspend/resume (?) */ if (devstat & UDC_SUS) { VDBG("suspend\n"); update_otg(udc); @@ -1842,12 +1784,14 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } - if (udc->transceiver) - otg_set_suspend(udc->transceiver, 1); + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_phy_set_suspend( + udc->transceiver, 1); } else { VDBG("resume\n"); - if (udc->transceiver) - otg_set_suspend(udc->transceiver, 0); + if (!IS_ERR_OR_NULL(udc->transceiver)) + usb_phy_set_suspend( + udc->transceiver, 0); if (udc->gadget.speed == USB_SPEED_FULL && udc->driver->resume) { spin_unlock(&udc->lock); @@ -2032,7 +1976,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) spin_lock_irqsave(&udc->lock, flags); /* handle all non-DMA ISO transfers */ - list_for_each_entry (ep, &udc->iso, iso) { + list_for_each_entry(ep, &udc->iso, iso) { u16 stat; struct omap_req *req; @@ -2091,40 +2035,24 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) static inline int machine_without_vbus_sense(void) { - return (machine_is_omap_innovator() + return machine_is_omap_innovator() || machine_is_omap_osk() - || machine_is_omap_apollon() -#ifndef CONFIG_MACH_OMAP_H4_OTG - || machine_is_omap_h4() -#endif || machine_is_sx1() - ); + /* No known omap7xx boards with vbus sense */ + || cpu_is_omap7xx(); } -int usb_gadget_register_driver (struct usb_gadget_driver *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->speed < USB_SPEED_FULL - || !driver->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) { + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { ep->irqs = 0; if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) continue; @@ -2138,40 +2066,30 @@ int usb_gadget_register_driver (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 = driver->bind (&udc->gadget); - 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 */ - if (udc->transceiver) { - status = otg_set_peripheral(udc->transceiver, &udc->gadget); + if (!IS_ERR_OR_NULL(udc->transceiver)) { + status = otg_set_peripheral(udc->transceiver->otg, + &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); if (driver->unbind) { - driver->unbind (&udc->gadget); - udc->gadget.dev.driver = NULL; + driver->unbind(&udc->gadget); udc->driver = NULL; } goto done; } } else { if (can_pullup(udc)) - pullup_enable (udc); + pullup_enable(udc); else - pullup_disable (udc); + pullup_disable(udc); } /* boards that don't have VBUS sensing can't autogate 48MHz; @@ -2183,28 +2101,24 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) done: if (udc->dc_clk != NULL) omap_udc_enable_clock(0); + return status; } -EXPORT_SYMBOL(usb_gadget_register_driver); -int usb_gadget_unregister_driver (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); if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 0); - if (udc->transceiver) - (void) otg_set_peripheral(udc->transceiver, NULL); + if (!IS_ERR_OR_NULL(udc->transceiver)) + (void) otg_set_peripheral(udc->transceiver->otg, NULL); else pullup_disable(udc); @@ -2212,17 +2126,13 @@ int usb_gadget_unregister_driver (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; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -2233,7 +2143,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); static const char proc_filename[] = "driver/udc"; #define FOURBITS "%s%s%s%s" -#define EIGHTBITS FOURBITS FOURBITS +#define EIGHTBITS "%s%s%s%s%s%s%s%s" static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) { @@ -2255,12 +2165,21 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", ep->name, buf, ep->double_buf ? "dbuf " : "", - ({char *s; switch(ep->ackwait){ - case 0: s = ""; break; - case 1: s = "(ackw) "; break; - case 2: s = "(ackw2) "; break; - default: s = "(?) "; break; - } s;}), + ({ char *s; + switch (ep->ackwait) { + case 0: + s = ""; + break; + case 1: + s = "(ackw) "; + break; + case 2: + s = "(ackw2) "; + break; + default: + s = "(?) "; + break; + } s; }), ep->irqs, stat_flg, (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", (stat_flg & UDC_MISS_IN) ? "miss_in " : "", @@ -2276,10 +2195,10 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "", (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : ""); - if (list_empty (&ep->queue)) + if (list_empty(&ep->queue)) seq_printf(s, "\t(queue empty)\n"); else - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { unsigned length = req->req.actual; if (use_dma && buf[0]) { @@ -2297,35 +2216,28 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) static char *trx_mode(unsigned m, int enabled) { switch (m) { - case 0: return enabled ? "*6wire" : "unused"; - case 1: return "4wire"; - case 2: return "3wire"; - case 3: return "6wire"; - default: return "unknown"; + case 0: + return enabled ? "*6wire" : "unused"; + case 1: + return "4wire"; + case 2: + return "3wire"; + case 3: + return "6wire"; + default: + return "unknown"; } } static int proc_otg_show(struct seq_file *s) { u32 tmp; - u32 trans; - char *ctrl_name; + u32 trans = 0; + char *ctrl_name = "(UNKNOWN)"; tmp = omap_readl(OTG_REV); - if (cpu_is_omap24xx()) { - /* - * REVISIT: Not clear how this works on OMAP2. trans - * is ANDed to produce bits 7 and 8, which might make - * sense for USB_TRANSCEIVER_CTRL on OMAP1, - * but with CONTROL_DEVCONF, these bits have something to - * do with the frame adjustment counter and McBSP2. - */ - ctrl_name = "control_devconf"; - trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); - } else { - ctrl_name = "tranceiver_ctrl"; - trans = omap_readw(USB_TRANSCEIVER_CTRL); - } + ctrl_name = "tranceiver_ctrl"; + trans = omap_readw(USB_TRANSCEIVER_CTRL); seq_printf(s, "\nOTG rev %d.%d, %s %05x\n", tmp >> 4, tmp & 0xf, ctrl_name, trans); tmp = omap_readw(OTG_SYSCON_1); @@ -2345,7 +2257,7 @@ static int proc_otg_show(struct seq_file *s) " b_ase_brst=%d hmc=%d\n", tmp, (tmp & OTG_EN) ? " otg_en" : "", (tmp & USBX_SYNCHRO) ? " synchro" : "", - // much more SRP stuff + /* much more SRP stuff */ (tmp & SRP_DATA) ? " srp_data" : "", (tmp & SRP_VBUS) ? " srp_vbus" : "", (tmp & OTG_PADEN) ? " otg_paden" : "", @@ -2412,14 +2324,12 @@ static int proc_udc_show(struct seq_file *s, void *_) HMC, udc->transceiver ? udc->transceiver->label - : ((cpu_is_omap1710() || cpu_is_omap24xx()) + : (cpu_is_omap1710() ? "external" : "(none)")); - if (cpu_class_is_omap1()) { - seq_printf(s, "ULPD control %04x req %04x status %04x\n", - omap_readw(ULPD_CLOCK_CTRL), - omap_readw(ULPD_SOFT_REQ), - omap_readw(ULPD_STATUS_REQ)); - } + seq_printf(s, "ULPD control %04x req %04x status %04x\n", + omap_readw(ULPD_CLOCK_CTRL), + omap_readw(ULPD_SOFT_REQ), + omap_readw(ULPD_STATUS_REQ)); /* OTG controller registers */ if (!cpu_is_omap15xx()) @@ -2435,7 +2345,7 @@ static int proc_udc_show(struct seq_file *s, void *_) (tmp & UDC_SELF_PWR) ? " self_pwr" : "", (tmp & UDC_SOFF_DIS) ? " soff_dis" : "", (tmp & UDC_PULLUP_EN) ? " PULLUP" : ""); - // syscon2 is write-only + /* syscon2 is write-only */ /* UDC controller registers */ if (!(tmp & UDC_PULLUP_EN)) { @@ -2519,9 +2429,9 @@ static int proc_udc_show(struct seq_file *s, void *_) if (tmp & UDC_ATT) { proc_ep_show(s, &udc->ep[0]); if (tmp & UDC_ADD) { - list_for_each_entry (ep, &udc->gadget.ep_list, + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { - if (ep->desc) + if (ep->ep.desc) proc_ep_show(s, ep); } } @@ -2570,7 +2480,7 @@ static inline void remove_proc_file(void) {} * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that * capability yet though. */ -static unsigned __init +static unsigned omap_ep_setup(char *name, u8 addr, u8 type, unsigned buf, unsigned maxp, int dbuf) { @@ -2588,14 +2498,29 @@ omap_ep_setup(char *name, u8 addr, u8 type, /* chip setup ... bit values are same for IN, OUT */ if (type == USB_ENDPOINT_XFER_ISOC) { switch (maxp) { - case 8: epn_rxtx = 0 << 12; break; - case 16: epn_rxtx = 1 << 12; break; - case 32: epn_rxtx = 2 << 12; break; - case 64: epn_rxtx = 3 << 12; break; - case 128: epn_rxtx = 4 << 12; break; - case 256: epn_rxtx = 5 << 12; break; - case 512: epn_rxtx = 6 << 12; break; - default: BUG(); + case 8: + epn_rxtx = 0 << 12; + break; + case 16: + epn_rxtx = 1 << 12; + break; + case 32: + epn_rxtx = 2 << 12; + break; + case 64: + epn_rxtx = 3 << 12; + break; + case 128: + epn_rxtx = 4 << 12; + break; + case 256: + epn_rxtx = 5 << 12; + break; + case 512: + epn_rxtx = 6 << 12; + break; + default: + BUG(); } epn_rxtx |= UDC_EPN_RX_ISO; dbuf = 1; @@ -2604,15 +2529,24 @@ omap_ep_setup(char *name, u8 addr, u8 type, * and ignored for PIO-IN on newer chips * (for more reliable behavior) */ - if (!use_dma || cpu_is_omap15xx() || cpu_is_omap24xx()) + if (!use_dma || cpu_is_omap15xx()) dbuf = 0; switch (maxp) { - case 8: epn_rxtx = 0 << 12; break; - case 16: epn_rxtx = 1 << 12; break; - case 32: epn_rxtx = 2 << 12; break; - case 64: epn_rxtx = 3 << 12; break; - default: BUG(); + case 8: + epn_rxtx = 0 << 12; + break; + case 16: + epn_rxtx = 1 << 12; + break; + case 32: + epn_rxtx = 2 << 12; + break; + case 64: + epn_rxtx = 3 << 12; + break; + default: + BUG(); } if (dbuf && addr) epn_rxtx |= UDC_EPN_RX_DB; @@ -2651,8 +2585,9 @@ 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; - list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list); + 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; } @@ -2660,12 +2595,12 @@ omap_ep_setup(char *name, u8 addr, u8 type, static void omap_udc_release(struct device *dev) { complete(udc->done); - kfree (udc); + kfree(udc); udc = NULL; } -static int __init -omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) +static int +omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) { unsigned tmp, buf; @@ -2678,28 +2613,21 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) omap_writew(0, UDC_TXDMA_CFG); /* UDC_PULLUP_EN gates the chip clock */ - // OTG_SYSCON_1 |= DEV_IDLE_EN; + /* OTG_SYSCON_1 |= DEV_IDLE_EN; */ udc = kzalloc(sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; - spin_lock_init (&udc->lock); + spin_lock_init(&udc->lock); udc->gadget.ops = &omap_gadget_ops; udc->gadget.ep0 = &udc->ep[0].ep; INIT_LIST_HEAD(&udc->gadget.ep_list); INIT_LIST_HEAD(&udc->iso); 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 */ @@ -2713,13 +2641,13 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) omap_writew(0, UDC_EP_TX(tmp)); } -#define OMAP_BULK_EP(name,addr) \ +#define OMAP_BULK_EP(name, addr) \ buf = omap_ep_setup(name "-bulk", addr, \ USB_ENDPOINT_XFER_BULK, buf, 64, 1); -#define OMAP_INT_EP(name,addr, maxp) \ +#define OMAP_INT_EP(name, addr, maxp) \ buf = omap_ep_setup(name "-int", addr, \ USB_ENDPOINT_XFER_INT, buf, maxp, 0); -#define OMAP_ISO_EP(name,addr, maxp) \ +#define OMAP_ISO_EP(name, addr, maxp) \ buf = omap_ep_setup(name "-iso", addr, \ USB_ENDPOINT_XFER_ISOC, buf, maxp, 1); @@ -2800,15 +2728,18 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) return 0; } -static int __init omap_udc_probe(struct platform_device *pdev) +static int omap_udc_probe(struct platform_device *pdev) { int status = -ENODEV; int hmc; - struct otg_transceiver *xceiv = NULL; + struct usb_phy *xceiv = NULL; const char *type = NULL; - struct omap_usb_config *config = pdev->dev.platform_data; - struct clk *dc_clk; - struct clk *hhc_clk; + struct omap_usb_config *config = dev_get_platdata(&pdev->dev); + struct clk *dc_clk = NULL; + struct clk *hhc_clk = NULL; + + if (cpu_is_omap7xx()) + use_dma = 0; /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, @@ -2828,9 +2759,9 @@ static int __init omap_udc_probe(struct platform_device *pdev) udelay(100); } - if (cpu_is_omap24xx()) { - dc_clk = clk_get(&pdev->dev, "usb_fck"); - hhc_clk = clk_get(&pdev->dev, "usb_l4_ick"); + if (cpu_is_omap7xx()) { + dc_clk = clk_get(&pdev->dev, "usb_dc_ck"); + hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck"); BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); /* can't use omap_udc_enable_clock yet */ clk_enable(dc_clk); @@ -2867,8 +2798,8 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = otg_get_transceiver(); - if (xceiv) + xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(xceiv)) type = xceiv->label; else if (config->otg) { DBG("OTG requires external transceiver!\n"); @@ -2877,14 +2808,6 @@ static int __init omap_udc_probe(struct platform_device *pdev) hmc = HMC_1610; - if (cpu_is_omap24xx()) { - /* this could be transceiverless in one of the - * "we don't need to know" modes. - */ - type = "external"; - goto known; - } - switch (hmc) { case 0: /* POWERUP DEFAULT == 0 */ case 4: @@ -2900,7 +2823,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) case 16: case 19: case 25: - if (!xceiv) { + if (IS_ERR_OR_NULL(xceiv)) { DBG("external transceiver not registered!\n"); type = "unknown"; } @@ -2923,16 +2846,16 @@ bad_on_1710: goto cleanup0; } } -known: + INFO("hmc mode %d, %s transceiver\n", hmc, type); /* a "gadget" abstracts/virtualizes the controller */ status = omap_udc_setup(pdev, xceiv); - if (status) { + if (status) goto cleanup0; - } + xceiv = NULL; - // "udc" is now valid + /* "udc" is now valid */ pullup_disable(udc); #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) udc->gadget.is_otg = (config->otg != 0); @@ -2946,7 +2869,7 @@ known: /* USB general purpose IRQ: ep0, state changes, dma, etc */ status = request_irq(pdev->resource[1].start, omap_udc_irq, - IRQF_SAMPLE_RANDOM, driver_name, udc); + 0, driver_name, udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[1].start, status); @@ -2955,7 +2878,7 @@ known: /* USB "non-iso" IRQ (PIO for all but ep0) */ status = request_irq(pdev->resource[2].start, omap_udc_pio_irq, - IRQF_SAMPLE_RANDOM, "omap_udc pio", udc); + 0, "omap_udc pio", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[2].start, status); @@ -2963,35 +2886,31 @@ known: } #ifdef USE_ISO status = request_irq(pdev->resource[3].start, omap_udc_iso_irq, - IRQF_DISABLED, "omap_udc iso", udc); + 0, "omap_udc iso", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[3].start, status); goto cleanup3; } #endif - if (cpu_is_omap16xx()) { - udc->dc_clk = dc_clk; - udc->hhc_clk = hhc_clk; - clk_disable(hhc_clk); - clk_disable(dc_clk); - } - - if (cpu_is_omap24xx()) { + if (cpu_is_omap16xx() || cpu_is_omap7xx()) { udc->dc_clk = dc_clk; udc->hhc_clk = hhc_clk; - /* FIXME OMAP2 don't release hhc & dc clock */ -#if 0 clk_disable(hhc_clk); clk_disable(dc_clk); -#endif } create_proc_file(); - status = device_add(&udc->gadget.dev); - if (!status) - return status; - /* If fail, fall through */ + status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, + omap_udc_release); + if (status) + goto cleanup4; + + return 0; + +cleanup4: + remove_proc_file(); + #ifdef USE_ISO cleanup3: free_irq(pdev->resource[2].start, udc); @@ -3001,14 +2920,14 @@ cleanup2: free_irq(pdev->resource[1].start, udc); cleanup1: - kfree (udc); + kfree(udc); udc = NULL; cleanup0: - if (xceiv) - otg_put_transceiver(xceiv); + if (!IS_ERR_OR_NULL(xceiv)) + usb_put_phy(xceiv); - if (cpu_is_omap16xx() || cpu_is_omap24xx()) { + if (cpu_is_omap16xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); clk_disable(dc_clk); clk_put(hhc_clk); @@ -3021,20 +2940,22 @@ cleanup0: return status; } -static int __exit omap_udc_remove(struct platform_device *pdev) +static int omap_udc_remove(struct platform_device *pdev) { DECLARE_COMPLETION_ONSTACK(done); if (!udc) return -ENODEV; + + usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; udc->done = &done; pullup_disable(udc); - if (udc->transceiver) { - otg_put_transceiver(udc->transceiver); + if (!IS_ERR_OR_NULL(udc->transceiver)) { + usb_put_phy(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); @@ -3057,7 +2978,6 @@ static int __exit 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; @@ -3104,7 +3024,8 @@ static int omap_udc_resume(struct platform_device *dev) /*-------------------------------------------------------------------------*/ static struct platform_driver udc_driver = { - .remove = __exit_p(omap_udc_remove), + .probe = omap_udc_probe, + .remove = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { @@ -3113,23 +3034,7 @@ static struct platform_driver udc_driver = { }, }; -static int __init udc_init(void) -{ - INFO("%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", driver_desc, - use_dma ? " (dma)" : ""); - return platform_driver_probe(&udc_driver, omap_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_LICENSE("GPL"); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 29edc51b6b2..cfadeb5fc5d 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -140,7 +140,6 @@ struct omap_ep { struct list_head queue; unsigned long irqs; struct list_head iso; - const struct usb_endpoint_descriptor *desc; char name[14]; u16 maxpacket; u8 bEndpointAddress; @@ -164,7 +163,7 @@ struct omap_udc { struct omap_ep ep[32]; u16 devstat; u16 clr_halt; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; struct list_head iso; unsigned softconnect:1; unsigned vbus_active:1; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c new file mode 100644 index 00000000000..eb8c3bedb57 --- /dev/null +++ b/drivers/usb/gadget/pch_udc.c @@ -0,0 +1,3248 @@ +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/gpio.h> +#include <linux/irq.h> + +/* GPIO port for VBUS detecting */ +static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */ + +#define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */ +#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */ + +/* Address offset of Registers */ +#define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */ + +#define UDC_EPCTL_ADDR 0x00 /* Endpoint control */ +#define UDC_EPSTS_ADDR 0x04 /* Endpoint status */ +#define UDC_BUFIN_FRAMENUM_ADDR 0x08 /* buffer size in / frame number out */ +#define UDC_BUFOUT_MAXPKT_ADDR 0x0C /* buffer size out / maxpkt in */ +#define UDC_SUBPTR_ADDR 0x10 /* setup buffer pointer */ +#define UDC_DESPTR_ADDR 0x14 /* Data descriptor pointer */ +#define UDC_CONFIRM_ADDR 0x18 /* Write/Read confirmation */ + +#define UDC_DEVCFG_ADDR 0x400 /* Device configuration */ +#define UDC_DEVCTL_ADDR 0x404 /* Device control */ +#define UDC_DEVSTS_ADDR 0x408 /* Device status */ +#define UDC_DEVIRQSTS_ADDR 0x40C /* Device irq status */ +#define UDC_DEVIRQMSK_ADDR 0x410 /* Device irq mask */ +#define UDC_EPIRQSTS_ADDR 0x414 /* Endpoint irq status */ +#define UDC_EPIRQMSK_ADDR 0x418 /* Endpoint irq mask */ +#define UDC_DEVLPM_ADDR 0x41C /* LPM control / status */ +#define UDC_CSR_BUSY_ADDR 0x4f0 /* UDC_CSR_BUSY Status register */ +#define UDC_SRST_ADDR 0x4fc /* SOFT RESET register */ +#define UDC_CSR_ADDR 0x500 /* USB_DEVICE endpoint register */ + +/* Endpoint control register */ +/* Bit position */ +#define UDC_EPCTL_MRXFLUSH (1 << 12) +#define UDC_EPCTL_RRDY (1 << 9) +#define UDC_EPCTL_CNAK (1 << 8) +#define UDC_EPCTL_SNAK (1 << 7) +#define UDC_EPCTL_NAK (1 << 6) +#define UDC_EPCTL_P (1 << 3) +#define UDC_EPCTL_F (1 << 1) +#define UDC_EPCTL_S (1 << 0) +#define UDC_EPCTL_ET_SHIFT 4 +/* Mask patern */ +#define UDC_EPCTL_ET_MASK 0x00000030 +/* Value for ET field */ +#define UDC_EPCTL_ET_CONTROL 0 +#define UDC_EPCTL_ET_ISO 1 +#define UDC_EPCTL_ET_BULK 2 +#define UDC_EPCTL_ET_INTERRUPT 3 + +/* Endpoint status register */ +/* Bit position */ +#define UDC_EPSTS_XFERDONE (1 << 27) +#define UDC_EPSTS_RSS (1 << 26) +#define UDC_EPSTS_RCS (1 << 25) +#define UDC_EPSTS_TXEMPTY (1 << 24) +#define UDC_EPSTS_TDC (1 << 10) +#define UDC_EPSTS_HE (1 << 9) +#define UDC_EPSTS_MRXFIFO_EMP (1 << 8) +#define UDC_EPSTS_BNA (1 << 7) +#define UDC_EPSTS_IN (1 << 6) +#define UDC_EPSTS_OUT_SHIFT 4 +/* Mask patern */ +#define UDC_EPSTS_OUT_MASK 0x00000030 +#define UDC_EPSTS_ALL_CLR_MASK 0x1F0006F0 +/* Value for OUT field */ +#define UDC_EPSTS_OUT_SETUP 2 +#define UDC_EPSTS_OUT_DATA 1 + +/* Device configuration register */ +/* Bit position */ +#define UDC_DEVCFG_CSR_PRG (1 << 17) +#define UDC_DEVCFG_SP (1 << 3) +/* SPD Valee */ +#define UDC_DEVCFG_SPD_HS 0x0 +#define UDC_DEVCFG_SPD_FS 0x1 +#define UDC_DEVCFG_SPD_LS 0x2 + +/* Device control register */ +/* Bit position */ +#define UDC_DEVCTL_THLEN_SHIFT 24 +#define UDC_DEVCTL_BRLEN_SHIFT 16 +#define UDC_DEVCTL_CSR_DONE (1 << 13) +#define UDC_DEVCTL_SD (1 << 10) +#define UDC_DEVCTL_MODE (1 << 9) +#define UDC_DEVCTL_BREN (1 << 8) +#define UDC_DEVCTL_THE (1 << 7) +#define UDC_DEVCTL_DU (1 << 4) +#define UDC_DEVCTL_TDE (1 << 3) +#define UDC_DEVCTL_RDE (1 << 2) +#define UDC_DEVCTL_RES (1 << 0) + +/* Device status register */ +/* Bit position */ +#define UDC_DEVSTS_TS_SHIFT 18 +#define UDC_DEVSTS_ENUM_SPEED_SHIFT 13 +#define UDC_DEVSTS_ALT_SHIFT 8 +#define UDC_DEVSTS_INTF_SHIFT 4 +#define UDC_DEVSTS_CFG_SHIFT 0 +/* Mask patern */ +#define UDC_DEVSTS_TS_MASK 0xfffc0000 +#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000 +#define UDC_DEVSTS_ALT_MASK 0x00000f00 +#define UDC_DEVSTS_INTF_MASK 0x000000f0 +#define UDC_DEVSTS_CFG_MASK 0x0000000f +/* value for maximum speed for SPEED field */ +#define UDC_DEVSTS_ENUM_SPEED_FULL 1 +#define UDC_DEVSTS_ENUM_SPEED_HIGH 0 +#define UDC_DEVSTS_ENUM_SPEED_LOW 2 +#define UDC_DEVSTS_ENUM_SPEED_FULLX 3 + +/* Device irq register */ +/* Bit position */ +#define UDC_DEVINT_RWKP (1 << 7) +#define UDC_DEVINT_ENUM (1 << 6) +#define UDC_DEVINT_SOF (1 << 5) +#define UDC_DEVINT_US (1 << 4) +#define UDC_DEVINT_UR (1 << 3) +#define UDC_DEVINT_ES (1 << 2) +#define UDC_DEVINT_SI (1 << 1) +#define UDC_DEVINT_SC (1 << 0) +/* Mask patern */ +#define UDC_DEVINT_MSK 0x7f + +/* Endpoint irq register */ +/* Bit position */ +#define UDC_EPINT_IN_SHIFT 0 +#define UDC_EPINT_OUT_SHIFT 16 +#define UDC_EPINT_IN_EP0 (1 << 0) +#define UDC_EPINT_OUT_EP0 (1 << 16) +/* Mask patern */ +#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff + +/* UDC_CSR_BUSY Status register */ +/* Bit position */ +#define UDC_CSR_BUSY (1 << 0) + +/* SOFT RESET register */ +/* Bit position */ +#define UDC_PSRST (1 << 1) +#define UDC_SRST (1 << 0) + +/* USB_DEVICE endpoint register */ +/* Bit position */ +#define UDC_CSR_NE_NUM_SHIFT 0 +#define UDC_CSR_NE_DIR_SHIFT 4 +#define UDC_CSR_NE_TYPE_SHIFT 5 +#define UDC_CSR_NE_CFG_SHIFT 7 +#define UDC_CSR_NE_INTF_SHIFT 11 +#define UDC_CSR_NE_ALT_SHIFT 15 +#define UDC_CSR_NE_MAX_PKT_SHIFT 19 +/* Mask patern */ +#define UDC_CSR_NE_NUM_MASK 0x0000000f +#define UDC_CSR_NE_DIR_MASK 0x00000010 +#define UDC_CSR_NE_TYPE_MASK 0x00000060 +#define UDC_CSR_NE_CFG_MASK 0x00000780 +#define UDC_CSR_NE_INTF_MASK 0x00007800 +#define UDC_CSR_NE_ALT_MASK 0x00078000 +#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000 + +#define PCH_UDC_CSR(ep) (UDC_CSR_ADDR + ep*4) +#define PCH_UDC_EPINT(in, num)\ + (1 << (num + (in ? UDC_EPINT_IN_SHIFT : UDC_EPINT_OUT_SHIFT))) + +/* Index of endpoint */ +#define UDC_EP0IN_IDX 0 +#define UDC_EP0OUT_IDX 1 +#define UDC_EPIN_IDX(ep) (ep * 2) +#define UDC_EPOUT_IDX(ep) (ep * 2 + 1) +#define PCH_UDC_EP0 0 +#define PCH_UDC_EP1 1 +#define PCH_UDC_EP2 2 +#define PCH_UDC_EP3 3 + +/* Number of endpoint */ +#define PCH_UDC_EP_NUM 32 /* Total number of EPs (16 IN,16 OUT) */ +#define PCH_UDC_USED_EP_NUM 4 /* EP number of EP's really used */ +/* Length Value */ +#define PCH_UDC_BRLEN 0x0F /* Burst length */ +#define PCH_UDC_THLEN 0x1F /* Threshold length */ +/* Value of EP Buffer Size */ +#define UDC_EP0IN_BUFF_SIZE 16 +#define UDC_EPIN_BUFF_SIZE 256 +#define UDC_EP0OUT_BUFF_SIZE 16 +#define UDC_EPOUT_BUFF_SIZE 256 +/* Value of EP maximum packet size */ +#define UDC_EP0IN_MAX_PKT_SIZE 64 +#define UDC_EP0OUT_MAX_PKT_SIZE 64 +#define UDC_BULK_MAX_PKT_SIZE 512 + +/* DMA */ +#define DMA_DIR_RX 1 /* DMA for data receive */ +#define DMA_DIR_TX 2 /* DMA for data transmit */ +#define DMA_ADDR_INVALID (~(dma_addr_t)0) +#define UDC_DMA_MAXPACKET 65536 /* maximum packet size for DMA */ + +/** + * struct pch_udc_data_dma_desc - Structure to hold DMA descriptor information + * for data + * @status: Status quadlet + * @reserved: Reserved + * @dataptr: Buffer descriptor + * @next: Next descriptor + */ +struct pch_udc_data_dma_desc { + u32 status; + u32 reserved; + u32 dataptr; + u32 next; +}; + +/** + * struct pch_udc_stp_dma_desc - Structure to hold DMA descriptor information + * for control data + * @status: Status + * @reserved: Reserved + * @data12: First setup word + * @data34: Second setup word + */ +struct pch_udc_stp_dma_desc { + u32 status; + u32 reserved; + struct usb_ctrlrequest request; +} __attribute((packed)); + +/* DMA status definitions */ +/* Buffer status */ +#define PCH_UDC_BUFF_STS 0xC0000000 +#define PCH_UDC_BS_HST_RDY 0x00000000 +#define PCH_UDC_BS_DMA_BSY 0x40000000 +#define PCH_UDC_BS_DMA_DONE 0x80000000 +#define PCH_UDC_BS_HST_BSY 0xC0000000 +/* Rx/Tx Status */ +#define PCH_UDC_RXTX_STS 0x30000000 +#define PCH_UDC_RTS_SUCC 0x00000000 +#define PCH_UDC_RTS_DESERR 0x10000000 +#define PCH_UDC_RTS_BUFERR 0x30000000 +/* Last Descriptor Indication */ +#define PCH_UDC_DMA_LAST 0x08000000 +/* Number of Rx/Tx Bytes Mask */ +#define PCH_UDC_RXTX_BYTES 0x0000ffff + +/** + * struct pch_udc_cfg_data - Structure to hold current configuration + * and interface information + * @cur_cfg: current configuration in use + * @cur_intf: current interface in use + * @cur_alt: current alt interface in use + */ +struct pch_udc_cfg_data { + u16 cur_cfg; + u16 cur_intf; + u16 cur_alt; +}; + +/** + * struct pch_udc_ep - Structure holding a PCH USB device Endpoint information + * @ep: embedded ep request + * @td_stp_phys: for setup request + * @td_data_phys: for data request + * @td_stp: for setup request + * @td_data: for data request + * @dev: reference to device struct + * @offset_addr: offset address of ep register + * @desc: for this ep + * @queue: queue for requests + * @num: endpoint number + * @in: endpoint is IN + * @halted: endpoint halted? + * @epsts: Endpoint status + */ +struct pch_udc_ep { + struct usb_ep ep; + dma_addr_t td_stp_phys; + dma_addr_t td_data_phys; + struct pch_udc_stp_dma_desc *td_stp; + struct pch_udc_data_dma_desc *td_data; + struct pch_udc_dev *dev; + unsigned long offset_addr; + struct list_head queue; + unsigned num:5, + in:1, + halted:1; + unsigned long epsts; +}; + +/** + * struct pch_vbus_gpio_data - Structure holding GPIO informaton + * for detecting VBUS + * @port: gpio port number + * @intr: gpio interrupt number + * @irq_work_fall Structure for WorkQueue + * @irq_work_rise Structure for WorkQueue + */ +struct pch_vbus_gpio_data { + int port; + int intr; + struct work_struct irq_work_fall; + struct work_struct irq_work_rise; +}; + +/** + * struct pch_udc_dev - Structure holding complete information + * of the PCH USB device + * @gadget: gadget driver data + * @driver: reference to gadget driver bound + * @pdev: reference to the PCI device + * @ep: array of endpoints + * @lock: protects all state + * @active: enabled the PCI device + * @stall: stall requested + * @prot_stall: protcol stall requested + * @irq_registered: irq registered with system + * @mem_region: device memory mapped + * @registered: driver regsitered with system + * @suspended: driver in suspended state + * @connected: gadget driver associated + * @vbus_session: required vbus_session state + * @set_cfg_not_acked: pending acknowledgement 4 setup + * @waiting_zlp_ack: pending acknowledgement 4 ZLP + * @data_requests: DMA pool for data requests + * @stp_requests: DMA pool for setup requests + * @dma_addr: DMA pool for received + * @ep0out_buf: Buffer for DMA + * @setup_data: Received setup data + * @phys_addr: of device memory + * @base_addr: for mapped device memory + * @irq: IRQ line for the device + * @cfg_data: current cfg, intf, and alt in use + * @vbus_gpio: GPIO informaton for detecting VBUS + */ +struct pch_udc_dev { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct pci_dev *pdev; + struct pch_udc_ep ep[PCH_UDC_EP_NUM]; + spinlock_t lock; /* protects all state */ + unsigned active:1, + stall:1, + prot_stall:1, + irq_registered:1, + mem_region:1, + suspended:1, + connected:1, + vbus_session:1, + set_cfg_not_acked:1, + waiting_zlp_ack:1; + struct pci_pool *data_requests; + struct pci_pool *stp_requests; + dma_addr_t dma_addr; + void *ep0out_buf; + struct usb_ctrlrequest setup_data; + unsigned long phys_addr; + void __iomem *base_addr; + unsigned irq; + 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 +#define PCI_VENDOR_ID_ROHM 0x10DB +#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D +#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808 + +static const char ep0_string[] = "ep0in"; +static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */ +static bool speed_fs; +module_param_named(speed_fs, speed_fs, bool, S_IRUGO); +MODULE_PARM_DESC(speed_fs, "true for Full speed operation"); + +/** + * struct pch_udc_request - Structure holding a PCH USB device request packet + * @req: embedded ep request + * @td_data_phys: phys. address + * @td_data: first dma desc. of chain + * @td_data_last: last dma desc. of chain + * @queue: associated queue + * @dma_going: DMA in progress for request + * @dma_mapped: DMA memory mapped for request + * @dma_done: DMA completed for request + * @chain_len: chain length + * @buf: Buffer memory for align adjustment + * @dma: DMA memory for align adjustment + */ +struct pch_udc_request { + struct usb_request req; + dma_addr_t td_data_phys; + struct pch_udc_data_dma_desc *td_data; + struct pch_udc_data_dma_desc *td_data_last; + struct list_head queue; + unsigned dma_going:1, + dma_mapped:1, + dma_done:1; + unsigned chain_len; + void *buf; + dma_addr_t dma; +}; + +static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg) +{ + return ioread32(dev->base_addr + reg); +} + +static inline void pch_udc_writel(struct pch_udc_dev *dev, + unsigned long val, unsigned long reg) +{ + iowrite32(val, dev->base_addr + reg); +} + +static inline void pch_udc_bit_set(struct pch_udc_dev *dev, + unsigned long reg, + unsigned long bitmask) +{ + pch_udc_writel(dev, pch_udc_readl(dev, reg) | bitmask, reg); +} + +static inline void pch_udc_bit_clr(struct pch_udc_dev *dev, + unsigned long reg, + unsigned long bitmask) +{ + pch_udc_writel(dev, pch_udc_readl(dev, reg) & ~(bitmask), reg); +} + +static inline u32 pch_udc_ep_readl(struct pch_udc_ep *ep, unsigned long reg) +{ + return ioread32(ep->dev->base_addr + ep->offset_addr + reg); +} + +static inline void pch_udc_ep_writel(struct pch_udc_ep *ep, + unsigned long val, unsigned long reg) +{ + iowrite32(val, ep->dev->base_addr + ep->offset_addr + reg); +} + +static inline void pch_udc_ep_bit_set(struct pch_udc_ep *ep, + unsigned long reg, + unsigned long bitmask) +{ + pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) | bitmask, reg); +} + +static inline void pch_udc_ep_bit_clr(struct pch_udc_ep *ep, + unsigned long reg, + unsigned long bitmask) +{ + pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) & ~(bitmask), reg); +} + +/** + * pch_udc_csr_busy() - Wait till idle. + * @dev: Reference to pch_udc_dev structure + */ +static void pch_udc_csr_busy(struct pch_udc_dev *dev) +{ + unsigned int count = 200; + + /* Wait till idle */ + while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY) + && --count) + cpu_relax(); + if (!count) + dev_err(&dev->pdev->dev, "%s: wait error\n", __func__); +} + +/** + * pch_udc_write_csr() - Write the command and status registers. + * @dev: Reference to pch_udc_dev structure + * @val: value to be written to CSR register + * @addr: address of CSR register + */ +static void pch_udc_write_csr(struct pch_udc_dev *dev, unsigned long val, + unsigned int ep) +{ + unsigned long reg = PCH_UDC_CSR(ep); + + pch_udc_csr_busy(dev); /* Wait till idle */ + pch_udc_writel(dev, val, reg); + pch_udc_csr_busy(dev); /* Wait till idle */ +} + +/** + * pch_udc_read_csr() - Read the command and status registers. + * @dev: Reference to pch_udc_dev structure + * @addr: address of CSR register + * + * Return codes: content of CSR register + */ +static u32 pch_udc_read_csr(struct pch_udc_dev *dev, unsigned int ep) +{ + unsigned long reg = PCH_UDC_CSR(ep); + + pch_udc_csr_busy(dev); /* Wait till idle */ + pch_udc_readl(dev, reg); /* Dummy read */ + pch_udc_csr_busy(dev); /* Wait till idle */ + return pch_udc_readl(dev, reg); +} + +/** + * pch_udc_rmt_wakeup() - Initiate for remote wakeup + * @dev: Reference to pch_udc_dev structure + */ +static inline void pch_udc_rmt_wakeup(struct pch_udc_dev *dev) +{ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); + mdelay(1); + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); +} + +/** + * pch_udc_get_frame() - Get the current frame from device status register + * @dev: Reference to pch_udc_dev structure + * Retern current frame + */ +static inline int pch_udc_get_frame(struct pch_udc_dev *dev) +{ + u32 frame = pch_udc_readl(dev, UDC_DEVSTS_ADDR); + return (frame & UDC_DEVSTS_TS_MASK) >> UDC_DEVSTS_TS_SHIFT; +} + +/** + * pch_udc_clear_selfpowered() - Clear the self power control + * @dev: Reference to pch_udc_regs structure + */ +static inline void pch_udc_clear_selfpowered(struct pch_udc_dev *dev) +{ + pch_udc_bit_clr(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP); +} + +/** + * pch_udc_set_selfpowered() - Set the self power control + * @dev: Reference to pch_udc_regs structure + */ +static inline void pch_udc_set_selfpowered(struct pch_udc_dev *dev) +{ + pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP); +} + +/** + * pch_udc_set_disconnect() - Set the disconnect status. + * @dev: Reference to pch_udc_regs structure + */ +static inline void pch_udc_set_disconnect(struct pch_udc_dev *dev) +{ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD); +} + +/** + * pch_udc_clear_disconnect() - Clear the disconnect status. + * @dev: Reference to pch_udc_regs structure + */ +static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) +{ + /* Clear the disconnect */ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD); + mdelay(1); + /* Resume USB signalling */ + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); +} + +/** + * pch_udc_reconnect() - This API initializes usb device controller, + * and clear the disconnect status. + * @dev: Reference to pch_udc_regs structure + */ +static void pch_udc_init(struct pch_udc_dev *dev); +static void pch_udc_reconnect(struct pch_udc_dev *dev) +{ + pch_udc_init(dev); + + /* enable device interrupts */ + /* pch_udc_enable_interrupts() */ + pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, + UDC_DEVINT_UR | UDC_DEVINT_ENUM); + + /* Clear the disconnect */ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD); + mdelay(1); + /* Resume USB signalling */ + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); +} + +/** + * pch_udc_vbus_session() - set or clearr the disconnect status. + * @dev: Reference to pch_udc_regs structure + * @is_active: Parameter specifying the action + * 0: indicating VBUS power is ending + * !0: indicating VBUS power is starting + */ +static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, + int is_active) +{ + if (is_active) { + pch_udc_reconnect(dev); + dev->vbus_session = 1; + } else { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + pch_udc_set_disconnect(dev); + dev->vbus_session = 0; + } +} + +/** + * pch_udc_ep_set_stall() - Set the stall of endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static void pch_udc_ep_set_stall(struct pch_udc_ep *ep) +{ + if (ep->in) { + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F); + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S); + } else { + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S); + } +} + +/** + * pch_udc_ep_clear_stall() - Clear the stall of endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static inline void pch_udc_ep_clear_stall(struct pch_udc_ep *ep) +{ + /* Clear the stall */ + pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S); + /* Clear NAK by writing CNAK */ + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK); +} + +/** + * pch_udc_ep_set_trfr_type() - Set the transfer type of endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + * @type: Type of endpoint + */ +static inline void pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep, + u8 type) +{ + pch_udc_ep_writel(ep, ((type << UDC_EPCTL_ET_SHIFT) & + UDC_EPCTL_ET_MASK), UDC_EPCTL_ADDR); +} + +/** + * pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + * @buf_size: The buffer word size + */ +static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep, + u32 buf_size, u32 ep_in) +{ + u32 data; + if (ep_in) { + data = pch_udc_ep_readl(ep, UDC_BUFIN_FRAMENUM_ADDR); + data = (data & 0xffff0000) | (buf_size & 0xffff); + pch_udc_ep_writel(ep, data, UDC_BUFIN_FRAMENUM_ADDR); + } else { + data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR); + data = (buf_size << 16) | (data & 0xffff); + pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR); + } +} + +/** + * pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + * @pkt_size: The packet byte size + */ +static void pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size) +{ + u32 data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR); + data = (data & 0xffff0000) | (pkt_size & 0xffff); + pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR); +} + +/** + * pch_udc_ep_set_subptr() - Set the Setup buffer pointer for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + * @addr: Address of the register + */ +static inline void pch_udc_ep_set_subptr(struct pch_udc_ep *ep, u32 addr) +{ + pch_udc_ep_writel(ep, addr, UDC_SUBPTR_ADDR); +} + +/** + * pch_udc_ep_set_ddptr() - Set the Data descriptor pointer for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + * @addr: Address of the register + */ +static inline void pch_udc_ep_set_ddptr(struct pch_udc_ep *ep, u32 addr) +{ + pch_udc_ep_writel(ep, addr, UDC_DESPTR_ADDR); +} + +/** + * pch_udc_ep_set_pd() - Set the poll demand bit for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static inline void pch_udc_ep_set_pd(struct pch_udc_ep *ep) +{ + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_P); +} + +/** + * pch_udc_ep_set_rrdy() - Set the receive ready bit for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static inline void pch_udc_ep_set_rrdy(struct pch_udc_ep *ep) +{ + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY); +} + +/** + * pch_udc_ep_clear_rrdy() - Clear the receive ready bit for the endpoint + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static inline void pch_udc_ep_clear_rrdy(struct pch_udc_ep *ep) +{ + pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY); +} + +/** + * pch_udc_set_dma() - Set the 'TDE' or RDE bit of device control + * register depending on the direction specified + * @dev: Reference to structure of type pch_udc_regs + * @dir: whether Tx or Rx + * DMA_DIR_RX: Receive + * DMA_DIR_TX: Transmit + */ +static inline void pch_udc_set_dma(struct pch_udc_dev *dev, int dir) +{ + if (dir == DMA_DIR_RX) + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE); + else if (dir == DMA_DIR_TX) + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE); +} + +/** + * pch_udc_clear_dma() - Clear the 'TDE' or RDE bit of device control + * register depending on the direction specified + * @dev: Reference to structure of type pch_udc_regs + * @dir: Whether Tx or Rx + * DMA_DIR_RX: Receive + * DMA_DIR_TX: Transmit + */ +static inline void pch_udc_clear_dma(struct pch_udc_dev *dev, int dir) +{ + if (dir == DMA_DIR_RX) + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE); + else if (dir == DMA_DIR_TX) + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE); +} + +/** + * pch_udc_set_csr_done() - Set the device control register + * CSR done field (bit 13) + * @dev: reference to structure of type pch_udc_regs + */ +static inline void pch_udc_set_csr_done(struct pch_udc_dev *dev) +{ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_CSR_DONE); +} + +/** + * pch_udc_disable_interrupts() - Disables the specified interrupts + * @dev: Reference to structure of type pch_udc_regs + * @mask: Mask to disable interrupts + */ +static inline void pch_udc_disable_interrupts(struct pch_udc_dev *dev, + u32 mask) +{ + pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, mask); +} + +/** + * pch_udc_enable_interrupts() - Enable the specified interrupts + * @dev: Reference to structure of type pch_udc_regs + * @mask: Mask to enable interrupts + */ +static inline void pch_udc_enable_interrupts(struct pch_udc_dev *dev, + u32 mask) +{ + pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, mask); +} + +/** + * pch_udc_disable_ep_interrupts() - Disable endpoint interrupts + * @dev: Reference to structure of type pch_udc_regs + * @mask: Mask to disable interrupts + */ +static inline void pch_udc_disable_ep_interrupts(struct pch_udc_dev *dev, + u32 mask) +{ + pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, mask); +} + +/** + * pch_udc_enable_ep_interrupts() - Enable endpoint interrupts + * @dev: Reference to structure of type pch_udc_regs + * @mask: Mask to enable interrupts + */ +static inline void pch_udc_enable_ep_interrupts(struct pch_udc_dev *dev, + u32 mask) +{ + pch_udc_bit_clr(dev, UDC_EPIRQMSK_ADDR, mask); +} + +/** + * pch_udc_read_device_interrupts() - Read the device interrupts + * @dev: Reference to structure of type pch_udc_regs + * Retern The device interrupts + */ +static inline u32 pch_udc_read_device_interrupts(struct pch_udc_dev *dev) +{ + return pch_udc_readl(dev, UDC_DEVIRQSTS_ADDR); +} + +/** + * pch_udc_write_device_interrupts() - Write device interrupts + * @dev: Reference to structure of type pch_udc_regs + * @val: The value to be written to interrupt register + */ +static inline void pch_udc_write_device_interrupts(struct pch_udc_dev *dev, + u32 val) +{ + pch_udc_writel(dev, val, UDC_DEVIRQSTS_ADDR); +} + +/** + * pch_udc_read_ep_interrupts() - Read the endpoint interrupts + * @dev: Reference to structure of type pch_udc_regs + * Retern The endpoint interrupt + */ +static inline u32 pch_udc_read_ep_interrupts(struct pch_udc_dev *dev) +{ + return pch_udc_readl(dev, UDC_EPIRQSTS_ADDR); +} + +/** + * pch_udc_write_ep_interrupts() - Clear endpoint interupts + * @dev: Reference to structure of type pch_udc_regs + * @val: The value to be written to interrupt register + */ +static inline void pch_udc_write_ep_interrupts(struct pch_udc_dev *dev, + u32 val) +{ + pch_udc_writel(dev, val, UDC_EPIRQSTS_ADDR); +} + +/** + * pch_udc_read_device_status() - Read the device status + * @dev: Reference to structure of type pch_udc_regs + * Retern The device status + */ +static inline u32 pch_udc_read_device_status(struct pch_udc_dev *dev) +{ + return pch_udc_readl(dev, UDC_DEVSTS_ADDR); +} + +/** + * pch_udc_read_ep_control() - Read the endpoint control + * @ep: Reference to structure of type pch_udc_ep_regs + * Retern The endpoint control register value + */ +static inline u32 pch_udc_read_ep_control(struct pch_udc_ep *ep) +{ + return pch_udc_ep_readl(ep, UDC_EPCTL_ADDR); +} + +/** + * pch_udc_clear_ep_control() - Clear the endpoint control register + * @ep: Reference to structure of type pch_udc_ep_regs + * Retern The endpoint control register value + */ +static inline void pch_udc_clear_ep_control(struct pch_udc_ep *ep) +{ + return pch_udc_ep_writel(ep, 0, UDC_EPCTL_ADDR); +} + +/** + * pch_udc_read_ep_status() - Read the endpoint status + * @ep: Reference to structure of type pch_udc_ep_regs + * Retern The endpoint status + */ +static inline u32 pch_udc_read_ep_status(struct pch_udc_ep *ep) +{ + return pch_udc_ep_readl(ep, UDC_EPSTS_ADDR); +} + +/** + * pch_udc_clear_ep_status() - Clear the endpoint status + * @ep: Reference to structure of type pch_udc_ep_regs + * @stat: Endpoint status + */ +static inline void pch_udc_clear_ep_status(struct pch_udc_ep *ep, + u32 stat) +{ + return pch_udc_ep_writel(ep, stat, UDC_EPSTS_ADDR); +} + +/** + * pch_udc_ep_set_nak() - Set the bit 7 (SNAK field) + * of the endpoint control register + * @ep: Reference to structure of type pch_udc_ep_regs + */ +static inline void pch_udc_ep_set_nak(struct pch_udc_ep *ep) +{ + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_SNAK); +} + +/** + * pch_udc_ep_clear_nak() - Set the bit 8 (CNAK field) + * of the endpoint control register + * @ep: reference to structure of type pch_udc_ep_regs + */ +static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep) +{ + unsigned int loopcnt = 0; + struct pch_udc_dev *dev = ep->dev; + + if (!(pch_udc_ep_readl(ep, UDC_EPCTL_ADDR) & UDC_EPCTL_NAK)) + return; + if (!ep->in) { + loopcnt = 10000; + while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) && + --loopcnt) + udelay(5); + if (!loopcnt) + dev_err(&dev->pdev->dev, "%s: RxFIFO not Empty\n", + __func__); + } + loopcnt = 10000; + while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_NAK) && --loopcnt) { + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK); + udelay(5); + } + if (!loopcnt) + dev_err(&dev->pdev->dev, "%s: Clear NAK not set for ep%d%s\n", + __func__, ep->num, (ep->in ? "in" : "out")); +} + +/** + * pch_udc_ep_fifo_flush() - Flush the endpoint fifo + * @ep: reference to structure of type pch_udc_ep_regs + * @dir: direction of endpoint + * 0: endpoint is OUT + * !0: endpoint is IN + */ +static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir) +{ + if (dir) { /* IN ep */ + pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F); + return; + } +} + +/** + * pch_udc_ep_enable() - This api enables endpoint + * @regs: Reference to structure pch_udc_ep_regs + * @desc: endpoint descriptor + */ +static void pch_udc_ep_enable(struct pch_udc_ep *ep, + struct pch_udc_cfg_data *cfg, + const struct usb_endpoint_descriptor *desc) +{ + u32 val = 0; + u32 buff_size = 0; + + pch_udc_ep_set_trfr_type(ep, desc->bmAttributes); + if (ep->in) + buff_size = UDC_EPIN_BUFF_SIZE; + else + buff_size = UDC_EPOUT_BUFF_SIZE; + pch_udc_ep_set_bufsz(ep, buff_size, ep->in); + pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc)); + pch_udc_ep_set_nak(ep); + pch_udc_ep_fifo_flush(ep, ep->in); + /* Configure the endpoint */ + val = ep->num << UDC_CSR_NE_NUM_SHIFT | ep->in << UDC_CSR_NE_DIR_SHIFT | + ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) << + UDC_CSR_NE_TYPE_SHIFT) | + (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) | + (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) | + (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) | + usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT; + + if (ep->in) + pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num)); + else + pch_udc_write_csr(ep->dev, val, UDC_EPOUT_IDX(ep->num)); +} + +/** + * pch_udc_ep_disable() - This api disables endpoint + * @regs: Reference to structure pch_udc_ep_regs + */ +static void pch_udc_ep_disable(struct pch_udc_ep *ep) +{ + if (ep->in) { + /* flush the fifo */ + pch_udc_ep_writel(ep, UDC_EPCTL_F, UDC_EPCTL_ADDR); + /* set NAK */ + pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR); + pch_udc_ep_bit_set(ep, UDC_EPSTS_ADDR, UDC_EPSTS_IN); + } else { + /* set NAK */ + pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR); + } + /* reset desc pointer */ + pch_udc_ep_writel(ep, 0, UDC_DESPTR_ADDR); +} + +/** + * pch_udc_wait_ep_stall() - Wait EP stall. + * @dev: Reference to pch_udc_dev structure + */ +static void pch_udc_wait_ep_stall(struct pch_udc_ep *ep) +{ + unsigned int count = 10000; + + /* Wait till idle */ + while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_S) && --count) + udelay(5); + if (!count) + dev_err(&ep->dev->pdev->dev, "%s: wait error\n", __func__); +} + +/** + * pch_udc_init() - This API initializes usb device controller + * @dev: Rreference to pch_udc_regs structure + */ +static void pch_udc_init(struct pch_udc_dev *dev) +{ + if (NULL == dev) { + pr_err("%s: Invalid address\n", __func__); + return; + } + /* Soft Reset and Reset PHY */ + pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR); + pch_udc_writel(dev, UDC_SRST | UDC_PSRST, UDC_SRST_ADDR); + mdelay(1); + pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR); + pch_udc_writel(dev, 0x00, UDC_SRST_ADDR); + mdelay(1); + /* mask and clear all device interrupts */ + pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK); + pch_udc_bit_set(dev, UDC_DEVIRQSTS_ADDR, UDC_DEVINT_MSK); + + /* mask and clear all ep interrupts */ + pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL); + pch_udc_bit_set(dev, UDC_EPIRQSTS_ADDR, UDC_EPINT_MSK_DISABLE_ALL); + + /* enable dynamic CSR programmingi, self powered and device speed */ + if (speed_fs) + pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG | + UDC_DEVCFG_SP | UDC_DEVCFG_SPD_FS); + else /* defaul high speed */ + pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG | + UDC_DEVCFG_SP | UDC_DEVCFG_SPD_HS); + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, + (PCH_UDC_THLEN << UDC_DEVCTL_THLEN_SHIFT) | + (PCH_UDC_BRLEN << UDC_DEVCTL_BRLEN_SHIFT) | + UDC_DEVCTL_MODE | UDC_DEVCTL_BREN | + UDC_DEVCTL_THE); +} + +/** + * pch_udc_exit() - This API exit usb device controller + * @dev: Reference to pch_udc_regs structure + */ +static void pch_udc_exit(struct pch_udc_dev *dev) +{ + /* mask all device interrupts */ + pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK); + /* mask all ep interrupts */ + pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL); + /* put device in disconnected state */ + pch_udc_set_disconnect(dev); +} + +/** + * pch_udc_pcd_get_frame() - This API is invoked to get the current frame number + * @gadget: Reference to the gadget driver + * + * Return codes: + * 0: Success + * -EINVAL: If the gadget passed is NULL + */ +static int pch_udc_pcd_get_frame(struct usb_gadget *gadget) +{ + struct pch_udc_dev *dev; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + return pch_udc_get_frame(dev); +} + +/** + * pch_udc_pcd_wakeup() - This API is invoked to initiate a remote wakeup + * @gadget: Reference to the gadget driver + * + * Return codes: + * 0: Success + * -EINVAL: If the gadget passed is NULL + */ +static int pch_udc_pcd_wakeup(struct usb_gadget *gadget) +{ + struct pch_udc_dev *dev; + unsigned long flags; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + spin_lock_irqsave(&dev->lock, flags); + pch_udc_rmt_wakeup(dev); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +/** + * pch_udc_pcd_selfpowered() - This API is invoked to specify whether the device + * is self powered or not + * @gadget: Reference to the gadget driver + * @value: Specifies self powered or not + * + * Return codes: + * 0: Success + * -EINVAL: If the gadget passed is NULL + */ +static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value) +{ + struct pch_udc_dev *dev; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + if (value) + pch_udc_set_selfpowered(dev); + else + pch_udc_clear_selfpowered(dev); + return 0; +} + +/** + * pch_udc_pcd_pullup() - This API is invoked to make the device + * visible/invisible to the host + * @gadget: Reference to the gadget driver + * @is_on: Specifies whether the pull up is made active or inactive + * + * Return codes: + * 0: Success + * -EINVAL: If the gadget passed is NULL + */ +static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on) +{ + struct pch_udc_dev *dev; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + if (is_on) { + pch_udc_reconnect(dev); + } else { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + pch_udc_set_disconnect(dev); + } + + return 0; +} + +/** + * pch_udc_pcd_vbus_session() - This API is used by a driver for an external + * transceiver (or GPIO) that + * detects a VBUS power session starting/ending + * @gadget: Reference to the gadget driver + * @is_active: specifies whether the session is starting or ending + * + * Return codes: + * 0: Success + * -EINVAL: If the gadget passed is NULL + */ +static int pch_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct pch_udc_dev *dev; + + if (!gadget) + return -EINVAL; + dev = container_of(gadget, struct pch_udc_dev, gadget); + pch_udc_vbus_session(dev, is_active); + return 0; +} + +/** + * pch_udc_pcd_vbus_draw() - This API is used by gadget drivers during + * SET_CONFIGURATION calls to + * specify how much power the device can consume + * @gadget: Reference to the gadget driver + * @mA: specifies the current limit in 2mA unit + * + * Return codes: + * -EINVAL: If the gadget passed is NULL + * -EOPNOTSUPP: + */ +static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA) +{ + return -EOPNOTSUPP; +} + +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, + .set_selfpowered = pch_udc_pcd_selfpowered, + .pullup = pch_udc_pcd_pullup, + .vbus_session = pch_udc_pcd_vbus_session, + .vbus_draw = pch_udc_pcd_vbus_draw, + .udc_start = pch_udc_start, + .udc_stop = pch_udc_stop, +}; + +/** + * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status. + * @dev: Reference to the driver structure + * + * Return value: + * 1: VBUS is high + * 0: VBUS is low + * -1: It is not enable to detect VBUS using GPIO + */ +static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev) +{ + int vbus = 0; + + if (dev->vbus_gpio.port) + vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0; + else + vbus = -1; + + return vbus; +} + +/** + * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low. + * If VBUS is Low, disconnect is processed + * @irq_work: Structure for WorkQueue + * + */ +static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) +{ + struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, + struct pch_vbus_gpio_data, irq_work_fall); + struct pch_udc_dev *dev = + container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); + int vbus_saved = -1; + int vbus; + int count; + + if (!dev->vbus_gpio.port) + return; + + for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL); + count++) { + vbus = pch_vbus_gpio_get_value(dev); + + if ((vbus_saved == vbus) && (vbus == 0)) { + dev_dbg(&dev->pdev->dev, "VBUS fell"); + if (dev->driver + && dev->driver->disconnect) { + dev->driver->disconnect( + &dev->gadget); + } + if (dev->vbus_gpio.intr) + pch_udc_init(dev); + else + pch_udc_reconnect(dev); + return; + } + vbus_saved = vbus; + mdelay(PCH_VBUS_INTERVAL); + } +} + +/** + * pch_vbus_gpio_work_rise() - This API checks VBUS is High. + * If VBUS is High, connect is processed + * @irq_work: Structure for WorkQueue + * + */ +static void pch_vbus_gpio_work_rise(struct work_struct *irq_work) +{ + struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, + struct pch_vbus_gpio_data, irq_work_rise); + struct pch_udc_dev *dev = + container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); + int vbus; + + if (!dev->vbus_gpio.port) + return; + + mdelay(PCH_VBUS_INTERVAL); + vbus = pch_vbus_gpio_get_value(dev); + + if (vbus == 1) { + dev_dbg(&dev->pdev->dev, "VBUS rose"); + pch_udc_reconnect(dev); + return; + } +} + +/** + * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS + * @irq: Interrupt request number + * @dev: Reference to the device structure + * + * Return codes: + * 0: Success + * -EINVAL: GPIO port is invalid or can't be initialized. + */ +static irqreturn_t pch_vbus_gpio_irq(int irq, void *data) +{ + struct pch_udc_dev *dev = (struct pch_udc_dev *)data; + + if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr) + return IRQ_NONE; + + if (pch_vbus_gpio_get_value(dev)) + schedule_work(&dev->vbus_gpio.irq_work_rise); + else + schedule_work(&dev->vbus_gpio.irq_work_fall); + + return IRQ_HANDLED; +} + +/** + * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. + * @dev: Reference to the driver structure + * @vbus_gpio Number of GPIO port to detect gpio + * + * Return codes: + * 0: Success + * -EINVAL: GPIO port is invalid or can't be initialized. + */ +static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) +{ + int err; + int irq_num = 0; + + dev->vbus_gpio.port = 0; + dev->vbus_gpio.intr = 0; + + if (vbus_gpio_port <= -1) + return -EINVAL; + + err = gpio_is_valid(vbus_gpio_port); + if (!err) { + pr_err("%s: gpio port %d is invalid\n", + __func__, vbus_gpio_port); + return -EINVAL; + } + + err = gpio_request(vbus_gpio_port, "pch_vbus"); + if (err) { + pr_err("%s: can't request gpio port %d, err: %d\n", + __func__, vbus_gpio_port, err); + return -EINVAL; + } + + dev->vbus_gpio.port = vbus_gpio_port; + gpio_direction_input(vbus_gpio_port); + INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); + + irq_num = gpio_to_irq(vbus_gpio_port); + if (irq_num > 0) { + irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH); + err = request_irq(irq_num, pch_vbus_gpio_irq, 0, + "vbus_detect", dev); + if (!err) { + dev->vbus_gpio.intr = irq_num; + INIT_WORK(&dev->vbus_gpio.irq_work_rise, + pch_vbus_gpio_work_rise); + } else { + pr_err("%s: can't request irq %d, err: %d\n", + __func__, irq_num, err); + } + } + + return 0; +} + +/** + * pch_vbus_gpio_free() - This API frees resources of GPIO port + * @dev: Reference to the driver structure + */ +static void pch_vbus_gpio_free(struct pch_udc_dev *dev) +{ + if (dev->vbus_gpio.intr) + free_irq(dev->vbus_gpio.intr, dev); + + if (dev->vbus_gpio.port) + gpio_free(dev->vbus_gpio.port); +} + +/** + * complete_req() - This API is invoked from the driver when processing + * of a request is complete + * @ep: Reference to the endpoint structure + * @req: Reference to the request structure + * @status: Indicates the success/failure of completion + */ +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; + + list_del_init(&req->queue); + + /* set new status if pending */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (req->dma_mapped) { + if (req->dma == DMA_ADDR_INVALID) { + if (ep->in) + dma_unmap_single(&dev->pdev->dev, req->req.dma, + req->req.length, + DMA_TO_DEVICE); + else + dma_unmap_single(&dev->pdev->dev, req->req.dma, + req->req.length, + DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + } else { + if (ep->in) + dma_unmap_single(&dev->pdev->dev, req->dma, + req->req.length, + DMA_TO_DEVICE); + else { + dma_unmap_single(&dev->pdev->dev, req->dma, + req->req.length, + DMA_FROM_DEVICE); + memcpy(req->req.buf, req->buf, req->req.length); + } + kfree(req->buf); + req->dma = DMA_ADDR_INVALID; + } + req->dma_mapped = 0; + } + ep->halted = 1; + spin_unlock(&dev->lock); + if (!ep->in) + pch_udc_ep_clear_rrdy(ep); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->halted = halted; +} + +/** + * empty_req_queue() - This API empties the request queue of an endpoint + * @ep: Reference to the endpoint structure + */ +static void empty_req_queue(struct pch_udc_ep *ep) +{ + struct pch_udc_request *req; + + ep->halted = 1; + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + complete_req(ep, req, -ESHUTDOWN); /* Remove from list */ + } +} + +/** + * pch_udc_free_dma_chain() - This function frees the DMA chain created + * for the request + * @dev Reference to the driver structure + * @req Reference to the request to be freed + * + * Return codes: + * 0: Success + */ +static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, + struct pch_udc_request *req) +{ + struct pch_udc_data_dma_desc *td = req->td_data; + unsigned i = req->chain_len; + + dma_addr_t addr2; + dma_addr_t addr = (dma_addr_t)td->next; + td->next = 0x00; + for (; i > 1; --i) { + /* do not free first desc., will be done by free for request */ + td = phys_to_virt(addr); + addr2 = (dma_addr_t)td->next; + pci_pool_free(dev->data_requests, td, addr); + td->next = 0x00; + addr = addr2; + } + req->chain_len = 1; +} + +/** + * pch_udc_create_dma_chain() - This function creates or reinitializes + * a DMA chain + * @ep: Reference to the endpoint structure + * @req: Reference to the request + * @buf_len: The buffer length + * @gfp_flags: Flags to be used while mapping the data buffer + * + * Return codes: + * 0: success, + * -ENOMEM: pci_pool_alloc invocation fails + */ +static int pch_udc_create_dma_chain(struct pch_udc_ep *ep, + struct pch_udc_request *req, + unsigned long buf_len, + gfp_t gfp_flags) +{ + struct pch_udc_data_dma_desc *td = req->td_data, *last; + unsigned long bytes = req->req.length, i = 0; + dma_addr_t dma_addr; + unsigned len = 1; + + if (req->chain_len > 1) + pch_udc_free_dma_chain(ep->dev, req); + + if (req->dma == DMA_ADDR_INVALID) + td->dataptr = req->req.dma; + else + td->dataptr = req->dma; + + td->status = PCH_UDC_BS_HST_BSY; + for (; ; bytes -= buf_len, ++len) { + td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes); + if (bytes <= buf_len) + break; + last = td; + td = pci_pool_alloc(ep->dev->data_requests, gfp_flags, + &dma_addr); + if (!td) + goto nomem; + i += buf_len; + td->dataptr = req->td_data->dataptr + i; + last->next = dma_addr; + } + + req->td_data_last = td; + td->status |= PCH_UDC_DMA_LAST; + td->next = req->td_data_phys; + req->chain_len = len; + return 0; + +nomem: + if (len > 1) { + req->chain_len = len; + pch_udc_free_dma_chain(ep->dev, req); + } + req->chain_len = 1; + return -ENOMEM; +} + +/** + * prepare_dma() - This function creates and initializes the DMA chain + * for the request + * @ep: Reference to the endpoint structure + * @req: Reference to the request + * @gfp: Flag to be used while mapping the data buffer + * + * Return codes: + * 0: Success + * Other 0: linux error number on failure + */ +static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req, + gfp_t gfp) +{ + int retval; + + /* Allocate and create a DMA chain */ + retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp); + if (retval) { + pr_err("%s: could not create DMA chain:%d\n", __func__, retval); + return retval; + } + if (ep->in) + req->td_data->status = (req->td_data->status & + ~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_RDY; + return 0; +} + +/** + * process_zlp() - This function process zero length packets + * from the gadget driver + * @ep: Reference to the endpoint structure + * @req: Reference to the request + */ +static void process_zlp(struct pch_udc_ep *ep, struct pch_udc_request *req) +{ + struct pch_udc_dev *dev = ep->dev; + + /* IN zlp's are handled by hardware */ + complete_req(ep, req, 0); + + /* if set_config or set_intf is waiting for ack by zlp + * then set CSR_DONE + */ + if (dev->set_cfg_not_acked) { + pch_udc_set_csr_done(dev); + dev->set_cfg_not_acked = 0; + } + /* setup command is ACK'ed now by zlp */ + if (!dev->stall && dev->waiting_zlp_ack) { + pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX])); + dev->waiting_zlp_ack = 0; + } +} + +/** + * pch_udc_start_rxrequest() - This function starts the receive requirement. + * @ep: Reference to the endpoint structure + * @req: Reference to the request structure + */ +static void pch_udc_start_rxrequest(struct pch_udc_ep *ep, + struct pch_udc_request *req) +{ + struct pch_udc_data_dma_desc *td_data; + + pch_udc_clear_dma(ep->dev, DMA_DIR_RX); + td_data = req->td_data; + /* Set the status bits for all descriptors */ + while (1) { + td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) | + PCH_UDC_BS_HST_RDY; + if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST) + break; + td_data = phys_to_virt(td_data->next); + } + /* Write the descriptor pointer */ + pch_udc_ep_set_ddptr(ep, req->td_data_phys); + req->dma_going = 1; + pch_udc_enable_ep_interrupts(ep->dev, UDC_EPINT_OUT_EP0 << ep->num); + pch_udc_set_dma(ep->dev, DMA_DIR_RX); + pch_udc_ep_clear_nak(ep); + pch_udc_ep_set_rrdy(ep); +} + +/** + * pch_udc_pcd_ep_enable() - This API enables the endpoint. It is called + * from gadget driver + * @usbep: Reference to the USB endpoint structure + * @desc: Reference to the USB endpoint descriptor structure + * + * Return codes: + * 0: Success + * -EINVAL: + * -ESHUTDOWN: + */ +static int pch_udc_pcd_ep_enable(struct usb_ep *usbep, + const struct usb_endpoint_descriptor *desc) +{ + struct pch_udc_ep *ep; + struct pch_udc_dev *dev; + unsigned long iflags; + + if (!usbep || (usbep->name == ep0_string) || !desc || + (desc->bDescriptorType != USB_DT_ENDPOINT) || !desc->wMaxPacketSize) + return -EINVAL; + + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + spin_lock_irqsave(&dev->lock, iflags); + ep->ep.desc = desc; + ep->halted = 0; + pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc); + ep->ep.maxpacket = usb_endpoint_maxp(desc); + pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); + spin_unlock_irqrestore(&dev->lock, iflags); + return 0; +} + +/** + * pch_udc_pcd_ep_disable() - This API disables endpoint and is called + * from gadget driver + * @usbep Reference to the USB endpoint structure + * + * Return codes: + * 0: Success + * -EINVAL: + */ +static int pch_udc_pcd_ep_disable(struct usb_ep *usbep) +{ + struct pch_udc_ep *ep; + struct pch_udc_dev *dev; + unsigned long iflags; + + if (!usbep) + return -EINVAL; + + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if ((usbep->name == ep0_string) || !ep->ep.desc) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, iflags); + empty_req_queue(ep); + ep->halted = 1; + pch_udc_ep_disable(ep); + pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); + ep->ep.desc = NULL; + INIT_LIST_HEAD(&ep->queue); + spin_unlock_irqrestore(&ep->dev->lock, iflags); + return 0; +} + +/** + * pch_udc_alloc_request() - This function allocates request structure. + * It is called by gadget driver + * @usbep: Reference to the USB endpoint structure + * @gfp: Flag to be used while allocating memory + * + * Return codes: + * NULL: Failure + * Allocated address: Success + */ +static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep, + gfp_t gfp) +{ + struct pch_udc_request *req; + struct pch_udc_ep *ep; + struct pch_udc_data_dma_desc *dma_desc; + struct pch_udc_dev *dev; + + if (!usbep) + return NULL; + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + req = kzalloc(sizeof *req, gfp); + if (!req) + return NULL; + req->req.dma = DMA_ADDR_INVALID; + req->dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + if (!ep->dev->dma_addr) + return &req->req; + /* ep0 in requests are allocated from data pool here */ + dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp, + &req->td_data_phys); + if (NULL == dma_desc) { + kfree(req); + return NULL; + } + /* prevent from using desc. - set HOST BUSY */ + dma_desc->status |= PCH_UDC_BS_HST_BSY; + dma_desc->dataptr = __constant_cpu_to_le32(DMA_ADDR_INVALID); + req->td_data = dma_desc; + req->td_data_last = dma_desc; + req->chain_len = 1; + return &req->req; +} + +/** + * pch_udc_free_request() - This function frees request structure. + * It is called by gadget driver + * @usbep: Reference to the USB endpoint structure + * @usbreq: Reference to the USB request + */ +static void pch_udc_free_request(struct usb_ep *usbep, + struct usb_request *usbreq) +{ + struct pch_udc_ep *ep; + struct pch_udc_request *req; + struct pch_udc_dev *dev; + + if (!usbep || !usbreq) + return; + ep = container_of(usbep, struct pch_udc_ep, ep); + req = container_of(usbreq, struct pch_udc_request, req); + dev = ep->dev; + if (!list_empty(&req->queue)) + dev_err(&dev->pdev->dev, "%s: %s req=0x%p queue not empty\n", + __func__, usbep->name, req); + if (req->td_data != NULL) { + if (req->chain_len > 1) + pch_udc_free_dma_chain(ep->dev, req); + pci_pool_free(ep->dev->data_requests, req->td_data, + req->td_data_phys); + } + kfree(req); +} + +/** + * pch_udc_pcd_queue() - This function queues a request packet. It is called + * by gadget driver + * @usbep: Reference to the USB endpoint structure + * @usbreq: Reference to the USB request + * @gfp: Flag to be used while mapping the data buffer + * + * Return codes: + * 0: Success + * linux error number: Failure + */ +static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq, + gfp_t gfp) +{ + int retval = 0; + struct pch_udc_ep *ep; + struct pch_udc_dev *dev; + struct pch_udc_request *req; + unsigned long iflags; + + if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf) + return -EINVAL; + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if (!ep->ep.desc && ep->num) + return -EINVAL; + req = container_of(usbreq, struct pch_udc_request, req); + if (!list_empty(&req->queue)) + return -EINVAL; + if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + spin_lock_irqsave(&dev->lock, iflags); + /* map the buffer for dma */ + if (usbreq->length && + ((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) { + if (!((unsigned long)(usbreq->buf) & 0x03)) { + if (ep->in) + usbreq->dma = dma_map_single(&dev->pdev->dev, + usbreq->buf, + usbreq->length, + DMA_TO_DEVICE); + else + usbreq->dma = dma_map_single(&dev->pdev->dev, + usbreq->buf, + usbreq->length, + DMA_FROM_DEVICE); + } else { + req->buf = kzalloc(usbreq->length, GFP_ATOMIC); + if (!req->buf) { + retval = -ENOMEM; + goto probe_end; + } + if (ep->in) { + memcpy(req->buf, usbreq->buf, usbreq->length); + req->dma = dma_map_single(&dev->pdev->dev, + req->buf, + usbreq->length, + DMA_TO_DEVICE); + } else + req->dma = dma_map_single(&dev->pdev->dev, + req->buf, + usbreq->length, + DMA_FROM_DEVICE); + } + req->dma_mapped = 1; + } + if (usbreq->length > 0) { + retval = prepare_dma(ep, req, GFP_ATOMIC); + if (retval) + goto probe_end; + } + usbreq->actual = 0; + usbreq->status = -EINPROGRESS; + req->dma_done = 0; + if (list_empty(&ep->queue) && !ep->halted) { + /* no pending transfer, so start this req */ + if (!usbreq->length) { + process_zlp(ep, req); + retval = 0; + goto probe_end; + } + if (!ep->in) { + pch_udc_start_rxrequest(ep, req); + } else { + /* + * For IN trfr the descriptors will be programmed and + * P bit will be set when + * we get an IN token + */ + pch_udc_wait_ep_stall(ep); + pch_udc_ep_clear_nak(ep); + pch_udc_enable_ep_interrupts(ep->dev, (1 << ep->num)); + } + } + /* Now add this request to the ep's pending requests */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + +probe_end: + spin_unlock_irqrestore(&dev->lock, iflags); + return retval; +} + +/** + * pch_udc_pcd_dequeue() - This function de-queues a request packet. + * It is called by gadget driver + * @usbep: Reference to the USB endpoint structure + * @usbreq: Reference to the USB request + * + * Return codes: + * 0: Success + * linux error number: Failure + */ +static int pch_udc_pcd_dequeue(struct usb_ep *usbep, + struct usb_request *usbreq) +{ + struct pch_udc_ep *ep; + struct pch_udc_request *req; + struct pch_udc_dev *dev; + unsigned long flags; + int ret = -EINVAL; + + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if (!usbep || !usbreq || (!ep->ep.desc && ep->num)) + return ret; + req = container_of(usbreq, struct pch_udc_request, req); + spin_lock_irqsave(&ep->dev->lock, flags); + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == usbreq) { + pch_udc_ep_set_nak(ep); + if (!list_empty(&req->queue)) + complete_req(ep, req, -ECONNRESET); + ret = 0; + break; + } + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + return ret; +} + +/** + * pch_udc_pcd_set_halt() - This function Sets or clear the endpoint halt + * feature + * @usbep: Reference to the USB endpoint structure + * @halt: Specifies whether to set or clear the feature + * + * Return codes: + * 0: Success + * linux error number: Failure + */ +static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt) +{ + struct pch_udc_ep *ep; + struct pch_udc_dev *dev; + unsigned long iflags; + int ret; + + if (!usbep) + return -EINVAL; + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if (!ep->ep.desc && !ep->num) + return -EINVAL; + if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + spin_lock_irqsave(&udc_stall_spinlock, iflags); + if (list_empty(&ep->queue)) { + if (halt) { + if (ep->num == PCH_UDC_EP0) + ep->dev->stall = 1; + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, + ep->num)); + } else { + pch_udc_ep_clear_stall(ep); + } + ret = 0; + } else { + ret = -EAGAIN; + } + spin_unlock_irqrestore(&udc_stall_spinlock, iflags); + return ret; +} + +/** + * pch_udc_pcd_set_wedge() - This function Sets or clear the endpoint + * halt feature + * @usbep: Reference to the USB endpoint structure + * @halt: Specifies whether to set or clear the feature + * + * Return codes: + * 0: Success + * linux error number: Failure + */ +static int pch_udc_pcd_set_wedge(struct usb_ep *usbep) +{ + struct pch_udc_ep *ep; + struct pch_udc_dev *dev; + unsigned long iflags; + int ret; + + if (!usbep) + return -EINVAL; + ep = container_of(usbep, struct pch_udc_ep, ep); + dev = ep->dev; + if (!ep->ep.desc && !ep->num) + return -EINVAL; + if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + spin_lock_irqsave(&udc_stall_spinlock, iflags); + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + } else { + if (ep->num == PCH_UDC_EP0) + ep->dev->stall = 1; + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + ep->dev->prot_stall = 1; + ret = 0; + } + spin_unlock_irqrestore(&udc_stall_spinlock, iflags); + return ret; +} + +/** + * pch_udc_pcd_fifo_flush() - This function Flush the FIFO of specified endpoint + * @usbep: Reference to the USB endpoint structure + */ +static void pch_udc_pcd_fifo_flush(struct usb_ep *usbep) +{ + struct pch_udc_ep *ep; + + if (!usbep) + return; + + ep = container_of(usbep, struct pch_udc_ep, ep); + if (ep->ep.desc || !ep->num) + pch_udc_ep_fifo_flush(ep, ep->in); +} + +static const struct usb_ep_ops pch_udc_ep_ops = { + .enable = pch_udc_pcd_ep_enable, + .disable = pch_udc_pcd_ep_disable, + .alloc_request = pch_udc_alloc_request, + .free_request = pch_udc_free_request, + .queue = pch_udc_pcd_queue, + .dequeue = pch_udc_pcd_dequeue, + .set_halt = pch_udc_pcd_set_halt, + .set_wedge = pch_udc_pcd_set_wedge, + .fifo_status = NULL, + .fifo_flush = pch_udc_pcd_fifo_flush, +}; + +/** + * pch_udc_init_setup_buff() - This function initializes the SETUP buffer + * @td_stp: Reference to the SETP buffer structure + */ +static void pch_udc_init_setup_buff(struct pch_udc_stp_dma_desc *td_stp) +{ + static u32 pky_marker; + + if (!td_stp) + return; + td_stp->reserved = ++pky_marker; + memset(&td_stp->request, 0xFF, sizeof td_stp->request); + td_stp->status = PCH_UDC_BS_HST_RDY; +} + +/** + * pch_udc_start_next_txrequest() - This function starts + * the next transmission requirement + * @ep: Reference to the endpoint structure + */ +static void pch_udc_start_next_txrequest(struct pch_udc_ep *ep) +{ + struct pch_udc_request *req; + struct pch_udc_data_dma_desc *td_data; + + if (pch_udc_read_ep_control(ep) & UDC_EPCTL_P) + return; + + if (list_empty(&ep->queue)) + return; + + /* next request */ + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + if (req->dma_going) + return; + if (!req->td_data) + return; + pch_udc_wait_ep_stall(ep); + req->dma_going = 1; + pch_udc_ep_set_ddptr(ep, 0); + td_data = req->td_data; + while (1) { + td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) | + PCH_UDC_BS_HST_RDY; + if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST) + break; + td_data = phys_to_virt(td_data->next); + } + pch_udc_ep_set_ddptr(ep, req->td_data_phys); + pch_udc_set_dma(ep->dev, DMA_DIR_TX); + pch_udc_ep_set_pd(ep); + pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); + pch_udc_ep_clear_nak(ep); +} + +/** + * pch_udc_complete_transfer() - This function completes a transfer + * @ep: Reference to the endpoint structure + */ +static void pch_udc_complete_transfer(struct pch_udc_ep *ep) +{ + struct pch_udc_request *req; + struct pch_udc_dev *dev = ep->dev; + + if (list_empty(&ep->queue)) + return; + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + if ((req->td_data_last->status & PCH_UDC_BUFF_STS) != + PCH_UDC_BS_DMA_DONE) + return; + if ((req->td_data_last->status & PCH_UDC_RXTX_STS) != + PCH_UDC_RTS_SUCC) { + dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) " + "epstatus=0x%08x\n", + (req->td_data_last->status & PCH_UDC_RXTX_STS), + (int)(ep->epsts)); + return; + } + + req->req.actual = req->req.length; + req->td_data_last->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST; + req->td_data->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST; + complete_req(ep, req, 0); + req->dma_going = 0; + if (!list_empty(&ep->queue)) { + pch_udc_wait_ep_stall(ep); + pch_udc_ep_clear_nak(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } else { + pch_udc_disable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } +} + +/** + * pch_udc_complete_receiver() - This function completes a receiver + * @ep: Reference to the endpoint structure + */ +static void pch_udc_complete_receiver(struct pch_udc_ep *ep) +{ + struct pch_udc_request *req; + struct pch_udc_dev *dev = ep->dev; + unsigned int count; + struct pch_udc_data_dma_desc *td; + dma_addr_t addr; + + if (list_empty(&ep->queue)) + return; + /* next request */ + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + pch_udc_clear_dma(ep->dev, DMA_DIR_RX); + pch_udc_ep_set_ddptr(ep, 0); + if ((req->td_data_last->status & PCH_UDC_BUFF_STS) == + PCH_UDC_BS_DMA_DONE) + td = req->td_data_last; + else + td = req->td_data; + + while (1) { + if ((td->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) { + dev_err(&dev->pdev->dev, "Invalid RXTX status=0x%08x " + "epstatus=0x%08x\n", + (req->td_data->status & PCH_UDC_RXTX_STS), + (int)(ep->epsts)); + return; + } + if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE) + if (td->status & PCH_UDC_DMA_LAST) { + count = td->status & PCH_UDC_RXTX_BYTES; + break; + } + if (td == req->td_data_last) { + dev_err(&dev->pdev->dev, "Not complete RX descriptor"); + return; + } + addr = (dma_addr_t)td->next; + td = phys_to_virt(addr); + } + /* on 64k packets the RXBYTES field is zero */ + if (!count && (req->req.length == UDC_DMA_MAXPACKET)) + count = UDC_DMA_MAXPACKET; + req->td_data->status |= PCH_UDC_DMA_LAST; + td->status |= PCH_UDC_BS_HST_BSY; + + req->dma_going = 0; + req->req.actual = count; + complete_req(ep, req, 0); + /* If there is a new/failed requests try that now */ + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + pch_udc_start_rxrequest(ep, req); + } +} + +/** + * pch_udc_svc_data_in() - This function process endpoint interrupts + * for IN endpoints + * @dev: Reference to the device structure + * @ep_num: Endpoint that generated the interrupt + */ +static void pch_udc_svc_data_in(struct pch_udc_dev *dev, int ep_num) +{ + u32 epsts; + struct pch_udc_ep *ep; + + ep = &dev->ep[UDC_EPIN_IDX(ep_num)]; + epsts = ep->epsts; + ep->epsts = 0; + + if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE | + UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY | + UDC_EPSTS_RSS | UDC_EPSTS_XFERDONE))) + return; + if ((epsts & UDC_EPSTS_BNA)) + return; + if (epsts & UDC_EPSTS_HE) + return; + if (epsts & UDC_EPSTS_RSS) { + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } + if (epsts & UDC_EPSTS_RCS) { + if (!dev->prot_stall) { + pch_udc_ep_clear_stall(ep); + } else { + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } + } + if (epsts & UDC_EPSTS_TDC) + pch_udc_complete_transfer(ep); + /* On IN interrupt, provide data if we have any */ + if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_RSS) && + !(epsts & UDC_EPSTS_TDC) && !(epsts & UDC_EPSTS_TXEMPTY)) + pch_udc_start_next_txrequest(ep); +} + +/** + * pch_udc_svc_data_out() - Handles interrupts from OUT endpoint + * @dev: Reference to the device structure + * @ep_num: Endpoint that generated the interrupt + */ +static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num) +{ + u32 epsts; + struct pch_udc_ep *ep; + struct pch_udc_request *req = NULL; + + ep = &dev->ep[UDC_EPOUT_IDX(ep_num)]; + epsts = ep->epsts; + ep->epsts = 0; + + if ((epsts & UDC_EPSTS_BNA) && (!list_empty(&ep->queue))) { + /* next request */ + req = list_entry(ep->queue.next, struct pch_udc_request, + queue); + if ((req->td_data_last->status & PCH_UDC_BUFF_STS) != + PCH_UDC_BS_DMA_DONE) { + if (!req->dma_going) + pch_udc_start_rxrequest(ep, req); + return; + } + } + if (epsts & UDC_EPSTS_HE) + return; + if (epsts & UDC_EPSTS_RSS) { + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } + if (epsts & UDC_EPSTS_RCS) { + if (!dev->prot_stall) { + pch_udc_ep_clear_stall(ep); + } else { + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } + } + if (((epsts & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) == + UDC_EPSTS_OUT_DATA) { + if (ep->dev->prot_stall == 1) { + pch_udc_ep_set_stall(ep); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + } else { + pch_udc_complete_receiver(ep); + } + } + if (list_empty(&ep->queue)) + pch_udc_set_dma(dev, DMA_DIR_RX); +} + +/** + * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts + * @dev: Reference to the device structure + */ +static void pch_udc_svc_control_in(struct pch_udc_dev *dev) +{ + u32 epsts; + struct pch_udc_ep *ep; + struct pch_udc_ep *ep_out; + + ep = &dev->ep[UDC_EP0IN_IDX]; + ep_out = &dev->ep[UDC_EP0OUT_IDX]; + epsts = ep->epsts; + ep->epsts = 0; + + if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE | + UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY | + UDC_EPSTS_XFERDONE))) + return; + if ((epsts & UDC_EPSTS_BNA)) + return; + if (epsts & UDC_EPSTS_HE) + return; + if ((epsts & UDC_EPSTS_TDC) && (!dev->stall)) { + pch_udc_complete_transfer(ep); + pch_udc_clear_dma(dev, DMA_DIR_RX); + ep_out->td_data->status = (ep_out->td_data->status & + ~PCH_UDC_BUFF_STS) | + PCH_UDC_BS_HST_RDY; + pch_udc_ep_clear_nak(ep_out); + pch_udc_set_dma(dev, DMA_DIR_RX); + pch_udc_ep_set_rrdy(ep_out); + } + /* On IN interrupt, provide data if we have any */ + if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_TDC) && + !(epsts & UDC_EPSTS_TXEMPTY)) + pch_udc_start_next_txrequest(ep); +} + +/** + * pch_udc_svc_control_out() - Routine that handle Control + * OUT endpoint interrupts + * @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; + struct pch_udc_ep *ep; + + ep = &dev->ep[UDC_EP0OUT_IDX]; + stat = ep->epsts; + ep->epsts = 0; + + /* If setup data */ + if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) == + UDC_EPSTS_OUT_SETUP) { + dev->stall = 0; + dev->ep[UDC_EP0IN_IDX].halted = 0; + dev->ep[UDC_EP0OUT_IDX].halted = 0; + dev->setup_data = ep->td_stp->request; + pch_udc_init_setup_buff(ep->td_stp); + pch_udc_clear_dma(dev, DMA_DIR_RX); + pch_udc_ep_fifo_flush(&(dev->ep[UDC_EP0IN_IDX]), + dev->ep[UDC_EP0IN_IDX].in); + if ((dev->setup_data.bRequestType & USB_DIR_IN)) + dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep; + else /* OUT */ + dev->gadget.ep0 = &ep->ep; + spin_unlock(&dev->lock); + /* If Mass storage Reset */ + if ((dev->setup_data.bRequestType == 0x21) && + (dev->setup_data.bRequest == 0xFF)) + dev->prot_stall = 0; + /* call gadget with setup data received */ + setup_supported = dev->driver->setup(&dev->gadget, + &dev->setup_data); + spin_lock(&dev->lock); + + if (dev->setup_data.bRequestType & USB_DIR_IN) { + ep->td_data->status = (ep->td_data->status & + ~PCH_UDC_BUFF_STS) | + PCH_UDC_BS_HST_RDY; + pch_udc_ep_set_ddptr(ep, ep->td_data_phys); + } + /* ep0 in returns data on IN phase */ + if (setup_supported >= 0 && setup_supported < + UDC_EP0IN_MAX_PKT_SIZE) { + pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX])); + /* Gadget would have queued a request when + * we called the setup */ + if (!(dev->setup_data.bRequestType & USB_DIR_IN)) { + pch_udc_set_dma(dev, DMA_DIR_RX); + pch_udc_ep_clear_nak(ep); + } + } else if (setup_supported < 0) { + /* if unsupported request, then stall */ + pch_udc_ep_set_stall(&(dev->ep[UDC_EP0IN_IDX])); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + dev->stall = 0; + pch_udc_set_dma(dev, DMA_DIR_RX); + } else { + dev->waiting_zlp_ack = 1; + } + } else if ((((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) == + UDC_EPSTS_OUT_DATA) && !dev->stall) { + pch_udc_clear_dma(dev, DMA_DIR_RX); + pch_udc_ep_set_ddptr(ep, 0); + if (!list_empty(&ep->queue)) { + ep->epsts = stat; + pch_udc_svc_data_out(dev, PCH_UDC_EP0); + } + pch_udc_set_dma(dev, DMA_DIR_RX); + } + pch_udc_ep_set_rrdy(ep); +} + + +/** + * pch_udc_postsvc_epinters() - This function enables end point interrupts + * and clears NAK status + * @dev: Reference to the device structure + * @ep_num: End point number + */ +static void pch_udc_postsvc_epinters(struct pch_udc_dev *dev, int ep_num) +{ + struct pch_udc_ep *ep; + struct pch_udc_request *req; + + ep = &dev->ep[UDC_EPIN_IDX(ep_num)]; + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct pch_udc_request, queue); + pch_udc_enable_ep_interrupts(ep->dev, + PCH_UDC_EPINT(ep->in, ep->num)); + pch_udc_ep_clear_nak(ep); + } +} + +/** + * pch_udc_read_all_epstatus() - This function read all endpoint status + * @dev: Reference to the device structure + * @ep_intr: Status of endpoint interrupt + */ +static void pch_udc_read_all_epstatus(struct pch_udc_dev *dev, u32 ep_intr) +{ + int i; + struct pch_udc_ep *ep; + + for (i = 0; i < PCH_UDC_USED_EP_NUM; i++) { + /* IN */ + if (ep_intr & (0x1 << i)) { + ep = &dev->ep[UDC_EPIN_IDX(i)]; + ep->epsts = pch_udc_read_ep_status(ep); + pch_udc_clear_ep_status(ep, ep->epsts); + } + /* OUT */ + if (ep_intr & (0x10000 << i)) { + ep = &dev->ep[UDC_EPOUT_IDX(i)]; + ep->epsts = pch_udc_read_ep_status(ep); + pch_udc_clear_ep_status(ep, ep->epsts); + } + } +} + +/** + * pch_udc_activate_control_ep() - This function enables the control endpoints + * for traffic after a reset + * @dev: Reference to the device structure + */ +static void pch_udc_activate_control_ep(struct pch_udc_dev *dev) +{ + struct pch_udc_ep *ep; + u32 val; + + /* Setup the IN endpoint */ + ep = &dev->ep[UDC_EP0IN_IDX]; + pch_udc_clear_ep_control(ep); + pch_udc_ep_fifo_flush(ep, ep->in); + pch_udc_ep_set_bufsz(ep, UDC_EP0IN_BUFF_SIZE, ep->in); + pch_udc_ep_set_maxpkt(ep, UDC_EP0IN_MAX_PKT_SIZE); + /* Initialize the IN EP Descriptor */ + ep->td_data = NULL; + ep->td_stp = NULL; + ep->td_data_phys = 0; + ep->td_stp_phys = 0; + + /* Setup the OUT endpoint */ + ep = &dev->ep[UDC_EP0OUT_IDX]; + pch_udc_clear_ep_control(ep); + pch_udc_ep_fifo_flush(ep, ep->in); + pch_udc_ep_set_bufsz(ep, UDC_EP0OUT_BUFF_SIZE, ep->in); + pch_udc_ep_set_maxpkt(ep, UDC_EP0OUT_MAX_PKT_SIZE); + val = UDC_EP0OUT_MAX_PKT_SIZE << UDC_CSR_NE_MAX_PKT_SHIFT; + pch_udc_write_csr(ep->dev, val, UDC_EP0OUT_IDX); + + /* Initialize the SETUP buffer */ + pch_udc_init_setup_buff(ep->td_stp); + /* Write the pointer address of dma descriptor */ + pch_udc_ep_set_subptr(ep, ep->td_stp_phys); + /* Write the pointer address of Setup descriptor */ + pch_udc_ep_set_ddptr(ep, ep->td_data_phys); + + /* Initialize the dma descriptor */ + ep->td_data->status = PCH_UDC_DMA_LAST; + ep->td_data->dataptr = dev->dma_addr; + ep->td_data->next = ep->td_data_phys; + + pch_udc_ep_clear_nak(ep); +} + + +/** + * pch_udc_svc_ur_interrupt() - This function handles a USB reset interrupt + * @dev: Reference to driver structure + */ +static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev) +{ + struct pch_udc_ep *ep; + int i; + + pch_udc_clear_dma(dev, DMA_DIR_TX); + pch_udc_clear_dma(dev, DMA_DIR_RX); + /* Mask all endpoint interrupts */ + pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); + /* clear all endpoint interrupts */ + pch_udc_write_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); + + for (i = 0; i < PCH_UDC_EP_NUM; i++) { + ep = &dev->ep[i]; + pch_udc_clear_ep_status(ep, UDC_EPSTS_ALL_CLR_MASK); + pch_udc_clear_ep_control(ep); + pch_udc_ep_set_ddptr(ep, 0); + pch_udc_write_csr(ep->dev, 0x00, i); + } + dev->stall = 0; + dev->prot_stall = 0; + dev->waiting_zlp_ack = 0; + dev->set_cfg_not_acked = 0; + + /* disable ep to empty req queue. Skip the control EP's */ + for (i = 0; i < (PCH_UDC_USED_EP_NUM*2); i++) { + ep = &dev->ep[i]; + pch_udc_ep_set_nak(ep); + pch_udc_ep_fifo_flush(ep, ep->in); + /* Complete request queue */ + empty_req_queue(ep); + } + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } +} + +/** + * pch_udc_svc_enum_interrupt() - This function handles a USB speed enumeration + * done interrupt + * @dev: Reference to driver structure + */ +static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev) +{ + u32 dev_stat, dev_speed; + u32 speed = USB_SPEED_FULL; + + dev_stat = pch_udc_read_device_status(dev); + dev_speed = (dev_stat & UDC_DEVSTS_ENUM_SPEED_MASK) >> + UDC_DEVSTS_ENUM_SPEED_SHIFT; + switch (dev_speed) { + case UDC_DEVSTS_ENUM_SPEED_HIGH: + speed = USB_SPEED_HIGH; + break; + case UDC_DEVSTS_ENUM_SPEED_FULL: + speed = USB_SPEED_FULL; + break; + case UDC_DEVSTS_ENUM_SPEED_LOW: + speed = USB_SPEED_LOW; + break; + default: + BUG(); + } + dev->gadget.speed = speed; + pch_udc_activate_control_ep(dev); + pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 | UDC_EPINT_OUT_EP0); + pch_udc_set_dma(dev, DMA_DIR_TX); + pch_udc_set_dma(dev, DMA_DIR_RX); + pch_udc_ep_set_rrdy(&(dev->ep[UDC_EP0OUT_IDX])); + + /* enable device interrupts */ + pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US | + UDC_DEVINT_ES | UDC_DEVINT_ENUM | + UDC_DEVINT_SI | UDC_DEVINT_SC); +} + +/** + * pch_udc_svc_intf_interrupt() - This function handles a set interface + * interrupt + * @dev: Reference to driver structure + */ +static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev) +{ + u32 reg, dev_stat = 0; + int i, ret; + + dev_stat = pch_udc_read_device_status(dev); + dev->cfg_data.cur_intf = (dev_stat & UDC_DEVSTS_INTF_MASK) >> + UDC_DEVSTS_INTF_SHIFT; + dev->cfg_data.cur_alt = (dev_stat & UDC_DEVSTS_ALT_MASK) >> + UDC_DEVSTS_ALT_SHIFT; + dev->set_cfg_not_acked = 1; + /* Construct the usb request for gadget driver and inform it */ + memset(&dev->setup_data, 0 , sizeof dev->setup_data); + dev->setup_data.bRequest = USB_REQ_SET_INTERFACE; + dev->setup_data.bRequestType = USB_RECIP_INTERFACE; + dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_alt); + dev->setup_data.wIndex = cpu_to_le16(dev->cfg_data.cur_intf); + /* programm the Endpoint Cfg registers */ + /* Only one end point cfg register */ + reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX); + reg = (reg & ~UDC_CSR_NE_INTF_MASK) | + (dev->cfg_data.cur_intf << UDC_CSR_NE_INTF_SHIFT); + reg = (reg & ~UDC_CSR_NE_ALT_MASK) | + (dev->cfg_data.cur_alt << UDC_CSR_NE_ALT_SHIFT); + pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX); + for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) { + /* clear stall bits */ + pch_udc_ep_clear_stall(&(dev->ep[i])); + dev->ep[i].halted = 0; + } + dev->stall = 0; + spin_unlock(&dev->lock); + ret = dev->driver->setup(&dev->gadget, &dev->setup_data); + spin_lock(&dev->lock); +} + +/** + * pch_udc_svc_cfg_interrupt() - This function handles a set configuration + * interrupt + * @dev: Reference to driver structure + */ +static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) +{ + int i, ret; + u32 reg, dev_stat = 0; + + dev_stat = pch_udc_read_device_status(dev); + dev->set_cfg_not_acked = 1; + dev->cfg_data.cur_cfg = (dev_stat & UDC_DEVSTS_CFG_MASK) >> + UDC_DEVSTS_CFG_SHIFT; + /* make usb request for gadget driver */ + memset(&dev->setup_data, 0 , sizeof dev->setup_data); + dev->setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_cfg); + /* program the NE registers */ + /* Only one end point cfg register */ + reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX); + reg = (reg & ~UDC_CSR_NE_CFG_MASK) | + (dev->cfg_data.cur_cfg << UDC_CSR_NE_CFG_SHIFT); + pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX); + for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) { + /* clear stall bits */ + pch_udc_ep_clear_stall(&(dev->ep[i])); + dev->ep[i].halted = 0; + } + dev->stall = 0; + + /* call gadget zero with setup data received */ + spin_unlock(&dev->lock); + ret = dev->driver->setup(&dev->gadget, &dev->setup_data); + spin_lock(&dev->lock); +} + +/** + * pch_udc_dev_isr() - This function services device interrupts + * by invoking appropriate routines. + * @dev: Reference to the device structure + * @dev_intr: The Device interrupt status. + */ +static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) +{ + int vbus; + + /* USB Reset Interrupt */ + if (dev_intr & UDC_DEVINT_UR) { + pch_udc_svc_ur_interrupt(dev); + dev_dbg(&dev->pdev->dev, "USB_RESET\n"); + } + /* Enumeration Done Interrupt */ + if (dev_intr & UDC_DEVINT_ENUM) { + pch_udc_svc_enum_interrupt(dev); + dev_dbg(&dev->pdev->dev, "USB_ENUM\n"); + } + /* Set Interface Interrupt */ + if (dev_intr & UDC_DEVINT_SI) + pch_udc_svc_intf_interrupt(dev); + /* Set Config Interrupt */ + if (dev_intr & UDC_DEVINT_SC) + pch_udc_svc_cfg_interrupt(dev); + /* USB Suspend interrupt */ + if (dev_intr & UDC_DEVINT_US) { + if (dev->driver + && dev->driver->suspend) { + spin_unlock(&dev->lock); + dev->driver->suspend(&dev->gadget); + spin_lock(&dev->lock); + } + + vbus = pch_vbus_gpio_get_value(dev); + if ((dev->vbus_session == 0) + && (vbus != 1)) { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + pch_udc_reconnect(dev); + } else if ((dev->vbus_session == 0) + && (vbus == 1) + && !dev->vbus_gpio.intr) + schedule_work(&dev->vbus_gpio.irq_work_fall); + + dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); + } + /* Clear the SOF interrupt, if enabled */ + if (dev_intr & UDC_DEVINT_SOF) + dev_dbg(&dev->pdev->dev, "SOF\n"); + /* ES interrupt, IDLE > 3ms on the USB */ + if (dev_intr & UDC_DEVINT_ES) + dev_dbg(&dev->pdev->dev, "ES\n"); + /* RWKP interrupt */ + if (dev_intr & UDC_DEVINT_RWKP) + dev_dbg(&dev->pdev->dev, "RWKP\n"); +} + +/** + * pch_udc_isr() - This function handles interrupts from the PCH USB Device + * @irq: Interrupt request number + * @dev: Reference to the device structure + */ +static irqreturn_t pch_udc_isr(int irq, void *pdev) +{ + struct pch_udc_dev *dev = (struct pch_udc_dev *) pdev; + u32 dev_intr, ep_intr; + int i; + + dev_intr = pch_udc_read_device_interrupts(dev); + ep_intr = pch_udc_read_ep_interrupts(dev); + + /* For a hot plug, this find that the controller is hung up. */ + if (dev_intr == ep_intr) + if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) { + dev_dbg(&dev->pdev->dev, "UDC: Hung up\n"); + /* The controller is reset */ + pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR); + return IRQ_HANDLED; + } + if (dev_intr) + /* Clear device interrupts */ + pch_udc_write_device_interrupts(dev, dev_intr); + if (ep_intr) + /* Clear ep interrupts */ + pch_udc_write_ep_interrupts(dev, ep_intr); + if (!dev_intr && !ep_intr) + return IRQ_NONE; + spin_lock(&dev->lock); + if (dev_intr) + pch_udc_dev_isr(dev, dev_intr); + if (ep_intr) { + pch_udc_read_all_epstatus(dev, ep_intr); + /* Process Control In interrupts, if present */ + if (ep_intr & UDC_EPINT_IN_EP0) { + pch_udc_svc_control_in(dev); + pch_udc_postsvc_epinters(dev, 0); + } + /* Process Control Out interrupts, if present */ + if (ep_intr & UDC_EPINT_OUT_EP0) + pch_udc_svc_control_out(dev); + /* Process data in end point interrupts */ + for (i = 1; i < PCH_UDC_USED_EP_NUM; i++) { + if (ep_intr & (1 << i)) { + pch_udc_svc_data_in(dev, i); + pch_udc_postsvc_epinters(dev, i); + } + } + /* Process data out end point interrupts */ + for (i = UDC_EPINT_OUT_SHIFT + 1; i < (UDC_EPINT_OUT_SHIFT + + PCH_UDC_USED_EP_NUM); i++) + if (ep_intr & (1 << i)) + pch_udc_svc_data_out(dev, i - + UDC_EPINT_OUT_SHIFT); + } + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +/** + * pch_udc_setup_ep0() - This function enables control endpoint for traffic + * @dev: Reference to the device structure + */ +static void pch_udc_setup_ep0(struct pch_udc_dev *dev) +{ + /* enable ep0 interrupts */ + pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 | + UDC_EPINT_OUT_EP0); + /* enable device interrupts */ + pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US | + UDC_DEVINT_ES | UDC_DEVINT_ENUM | + UDC_DEVINT_SI | UDC_DEVINT_SC); +} + +/** + * gadget_release() - Free the gadget driver private data + * @pdev reference to struct pci_dev + */ +static void gadget_release(struct device *pdev) +{ + struct pch_udc_dev *dev = dev_get_drvdata(pdev); + + kfree(dev); +} + +/** + * pch_udc_pcd_reinit() - This API initializes the endpoint structures + * @dev: Reference to the driver structure + */ +static void pch_udc_pcd_reinit(struct pch_udc_dev *dev) +{ + const char *const ep_string[] = { + ep0_string, "ep0out", "ep1in", "ep1out", "ep2in", "ep2out", + "ep3in", "ep3out", "ep4in", "ep4out", "ep5in", "ep5out", + "ep6in", "ep6out", "ep7in", "ep7out", "ep8in", "ep8out", + "ep9in", "ep9out", "ep10in", "ep10out", "ep11in", "ep11out", + "ep12in", "ep12out", "ep13in", "ep13out", "ep14in", "ep14out", + "ep15in", "ep15out", + }; + int i; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + INIT_LIST_HEAD(&dev->gadget.ep_list); + + /* Initialize the endpoints structures */ + memset(dev->ep, 0, sizeof dev->ep); + for (i = 0; i < PCH_UDC_EP_NUM; i++) { + struct pch_udc_ep *ep = &dev->ep[i]; + ep->dev = dev; + ep->halted = 1; + ep->num = i / 2; + ep->in = ~i & 1; + ep->ep.name = ep_string[i]; + ep->ep.ops = &pch_udc_ep_ops; + if (ep->in) + ep->offset_addr = ep->num * UDC_EP_REG_SHIFT; + else + ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) * + UDC_EP_REG_SHIFT; + /* need to set ep->ep.maxpacket and set Default Configuration?*/ + 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); + } + 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); + list_del_init(&dev->ep[UDC_EP0OUT_IDX].ep.ep_list); + + dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); +} + +/** + * pch_udc_pcd_init() - This API initializes the driver structure + * @dev: Reference to the driver structure + * + * Return codes: + * 0: Success + */ +static int pch_udc_pcd_init(struct pch_udc_dev *dev) +{ + pch_udc_init(dev); + pch_udc_pcd_reinit(dev); + pch_vbus_gpio_init(dev, vbus_gpio_port); + return 0; +} + +/** + * init_dma_pools() - create dma pools during initialization + * @pdev: reference to struct pci_dev + */ +static int init_dma_pools(struct pch_udc_dev *dev) +{ + struct pch_udc_stp_dma_desc *td_stp; + struct pch_udc_data_dma_desc *td_data; + + /* DMA setup */ + dev->data_requests = pci_pool_create("data_requests", dev->pdev, + sizeof(struct pch_udc_data_dma_desc), 0, 0); + if (!dev->data_requests) { + dev_err(&dev->pdev->dev, "%s: can't get request data pool\n", + __func__); + return -ENOMEM; + } + + /* dma desc for setup data */ + dev->stp_requests = pci_pool_create("setup requests", dev->pdev, + sizeof(struct pch_udc_stp_dma_desc), 0, 0); + if (!dev->stp_requests) { + dev_err(&dev->pdev->dev, "%s: can't get setup request pool\n", + __func__); + return -ENOMEM; + } + /* setup */ + td_stp = pci_pool_alloc(dev->stp_requests, GFP_KERNEL, + &dev->ep[UDC_EP0OUT_IDX].td_stp_phys); + if (!td_stp) { + dev_err(&dev->pdev->dev, + "%s: can't allocate setup dma descriptor\n", __func__); + return -ENOMEM; + } + dev->ep[UDC_EP0OUT_IDX].td_stp = td_stp; + + /* data: 0 packets !? */ + td_data = pci_pool_alloc(dev->data_requests, GFP_KERNEL, + &dev->ep[UDC_EP0OUT_IDX].td_data_phys); + if (!td_data) { + dev_err(&dev->pdev->dev, + "%s: can't allocate data dma descriptor\n", __func__); + return -ENOMEM; + } + dev->ep[UDC_EP0OUT_IDX].td_data = td_data; + dev->ep[UDC_EP0IN_IDX].td_stp = NULL; + dev->ep[UDC_EP0IN_IDX].td_stp_phys = 0; + dev->ep[UDC_EP0IN_IDX].td_data = NULL; + dev->ep[UDC_EP0IN_IDX].td_data_phys = 0; + + dev->ep0out_buf = kzalloc(UDC_EP0OUT_BUFF_SIZE * 4, GFP_KERNEL); + if (!dev->ep0out_buf) + return -ENOMEM; + dev->dma_addr = dma_map_single(&dev->pdev->dev, dev->ep0out_buf, + UDC_EP0OUT_BUFF_SIZE * 4, + DMA_FROM_DEVICE); + return 0; +} + +static int pch_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct pch_udc_dev *dev = to_pch_udc(g); + + driver->driver.bus = NULL; + dev->driver = driver; + + /* get ready for ep0 traffic */ + pch_udc_setup_ep0(dev); + + /* clear SD */ + if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr) + pch_udc_clear_disconnect(dev); + + dev->connected = 1; + return 0; +} + +static int pch_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + 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 */ + dev->driver = NULL; + dev->connected = 0; + + /* set SD */ + pch_udc_set_disconnect(dev); + + return 0; +} + +static void pch_udc_shutdown(struct pci_dev *pdev) +{ + struct pch_udc_dev *dev = pci_get_drvdata(pdev); + + pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); + pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); + + /* disable the pullup so the host will think we're gone */ + pch_udc_set_disconnect(dev); +} + +static void pch_udc_remove(struct pci_dev *pdev) +{ + struct pch_udc_dev *dev = pci_get_drvdata(pdev); + + usb_del_gadget_udc(&dev->gadget); + + /* gadget driver must not be registered */ + if (dev->driver) + dev_err(&pdev->dev, + "%s: gadget driver still bound!!!\n", __func__); + /* dma pool cleanup */ + if (dev->data_requests) + pci_pool_destroy(dev->data_requests); + + if (dev->stp_requests) { + /* cleanup DMA desc's for ep0in */ + if (dev->ep[UDC_EP0OUT_IDX].td_stp) { + pci_pool_free(dev->stp_requests, + dev->ep[UDC_EP0OUT_IDX].td_stp, + dev->ep[UDC_EP0OUT_IDX].td_stp_phys); + } + if (dev->ep[UDC_EP0OUT_IDX].td_data) { + pci_pool_free(dev->stp_requests, + dev->ep[UDC_EP0OUT_IDX].td_data, + dev->ep[UDC_EP0OUT_IDX].td_data_phys); + } + pci_pool_destroy(dev->stp_requests); + } + + if (dev->dma_addr) + dma_unmap_single(&dev->pdev->dev, dev->dma_addr, + UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); + kfree(dev->ep0out_buf); + + pch_vbus_gpio_free(dev); + + pch_udc_exit(dev); + + if (dev->irq_registered) + free_irq(pdev->irq, dev); + if (dev->base_addr) + iounmap(dev->base_addr); + if (dev->mem_region) + release_mem_region(dev->phys_addr, + pci_resource_len(pdev, PCH_UDC_PCI_BAR)); + if (dev->active) + pci_disable_device(pdev); + kfree(dev); +} + +#ifdef CONFIG_PM +static int pch_udc_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct pch_udc_dev *dev = pci_get_drvdata(pdev); + + pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); + pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); + + pci_disable_device(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + + if (pci_save_state(pdev)) { + dev_err(&pdev->dev, + "%s: could not save PCI config state\n", __func__); + return -ENOMEM; + } + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int pch_udc_resume(struct pci_dev *pdev) +{ + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: pci_enable_device failed\n", __func__); + return ret; + } + pci_enable_wake(pdev, PCI_D3hot, 0); + return 0; +} +#else +#define pch_udc_suspend NULL +#define pch_udc_resume NULL +#endif /* CONFIG_PM */ + +static int pch_udc_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long resource; + unsigned long len; + int retval; + struct pch_udc_dev *dev; + + /* init */ + dev = kzalloc(sizeof *dev, GFP_KERNEL); + if (!dev) { + pr_err("%s: no memory for device structure\n", __func__); + return -ENOMEM; + } + /* pci setup */ + if (pci_enable_device(pdev) < 0) { + kfree(dev); + pr_err("%s: pci_enable_device failed\n", __func__); + return -ENODEV; + } + dev->active = 1; + pci_set_drvdata(pdev, dev); + + /* PCI resource allocation */ + resource = pci_resource_start(pdev, 1); + len = pci_resource_len(pdev, 1); + + if (!request_mem_region(resource, len, KBUILD_MODNAME)) { + dev_err(&pdev->dev, "%s: pci device used already\n", __func__); + retval = -EBUSY; + goto finished; + } + dev->phys_addr = resource; + dev->mem_region = 1; + + dev->base_addr = ioremap_nocache(resource, len); + if (!dev->base_addr) { + pr_err("%s: device memory cannot be mapped\n", __func__); + retval = -ENOMEM; + goto finished; + } + if (!pdev->irq) { + dev_err(&pdev->dev, "%s: irq not set\n", __func__); + retval = -ENODEV; + goto finished; + } + /* initialize the hardware */ + if (pch_udc_pcd_init(dev)) { + retval = -ENODEV; + goto finished; + } + if (request_irq(pdev->irq, pch_udc_isr, IRQF_SHARED, KBUILD_MODNAME, + dev)) { + dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__, + pdev->irq); + retval = -ENODEV; + goto finished; + } + dev->irq = pdev->irq; + dev->irq_registered = 1; + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + /* device struct setup */ + spin_lock_init(&dev->lock); + dev->pdev = pdev; + dev->gadget.ops = &pch_udc_ops; + + retval = init_dma_pools(dev); + if (retval) + goto finished; + + dev->gadget.name = KBUILD_MODNAME; + dev->gadget.max_speed = USB_SPEED_HIGH; + + /* Put the device in disconnected state till a driver is bound */ + pch_udc_set_disconnect(dev); + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto finished; + return 0; + +finished: + pch_udc_remove(pdev); + return retval; +} + +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, + .class_mask = 0xffffffff, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7213_IOH_UDC), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_ROHM, PCI_DEVICE_ID_ML7831_IOH_UDC), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + { 0 }, +}; + +MODULE_DEVICE_TABLE(pci, pch_udc_pcidev_id); + +static struct pci_driver pch_udc_driver = { + .name = KBUILD_MODNAME, + .id_table = pch_udc_pcidev_id, + .probe = pch_udc_probe, + .remove = pch_udc_remove, + .suspend = pch_udc_suspend, + .resume = pch_udc_resume, + .shutdown = pch_udc_shutdown, +}; + +module_pci_driver(pch_udc_driver); + +MODULE_DESCRIPTION("Intel EG20T USB Device Controller"); +MODULE_AUTHOR("LAPIS Semiconductor, <tomoya-linux@dsn.lapis-semi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 29500154d00..6474081dcba 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> @@ -25,13 +16,12 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <linux/utsname.h> #include <linux/device.h> #include <linux/moduleparam.h> #include <linux/fs.h> @@ -43,33 +33,22 @@ #include <asm/byteorder.h> #include <linux/io.h> #include <linux/irq.h> -#include <asm/system.h> #include <linux/uaccess.h> #include <asm/unaligned.h> #include <linux/usb/ch9.h> +#include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/usb/g_printer.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 "usbstring.c" -#include "config.c" -#include "epautoconf.c" - -/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" #define DRIVER_VERSION "2007 OCT 06" +static DEFINE_MUTEX(printer_mutex); static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; @@ -82,14 +61,11 @@ static struct class *usb_gadget_class; struct printer_dev { spinlock_t lock; /* lock this structure */ /* lock buffer lists during read/write calls */ - spinlock_t lock_printer_io; + struct mutex lock_printer_io; struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - u8 config; s8 interface; struct usb_ep *in_ep, *out_ep; - const struct usb_endpoint_descriptor - *in, *out; + struct list_head rx_reqs; /* List of free RX structs */ struct list_head rx_reqs_active; /* List of Active RX xfers */ struct list_head rx_buffers; /* List of completed xfers */ @@ -110,6 +86,7 @@ struct printer_dev { struct device *pdev; u8 printer_cdev_open; wait_queue_head_t wait; + struct usb_function function; }; static struct printer_dev usb_printer_gadget; @@ -125,36 +102,15 @@ static struct printer_dev usb_printer_gadget; #define PRINTER_VENDOR_NUM 0x0525 /* NetChip */ #define PRINTER_PRODUCT_NUM 0xa4a8 /* Linux-USB Printer Gadget */ -/* Some systems will want different product identifers published in the +/* Some systems will want different product identifiers published in the * device descriptor, either numbers or strings or both. These string * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -static ushort __initdata idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort __initdata idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort __initdata bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *__initdata iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *__initdata iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *__initdata iSerialNum; -module_param(iSerialNum, charp, S_IRUGO); +module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO); MODULE_PARM_DESC(iSerialNum, "1"); -static char *__initdata iPNPstring; +static char *iPNPstring; module_param(iPNPstring, charp, S_IRUGO); MODULE_PARM_DESC(iPNPstring, "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;"); @@ -164,64 +120,17 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define QLEN qlen -#ifdef CONFIG_USB_GADGET_DUALSPEED -#define DEVSPEED USB_SPEED_HIGH -#else /* full speed (low speed doesn't do bulk) */ -#define DEVSPEED USB_SPEED_FULL -#endif - /*-------------------------------------------------------------------------*/ -#define xprintk(d, level, fmt, args...) \ - printk(level "%s: " fmt, DRIVER_DESC, ## args) - -#ifdef DEBUG -#define DBG(dev, fmt, args...) \ - xprintk(dev, KERN_DEBUG, fmt, ## args) -#else -#define DBG(dev, fmt, args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE -#define VDBG(dev, fmt, args...) \ - xprintk(dev, KERN_DEBUG, fmt, ## args) -#else -#define VDBG(dev, fmt, args...) \ - do { } while (0) -#endif /* VERBOSE */ - -#define ERROR(dev, fmt, args...) \ - xprintk(dev, KERN_ERR, fmt, ## args) -#define WARNING(dev, fmt, args...) \ - xprintk(dev, KERN_WARNING, fmt, ## args) -#define INFO(dev, fmt, args...) \ - xprintk(dev, KERN_INFO, fmt, ## args) - -/*-------------------------------------------------------------------------*/ - -/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly - * ep0 implementation: descriptors, config management, setup(). - * also optional class-specific notification interrupt transfer. - */ - /* * DESCRIPTORS ... most are static, but strings and (full) configuration * descriptors are built on demand. */ -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_SERIALNUM 3 - /* holds our biggest descriptor */ #define USB_DESC_BUFSIZE 256 #define USB_BUFSIZE 8192 -/* This device advertises one configuration. */ -#define DEV_CONFIG_VALUE 1 -#define PRINTER_INTERFACE 0 - static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -231,34 +140,12 @@ static struct usb_device_descriptor device_desc = { .bDeviceProtocol = 0, .idVendor = cpu_to_le16(PRINTER_VENDOR_NUM), .idProduct = cpu_to_le16(PRINTER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIALNUM, .bNumConfigurations = 1 }; -static struct usb_otg_descriptor otg_desc = { - .bLength = sizeof otg_desc, - .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP -}; - -static struct usb_config_descriptor config_desc = { - .bLength = sizeof config_desc, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 1, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = 0, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - static struct usb_interface_descriptor intf_desc = { .bLength = sizeof intf_desc, .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = PRINTER_INTERFACE, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_PRINTER, .bInterfaceSubClass = 1, /* Printer Sub-Class */ @@ -280,16 +167,13 @@ static struct usb_endpoint_descriptor fs_ep_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK }; -static const struct usb_descriptor_header *fs_printer_function [11] = { - (struct usb_descriptor_header *) &otg_desc, +static struct usb_descriptor_header *fs_printer_function[] = { (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &fs_ep_in_desc, (struct usb_descriptor_header *) &fs_ep_out_desc, NULL }; -#ifdef CONFIG_USB_GADGET_DUALSPEED - /* * usb 2.0 devices need to expose both high speed and full speed * descriptors, unless they only run at full speed. @@ -317,29 +201,31 @@ static struct usb_qualifier_descriptor dev_qualifier = { .bNumConfigurations = 1 }; -static const struct usb_descriptor_header *hs_printer_function [11] = { - (struct usb_descriptor_header *) &otg_desc, +static struct usb_descriptor_header *hs_printer_function[] = { (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_ep_in_desc, (struct usb_descriptor_header *) &hs_ep_out_desc, NULL }; -/* maxpacket and other transfer characteristics vary by speed. */ -#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs)) - -#else +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + .bmAttributes = USB_OTG_SRP, +}; -/* if there's no high speed support, maxpacket doesn't change. */ -#define ep_desc(g, hs, fs) (((void)(g)), (fs)) +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; -#endif /* !CONFIG_USB_GADGET_DUALSPEED */ +/* maxpacket and other transfer characteristics vary by speed. */ +#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs)) /*-------------------------------------------------------------------------*/ /* descriptors that are built on-demand */ -static char manufacturer [50]; static char product_desc [40] = DRIVER_DESC; static char serial_num [40] = "1"; static char pnp_string [1024] = @@ -347,17 +233,22 @@ static char pnp_string [1024] = /* static strings, in UTF-8 */ static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIALNUM, serial_num, }, + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = product_desc, + [USB_GADGET_SERIAL_IDX].s = serial_num, { } /* end of list */ }; -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ .strings = strings, }; +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + /*-------------------------------------------------------------------------*/ static struct usb_request * @@ -476,7 +367,7 @@ printer_open(struct inode *inode, struct file *fd) unsigned long flags; int ret = -EBUSY; - lock_kernel(); + mutex_lock(&printer_mutex); dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); spin_lock_irqsave(&dev->lock, flags); @@ -492,7 +383,7 @@ printer_open(struct inode *inode, struct file *fd) spin_unlock_irqrestore(&dev->lock, flags); DBG(dev, "printer_open returned %x\n", ret); - unlock_kernel(); + mutex_unlock(&printer_mutex); return ret; } @@ -536,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); } } @@ -567,7 +463,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) DBG(dev, "printer_read trying to read %d bytes\n", (int)len); - spin_lock(&dev->lock_printer_io); + mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); /* We will use this flag later to check if a printer reset happened @@ -601,7 +497,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) * call or not. */ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } @@ -648,7 +544,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) if (dev->reset_printer) { list_add(¤t_rx_req->list, &dev->rx_reqs); spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } @@ -673,7 +569,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) dev->current_rx_buf = current_rx_buf; spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); @@ -697,7 +593,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (len == 0) return -EINVAL; - spin_lock(&dev->lock_printer_io); + mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); /* Check if a printer reset happens while we have interrupts on */ @@ -713,7 +609,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) * a NON-Blocking call or not. */ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } @@ -752,7 +648,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (copy_from_user(req->buf, buf, size)) { list_add(&req->list, &dev->tx_reqs); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return bytes_copied; } @@ -766,14 +662,14 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) if (dev->reset_printer) { list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); return -EAGAIN; } @@ -782,7 +678,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); @@ -794,12 +690,14 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) } static int -printer_fsync(struct file *fd, struct dentry *dentry, int datasync) +printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) { struct printer_dev *dev = fd->private_data; + struct inode *inode = file_inode(fd); unsigned long flags; int tx_list_empty; + mutex_lock(&inode->i_mutex); spin_lock_irqsave(&dev->lock, flags); tx_list_empty = (likely(list_empty(&dev->tx_reqs))); spin_unlock_irqrestore(&dev->lock, flags); @@ -809,6 +707,7 @@ printer_fsync(struct file *fd, struct dentry *dentry, int datasync) wait_event_interruptible(dev->tx_flush_wait, (likely(list_empty(&dev->tx_reqs_active)))); } + mutex_unlock(&inode->i_mutex); return 0; } @@ -820,11 +719,11 @@ printer_poll(struct file *fd, poll_table *wait) unsigned long flags; int status = 0; - spin_lock(&dev->lock_printer_io); + mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); setup_rx_reqs(dev); spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock(&dev->lock_printer_io); + mutex_unlock(&dev->lock_printer_io); poll_wait(fd, &dev->rx_wait, wait); poll_wait(fd, &dev->tx_wait, wait); @@ -875,7 +774,7 @@ printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) } /* used after endpoint configuration */ -static struct file_operations printer_io_operations = { +static const struct file_operations printer_io_operations = { .owner = THIS_MODULE, .open = printer_open, .read = printer_read, @@ -883,7 +782,8 @@ static struct file_operations printer_io_operations = { .fsync = printer_fsync, .poll = printer_poll, .unlocked_ioctl = printer_ioctl, - .release = printer_close + .release = printer_close, + .llseek = noop_llseek, }; /*-------------------------------------------------------------------------*/ @@ -893,19 +793,20 @@ set_printer_interface(struct printer_dev *dev) { int result = 0; - dev->in = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); + dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); dev->in_ep->driver_data = dev; - dev->out = ep_desc(dev->gadget, &hs_ep_out_desc, &fs_ep_out_desc); + dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc, + &fs_ep_out_desc); dev->out_ep->driver_data = dev; - result = usb_ep_enable(dev->in_ep, dev->in); + result = usb_ep_enable(dev->in_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; } - result = usb_ep_enable(dev->out_ep, dev->out); + result = usb_ep_enable(dev->out_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; @@ -916,8 +817,8 @@ done: if (result != 0) { (void) usb_ep_disable(dev->in_ep); (void) usb_ep_disable(dev->out_ep); - dev->in = NULL; - dev->out = NULL; + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; } /* caller is responsible for cleanup on error */ @@ -931,134 +832,30 @@ static void printer_reset_interface(struct printer_dev *dev) DBG(dev, "%s\n", __func__); - if (dev->in) + if (dev->in_ep->desc) usb_ep_disable(dev->in_ep); - if (dev->out) + if (dev->out_ep->desc) usb_ep_disable(dev->out_ep); + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; dev->interface = -1; } -/* change our operational config. must agree with the code - * that returns config descriptors, and altsetting code. - */ -static int -printer_set_config(struct printer_dev *dev, unsigned number) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - - if (gadget_is_sa1100(gadget) && dev->config) { - /* tx fifo is full, but we can't clear it...*/ - INFO(dev, "can't change configurations\n"); - return -ESPIPE; - } - - switch (number) { - case DEV_CONFIG_VALUE: - result = 0; - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - break; - } - - if (result) { - usb_gadget_vbus_draw(dev->gadget, - dev->gadget->is_otg ? 8 : 100); - } else { - char *speed; - unsigned power; - - power = 2 * config_desc.bMaxPower; - usb_gadget_vbus_draw(dev->gadget, power); - - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed config #%d: %d mA, %s\n", - speed, number, power, driver_desc); - } - return result; -} - -static int -config_buf(enum usb_device_speed speed, u8 *buf, u8 type, unsigned index, - int is_otg) -{ - int len; - const struct usb_descriptor_header **function; -#ifdef CONFIG_USB_GADGET_DUALSPEED - int hs = (speed == USB_SPEED_HIGH); - - if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - - if (hs) { - function = hs_printer_function; - } else { - function = fs_printer_function; - } -#else - function = fs_printer_function; -#endif - - if (index >= device_desc.bNumConfigurations) - return -EINVAL; - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf(&config_desc, buf, USB_DESC_BUFSIZE, - function); - if (len < 0) - return len; - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - /* Change our operational Interface. */ -static int -set_interface(struct printer_dev *dev, unsigned number) +static int set_interface(struct printer_dev *dev, unsigned number) { int result = 0; - if (gadget_is_sa1100(dev->gadget) && dev->interface < 0) { - /* tx fifo is full, but we can't clear it...*/ - INFO(dev, "can't change interfaces\n"); - return -ESPIPE; - } - /* Free the current interface */ - switch (dev->interface) { - case PRINTER_INTERFACE: - printer_reset_interface(dev); - break; - } + printer_reset_interface(dev); - switch (number) { - case PRINTER_INTERFACE: - result = set_printer_interface(dev); - if (result) { - printer_reset_interface(dev); - } else { - dev->interface = PRINTER_INTERFACE; - } - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - } + result = set_printer_interface(dev); + if (result) + printer_reset_interface(dev); + else + dev->interface = number; if (!result) INFO(dev, "Using interface %x\n", number); @@ -1066,14 +863,6 @@ set_interface(struct printer_dev *dev, unsigned number) return result; } -static void printer_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DBG((struct printer_dev *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); -} - static void printer_soft_reset(struct printer_dev *dev) { struct usb_request *req; @@ -1114,9 +903,9 @@ static void printer_soft_reset(struct printer_dev *dev) list_add(&req->list, &dev->tx_reqs); } - if (usb_ep_enable(dev->in_ep, dev->in)) + if (usb_ep_enable(dev->in_ep)) DBG(dev, "Failed to enable USB in_ep\n"); - if (usb_ep_enable(dev->out_ep, dev->out)) + if (usb_ep_enable(dev->out_ep)) DBG(dev, "Failed to enable USB out_ep\n"); wake_up_interruptible(&dev->rx_wait); @@ -1130,11 +919,12 @@ static void printer_soft_reset(struct printer_dev *dev) * The setup() callback implements all the ep0 functionality that's not * handled lower down. */ -static int -printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +static int printer_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { - struct printer_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->req; + struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; u16 wIndex = le16_to_cpu(ctrl->wIndex); u16 wValue = le16_to_cpu(ctrl->wValue); @@ -1143,97 +933,12 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); - req->complete = printer_setup_complete; - switch (ctrl->bRequestType&USB_TYPE_MASK) { - - case USB_TYPE_STANDARD: - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - break; - switch (wValue >> 8) { - - case USB_DT_DEVICE: - value = min(wLength, (u16) sizeof device_desc); - memcpy(req->buf, &device_desc, value); - break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_DT_DEVICE_QUALIFIER: - if (!gadget->is_dualspeed) - break; - value = min(wLength, - (u16) sizeof dev_qualifier); - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget->is_dualspeed) - break; - /* FALLTHROUGH */ -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - case USB_DT_CONFIG: - value = config_buf(gadget->speed, req->buf, - wValue >> 8, - wValue & 0xff, - gadget->is_otg); - if (value >= 0) - value = min(wLength, (u16) value); - break; - - case USB_DT_STRING: - value = usb_gadget_get_string(&stringtab, - wValue & 0xff, req->buf); - if (value >= 0) - value = min(wLength, (u16) value); - break; - } - break; - - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - break; - if (gadget->a_hnp_support) - DBG(dev, "HNP available\n"); - else if (gadget->a_alt_hnp_support) - DBG(dev, "HNP needs a different root port\n"); - value = printer_set_config(dev, wValue); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - break; - *(u8 *)req->buf = dev->config; - value = min(wLength, (u16) 1); - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE || - !dev->config) - break; - - value = set_interface(dev, PRINTER_INTERFACE); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != - (USB_DIR_IN|USB_RECIP_INTERFACE) - || !dev->config) - break; - - *(u8 *)req->buf = dev->interface; - value = min(wLength, (u16) 1); - break; - - default: - goto unknown; - } - break; - case USB_TYPE_CLASS: switch (ctrl->bRequest) { case 0: /* Get the IEEE-1284 PNP String */ /* Only one printer interface is supported. */ - if ((wIndex>>8) != PRINTER_INTERFACE) + if ((wIndex>>8) != dev->interface) break; value = (pnp_string[0]<<8)|pnp_string[1]; @@ -1244,7 +949,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case 1: /* Get Port Status */ /* Only one printer interface is supported. */ - if (wIndex != PRINTER_INTERFACE) + if (wIndex != dev->interface) break; *(u8 *)req->buf = dev->printer_status; @@ -1253,7 +958,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case 2: /* Soft Reset */ /* Only one printer interface is supported. */ - if (wIndex != PRINTER_INTERFACE) + if (wIndex != dev->interface) break; printer_soft_reset(dev); @@ -1274,44 +979,90 @@ unknown: wValue, wIndex, wLength); break; } + /* host either stalls (value < 0) or reports success */ + return value; +} - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < wLength; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - printer_setup_complete(gadget->ep0, req); - } +static int __init printer_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct usb_composite_dev *cdev = c->cdev; + struct usb_ep *in_ep; + struct usb_ep *out_ep = NULL; + int id; + int ret; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + intf_desc.bInterfaceNumber = id; + + /* all we really need is bulk IN/OUT */ + in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); + if (!in_ep) { +autoconf_fail: + dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", + cdev->gadget->name); + return -ENODEV; } + in_ep->driver_data = in_ep; /* claim */ - /* host either stalls (value < 0) or reports success */ - return value; + out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + + /* assumes that all endpoints are dual-speed */ + hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_printer_function, + hs_printer_function, NULL); + if (ret) + return ret; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + return 0; } -static void -printer_disconnect(struct usb_gadget *gadget) +static void printer_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static int printer_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) { - struct printer_dev *dev = get_gadget_data(gadget); + struct printer_dev *dev = container_of(f, struct printer_dev, function); + int ret = -ENOTSUPP; + + if (!alt) + ret = set_interface(dev, intf); + + return ret; +} + +static void printer_func_disable(struct usb_function *f) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); unsigned long flags; DBG(dev, "%s\n", __func__); spin_lock_irqsave(&dev->lock, flags); - printer_reset_interface(dev); - spin_unlock_irqrestore(&dev->lock, flags); } -static void -printer_unbind(struct usb_gadget *gadget) +static void printer_cfg_unbind(struct usb_configuration *c) { - struct printer_dev *dev = get_gadget_data(gadget); + struct printer_dev *dev; struct usb_request *req; + dev = &usb_printer_gadget; DBG(dev, "%s\n", __func__); @@ -1349,34 +1100,45 @@ printer_unbind(struct usb_gadget *gadget) list_del(&req->list); printer_req_free(dev->out_ep, req); } - - if (dev->req) { - printer_req_free(gadget->ep0, dev->req); - dev->req = NULL; - } - - set_gadget_data(gadget, NULL); } -static int __init -printer_bind(struct usb_gadget *gadget) +static struct usb_configuration printer_cfg_driver = { + .label = "printer", + .unbind = printer_cfg_unbind, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +}; + +static int __init printer_bind_config(struct usb_configuration *c) { + struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; - struct usb_ep *in_ep, *out_ep; int status = -ENOMEM; - int gcnum; size_t len; u32 i; struct usb_request *req; + usb_ep_autoconfig_reset(gadget); + dev = &usb_printer_gadget; + dev->function.name = shortname; + dev->function.bind = printer_func_bind; + dev->function.setup = printer_func_setup; + dev->function.unbind = printer_func_unbind; + dev->function.set_alt = printer_func_set_alt; + dev->function.disable = printer_func_disable; + + status = usb_add_function(c, &dev->function); + if (status) + return status; /* Setup the sysfs files for the printer gadget. */ dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); + status = PTR_ERR(dev->pdev); goto fail; } @@ -1392,52 +1154,6 @@ printer_bind(struct usb_gadget *gadget) goto fail; } - if (gadget_is_sa1100(gadget)) { - /* hardware can't write zero length packets. */ - ERROR(dev, "SA1100 controller is unsupport by this driver\n"); - goto fail; - } - - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { - dev_warn(&gadget->dev, "controller '%s' not recognized\n", - gadget->name); - /* unrecognized, but safe unless bulk is REALLY quirky */ - device_desc.bcdDevice = - cpu_to_le16(0xFFFF); - } - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - device_desc.idVendor = - cpu_to_le16(PRINTER_VENDOR_NUM); - device_desc.idProduct = - cpu_to_le16(PRINTER_PRODUCT_NUM); - - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - dev_err(&gadget->dev, "idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - - if (iManufacturer) - strlcpy(manufacturer, iManufacturer, sizeof manufacturer); - - if (iProduct) - strlcpy(product_desc, iProduct, sizeof product_desc); - - if (iSerialNum) - strlcpy(serial_num, iSerialNum, sizeof serial_num); - if (iPNPstring) strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); @@ -1445,41 +1161,16 @@ printer_bind(struct usb_gadget *gadget) pnp_string[0] = (len >> 8) & 0xFF; pnp_string[1] = len & 0xFF; - /* all we really need is bulk IN/OUT */ - usb_ep_autoconfig_reset(gadget); - in_ep = usb_ep_autoconfig(gadget, &fs_ep_in_desc); - if (!in_ep) { -autoconf_fail: - dev_err(&gadget->dev, "can't autoconfigure on %s\n", - gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig(gadget, &fs_ep_out_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - -#ifdef CONFIG_USB_GADGET_DUALSPEED - /* assumes ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ - hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; -#endif /* DUALSPEED */ - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered(gadget); - if (gadget->is_otg) { - otg_desc.bmAttributes |= USB_OTG_HNP, - config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + 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; } spin_lock_init(&dev->lock); - spin_lock_init(&dev->lock_printer_io); + mutex_init(&dev->lock_printer_io); INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->tx_reqs_active); INIT_LIST_HEAD(&dev->rx_reqs); @@ -1489,7 +1180,6 @@ autoconf_fail: init_waitqueue_head(&dev->tx_wait); init_waitqueue_head(&dev->tx_flush_wait); - dev->config = 0; dev->interface = -1; dev->printer_cdev_open = 0; dev->printer_status = PRINTER_NOT_ERROR; @@ -1497,17 +1187,6 @@ autoconf_fail: dev->current_rx_bytes = 0; dev->current_rx_buf = NULL; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - - /* preallocate control message data and buffer */ - dev->req = printer_req_alloc(gadget->ep0, USB_DESC_BUFSIZE, - GFP_KERNEL); - if (!dev->req) { - status = -ENOMEM; - goto fail; - } - for (i = 0; i < QLEN; i++) { req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); if (!req) { @@ -1536,46 +1215,49 @@ autoconf_fail: list_add(&req->list, &dev->rx_reqs); } - dev->req->complete = printer_setup_complete; - /* finish hookup to lower layer ... */ dev->gadget = gadget; - set_gadget_data(gadget, dev); - gadget->ep0->driver_data = dev; INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name, - in_ep->name); - return 0; fail: - printer_unbind(gadget); + printer_cfg_unbind(c); return status; } -/*-------------------------------------------------------------------------*/ +static int printer_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} -static struct usb_gadget_driver printer_driver = { - .speed = DEVSPEED, +static int __init printer_bind(struct usb_composite_dev *cdev) +{ + int ret; + + ret = usb_string_ids_tab(cdev, strings); + if (ret < 0) + return ret; + device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; + + ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); + if (ret) + return ret; + usb_composite_overwrite_options(cdev, &coverwrite); + return ret; +} - .function = (char *) driver_desc, +static __refdata struct usb_composite_driver printer_driver = { + .name = shortname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .bind = printer_bind, .unbind = printer_unbind, - - .setup = printer_setup, - .disconnect = printer_disconnect, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - }, }; -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Craig Nadler"); -MODULE_LICENSE("GPL"); - static int __init init(void) { @@ -1584,23 +1266,23 @@ init(void) usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); if (IS_ERR(usb_gadget_class)) { status = PTR_ERR(usb_gadget_class); - ERROR(dev, "unable to create usb_gadget class %d\n", status); + pr_err("unable to create usb_gadget class %d\n", status); return status; } status = alloc_chrdev_region(&g_printer_devno, 0, 1, "USB printer gadget"); if (status) { - ERROR(dev, "alloc_chrdev_region %d\n", status); + pr_err("alloc_chrdev_region %d\n", status); class_destroy(usb_gadget_class); return status; } - status = usb_gadget_register_driver(&printer_driver); + status = usb_composite_probe(&printer_driver); if (status) { class_destroy(usb_gadget_class); unregister_chrdev_region(g_printer_devno, 1); - DBG(dev, "usb_gadget_register_driver %x\n", status); + pr_err("usb_gadget_probe_driver %x\n", status); } return status; @@ -1610,16 +1292,14 @@ module_init(init); static void __exit cleanup(void) { - int status; - - spin_lock(&usb_printer_gadget.lock_printer_io); + mutex_lock(&usb_printer_gadget.lock_printer_io); + usb_composite_unregister(&printer_driver); + unregister_chrdev_region(g_printer_devno, 1); class_destroy(usb_gadget_class); - unregister_chrdev_region(g_printer_devno, 2); - - status = usb_gadget_unregister_driver(&printer_driver); - if (status) - ERROR(dev, "usb_gadget_unregister_driver %x\n", status); - - spin_unlock(&usb_printer_gadget.lock_printer_io); + mutex_unlock(&usb_printer_gadget.lock_printer_io); } module_exit(cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Craig Nadler"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 0ce4e281984..9984437d295 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -11,16 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ /* #define VERBOSE_DEBUG */ @@ -31,41 +21,44 @@ #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> +#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> #include <linux/mm.h> +#include <linux/platform_data/pxa2xx_udc.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/irq.h> #include <linux/clk.h> -#include <linux/err.h> #include <linux/seq_file.h> #include <linux/debugfs.h> #include <linux/io.h> +#include <linux/prefetch.h> #include <asm/byteorder.h> #include <asm/dma.h> #include <asm/gpio.h> -#include <asm/system.h> #include <asm/mach-types.h> #include <asm/unaligned.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> /* * This driver is PXA25x only. Grab the right register definitions. */ #ifdef CONFIG_ARCH_PXA #include <mach/pxa25x-udc.h> +#include <mach/hardware.h> #endif -#include <asm/mach/udc_pxa2xx.h> - +#ifdef CONFIG_ARCH_LUBBOCK +#include <mach/lubbock.h> +#endif /* * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x @@ -134,31 +127,13 @@ static const char ep0name [] = "ep0"; static void pxa25x_ep_fifo_flush (struct usb_ep *ep); static void nuke (struct pxa25x_ep *, int status); -/* one GPIO should be used to detect VBUS from the host */ -static int is_vbus_present(void) -{ - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - - if (mach->gpio_vbus) { - int value = gpio_get_value(mach->gpio_vbus); - - if (mach->gpio_vbus_inverted) - return !value; - else - return !!value; - } - if (mach->udc_is_connected) - return mach->udc_is_connected(); - return 1; -} - /* one GPIO should control a D+ pullup, so host sees this device (or not) */ static void pullup_off(void) { struct pxa2xx_udc_mach_info *mach = the_controller->mach; int off_level = mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, off_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); @@ -169,7 +144,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; int on_level = !mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, on_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); @@ -241,11 +216,10 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, struct pxa25x_udc *dev; ep = container_of (_ep, struct pxa25x_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name + if (!_ep || !desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->fifo_size < usb_endpoint_maxp (desc)) { DMSG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } @@ -260,7 +234,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu (desc->wMaxPacketSize) + && usb_endpoint_maxp (desc) != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); @@ -273,10 +247,10 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, return -ESHUTDOWN; } - ep->desc = desc; + ep->ep.desc = desc; ep->stopped = 0; ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp (desc); /* flush fifo (mostly for OUT buffers) */ pxa25x_ep_fifo_flush (_ep); @@ -293,7 +267,7 @@ static int pxa25x_ep_disable (struct usb_ep *_ep) unsigned long flags; ep = container_of (_ep, struct pxa25x_ep, ep); - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { DMSG("%s, %s not enabled\n", __func__, _ep ? ep->ep.name : NULL); return -EINVAL; @@ -305,7 +279,7 @@ static int pxa25x_ep_disable (struct usb_ep *_ep) /* flush fifo (mostly for IN buffers) */ pxa25x_ep_fifo_flush (_ep); - ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; local_irq_restore(flags); @@ -413,7 +387,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) { unsigned max; - max = le16_to_cpu(ep->desc->wMaxPacketSize); + max = usb_endpoint_maxp(ep->ep.desc); do { unsigned count; int is_last, is_short; @@ -667,7 +641,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } ep = container_of(_ep, struct pxa25x_ep, ep); - if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) { DMSG("%s, bad ep\n", __func__); return -EINVAL; } @@ -683,8 +657,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * we can report per-packet status. that also helps with dma. */ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - && req->req.length > le16_to_cpu - (ep->desc->wMaxPacketSize))) + && req->req.length > usb_endpoint_maxp(ep->ep.desc))) return -EMSGSIZE; DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", @@ -697,7 +670,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* kickstart this i/o queue? */ if (list_empty(&ep->queue) && !ep->stopped) { - if (ep->desc == NULL/* ep0 */) { + if (ep->ep.desc == NULL/* ep0 */) { unsigned length = _req->length; switch (dev->ep0state) { @@ -746,7 +719,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req = NULL; } - if (likely (req && ep->desc)) + if (likely(req && ep->ep.desc)) pio_irq_enable(ep->bEndpointAddress); } @@ -773,7 +746,7 @@ static void nuke(struct pxa25x_ep *ep, int status) queue); done(ep, req, status); } - if (ep->desc) + if (ep->ep.desc) pio_irq_disable (ep->bEndpointAddress); } @@ -816,7 +789,7 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) ep = container_of(_ep, struct pxa25x_ep, ep); if (unlikely (!_ep - || (!ep->desc && ep->ep.name != ep0name)) + || (!ep->ep.desc && ep->ep.name != ep0name)) || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { DMSG("%s, bad ep\n", __func__); return -EINVAL; @@ -844,7 +817,7 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; /* ep0 needs special care */ - if (!ep->desc) { + if (!ep->ep.desc) { start_watchdog(ep->dev); ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; @@ -1000,7 +973,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) udc = container_of(_gadget, struct pxa25x_udc, gadget); /* not all boards support pullup control */ - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) return -EOPNOTSUPP; udc->pullup = (is_active != 0); @@ -1008,15 +981,34 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) return 0; } +/* boards may consume current from VBUS, up to 100-500mA based on config. + * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs + * violate USB specs. + */ +static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + + if (!IS_ERR_OR_NULL(udc->transceiver)) + return usb_phy_set_power(udc->transceiver, mA); + return -EOPNOTSUPP; +} + +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, .wakeup = pxa25x_udc_wakeup, .vbus_session = pxa25x_udc_vbus_session, .pullup = pxa25x_udc_pullup, - - // .vbus_draw ... boards may consume current from VBUS, up to - // 100-500mA based on config. the 500uA suspend ceiling means - // that exclusively vbus-powered PXA designs violate USB specs. + .vbus_draw = pxa25x_udc_vbus_draw, + .udc_start = pxa25x_udc_start, + .udc_stop = pxa25x_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -1038,7 +1030,7 @@ udc_seq_show(struct seq_file *m, void *_d) "%s version: %s\nGadget driver: %s\nHost %s\n\n", driver_name, DRIVER_VERSION SIZE_STR "(pio)", dev->driver ? dev->driver->driver.name : "(none)", - is_vbus_present() ? "full speed" : "disconnected"); + dev->gadget.speed == USB_SPEED_FULL ? "full speed" : "disconnected"); /* registers for device and ep0 */ seq_printf(m, @@ -1077,7 +1069,7 @@ udc_seq_show(struct seq_file *m, void *_d) (tmp & UDCCFR_ACM) ? " acm" : ""); } - if (!is_vbus_present() || !dev->driver) + if (dev->gadget.speed != USB_SPEED_FULL || !dev->driver) goto done; seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", @@ -1093,13 +1085,13 @@ udc_seq_show(struct seq_file *m, void *_d) if (i != 0) { const struct usb_endpoint_descriptor *desc; - desc = ep->desc; + desc = ep->ep.desc; if (!desc) continue; tmp = *dev->ep [i].reg_udccs; seq_printf(m, "%s max %d %s udccs %02x irqs %lu\n", - ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), + ep->ep.name, usb_endpoint_maxp(desc), "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ @@ -1197,10 +1189,11 @@ static void udc_reinit(struct pxa25x_udc *dev) if (i != 0) list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); - ep->desc = NULL; + ep->ep.desc = NULL; 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 */ @@ -1263,51 +1256,33 @@ static void udc_enable (struct pxa25x_udc *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +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->speed < USB_SPEED_FULL - || !driver->bind - || !driver->disconnect - || !driver->setup) - return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; - /* first hook up the driver ... */ dev->driver = driver; - dev->gadget.dev.driver = &driver->driver; dev->pullup = 1; - retval = device_add (&dev->gadget.dev); - if (retval) { -fail: - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = driver->bind(&dev->gadget); - if (retval) { - DMSG("bind to driver %s --> error %d\n", - driver->driver.name, retval); - device_del (&dev->gadget.dev); - goto fail; - } - /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ - DMSG("registered gadget driver '%s'\n", driver->driver.name); + /* connect to bus through transceiver */ + if (!IS_ERR_OR_NULL(dev->transceiver)) { + retval = otg_set_peripheral(dev->transceiver->otg, + &dev->gadget); + if (retval) + goto bind_fail; + } + pullup(dev); dump_state(dev); return 0; +bind_fail: + return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); static void stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) @@ -1336,14 +1311,10 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) udc_reinit(dev); } -int usb_gadget_unregister_driver(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; @@ -1351,18 +1322,15 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) stop_activity(dev, driver); local_irq_enable(); - driver->unbind(&dev->gadget); - dev->gadget.dev.driver = NULL; - dev->driver = NULL; + if (!IS_ERR_OR_NULL(dev->transceiver)) + (void) otg_set_peripheral(dev->transceiver->otg, NULL); - device_del (&dev->gadget.dev); + dev->driver = NULL; - DMSG("unregistered gadget driver '%s'\n", driver->driver.name); dump_state(dev); + return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -1400,14 +1368,6 @@ lubbock_vbus_irq(int irq, void *_dev) #endif -static irqreturn_t udc_vbus_irq(int irq, void *_dev) -{ - struct pxa25x_udc *dev = _dev; - - pxa25x_udc_vbus_session(&dev->gadget, is_vbus_present()); - return IRQ_HANDLED; -} - /*-------------------------------------------------------------------------*/ @@ -1731,12 +1691,9 @@ pxa25x_udc_irq(int irq, void *_dev) if (unlikely(udccr & UDCCR_SUSIR)) { udc_ack_int_UDCCR(UDCCR_SUSIR); handled = 1; - DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present() - ? "" : "+disconnect"); + DBG(DBG_VERBOSE, "USB suspend\n"); - if (!is_vbus_present()) - stop_activity(dev, dev->driver); - else if (dev->gadget.speed != USB_SPEED_UNKNOWN + if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) dev->driver->suspend(&dev->gadget); @@ -1751,8 +1708,7 @@ pxa25x_udc_irq(int irq, void *_dev) if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver - && dev->driver->resume - && is_vbus_present()) + && dev->driver->resume) dev->driver->resume(&dev->gadget); } @@ -1802,11 +1758,13 @@ pxa25x_udc_irq(int irq, void *_dev) USIR0 |= tmp; handled = 1; } +#ifndef CONFIG_USB_PXA25X_SMALL if (usir1 & tmp) { handle_ep(&dev->ep[i+8]); USIR1 |= tmp; handled = 1; } +#endif } } @@ -2097,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, vbus_irq, irq; + 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) { @@ -2158,22 +2118,11 @@ 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); - if (dev->mach->gpio_vbus) { - if ((retval = gpio_request(dev->mach->gpio_vbus, - "pxa25x_udc GPIO VBUS"))) { - dev_dbg(&pdev->dev, - "can't get vbus gpio %d, err: %d\n", - dev->mach->gpio_vbus, retval); - goto err_gpio_vbus; - } - gpio_direction_input(dev->mach->gpio_vbus); - vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); - } else - vbus_irq = 0; + dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (dev->mach->gpio_pullup) { + if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, "pca25x_udc GPIO PULLUP"))) { dev_dbg(&pdev->dev, @@ -2188,21 +2137,17 @@ 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); udc_disable(dev); udc_reinit(dev); - dev->vbus = !!is_vbus_present(); + dev->vbus = 0; /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, - IRQF_DISABLED, driver_name, dev); + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); @@ -2212,56 +2157,43 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, - lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - driver_name, dev); + retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); -lubbock_fail0: goto err_irq_lub; } - retval = request_irq(LUBBOCK_USB_IRQ, - lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - driver_name, dev); + retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); - free_irq(LUBBOCK_USB_DISC_IRQ, dev); goto lubbock_fail0; } } else #endif - if (vbus_irq) { - retval = request_irq(vbus_irq, udc_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - driver_name, dev); - if (retval != 0) { - pr_err("%s: can't get irq %i, err %d\n", - driver_name, vbus_irq, retval); - goto err_vbus_irq; - } - } create_debug_files(dev); - return 0; + retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + if (!retval) + return retval; - err_vbus_irq: + remove_debug_files(dev); #ifdef CONFIG_ARCH_LUBBOCK +lubbock_fail0: free_irq(LUBBOCK_USB_DISC_IRQ, dev); err_irq_lub: -#endif free_irq(irq, dev); +#endif err_irq1: - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: - if (dev->mach->gpio_vbus) - gpio_free(dev->mach->gpio_vbus); - err_gpio_vbus: + if (!IS_ERR_OR_NULL(dev->transceiver)) { + usb_put_phy(dev->transceiver); + dev->transceiver = NULL; + } clk_put(dev->clk); err_clk: return retval; @@ -2272,13 +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); if (dev->driver) return -EBUSY; + usb_del_gadget_udc(&dev->gadget); dev->pullup = 0; pullup(dev); @@ -2294,16 +2227,16 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) free_irq(LUBBOCK_USB_IRQ, dev); } #endif - if (dev->mach->gpio_vbus) { - free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); - gpio_free(dev->mach->gpio_vbus); - } - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); clk_put(dev->clk); - platform_set_drvdata(pdev, NULL); + if (!IS_ERR_OR_NULL(dev->transceiver)) { + usb_put_phy(dev->transceiver); + dev->transceiver = NULL; + } + the_controller = NULL; return 0; } @@ -2329,7 +2262,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) struct pxa25x_udc *udc = platform_get_drvdata(dev); unsigned long flags; - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; @@ -2362,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 = { @@ -2371,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 1d51aa21e6e..3fe5931dc21 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA25X_H @@ -50,7 +41,6 @@ struct pxa25x_ep { struct usb_ep ep; struct pxa25x_udc *dev; - const struct usb_endpoint_descriptor *desc; struct list_head queue; unsigned long pio_irqs; @@ -128,6 +118,7 @@ struct pxa25x_udc { struct device *dev; struct clk *clk; struct pxa2xx_udc_mach_info *mach; + struct usb_phy *transceiver; u64 dma_mask; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; @@ -135,6 +126,7 @@ struct pxa25x_udc { struct dentry *debugfs_udc; #endif }; +#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget)) /*-------------------------------------------------------------------------*/ @@ -160,8 +152,6 @@ static struct pxa25x_udc *the_controller; #ifdef DEBUG -static int is_vbus_present(void); - static const char *state_name[] = { "EP0_IDLE", "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", @@ -213,8 +203,7 @@ dump_state(struct pxa25x_udc *dev) u32 tmp; unsigned i; - DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - is_vbus_present() ? "host " : "disconnected", + DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", state_name[dev->ep0state], UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); dump_udccr("udccr"); @@ -231,16 +220,13 @@ dump_state(struct pxa25x_udc *dev) } else DMSG("ep0 driver '%s'\n", dev->driver->driver.name); - if (!is_vbus_present()) - return; - dump_udccs0 ("udccs0"); DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", dev->stats.write.bytes, dev->stats.write.ops, dev->stats.read.bytes, dev->stats.read.ops); for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { - if (dev->ep [i].desc == NULL) + if (dev->ep[i].ep.desc == NULL) continue; DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 8cc676ecbb2..cdf4d678be9 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -8,21 +8,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/errno.h> +#include <linux/err.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/list.h> @@ -31,15 +22,14 @@ #include <linux/clk.h> #include <linux/irq.h> #include <linux/gpio.h> - -#include <asm/byteorder.h> -#include <mach/hardware.h> +#include <linux/slab.h> +#include <linux/prefetch.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/pxa2xx-regs.h> /* FIXME: for PSSR */ -#include <mach/udc.h> #include "pxa27x_udc.h" @@ -474,6 +464,23 @@ static inline void udc_clear_mask_UDCCR(struct pxa_udc *udc, int mask) } /** + * ep_write_UDCCSR - set bits in UDCCSR + * @udc: udc device + * @mask: bits to set in UDCCR + * + * Sets bits in UDCCSR (UDCCSR0 and UDCCSR*). + * + * A specific case is applied to ep0 : the ACM bit is always set to 1, for + * SET_INTERFACE and SET_CONFIGURATION. + */ +static inline void ep_write_UDCCSR(struct pxa_ep *ep, int mask) +{ + if (is_ep0(ep)) + mask |= UDCCSR0_ACM; + udc_ep_writel(ep, UDCCSR, mask); +} + +/** * ep_count_bytes_remain - get how many bytes in udc endpoint * @ep: udc endpoint * @@ -585,7 +592,7 @@ static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in) /** * inc_ep_stats_bytes - Update ep stats counts * @ep: physical endpoint - * @count: bytes transfered on endpoint + * @count: bytes transferred on endpoint * @is_in: ep direction (USB_DIR_IN or 0) */ static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) @@ -602,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; @@ -624,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; @@ -726,13 +733,17 @@ static void ep_del_request(struct pxa_ep *ep, struct pxa27x_request *req) * @ep: pxa physical endpoint * @req: pxa request * @status: usb request status sent to gadget API + * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held * - * Context: ep->lock held + * Context: ep->lock held if flags not NULL, else ep->lock released * * Retire a pxa27x usb request. Endpoint must be locked. */ -static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status) +static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status, + unsigned long *pflags) { + unsigned long flags; + ep_del_request(ep, req); if (likely(req->req.status == -EINPROGRESS)) req->req.status = status; @@ -744,38 +755,48 @@ static void req_done(struct pxa_ep *ep, struct pxa27x_request *req, int status) &req->req, status, req->req.actual, req->req.length); + if (pflags) + spin_unlock_irqrestore(&ep->lock, *pflags); + local_irq_save(flags); req->req.complete(&req->udc_usb_ep->usb_ep, &req->req); + local_irq_restore(flags); + if (pflags) + spin_lock_irqsave(&ep->lock, *pflags); } /** * ep_end_out_req - Ends endpoint OUT request * @ep: physical endpoint * @req: pxa request + * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held * - * Context: ep->lock held + * Context: ep->lock held or released (see req_done()) * * Ends endpoint OUT request (completes usb request). */ -static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req) +static void ep_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req, + unsigned long *pflags) { inc_ep_stats_reqs(ep, !USB_DIR_IN); - req_done(ep, req, 0); + req_done(ep, req, 0, pflags); } /** * ep0_end_out_req - Ends control endpoint OUT request (ends data stage) * @ep: physical endpoint * @req: pxa request + * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held * - * Context: ep->lock held + * Context: ep->lock held or released (see req_done()) * * Ends control endpoint OUT request (completes usb request), and puts * control endpoint into idle state */ -static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req) +static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req, + unsigned long *pflags) { set_ep0state(ep->dev, OUT_STATUS_STAGE); - ep_end_out_req(ep, req); + ep_end_out_req(ep, req, pflags); ep0_idle(ep->dev); } @@ -783,31 +804,35 @@ static void ep0_end_out_req(struct pxa_ep *ep, struct pxa27x_request *req) * ep_end_in_req - Ends endpoint IN request * @ep: physical endpoint * @req: pxa request + * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held * - * Context: ep->lock held + * Context: ep->lock held or released (see req_done()) * * Ends endpoint IN request (completes usb request). */ -static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req) +static void ep_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req, + unsigned long *pflags) { inc_ep_stats_reqs(ep, USB_DIR_IN); - req_done(ep, req, 0); + req_done(ep, req, 0, pflags); } /** * ep0_end_in_req - Ends control endpoint IN request (ends data stage) * @ep: physical endpoint * @req: pxa request + * @pflags: flags of previous spinlock_irq_save() or NULL if no lock held * - * Context: ep->lock held + * Context: ep->lock held or released (see req_done()) * * Ends control endpoint IN request (completes usb request), and puts * control endpoint into status state */ -static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req) +static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req, + unsigned long *pflags) { set_ep0state(ep->dev, IN_STATUS_STAGE); - ep_end_in_req(ep, req); + ep_end_in_req(ep, req, pflags); } /** @@ -815,19 +840,22 @@ static void ep0_end_in_req(struct pxa_ep *ep, struct pxa27x_request *req) * @ep: pxa endpoint * @status: usb request status * - * Context: ep->lock held + * Context: ep->lock released * * Dequeues all requests on an endpoint. As a side effect, interrupts will be * disabled on that endpoint (because no more requests). */ static void nuke(struct pxa_ep *ep, int status) { - struct pxa27x_request *req; + struct pxa27x_request *req; + unsigned long flags; + spin_lock_irqsave(&ep->lock, flags); while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa27x_request, queue); - req_done(ep, req, status); + req_done(ep, req, status, &flags); } + spin_unlock_irqrestore(&ep->lock, flags); } /** @@ -839,7 +867,7 @@ static void nuke(struct pxa_ep *ep, int status) * If there is less space in request than bytes received in OUT endpoint, * bytes are left in the OUT endpoint. * - * Returns how many bytes were actually transfered + * Returns how many bytes were actually transferred */ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req) { @@ -861,7 +889,7 @@ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req) *buf++ = udc_ep_readl(ep, UDCDR); req->req.actual += count; - udc_ep_writel(ep, UDCCSR, UDCCSR_PC); + ep_write_UDCCSR(ep, UDCCSR_PC); return count; } @@ -876,7 +904,7 @@ static int read_packet(struct pxa_ep *ep, struct pxa27x_request *req) * endpoint. If there are no bytes to transfer, doesn't write anything * to physical endpoint. * - * Returns how many bytes were actually transfered. + * Returns how many bytes were actually transferred. */ static int write_packet(struct pxa_ep *ep, struct pxa27x_request *req, unsigned int max) @@ -953,7 +981,7 @@ static int read_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * caller guarantees at least one packet buffer is ready (or a zlp). * Doesn't complete the request, that's the caller's job * - * Returns 1 if request fully transfered, 0 if partial transfer + * Returns 1 if request fully transferred, 0 if partial transfer */ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) { @@ -969,12 +997,12 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) if (udccsr & UDCCSR_PC) { ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n", udccsr); - udc_ep_writel(ep, UDCCSR, UDCCSR_PC); + ep_write_UDCCSR(ep, UDCCSR_PC); } if (udccsr & UDCCSR_TRN) { ep_vdbg(ep, "Clearing Underrun on, udccsr=%x\n", udccsr); - udc_ep_writel(ep, UDCCSR, UDCCSR_TRN); + ep_write_UDCCSR(ep, UDCCSR_TRN); } count = write_packet(ep, req, max); @@ -996,7 +1024,7 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) } if (is_short) - udc_ep_writel(ep, UDCCSR, UDCCSR_SP); + ep_write_UDCCSR(ep, UDCCSR_SP); /* requests complete when all IN data is in the FIFO */ if (is_last) { @@ -1029,7 +1057,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) while (epout_has_pkt(ep)) { count = read_packet(ep, req); - udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_OPC); inc_ep_stats_bytes(ep, count, !USB_DIR_IN); is_short = (count < ep->fifo_size); @@ -1056,7 +1084,7 @@ static int read_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) * Sends a request (or a part of the request) to the control endpoint (ep0 in). * If the request doesn't fit, the remaining part will be sent from irq. * The request is considered fully written only if either : - * - last write transfered all remaining bytes, but fifo was not fully filled + * - last write transferred all remaining bytes, but fifo was not fully filled * - last write was a 0 length write * * Returns 1 if request fully written, 0 if request only partially sent @@ -1074,7 +1102,7 @@ static int write_ep0_fifo(struct pxa_ep *ep, struct pxa27x_request *req) /* Sends either a short packet or a 0 length packet */ if (unlikely(is_short)) - udc_ep_writel(ep, UDCCSR, UDCCSR0_IPR); + ep_write_UDCCSR(ep, UDCCSR0_IPR); ep_dbg(ep, "in %d bytes%s%s, %d left, req=%p, udccsr0=0x%03x\n", count, is_short ? "/S" : "", is_last ? "/L" : "", @@ -1107,6 +1135,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int rc = 0; int is_first_req; unsigned length; + int recursion_detected; req = container_of(_req, struct pxa27x_request, req); udc_usb_ep = container_of(_ep, struct udc_usb_ep, usb_ep); @@ -1136,6 +1165,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, return -EMSGSIZE; spin_lock_irqsave(&ep->lock, flags); + recursion_detected = ep->in_handle_ep; is_first_req = list_empty(&ep->queue); ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n", @@ -1145,12 +1175,12 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (!ep->enabled) { _req->status = -ESHUTDOWN; rc = -ESHUTDOWN; - goto out; + goto out_locked; } if (req->in_use) { ep_err(ep, "refusing to queue req %p (already queued)\n", req); - goto out; + goto out_locked; } length = _req->length; @@ -1158,12 +1188,13 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, _req->actual = 0; ep_add_request(ep, req); + spin_unlock_irqrestore(&ep->lock, flags); if (is_ep0(ep)) { switch (dev->ep0state) { case WAIT_ACK_SET_CONF_INTERF: if (length == 0) { - ep_end_in_req(ep, req); + ep_end_in_req(ep, req, NULL); } else { ep_err(ep, "got a request of %d bytes while" "in state WAIT_ACK_SET_CONF_INTERF\n", @@ -1176,12 +1207,12 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, case IN_DATA_STAGE: if (!ep_is_full(ep)) if (write_ep0_fifo(ep, req)) - ep0_end_in_req(ep, req); + ep0_end_in_req(ep, req, NULL); break; case OUT_DATA_STAGE: if ((length == 0) || !epout_has_pkt(ep)) if (read_ep0_fifo(ep, req)) - ep0_end_out_req(ep, req); + ep0_end_out_req(ep, req, NULL); break; default: ep_err(ep, "odd state %s to send me a request\n", @@ -1191,12 +1222,15 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, break; } } else { - handle_ep(ep); + if (!recursion_detected) + handle_ep(ep); } out: - spin_unlock_irqrestore(&ep->lock, flags); return rc; +out_locked: + spin_unlock_irqrestore(&ep->lock, flags); + goto out; } /** @@ -1226,13 +1260,14 @@ static int pxa_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) /* make sure it's actually queued on this endpoint */ list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) { - req_done(ep, req, -ECONNRESET); rc = 0; break; } } spin_unlock_irqrestore(&ep->lock, flags); + if (!rc) + req_done(ep, req, -ECONNRESET, NULL); return rc; } @@ -1277,7 +1312,7 @@ static int pxa_ep_set_halt(struct usb_ep *_ep, int value) /* FST, FEF bits are the same for control and non control endpoints */ rc = 0; - udc_ep_writel(ep, UDCCSR, UDCCSR_FST | UDCCSR_FEF); + ep_write_UDCCSR(ep, UDCCSR_FST | UDCCSR_FEF); if (is_ep0(ep)) set_ep0state(ep->dev, STALL); @@ -1343,14 +1378,12 @@ static void pxa_ep_fifo_flush(struct usb_ep *_ep) udc_ep_readl(ep, UDCDR); } else { /* most IN status is the same, but ISO can't stall */ - udc_ep_writel(ep, UDCCSR, + ep_write_UDCCSR(ep, UDCCSR_PC | UDCCSR_FEF | UDCCSR_TRN | (EPXFERTYPE_is_ISO(ep) ? 0 : UDCCSR_SST)); } spin_unlock_irqrestore(&ep->lock, flags); - - return; } /** @@ -1395,7 +1428,7 @@ static int pxa_ep_enable(struct usb_ep *_ep, return -EINVAL; } - if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + if (ep->fifo_size < usb_endpoint_maxp(desc)) { ep_err(ep, "bad maxpacket\n"); return -ERANGE; } @@ -1429,7 +1462,6 @@ static int pxa_ep_disable(struct usb_ep *_ep) { struct pxa_ep *ep; struct udc_usb_ep *udc_usb_ep; - unsigned long flags; if (!_ep) return -EINVAL; @@ -1439,10 +1471,8 @@ static int pxa_ep_disable(struct usb_ep *_ep) if (!ep || is_ep0(ep) || !list_empty(&ep->queue)) return -EINVAL; - spin_lock_irqsave(&ep->lock, flags); ep->enabled = 0; nuke(ep, -ESHUTDOWN); - spin_unlock_irqrestore(&ep->lock, flags); pxa_ep_fifo_flush(_ep); udc_usb_ep->pxa_ep = NULL; @@ -1508,7 +1538,7 @@ static int pxa_udc_get_frame(struct usb_gadget *_gadget) * pxa_udc_wakeup - Force udc device out of suspend * @_gadget: usb gadget * - * Returns 0 if succesfull, error code otherwise + * Returns 0 if successful, error code otherwise */ static int pxa_udc_wakeup(struct usb_gadget *_gadget) { @@ -1542,7 +1572,7 @@ static int should_enable_udc(struct pxa_udc *udc) int put_on; put_on = ((udc->pullup_on) && (udc->driver)); - put_on &= ((udc->vbus_sensed) || (!udc->transceiver)); + put_on &= ((udc->vbus_sensed) || (IS_ERR_OR_NULL(udc->transceiver))); return put_on; } @@ -1563,7 +1593,7 @@ static int should_disable_udc(struct pxa_udc *udc) int put_off; put_off = ((!udc->pullup_on) || (!udc->driver)); - put_off |= ((!udc->vbus_sensed) && (udc->transceiver)); + put_off |= ((!udc->vbus_sensed) && (!IS_ERR_OR_NULL(udc->transceiver))); return put_off; } @@ -1634,17 +1664,24 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) struct pxa_udc *udc; udc = to_gadget_udc(_gadget); - if (udc->transceiver) - return otg_set_power(udc->transceiver, mA); + if (!IS_ERR_OR_NULL(udc->transceiver)) + return usb_phy_set_power(udc->transceiver, mA); return -EOPNOTSUPP; } +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, .wakeup = pxa_udc_wakeup, .pullup = pxa_udc_pullup, .vbus_session = pxa_udc_vbus_session, .vbus_draw = pxa_udc_vbus_draw, + .udc_start = pxa27x_udc_start, + .udc_stop = pxa27x_udc_stop, }; /** @@ -1679,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; @@ -1700,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); + } } /** @@ -1728,6 +1768,7 @@ static void udc_enable(struct pxa_udc *udc) memset(&udc->stats, 0, sizeof(udc->stats)); udc_set_mask_UDCCR(udc, UDCCR_UDE); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM); udelay(2); if (udc_readl(udc, UDCCR) & UDCCR_EMCE) dev_err(udc->dev, "Configuration errors, udc disabled\n"); @@ -1749,8 +1790,9 @@ static void udc_enable(struct pxa_udc *udc) } /** - * usb_gadget_register_driver - Register gadget driver + * pxa27x_start - Register gadget driver * @driver: gadget driver + * @bind: bind function * * When a driver is successfully registered, it will receive control requests * including set_configuration(), which enables non-control requests. Then @@ -1762,43 +1804,22 @@ static void udc_enable(struct pxa_udc *udc) * * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise */ -int usb_gadget_register_driver(struct usb_gadget_driver *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->speed < USB_SPEED_FULL || !driver->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 = driver->bind(&udc->gadget); - 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 (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + 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; } } @@ -1806,18 +1827,10 @@ int usb_gadget_register_driver(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; } -EXPORT_SYMBOL(usb_gadget_register_driver); - /** * stop_activity - Stops udc endpoints @@ -1838,42 +1851,29 @@ 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); } /** - * usb_gadget_unregister_driver - Unregister the gadget driver + * pxa27x_udc_stop - Unregister the gadget driver * @driver: gadget driver * * Returns 0 if no error, -ENODEV, -EINVAL otherwise */ -int usb_gadget_unregister_driver(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 (udc->transceiver) - return otg_set_peripheral(udc->transceiver, NULL); + if (!IS_ERR_OR_NULL(udc->transceiver)) + return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /** * handle_ep0_ctrl_req - handle control endpoint control request @@ -1890,8 +1890,19 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc, } u; int i; int have_extrabytes = 0; + unsigned long flags; nuke(ep, -EPROTO); + spin_lock_irqsave(&ep->lock, flags); + + /* + * In the PXA320 manual, in the section about Back-to-Back setup + * packets, it describes this situation. The solution is to set OPC to + * get rid of the status packet, and then continue with the setup + * packet. Generalize to pxa27x CPUs. + */ + if (epout_has_pkt(ep) && (ep_count_bytes_remain(ep) == 0)) + ep_write_UDCCSR(ep, UDCCSR0_OPC); /* read SETUP packet */ for (i = 0; i < 2; i++) { @@ -1919,17 +1930,20 @@ static void handle_ep0_ctrl_req(struct pxa_udc *udc, set_ep0state(udc, OUT_DATA_STAGE); /* Tell UDC to enter Data Stage */ - udc_ep_writel(ep, UDCCSR, UDCCSR0_SA | UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_SA | UDCCSR0_OPC); + spin_unlock_irqrestore(&ep->lock, flags); i = udc->driver->setup(&udc->gadget, &u.r); + spin_lock_irqsave(&ep->lock, flags); if (i < 0) goto stall; out: + spin_unlock_irqrestore(&ep->lock, flags); return; stall: ep_dbg(ep, "protocol STALL, udccsr0=%03x err %d\n", udc_ep_readl(ep, UDCCSR), i); - udc_ep_writel(ep, UDCCSR, UDCCSR0_FST | UDCCSR0_FTF); + ep_write_UDCCSR(ep, UDCCSR0_FST | UDCCSR0_FTF); set_ep0state(udc, STALL); goto out; } @@ -1966,6 +1980,8 @@ stall: * cleared by software. * - clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it * before reading ep0. + * This is true only for PXA27x. This is not true anymore for PXA3xx family + * (check Back-to-Back setup packet in developers guide). * - irq can be called on a "packet complete" event (opc_irq=1), while * UDCCSR0_OPC is not yet raised (delta can be as big as 100ms * from experimentation). @@ -1998,7 +2014,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) if (udccsr0 & UDCCSR0_SST) { ep_dbg(ep, "clearing stall status\n"); nuke(ep, -EPIPE); - udc_ep_writel(ep, UDCCSR, UDCCSR0_SST); + ep_write_UDCCSR(ep, UDCCSR0_SST); ep0_idle(udc); } @@ -2023,20 +2039,20 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) break; case IN_DATA_STAGE: /* GET_DESCRIPTOR */ if (epout_has_pkt(ep)) - udc_ep_writel(ep, UDCCSR, UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_OPC); if (req && !ep_is_full(ep)) completed = write_ep0_fifo(ep, req); if (completed) - ep0_end_in_req(ep, req); + ep0_end_in_req(ep, req, NULL); break; case OUT_DATA_STAGE: /* SET_DESCRIPTOR */ if (epout_has_pkt(ep) && req) completed = read_ep0_fifo(ep, req); if (completed) - ep0_end_out_req(ep, req); + ep0_end_out_req(ep, req, NULL); break; case STALL: - udc_ep_writel(ep, UDCCSR, UDCCSR0_FST); + ep_write_UDCCSR(ep, UDCCSR0_FST); break; case IN_STATUS_STAGE: /* @@ -2063,7 +2079,7 @@ static void handle_ep0(struct pxa_udc *udc, int fifo_irq, int opc_irq) * Tries to transfer all pending request data into the endpoint and/or * transfer all pending data in the endpoint into usb requests. * - * Is always called when in_interrupt() or with ep->lock held. + * Is always called when in_interrupt() and with ep->lock released. */ static void handle_ep(struct pxa_ep *ep) { @@ -2072,10 +2088,17 @@ static void handle_ep(struct pxa_ep *ep) u32 udccsr; int is_in = ep->dir_in; int loop = 0; + unsigned long flags; + + spin_lock_irqsave(&ep->lock, flags); + if (ep->in_handle_ep) + goto recursion_detected; + ep->in_handle_ep = 1; do { completed = 0; udccsr = udc_ep_readl(ep, UDCCSR); + if (likely(!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct pxa27x_request, queue); @@ -2094,15 +2117,22 @@ static void handle_ep(struct pxa_ep *ep) if (unlikely(is_in)) { if (likely(!ep_is_full(ep))) completed = write_fifo(ep, req); - if (completed) - ep_end_in_req(ep, req); } else { if (likely(epout_has_pkt(ep))) completed = read_fifo(ep, req); - if (completed) - ep_end_out_req(ep, req); + } + + if (completed) { + if (is_in) + ep_end_in_req(ep, req, &flags); + else + ep_end_out_req(ep, req, &flags); } } while (completed); + + ep->in_handle_ep = 0; +recursion_detected: + spin_unlock_irqrestore(&ep->lock, flags); } /** @@ -2131,6 +2161,7 @@ static void pxa27x_change_configuration(struct pxa_udc *udc, int config) set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); udc->driver->setup(&udc->gadget, &req); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); } /** @@ -2159,6 +2190,7 @@ static void pxa27x_change_interface(struct pxa_udc *udc, int iface, int alt) set_ep0state(udc, WAIT_ACK_SET_CONF_INTERF); udc->driver->setup(&udc->gadget, &req); + ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_AREN); } /* @@ -2188,9 +2220,13 @@ static void irq_handle_data(int irq, struct pxa_udc *udc) continue; udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK)); - ep = &udc->pxa_ep[i]; - ep->stats.irqs++; - handle_ep(ep); + + WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep)); + if (i < ARRAY_SIZE(udc->pxa_ep)) { + ep = &udc->pxa_ep[i]; + ep->stats.irqs++; + handle_ep(ep); + } } for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) { @@ -2198,9 +2234,12 @@ static void irq_handle_data(int irq, struct pxa_udc *udc) if (!(udcisr1 & UDCISR_INT_MASK)) continue; - ep = &udc->pxa_ep[i]; - ep->stats.irqs++; - handle_ep(ep); + WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep)); + if (i < ARRAY_SIZE(udc->pxa_ep)) { + ep = &udc->pxa_ep[i]; + ep->stats.irqs++; + handle_ep(ep); + } } } @@ -2280,7 +2319,7 @@ static void irq_udc_reset(struct pxa_udc *udc) memset(&udc->stats, 0, sizeof udc->stats); nuke(ep, -EPROTO); - udc_ep_writel(ep, UDCCSR, UDCCSR0_FTF | UDCCSR0_OPC); + ep_write_UDCCSR(ep, UDCCSR0_FTF | UDCCSR0_OPC); ep0_idle(udc); } @@ -2372,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; @@ -2386,8 +2425,8 @@ static int __init pxa_udc_probe(struct platform_device *pdev) return udc->irq; udc->dev = &pdev->dev; - udc->mach = pdev->dev.platform_data; - udc->transceiver = otg_get_transceiver(); + udc->mach = dev_get_platdata(&pdev->dev); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { @@ -2409,15 +2448,12 @@ static int __init pxa_udc_probe(struct platform_device *pdev) } retval = -ENOMEM; - udc->regs = ioremap(regs->start, regs->end - regs->start + 1); + udc->regs = ioremap(regs->start, resource_size(regs)); if (!udc->regs) { dev_err(&pdev->dev, "Unable to map UDC I/O memory\n"); 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; @@ -2430,12 +2466,20 @@ static int __init pxa_udc_probe(struct platform_device *pdev) IRQF_SHARED, driver_name, udc); if (retval != 0) { dev_err(udc->dev, "%s: can't get irq %i, err %d\n", - driver_name, IRQ_USB, retval); + 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: iounmap(udc->regs); err_map: @@ -2449,21 +2493,21 @@ 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; + usb_del_gadget_udc(&udc->gadget); usb_gadget_unregister_driver(udc->driver); free_irq(udc->irq, udc); pxa_cleanup_debugfs(udc); if (gpio_is_valid(gpio)) gpio_free(gpio); - otg_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; - platform_set_drvdata(_dev, NULL); the_controller = NULL; clk_put(udc->clk); iounmap(udc->regs); @@ -2479,6 +2523,12 @@ static void pxa_udc_shutdown(struct platform_device *_dev) udc_disable(udc); } +#ifdef CONFIG_PXA27x +extern void pxa27x_clear_otgph(void); +#else +#define pxa27x_clear_otgph() do {} while (0) +#endif + #ifdef CONFIG_PM /** * pxa_udc_suspend - Suspend udc device @@ -2546,8 +2596,7 @@ static int pxa_udc_resume(struct platform_device *_dev) * Software must configure the USB OTG pad, UDC, and UHC * to the state they were in before entering sleep mode. */ - if (cpu_is_pxa27x()) - PSSR |= PSSR_OTGPH; + pxa27x_clear_otgph(); return 0; } @@ -2561,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, @@ -2569,22 +2619,7 @@ static struct platform_driver udc_driver = { #endif }; -static int __init udc_init(void) -{ - if (!cpu_is_pxa27x()) - 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 db58125331d..28f2b53530f 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA27X_H @@ -88,9 +79,9 @@ #define UDCISR_INT_MASK (UDCICR_FIFOERR | UDCICR_PKTCOMPL) #define UDCOTGICR_IESF (1 << 24) /* OTG SET_FEATURE command recvd */ -#define UDCOTGICR_IEXR (1 << 17) /* Extra Transciever Interrupt +#define UDCOTGICR_IEXR (1 << 17) /* Extra Transceiver Interrupt Rising Edge Interrupt Enable */ -#define UDCOTGICR_IEXF (1 << 16) /* Extra Transciever Interrupt +#define UDCOTGICR_IEXF (1 << 16) /* Extra Transceiver Interrupt Falling Edge Interrupt Enable */ #define UDCOTGICR_IEVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge Interrupt Enable */ @@ -130,6 +121,8 @@ #define UP2OCR_HXOE (1 << 17) /* Transceiver Output Enable */ #define UP2OCR_SEOS (1 << 24) /* Single-Ended Output Select */ +#define UDCCSR0_ACM (1 << 9) /* Ack Control Mode */ +#define UDCCSR0_AREN (1 << 8) /* Ack Response Enable */ #define UDCCSR0_SA (1 << 7) /* Setup Active */ #define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */ #define UDCCSR0_FST (1 << 5) /* Force Stall */ @@ -316,6 +309,11 @@ struct udc_usb_ep { * @queue: requests queue * @lock: lock to pxa_ep data (queues and stats) * @enabled: true when endpoint enabled (not stopped by gadget layer) + * @in_handle_ep: number of recursions of handle_ep() function + * Prevents deadlocks or infinite recursions of types : + * irq->handle_ep()->req_done()->req.complete()->pxa_ep_queue()->handle_ep() + * or + * pxa_ep_queue()->handle_ep()->req_done()->req.complete()->pxa_ep_queue() * @idx: endpoint index (1 => epA, 2 => epB, ..., 24 => epX) * @name: endpoint name (for trace/debug purpose) * @dir_in: 1 if IN endpoint, 0 if OUT endpoint @@ -344,6 +342,7 @@ struct pxa_ep { spinlock_t lock; /* Protects this structure */ /* (queues, stats) */ unsigned enabled:1; + unsigned in_handle_ep:1; unsigned idx:5; char *name; @@ -352,7 +351,7 @@ struct pxa_ep { * Specific pxa endpoint data, needed for hardware initialization */ unsigned dir_in:1; - unsigned addr:3; + unsigned addr:4; unsigned config:2; unsigned interface:3; unsigned alternate:3; @@ -419,7 +418,7 @@ struct udc_stats { * @irq: udc irq * @clk: udc clock * @usb_gadget: udc gadget structure - * @driver: bound gadget (zero, g_ether, g_file_storage, ...) + * @driver: bound gadget (zero, g_ether, g_mass_storage, ...) * @dev: device * @mach: machine info, used to activate specific GPIO * @transceiver: external transceiver to handle vbus sense and D+ pullup @@ -448,7 +447,7 @@ struct pxa_udc { struct usb_gadget_driver *driver; struct device *dev; struct pxa2xx_udc_mach_info *mach; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; enum ep0_state ep0state; struct udc_stats stats; @@ -474,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 new file mode 100644 index 00000000000..b698a490cc7 --- /dev/null +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -0,0 +1,2029 @@ +/* + * R8A66597 UDC (USB gadget) + * + * Copyright (C) 2006-2009 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "r8a66597-udc.h" + +#define DRIVER_VERSION "2011-09-26" + +static const char udc_name[] = "r8a66597_udc"; +static const char *r8a66597_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", + "ep8", "ep9", +}; + +static void init_controller(struct r8a66597 *r8a66597); +static void disable_controller(struct r8a66597 *r8a66597); +static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req); +static void irq_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req); +static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags); + +static void transfer_complete(struct r8a66597_ep *ep, + struct r8a66597_request *req, int status); + +/*-------------------------------------------------------------------------*/ +static inline u16 get_usb_speed(struct r8a66597 *r8a66597) +{ + return r8a66597_read(r8a66597, DVSTCTR0) & RHST; +} + +static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, + INTENB0); + r8a66597_bset(r8a66597, (1 << pipenum), reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, + INTENB0); + r8a66597_bclr(r8a66597, (1 << pipenum), reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +static void r8a66597_usb_connect(struct r8a66597 *r8a66597) +{ + r8a66597_bset(r8a66597, CTRE, INTENB0); + r8a66597_bset(r8a66597, BEMPE | BRDYE, INTENB0); + + r8a66597_bset(r8a66597, DPRPU, SYSCFG0); +} + +static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + r8a66597_bclr(r8a66597, CTRE, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | BRDYE, INTENB0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + + r8a66597->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&r8a66597->lock); + r8a66597->driver->disconnect(&r8a66597->gadget); + spin_lock(&r8a66597->lock); + + disable_controller(r8a66597); + init_controller(r8a66597); + r8a66597_bset(r8a66597, VBSE, INTENB0); + INIT_LIST_HEAD(&r8a66597->ep[0].queue); +} + +static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 pid = 0; + unsigned long offset; + + if (pipenum == 0) { + pid = r8a66597_read(r8a66597, DCPCTR) & PID; + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + pid = r8a66597_read(r8a66597, offset) & PID; + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } + + return pid; +} + +static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum, + u16 pid) +{ + unsigned long offset; + + if (pipenum == 0) { + r8a66597_mdfy(r8a66597, pid, PID, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_mdfy(r8a66597, pid, PID, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } +} + +static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_BUF); +} + +static inline void pipe_stop(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_NAK); +} + +static inline void pipe_stall(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_STALL); +} + +static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 ret = 0; + unsigned long offset; + + if (pipenum == 0) { + ret = r8a66597_read(r8a66597, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + ret = r8a66597_read(r8a66597, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } + + return ret; +} + +static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) { + r8a66597_bset(r8a66597, SQCLR, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQCLR, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } +} + +static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) { + r8a66597_bset(r8a66597, SQSET, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQSET, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } +} + +static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + if (pipenum == 0) { + return r8a66597_read(r8a66597, DCPCTR) & SQMON; + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + return r8a66597_read(r8a66597, offset) & SQMON; + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } + + return 0; +} + +static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum) +{ + return control_reg_sqmon(r8a66597, pipenum); +} + +static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum, + u16 toggle) +{ + if (toggle) + control_reg_sqset(r8a66597, pipenum); + else + control_reg_sqclr(r8a66597, pipenum); +} + +static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 tmp; + int size; + + if (pipenum == 0) { + tmp = r8a66597_read(r8a66597, DCPCFG); + if ((tmp & R8A66597_CNTMD) != 0) + size = 256; + else { + tmp = r8a66597_read(r8a66597, DCPMAXP); + size = tmp & MAXP; + } + } else { + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG); + if ((tmp & R8A66597_CNTMD) != 0) { + tmp = r8a66597_read(r8a66597, PIPEBUF); + size = ((tmp >> 10) + 1) * 64; + } else { + tmp = r8a66597_read(r8a66597, PIPEMAXP); + size = tmp & MXPS; + } + } + + return size; +} + +static inline unsigned short mbw_value(struct r8a66597 *r8a66597) +{ + if (r8a66597->pdata->on_chip) + return MBW_32; + else + return MBW_16; +} + +static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum, + u16 isel, u16 fifosel) +{ + u16 tmp, mask, loop; + int i = 0; + + if (!pipenum) { + mask = ISEL | CURPIPE; + loop = isel; + } else { + mask = CURPIPE; + loop = pipenum; + } + r8a66597_mdfy(r8a66597, loop, mask, fifosel); + + do { + tmp = r8a66597_read(r8a66597, fifosel); + if (i++ > 1000000) { + dev_err(r8a66597_to_dev(r8a66597), + "r8a66597: register%x, loop %x " + "is timeout\n", fifosel, loop); + break; + } + ndelay(1); + } while ((tmp & mask) != loop); +} + +static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + + if (ep->use_dma) + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); + + r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel); + + ndelay(450); + + if (r8a66597_is_sudmac(r8a66597) && ep->use_dma) + r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel); + else + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + + if (ep->use_dma) + r8a66597_bset(r8a66597, DREQE, ep->fifosel); +} + +static int pipe_buffer_setting(struct r8a66597 *r8a66597, + struct r8a66597_pipe_info *info) +{ + u16 bufnum = 0, buf_bsize = 0; + u16 pipecfg = 0; + + if (info->pipe == 0) + return -EINVAL; + + r8a66597_write(r8a66597, info->pipe, PIPESEL); + + if (info->dir_in) + pipecfg |= R8A66597_DIR; + pipecfg |= info->type; + pipecfg |= info->epnum; + switch (info->type) { + case R8A66597_INT: + bufnum = 4 + (info->pipe - R8A66597_BASE_PIPENUM_INT); + buf_bsize = 0; + break; + case R8A66597_BULK: + /* isochronous pipes may be used as bulk pipes */ + if (info->pipe >= R8A66597_BASE_PIPENUM_BULK) + bufnum = info->pipe - R8A66597_BASE_PIPENUM_BULK; + else + bufnum = info->pipe - R8A66597_BASE_PIPENUM_ISOC; + + bufnum = R8A66597_BASE_BUFNUM + (bufnum * 16); + buf_bsize = 7; + pipecfg |= R8A66597_DBLB; + if (!info->dir_in) + pipecfg |= R8A66597_SHTNAK; + break; + case R8A66597_ISO: + bufnum = R8A66597_BASE_BUFNUM + + (info->pipe - R8A66597_BASE_PIPENUM_ISOC) * 16; + buf_bsize = 7; + break; + } + + if (buf_bsize && ((bufnum + 16) >= R8A66597_MAX_BUFNUM)) { + pr_err("r8a66597 pipe memory is insufficient\n"); + return -ENOMEM; + } + + r8a66597_write(r8a66597, pipecfg, PIPECFG); + r8a66597_write(r8a66597, (buf_bsize << 10) | (bufnum), PIPEBUF); + r8a66597_write(r8a66597, info->maxpacket, PIPEMAXP); + if (info->interval) + info->interval--; + r8a66597_write(r8a66597, info->interval, PIPEPERI); + + return 0; +} + +static void pipe_buffer_release(struct r8a66597 *r8a66597, + struct r8a66597_pipe_info *info) +{ + if (info->pipe == 0) + return; + + if (is_bulk_pipe(info->pipe)) { + r8a66597->bulk--; + } else if (is_interrupt_pipe(info->pipe)) { + r8a66597->interrupt--; + } else if (is_isoc_pipe(info->pipe)) { + r8a66597->isochronous--; + if (info->type == R8A66597_BULK) + r8a66597->bulk--; + } else { + dev_err(r8a66597_to_dev(r8a66597), + "ep_release: unexpect pipenum (%d)\n", info->pipe); + } +} + +static void pipe_initialize(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + + r8a66597_mdfy(r8a66597, 0, CURPIPE, ep->fifosel); + + r8a66597_write(r8a66597, ACLRM, ep->pipectr); + r8a66597_write(r8a66597, 0, ep->pipectr); + r8a66597_write(r8a66597, SQCLR, ep->pipectr); + if (ep->use_dma) { + r8a66597_mdfy(r8a66597, ep->pipenum, CURPIPE, ep->fifosel); + + ndelay(450); + + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + } +} + +static void r8a66597_ep_setting(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + const struct usb_endpoint_descriptor *desc, + u16 pipenum, int dma) +{ + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; + + ep->pipectr = get_pipectr_addr(pipenum); + if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) { + ep->pipetre = get_pipetre_addr(pipenum); + ep->pipetrn = get_pipetrn_addr(pipenum); + } else { + ep->pipetre = 0; + ep->pipetrn = 0; + } + ep->pipenum = pipenum; + ep->ep.maxpacket = usb_endpoint_maxp(desc); + r8a66597->pipenum2ep[pipenum] = ep; + r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] + = ep; + INIT_LIST_HEAD(&ep->queue); +} + +static void r8a66597_ep_release(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 pipenum = ep->pipenum; + + if (pipenum == 0) + return; + + if (ep->use_dma) + r8a66597->num_dma--; + ep->pipenum = 0; + ep->busy = 0; + ep->use_dma = 0; +} + +static int alloc_pipe_config(struct r8a66597_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + struct r8a66597_pipe_info info; + int dma = 0; + unsigned char *counter; + int ret; + + ep->ep.desc = desc; + + if (ep->pipenum) /* already allocated pipe */ + return 0; + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { + if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { + dev_err(r8a66597_to_dev(r8a66597), + "bulk pipe is insufficient\n"); + return -ENODEV; + } else { + info.pipe = R8A66597_BASE_PIPENUM_ISOC + + r8a66597->isochronous; + counter = &r8a66597->isochronous; + } + } else { + info.pipe = R8A66597_BASE_PIPENUM_BULK + r8a66597->bulk; + counter = &r8a66597->bulk; + } + info.type = R8A66597_BULK; + dma = 1; + break; + case USB_ENDPOINT_XFER_INT: + if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) { + dev_err(r8a66597_to_dev(r8a66597), + "interrupt pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt; + info.type = R8A66597_INT; + counter = &r8a66597->interrupt; + break; + case USB_ENDPOINT_XFER_ISOC: + if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { + dev_err(r8a66597_to_dev(r8a66597), + "isochronous pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous; + info.type = R8A66597_ISO; + counter = &r8a66597->isochronous; + break; + default: + dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n"); + return -EINVAL; + } + ep->type = info.type; + + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.maxpacket = usb_endpoint_maxp(desc); + info.interval = desc->bInterval; + if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + info.dir_in = 1; + else + info.dir_in = 0; + + ret = pipe_buffer_setting(r8a66597, &info); + if (ret < 0) { + dev_err(r8a66597_to_dev(r8a66597), + "pipe_buffer_setting fail\n"); + return ret; + } + + (*counter)++; + if ((counter == &r8a66597->isochronous) && info.type == R8A66597_BULK) + r8a66597->bulk++; + + r8a66597_ep_setting(r8a66597, ep, desc, info.pipe, dma); + pipe_initialize(ep); + + return 0; +} + +static int free_pipe_config(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + struct r8a66597_pipe_info info; + + info.pipe = ep->pipenum; + info.type = ep->type; + pipe_buffer_release(r8a66597, &info); + r8a66597_ep_release(ep); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +static void pipe_irq_enable(struct r8a66597 *r8a66597, u16 pipenum) +{ + enable_irq_ready(r8a66597, pipenum); + enable_irq_nrdy(r8a66597, pipenum); +} + +static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum) +{ + disable_irq_ready(r8a66597, pipenum); + disable_irq_nrdy(r8a66597, pipenum); +} + +/* if complete is true, gadget driver complete function is not call */ +static void control_end(struct r8a66597 *r8a66597, unsigned ccpl) +{ + r8a66597->ep[0].internal_ccpl = ccpl; + pipe_start(r8a66597, 0); + r8a66597_bset(r8a66597, CCPL, DCPCTR); +} + +static void start_ep0_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, ep->pipenum); + r8a66597_mdfy(r8a66597, ISEL, (ISEL | CURPIPE), CFIFOSEL); + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + if (req->req.length == 0) { + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + pipe_start(r8a66597, 0); + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + irq_ep0_write(ep, req); + } +} + +static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum, + u16 fifosel) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE; + if (tmp == pipenum) + r8a66597_change_curpipe(r8a66597, 0, 0, fifosel); +} + +static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum, + int enable) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + u16 tmp, toggle; + + /* check current BFRE bit */ + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE; + if ((enable && tmp) || (!enable && !tmp)) + return; + + /* change BFRE bit */ + pipe_stop(r8a66597, pipenum); + disable_fifosel(r8a66597, pipenum, CFIFOSEL); + disable_fifosel(r8a66597, pipenum, D0FIFOSEL); + disable_fifosel(r8a66597, pipenum, D1FIFOSEL); + + toggle = save_usb_toggle(r8a66597, pipenum); + + r8a66597_write(r8a66597, pipenum, PIPESEL); + if (enable) + r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG); + else + r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG); + + /* initialize for internal BFRE flag */ + r8a66597_bset(r8a66597, ACLRM, ep->pipectr); + r8a66597_bclr(r8a66597, ACLRM, ep->pipectr); + + restore_usb_toggle(r8a66597, pipenum, toggle); +} + +static int sudmac_alloc_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597_dma *dma; + + if (!r8a66597_is_sudmac(r8a66597)) + return -ENODEV; + + /* Check transfer type */ + if (!is_bulk_pipe(ep->pipenum)) + return -EIO; + + if (r8a66597->dma.used) + return -EBUSY; + + /* set SUDMAC parameters */ + dma = &r8a66597->dma; + dma->used = 1; + if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) { + dma->dir = 1; + } else { + dma->dir = 0; + change_bfre_mode(r8a66597, ep->pipenum, 1); + } + + /* set r8a66597_ep paramters */ + ep->use_dma = 1; + ep->dma = dma; + ep->fifoaddr = D0FIFO; + ep->fifosel = D0FIFOSEL; + ep->fifoctr = D0FIFOCTR; + + /* dma mapping */ + return usb_gadget_map_request(&r8a66597->gadget, &req->req, dma->dir); +} + +static void sudmac_free_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + if (!r8a66597_is_sudmac(r8a66597)) + return; + + usb_gadget_unmap_request(&r8a66597->gadget, &req->req, ep->dma->dir); + + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); + r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel); + + ep->dma->used = 0; + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; +} + +static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + BUG_ON(req->req.length == 0); + + r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG); + r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA); + r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC); + r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL); + + r8a66597_sudmac_write(r8a66597, DEN, CH0DEN); +} + +static void start_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 tmp; + + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + + if (req->req.length == 0) { + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS); + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, ep->pipenum); + else + irq_packet_write(ep, req); + } else { + /* DMA mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_nrdy(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + enable_irq_nrdy(r8a66597, ep->pipenum); + sudmac_start(r8a66597, ep, req); + } + } +} + +static void start_packet_read(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 pipenum = ep->pipenum; + + if (ep->pipenum == 0) { + r8a66597_mdfy(r8a66597, 0, (ISEL | CURPIPE), CFIFOSEL); + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + pipe_start(r8a66597, pipenum); + pipe_irq_enable(r8a66597, pipenum); + } else { + pipe_stop(r8a66597, pipenum); + if (ep->pipetre) { + enable_irq_nrdy(r8a66597, pipenum); + r8a66597_write(r8a66597, TRCLR, ep->pipetre); + r8a66597_write(r8a66597, + DIV_ROUND_UP(req->req.length, ep->ep.maxpacket), + ep->pipetrn); + r8a66597_bset(r8a66597, TRENB, ep->pipetre); + } + + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + change_bfre_mode(r8a66597, ep->pipenum, 0); + pipe_start(r8a66597, pipenum); /* trigger once */ + pipe_irq_enable(r8a66597, pipenum); + } else { + pipe_change(r8a66597, pipenum); + sudmac_start(r8a66597, ep, req); + pipe_start(r8a66597, pipenum); /* trigger once */ + } + } +} + +static void start_packet(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) + start_packet_write(ep, req); + else + start_packet_read(ep, req); +} + +static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + u16 ctsq; + + ctsq = r8a66597_read(ep->r8a66597, INTSTS0) & CTSQ; + + switch (ctsq) { + case CS_RDDS: + start_ep0_write(ep, req); + break; + case CS_WRDS: + start_packet_read(ep, req); + break; + + case CS_WRND: + control_end(ep->r8a66597, 0); + break; + default: + dev_err(r8a66597_to_dev(ep->r8a66597), + "start_ep0: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static void init_controller(struct r8a66597 *r8a66597) +{ + u16 vif = r8a66597->pdata->vif ? LDRV : 0; + u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0; + u16 endian = r8a66597->pdata->endian ? BIGEND : 0; + + if (r8a66597->pdata->on_chip) { + if (r8a66597->pdata->buswait) + r8a66597_write(r8a66597, r8a66597->pdata->buswait, + SYSCFG1); + else + r8a66597_write(r8a66597, 0x0f, SYSCFG1); + r8a66597_bset(r8a66597, HSE, SYSCFG0); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + + r8a66597_bset(r8a66597, irq_sense, INTENB1); + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, + DMA0CFG); + } else { + r8a66597_bset(r8a66597, vif | endian, PINCFG); + r8a66597_bset(r8a66597, HSE, SYSCFG0); /* High spd */ + r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), + XTAL, SYSCFG0); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + + msleep(3); + + r8a66597_bset(r8a66597, PLLC, SYSCFG0); + + msleep(1); + + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + + r8a66597_bset(r8a66597, irq_sense, INTENB1); + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, + DMA0CFG); + } +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + if (r8a66597->pdata->on_chip) { + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + r8a66597_bclr(r8a66597, UTST, TESTMODE); + + /* disable interrupts */ + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, BRDYENB); + r8a66597_write(r8a66597, 0, BEMPENB); + r8a66597_write(r8a66597, 0, NRDYENB); + + /* clear status */ + r8a66597_write(r8a66597, 0, BRDYSTS); + r8a66597_write(r8a66597, 0, NRDYSTS); + r8a66597_write(r8a66597, 0, BEMPSTS); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + + } else { + r8a66597_bclr(r8a66597, UTST, TESTMODE); + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + udelay(1); + udelay(1); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + } +} + +static void r8a66597_start_xclock(struct r8a66597 *r8a66597) +{ + u16 tmp; + + if (!r8a66597->pdata->on_chip) { + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (!(tmp & XCKE)) + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + } +} + +static struct r8a66597_request *get_request_from_ep(struct r8a66597_ep *ep) +{ + return list_entry(ep->queue.next, struct r8a66597_request, queue); +} + +/*-------------------------------------------------------------------------*/ +static void transfer_complete(struct r8a66597_ep *ep, + struct r8a66597_request *req, int status) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + int restart = 0; + + if (unlikely(ep->pipenum == 0)) { + if (ep->internal_ccpl) { + ep->internal_ccpl = 0; + return; + } + } + + list_del_init(&req->queue); + if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + if (!list_empty(&ep->queue)) + restart = 1; + + if (ep->use_dma) + sudmac_free_channel(ep->r8a66597, ep, req); + + spin_unlock(&ep->r8a66597->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->r8a66597->lock); + + if (restart) { + req = get_request_from_ep(ep); + if (ep->ep.desc) + start_packet(ep, req); + } +} + +static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + int i; + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, pipenum); + r8a66597_bset(r8a66597, ISEL, ep->fifosel); + + i = 0; + do { + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (i++ > 100000) { + dev_err(r8a66597_to_dev(r8a66597), + "pipe0 is busy. maybe cpu i/o bus " + "conflict. please power off this controller."); + return; + } + ndelay(1); + } while ((tmp & FRDY) == 0); + + /* prepare parameters */ + bufsize = get_buffer_size(r8a66597, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + if (size > 0) + r8a66597_write_fifo(r8a66597, ep, buf, size); + if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(r8a66597, pipenum); + disable_irq_empty(r8a66597, pipenum); + } else { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } + pipe_start(r8a66597, pipenum); +} + +static void irq_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + dev_err(r8a66597_to_dev(r8a66597), + "write fifo not ready. pipnum=%d\n", pipenum); + return; + } + + /* prepare parameters */ + bufsize = get_buffer_size(r8a66597, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + r8a66597_write_fifo(r8a66597, ep, buf, size); + if ((size == 0) + || ((size % ep->ep.maxpacket) != 0) + || ((bufsize != ep->ep.maxpacket) + && (bufsize > size))) + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + disable_irq_empty(r8a66597, pipenum); + pipe_irq_enable(r8a66597, pipenum); + } +} + +static void irq_packet_read(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + u16 tmp; + int rcv_len, bufsize, req_len; + int size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + int finish = 0; + + pipe_change(r8a66597, pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + req->req.status = -EPIPE; + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready"); + return; + } + + /* prepare parameters */ + rcv_len = tmp & DTLN; + bufsize = get_buffer_size(r8a66597, pipenum); + + buf = req->req.buf + req->req.actual; + req_len = req->req.length - req->req.actual; + if (rcv_len < bufsize) + size = min(rcv_len, req_len); + else + size = min(bufsize, req_len); + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + finish = 1; + } + + /* read fifo */ + if (req->req.buf) { + if (size == 0) + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + else + r8a66597_read_fifo(r8a66597, ep->fifoaddr, buf, size); + + } + + if ((ep->pipenum != 0) && finish) + transfer_complete(ep, req, 0); +} + +static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb) +{ + u16 check; + u16 pipenum; + struct r8a66597_ep *ep; + struct r8a66597_request *req; + + if ((status & BRDY0) && (enb & BRDY0)) { + r8a66597_write(r8a66597, ~BRDY0, BRDYSTS); + r8a66597_mdfy(r8a66597, 0, CURPIPE, CFIFOSEL); + + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + irq_packet_read(ep, req); + } else { + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + r8a66597_write(r8a66597, ~check, BRDYSTS); + ep = r8a66597->pipenum2ep[pipenum]; + req = get_request_from_ep(ep); + if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) + irq_packet_write(ep, req); + else + irq_packet_read(ep, req); + } + } + } +} + +static void irq_pipe_empty(struct r8a66597 *r8a66597, u16 status, u16 enb) +{ + u16 tmp; + u16 check; + u16 pipenum; + struct r8a66597_ep *ep; + struct r8a66597_request *req; + + if ((status & BEMP0) && (enb & BEMP0)) { + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + irq_ep0_write(ep, req); + } else { + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + r8a66597_write(r8a66597, ~check, BEMPSTS); + tmp = control_reg_get(r8a66597, pipenum); + if ((tmp & INBUFM) == 0) { + disable_irq_empty(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + pipe_stop(r8a66597, pipenum); + ep = r8a66597->pipenum2ep[pipenum]; + req = get_request_from_ep(ep); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, 0); + } + } + } + } +} + +static void get_status(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + struct r8a66597_ep *ep; + u16 pid; + u16 status = 0; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = r8a66597->device_status; + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pid = control_reg_get_pid(r8a66597, ep->pipenum); + if (pid == PID_STALL) + status = 1 << USB_ENDPOINT_HALT; + else + status = 0; + break; + default: + pipe_stall(r8a66597, 0); + return; /* exit */ + } + + r8a66597->ep0_data = cpu_to_le16(status); + r8a66597->ep0_req->buf = &r8a66597->ep0_data; + r8a66597->ep0_req->length = 2; + /* AV: what happens if we get called again before that gets through? */ + spin_unlock(&r8a66597->lock); + r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_KERNEL); + spin_lock(&r8a66597->lock); +} + +static void clear_feature(struct r8a66597 *r8a66597, + struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(r8a66597, 1); + break; + case USB_RECIP_INTERFACE: + control_end(r8a66597, 1); + break; + case USB_RECIP_ENDPOINT: { + struct r8a66597_ep *ep; + struct r8a66597_request *req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + if (!ep->wedge) { + pipe_stop(r8a66597, ep->pipenum); + control_reg_sqclr(r8a66597, ep->pipenum); + spin_unlock(&r8a66597->lock); + usb_ep_clear_halt(&ep->ep); + spin_lock(&r8a66597->lock); + } + + control_end(r8a66597, 1); + + req = get_request_from_ep(ep); + if (ep->busy) { + ep->busy = 0; + if (list_empty(&ep->queue)) + break; + start_packet(ep, req); + } else if (!list_empty(&ep->queue)) + pipe_start(r8a66597, ep->pipenum); + } + break; + default: + pipe_stall(r8a66597, 0); + break; + } +} + +static void set_feature(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +{ + u16 tmp; + int timeout = 3000; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (le16_to_cpu(ctrl->wValue)) { + case USB_DEVICE_TEST_MODE: + control_end(r8a66597, 1); + /* Wait for the completion of status stage */ + do { + tmp = r8a66597_read(r8a66597, INTSTS0) & CTSQ; + udelay(1); + } while (tmp != CS_IDST || timeout-- > 0); + + if (tmp == CS_IDST) + r8a66597_bset(r8a66597, + le16_to_cpu(ctrl->wIndex >> 8), + TESTMODE); + break; + default: + pipe_stall(r8a66597, 0); + break; + } + break; + case USB_RECIP_INTERFACE: + control_end(r8a66597, 1); + break; + case USB_RECIP_ENDPOINT: { + struct r8a66597_ep *ep; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pipe_stall(r8a66597, ep->pipenum); + + control_end(r8a66597, 1); + } + break; + default: + pipe_stall(r8a66597, 0); + break; + } +} + +/* if return value is true, call class driver's setup() */ +static int setup_packet(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +{ + u16 *p = (u16 *)ctrl; + unsigned long offset = USBREQ; + int i, ret = 0; + + /* read fifo */ + r8a66597_write(r8a66597, ~VALID, INTSTS0); + + for (i = 0; i < 4; i++) + p[i] = r8a66597_read(r8a66597, offset + i*2); + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(r8a66597, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(r8a66597, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(r8a66597, ctrl); + break; + default: + ret = 1; + break; + } + } else + ret = 1; + return ret; +} + +static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597) +{ + u16 speed = get_usb_speed(r8a66597); + + switch (speed) { + case HSMODE: + r8a66597->gadget.speed = USB_SPEED_HIGH; + break; + case FSMODE: + r8a66597->gadget.speed = USB_SPEED_FULL; + break; + default: + r8a66597->gadget.speed = USB_SPEED_UNKNOWN; + dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n"); + } +} + +static void irq_device_state(struct r8a66597 *r8a66597) +{ + u16 dvsq; + + dvsq = r8a66597_read(r8a66597, INTSTS0) & DVSQ; + r8a66597_write(r8a66597, ~DVST, INTSTS0); + + if (dvsq == DS_DFLT) { + /* bus reset */ + spin_unlock(&r8a66597->lock); + r8a66597->driver->disconnect(&r8a66597->gadget); + spin_lock(&r8a66597->lock); + r8a66597_update_usb_speed(r8a66597); + } + if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG) + r8a66597_update_usb_speed(r8a66597); + if ((dvsq == DS_CNFG || dvsq == DS_ADDS) + && r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + r8a66597_update_usb_speed(r8a66597); + + r8a66597->old_dvsq = dvsq; +} + +static void irq_control_stage(struct r8a66597 *r8a66597) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + struct usb_ctrlrequest ctrl; + u16 ctsq; + + ctsq = r8a66597_read(r8a66597, INTSTS0) & CTSQ; + r8a66597_write(r8a66597, ~CTRT, INTSTS0); + + switch (ctsq) { + case CS_IDST: { + struct r8a66597_ep *ep; + struct r8a66597_request *req; + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + transfer_complete(ep, req, 0); + } + break; + + case CS_RDDS: + case CS_WRDS: + case CS_WRND: + if (setup_packet(r8a66597, &ctrl)) { + spin_unlock(&r8a66597->lock); + if (r8a66597->driver->setup(&r8a66597->gadget, &ctrl) + < 0) + pipe_stall(r8a66597, 0); + spin_lock(&r8a66597->lock); + } + break; + case CS_RDSS: + case CS_WRSS: + control_end(r8a66597, 0); + break; + default: + dev_err(r8a66597_to_dev(r8a66597), + "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep) +{ + u16 pipenum; + struct r8a66597_request *req; + u32 len; + int i = 0; + + pipenum = ep->pipenum; + pipe_change(r8a66597, pipenum); + + while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) { + udelay(1); + if (unlikely(i++ >= 10000)) { /* timeout = 10 msec */ + dev_err(r8a66597_to_dev(r8a66597), + "%s: FRDY was not set (%d)\n", + __func__, pipenum); + return; + } + } + + r8a66597_bset(r8a66597, BCLR, ep->fifoctr); + req = get_request_from_ep(ep); + + /* prepare parameters */ + len = r8a66597_sudmac_read(r8a66597, CH0CBC); + req->req.actual += len; + + /* clear */ + r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR); + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (len % ep->ep.maxpacket)) { + if (ep->dma->dir) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + /* Clear the interrupt flag for next transfer */ + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + transfer_complete(ep, req, 0); + } + } +} + +static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597) +{ + u32 irqsts; + struct r8a66597_ep *ep; + u16 pipenum; + + irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS); + if (irqsts & CH0ENDS) { + r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR); + pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE); + ep = r8a66597->pipenum2ep[pipenum]; + sudmac_finish(r8a66597, ep); + } +} + +static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) +{ + struct r8a66597 *r8a66597 = _r8a66597; + u16 intsts0; + u16 intenb0; + u16 brdysts, nrdysts, bempsts; + u16 brdyenb, nrdyenb, bempenb; + u16 savepipe; + u16 mask0; + + spin_lock(&r8a66597->lock); + + if (r8a66597_is_sudmac(r8a66597)) + r8a66597_sudmac_irq(r8a66597); + + intsts0 = r8a66597_read(r8a66597, INTSTS0); + intenb0 = r8a66597_read(r8a66597, INTENB0); + + savepipe = r8a66597_read(r8a66597, CFIFOSEL); + + mask0 = intsts0 & intenb0; + if (mask0) { + brdysts = r8a66597_read(r8a66597, BRDYSTS); + nrdysts = r8a66597_read(r8a66597, NRDYSTS); + bempsts = r8a66597_read(r8a66597, BEMPSTS); + brdyenb = r8a66597_read(r8a66597, BRDYENB); + nrdyenb = r8a66597_read(r8a66597, NRDYENB); + bempenb = r8a66597_read(r8a66597, BEMPENB); + + if (mask0 & VBINT) { + r8a66597_write(r8a66597, 0xffff & ~VBINT, + INTSTS0); + r8a66597_start_xclock(r8a66597); + + /* start vbus sampling */ + r8a66597->old_vbus = r8a66597_read(r8a66597, INTSTS0) + & VBSTS; + r8a66597->scount = R8A66597_MAX_SAMPLING; + + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + if (intsts0 & DVSQ) + irq_device_state(r8a66597); + + if ((intsts0 & BRDY) && (intenb0 & BRDYE) + && (brdysts & brdyenb)) + irq_pipe_ready(r8a66597, brdysts, brdyenb); + if ((intsts0 & BEMP) && (intenb0 & BEMPE) + && (bempsts & bempenb)) + irq_pipe_empty(r8a66597, bempsts, bempenb); + + if (intsts0 & CTRT) + irq_control_stage(r8a66597); + } + + r8a66597_write(r8a66597, savepipe, CFIFOSEL); + + spin_unlock(&r8a66597->lock); + return IRQ_HANDLED; +} + +static void r8a66597_timer(unsigned long _r8a66597) +{ + struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; + unsigned long flags; + u16 tmp; + + spin_lock_irqsave(&r8a66597->lock, flags); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (r8a66597->scount > 0) { + tmp = r8a66597_read(r8a66597, INTSTS0) & VBSTS; + if (tmp == r8a66597->old_vbus) { + r8a66597->scount--; + if (r8a66597->scount == 0) { + if (tmp == VBSTS) + r8a66597_usb_connect(r8a66597); + else + r8a66597_usb_disconnect(r8a66597); + } else { + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + } else { + r8a66597->scount = R8A66597_MAX_SAMPLING; + r8a66597->old_vbus = tmp; + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + } + spin_unlock_irqrestore(&r8a66597->lock, flags); +} + +/*-------------------------------------------------------------------------*/ +static int r8a66597_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct r8a66597_ep *ep; + + ep = container_of(_ep, struct r8a66597_ep, ep); + return alloc_pipe_config(ep, desc); +} + +static int r8a66597_disable(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = get_request_from_ep(ep); + spin_lock_irqsave(&ep->r8a66597->lock, flags); + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + } + + pipe_irq_disable(ep->r8a66597, ep->pipenum); + return free_pipe_config(ep); +} + +static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct r8a66597_request *req; + + req = kzalloc(sizeof(struct r8a66597_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void r8a66597_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct r8a66597_request *req; + + req = container_of(_req, struct r8a66597_request, req); + kfree(req); +} + +static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = container_of(_req, struct r8a66597_request, req); + + if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->r8a66597->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->ep.desc == NULL) /* control */ + start_ep0(ep, req); + else { + if (request && !ep->busy) + start_packet(ep, req); + } + + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return 0; +} + +static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = container_of(_req, struct r8a66597_request, req); + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return 0; +} + +static int r8a66597_set_halt(struct usb_ep *_ep, int value) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = get_request_from_ep(ep); + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + if (value) { + ep->busy = 1; + pipe_stall(ep->r8a66597, ep->pipenum); + } else { + ep->busy = 0; + ep->wedge = 0; + pipe_stop(ep->r8a66597, ep->pipenum); + } + +out: + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + return ret; +} + +static int r8a66597_set_wedge(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + + if (!ep || !ep->ep.desc) + return -EINVAL; + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + ep->wedge = 1; + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return usb_ep_set_halt(_ep); +} + +static void r8a66597_fifo_flush(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (list_empty(&ep->queue) && !ep->busy) { + pipe_stop(ep->r8a66597, ep->pipenum); + r8a66597_bclr(ep->r8a66597, BCLR, ep->fifoctr); + r8a66597_write(ep->r8a66597, ACLRM, ep->pipectr); + r8a66597_write(ep->r8a66597, 0, ep->pipectr); + } + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); +} + +static struct usb_ep_ops r8a66597_ep_ops = { + .enable = r8a66597_enable, + .disable = r8a66597_disable, + + .alloc_request = r8a66597_alloc_request, + .free_request = r8a66597_free_request, + + .queue = r8a66597_queue, + .dequeue = r8a66597_dequeue, + + .set_halt = r8a66597_set_halt, + .set_wedge = r8a66597_set_wedge, + .fifo_flush = r8a66597_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ +static int r8a66597_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + + if (!driver + || driver->max_speed < USB_SPEED_HIGH + || !driver->setup) + return -EINVAL; + if (!r8a66597) + return -ENODEV; + + /* hook up the driver */ + r8a66597->driver = driver; + + init_controller(r8a66597); + r8a66597_bset(r8a66597, VBSE, INTENB0); + if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) { + r8a66597_start_xclock(r8a66597); + /* start vbus sampling */ + r8a66597->old_vbus = r8a66597_read(r8a66597, + INTSTS0) & VBSTS; + r8a66597->scount = R8A66597_MAX_SAMPLING; + mod_timer(&r8a66597->timer, jiffies + msecs_to_jiffies(50)); + } + + return 0; +} + +static int r8a66597_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + r8a66597_bclr(r8a66597, VBSE, INTENB0); + disable_controller(r8a66597); + spin_unlock_irqrestore(&r8a66597->lock, flags); + + r8a66597->driver = NULL; + return 0; +} + +/*-------------------------------------------------------------------------*/ +static int r8a66597_get_frame(struct usb_gadget *_gadget) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(_gadget); + return r8a66597_read(r8a66597, FRMNUM) & 0x03FF; +} + +static int r8a66597_pullup(struct usb_gadget *gadget, int is_on) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + if (is_on) + r8a66597_bset(r8a66597, DPRPU, SYSCFG0); + else + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + spin_unlock_irqrestore(&r8a66597->lock, flags); + + return 0; +} + +static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + + if (is_self) + r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED; + else + r8a66597->device_status &= ~(1 << USB_DEVICE_SELF_POWERED); + + return 0; +} + +static const struct usb_gadget_ops r8a66597_gadget_ops = { + .get_frame = r8a66597_get_frame, + .udc_start = r8a66597_start, + .udc_stop = r8a66597_stop, + .pullup = r8a66597_pullup, + .set_selfpowered = r8a66597_set_selfpowered, +}; + +static int __exit r8a66597_remove(struct platform_device *pdev) +{ + struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&r8a66597->gadget); + del_timer_sync(&r8a66597->timer); + iounmap(r8a66597->reg); + if (r8a66597->pdata->sudmac) + iounmap(r8a66597->sudmac_reg); + free_irq(platform_get_irq(pdev, 0), r8a66597); + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); + + if (r8a66597->pdata->on_chip) { + clk_disable_unprepare(r8a66597->clk); + clk_put(r8a66597->clk); + } + + kfree(r8a66597); + return 0; +} + +static void nop_completion(struct usb_ep *ep, struct usb_request *r) +{ +} + +static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, + struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); + if (!res) { + dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); + return -ENODEV; + } + + r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); + if (r8a66597->sudmac_reg == NULL) { + dev_err(&pdev->dev, "ioremap error(sudmac).\n"); + return -ENOMEM; + } + + return 0; +} + +static int __init r8a66597_probe(struct platform_device *pdev) +{ + char clk_name[8]; + struct resource *res, *ires; + int irq; + void __iomem *reg = NULL; + struct r8a66597 *r8a66597 = NULL; + int ret = 0; + int i; + unsigned long irq_trigger; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "platform_get_resource error.\n"); + goto clean_up; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + irq = ires->start; + irq_trigger = ires->flags & IRQF_TRIGGER_MASK; + + if (irq < 0) { + ret = -ENODEV; + dev_err(&pdev->dev, "platform_get_irq error.\n"); + goto clean_up; + } + + reg = ioremap(res->start, resource_size(res)); + if (reg == NULL) { + ret = -ENOMEM; + dev_err(&pdev->dev, "ioremap error.\n"); + goto clean_up; + } + + /* initialize ucd */ + r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); + if (r8a66597 == NULL) { + ret = -ENOMEM; + goto clean_up; + } + + spin_lock_init(&r8a66597->lock); + 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; + r8a66597->gadget.max_speed = USB_SPEED_HIGH; + r8a66597->gadget.name = udc_name; + + init_timer(&r8a66597->timer); + r8a66597->timer.function = r8a66597_timer; + r8a66597->timer.data = (unsigned long)r8a66597; + r8a66597->reg = reg; + + if (r8a66597->pdata->on_chip) { + snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); + r8a66597->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(r8a66597->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", + clk_name); + ret = PTR_ERR(r8a66597->clk); + goto clean_up; + } + clk_prepare_enable(r8a66597->clk); + } + + if (r8a66597->pdata->sudmac) { + ret = r8a66597_sudmac_ioremap(r8a66597, pdev); + if (ret < 0) + goto clean_up2; + } + + disable_controller(r8a66597); /* make sure controller is disabled */ + + ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, + udc_name, r8a66597); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq error (%d)\n", ret); + goto clean_up2; + } + + INIT_LIST_HEAD(&r8a66597->gadget.ep_list); + r8a66597->gadget.ep0 = &r8a66597->ep[0].ep; + INIT_LIST_HEAD(&r8a66597->gadget.ep0->ep_list); + for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) { + struct r8a66597_ep *ep = &r8a66597->ep[i]; + + if (i != 0) { + INIT_LIST_HEAD(&r8a66597->ep[i].ep.ep_list); + list_add_tail(&r8a66597->ep[i].ep.ep_list, + &r8a66597->gadget.ep_list); + } + ep->r8a66597 = r8a66597; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = r8a66597_ep_name[i]; + ep->ep.ops = &r8a66597_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, 512); + } + 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; + r8a66597->ep[0].fifoctr = CFIFOCTR; + r8a66597->ep[0].pipectr = get_pipectr_addr(0); + r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; + r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; + + r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, + GFP_KERNEL); + 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); + if (ret) + goto err_add_udc; + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + return 0; + +err_add_udc: + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); +clean_up3: + free_irq(irq, r8a66597); +clean_up2: + if (r8a66597->pdata->on_chip) { + clk_disable_unprepare(r8a66597->clk); + clk_put(r8a66597->clk); + } +clean_up: + if (r8a66597) { + if (r8a66597->sudmac_reg) + iounmap(r8a66597->sudmac_reg); + if (r8a66597->ep0_req) + r8a66597_free_request(&r8a66597->ep[0].ep, + r8a66597->ep0_req); + kfree(r8a66597); + } + if (reg) + iounmap(reg); + + return ret; +} + +/*-------------------------------------------------------------------------*/ +static struct platform_driver r8a66597_driver = { + .remove = __exit_p(r8a66597_remove), + .driver = { + .name = (char *) udc_name, + }, +}; + +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/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h new file mode 100644 index 00000000000..45c4b2df178 --- /dev/null +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -0,0 +1,290 @@ +/* + * R8A66597 UDC + * + * Copyright (C) 2007-2009 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.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. + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#include <linux/clk.h> +#include <linux/usb/r8a66597.h> + +#define R8A66597_MAX_SAMPLING 10 + +#define R8A66597_MAX_NUM_PIPE 8 +#define R8A66597_MAX_NUM_BULK 3 +#define R8A66597_MAX_NUM_ISOC 2 +#define R8A66597_MAX_NUM_INT 2 + +#define R8A66597_BASE_PIPENUM_BULK 3 +#define R8A66597_BASE_PIPENUM_ISOC 1 +#define R8A66597_BASE_PIPENUM_INT 6 + +#define R8A66597_BASE_BUFNUM 6 +#define R8A66597_MAX_BUFNUM 0x4F + +#define is_bulk_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_BULK) && \ + (pipenum < (R8A66597_BASE_PIPENUM_BULK + R8A66597_MAX_NUM_BULK))) +#define is_interrupt_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_INT) && \ + (pipenum < (R8A66597_BASE_PIPENUM_INT + R8A66597_MAX_NUM_INT))) +#define is_isoc_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \ + (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) + +#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac) +struct r8a66597_pipe_info { + u16 pipe; + u16 epnum; + u16 maxpacket; + u16 type; + u16 interval; + u16 dir_in; +}; + +struct r8a66597_request { + struct usb_request req; + struct list_head queue; +}; + +struct r8a66597_ep { + struct usb_ep ep; + struct r8a66597 *r8a66597; + struct r8a66597_dma *dma; + + struct list_head queue; + unsigned busy:1; + unsigned wedge:1; + unsigned internal_ccpl:1; /* use only control */ + + /* this member can able to after r8a66597_enable */ + unsigned use_dma:1; + u16 pipenum; + u16 type; + + /* register address */ + unsigned char fifoaddr; + unsigned char fifosel; + unsigned char fifoctr; + unsigned char pipectr; + unsigned char pipetre; + unsigned char pipetrn; +}; + +struct r8a66597_dma { + unsigned used:1; + unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */ +}; + +struct r8a66597 { + spinlock_t lock; + void __iomem *reg; + void __iomem *sudmac_reg; + + struct clk *clk; + struct r8a66597_platdata *pdata; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE]; + struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE]; + struct r8a66597_ep *epaddr2ep[16]; + struct r8a66597_dma dma; + + struct timer_list timer; + struct usb_request *ep0_req; /* for internal request */ + u16 ep0_data; /* for internal request */ + u16 old_vbus; + u16 scount; + u16 old_dvsq; + u16 device_status; /* for GET_STATUS */ + + /* pipe config */ + unsigned char bulk; + unsigned char interrupt; + unsigned char isochronous; + unsigned char num_dma; + + unsigned irq_sense_low:1; +}; + +#define gadget_to_r8a66597(_gadget) \ + container_of(_gadget, struct r8a66597, gadget) +#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget) +#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent) + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return ioread16(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, + unsigned char *buf, + int len) +{ + void __iomem *fifoaddr = r8a66597->reg + offset; + unsigned int data = 0; + int i; + + if (r8a66597->pdata->on_chip) { + /* 32-bit accesses for on_chip controllers */ + + /* aligned buf case */ + if (len >= 4 && !((unsigned long)buf & 0x03)) { + ioread32_rep(fifoaddr, buf, len / 4); + buf += len & ~0x03; + len &= 0x03; + } + + /* unaligned buf case */ + for (i = 0; i < len; i++) { + if (!(i & 0x03)) + data = ioread32(fifoaddr); + + buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; + } + } else { + /* 16-bit accesses for external controllers */ + + /* aligned buf case */ + if (len >= 2 && !((unsigned long)buf & 0x01)) { + ioread16_rep(fifoaddr, buf, len / 2); + buf += len & ~0x01; + len &= 0x01; + } + + /* unaligned buf case */ + for (i = 0; i < len; i++) { + if (!(i & 0x01)) + data = ioread16(fifoaddr); + + buf[i] = (data >> ((i & 0x01) * 8)) & 0xff; + } + } +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + iowrite16(val, r8a66597->reg + offset); +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + unsigned char *buf, + int len) +{ + void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr; + int adj = 0; + int i; + + if (r8a66597->pdata->on_chip) { + /* 32-bit access only if buf is 32-bit aligned */ + if (len >= 4 && !((unsigned long)buf & 0x03)) { + iowrite32_rep(fifoaddr, buf, len / 4); + buf += len & ~0x03; + len &= 0x03; + } + } else { + /* 16-bit access only if buf is 16-bit aligned */ + if (len >= 2 && !((unsigned long)buf & 0x01)) { + iowrite16_rep(fifoaddr, buf, len / 2); + buf += len & ~0x01; + len &= 0x01; + } + } + + /* adjust fifo address in the little endian case */ + if (!(r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)) { + if (r8a66597->pdata->on_chip) + adj = 0x03; /* 32-bit wide */ + else + adj = 0x01; /* 16-bit wide */ + } + + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); + for (i = 0; i < len; i++) + iowrite8(buf[i], fifoaddr + adj - (i & adj)); + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); +} + +static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) +{ + u16 clock = 0; + + switch (pdata->xtal) { + case R8A66597_PLATDATA_XTAL_12MHZ: + clock = XTAL12; + break; + case R8A66597_PLATDATA_XTAL_24MHZ: + clock = XTAL24; + break; + case R8A66597_PLATDATA_XTAL_48MHZ: + clock = XTAL48; + break; + default: + printk(KERN_ERR "r8a66597: platdata clock is wrong.\n"); + break; + } + + return clock; +} + +static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597, + unsigned long offset) +{ + return ioread32(r8a66597->sudmac_reg + offset); +} + +static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val, + unsigned long offset) +{ + iowrite32(val, r8a66597->sudmac_reg + offset); +} + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) + +#define enable_irq_ready(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define disable_irq_ready(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define enable_irq_empty(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define disable_irq_empty(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define enable_irq_nrdy(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, NRDYENB) +#define disable_irq_nrdy(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, NRDYENB) + +#endif /* __R8A66597_H__ */ + diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 2b4660e08c4..95d2324f697 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -25,17 +25,17 @@ #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> #include <linux/seq_file.h> #include <linux/netdevice.h> #include <asm/io.h> #include <asm/byteorder.h> -#include <asm/system.h> #include <asm/unaligned.h> +#include "u_rndis.h" #undef VERBOSE_DEBUG @@ -60,78 +60,78 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define RNDIS_MAX_CONFIGS 1 -static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; +static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; /* Driver Version */ -static const __le32 rndis_driver_version = cpu_to_le32 (1); +static const __le32 rndis_driver_version = cpu_to_le32(1); /* Function Prototypes */ -static rndis_resp_t *rndis_add_response (int configNr, u32 length); +static rndis_resp_t *rndis_add_response(int configNr, u32 length); /* supported OIDs */ -static const u32 oid_supported_list [] = +static const u32 oid_supported_list[] = { /* the general stuff */ - OID_GEN_SUPPORTED_LIST, - OID_GEN_HARDWARE_STATUS, - OID_GEN_MEDIA_SUPPORTED, - OID_GEN_MEDIA_IN_USE, - OID_GEN_MAXIMUM_FRAME_SIZE, - OID_GEN_LINK_SPEED, - OID_GEN_TRANSMIT_BLOCK_SIZE, - OID_GEN_RECEIVE_BLOCK_SIZE, - OID_GEN_VENDOR_ID, - OID_GEN_VENDOR_DESCRIPTION, - OID_GEN_VENDOR_DRIVER_VERSION, - OID_GEN_CURRENT_PACKET_FILTER, - OID_GEN_MAXIMUM_TOTAL_SIZE, - OID_GEN_MEDIA_CONNECT_STATUS, - OID_GEN_PHYSICAL_MEDIUM, + RNDIS_OID_GEN_SUPPORTED_LIST, + RNDIS_OID_GEN_HARDWARE_STATUS, + RNDIS_OID_GEN_MEDIA_SUPPORTED, + RNDIS_OID_GEN_MEDIA_IN_USE, + RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, + RNDIS_OID_GEN_LINK_SPEED, + RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE, + RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE, + RNDIS_OID_GEN_VENDOR_ID, + RNDIS_OID_GEN_VENDOR_DESCRIPTION, + RNDIS_OID_GEN_VENDOR_DRIVER_VERSION, + RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE, + RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + RNDIS_OID_GEN_PHYSICAL_MEDIUM, /* the statistical stuff */ - OID_GEN_XMIT_OK, - OID_GEN_RCV_OK, - OID_GEN_XMIT_ERROR, - OID_GEN_RCV_ERROR, - OID_GEN_RCV_NO_BUFFER, + RNDIS_OID_GEN_XMIT_OK, + RNDIS_OID_GEN_RCV_OK, + RNDIS_OID_GEN_XMIT_ERROR, + RNDIS_OID_GEN_RCV_ERROR, + RNDIS_OID_GEN_RCV_NO_BUFFER, #ifdef RNDIS_OPTIONAL_STATS - OID_GEN_DIRECTED_BYTES_XMIT, - OID_GEN_DIRECTED_FRAMES_XMIT, - OID_GEN_MULTICAST_BYTES_XMIT, - OID_GEN_MULTICAST_FRAMES_XMIT, - OID_GEN_BROADCAST_BYTES_XMIT, - OID_GEN_BROADCAST_FRAMES_XMIT, - OID_GEN_DIRECTED_BYTES_RCV, - OID_GEN_DIRECTED_FRAMES_RCV, - OID_GEN_MULTICAST_BYTES_RCV, - OID_GEN_MULTICAST_FRAMES_RCV, - OID_GEN_BROADCAST_BYTES_RCV, - OID_GEN_BROADCAST_FRAMES_RCV, - OID_GEN_RCV_CRC_ERROR, - OID_GEN_TRANSMIT_QUEUE_LENGTH, + RNDIS_OID_GEN_DIRECTED_BYTES_XMIT, + RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT, + RNDIS_OID_GEN_MULTICAST_BYTES_XMIT, + RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT, + RNDIS_OID_GEN_BROADCAST_BYTES_XMIT, + RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT, + RNDIS_OID_GEN_DIRECTED_BYTES_RCV, + RNDIS_OID_GEN_DIRECTED_FRAMES_RCV, + RNDIS_OID_GEN_MULTICAST_BYTES_RCV, + RNDIS_OID_GEN_MULTICAST_FRAMES_RCV, + RNDIS_OID_GEN_BROADCAST_BYTES_RCV, + RNDIS_OID_GEN_BROADCAST_FRAMES_RCV, + RNDIS_OID_GEN_RCV_CRC_ERROR, + RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH, #endif /* RNDIS_OPTIONAL_STATS */ /* mandatory 802.3 */ /* the general stuff */ - OID_802_3_PERMANENT_ADDRESS, - OID_802_3_CURRENT_ADDRESS, - OID_802_3_MULTICAST_LIST, - OID_802_3_MAC_OPTIONS, - OID_802_3_MAXIMUM_LIST_SIZE, + RNDIS_OID_802_3_PERMANENT_ADDRESS, + RNDIS_OID_802_3_CURRENT_ADDRESS, + RNDIS_OID_802_3_MULTICAST_LIST, + RNDIS_OID_802_3_MAC_OPTIONS, + RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, /* the statistical stuff */ - OID_802_3_RCV_ERROR_ALIGNMENT, - OID_802_3_XMIT_ONE_COLLISION, - OID_802_3_XMIT_MORE_COLLISIONS, + RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT, + RNDIS_OID_802_3_XMIT_ONE_COLLISION, + RNDIS_OID_802_3_XMIT_MORE_COLLISIONS, #ifdef RNDIS_OPTIONAL_STATS - OID_802_3_XMIT_DEFERRED, - OID_802_3_XMIT_MAX_COLLISIONS, - OID_802_3_RCV_OVERRUN, - OID_802_3_XMIT_UNDERRUN, - OID_802_3_XMIT_HEARTBEAT_FAILURE, - OID_802_3_XMIT_TIMES_CRS_LOST, - OID_802_3_XMIT_LATE_COLLISIONS, + RNDIS_OID_802_3_XMIT_DEFERRED, + RNDIS_OID_802_3_XMIT_MAX_COLLISIONS, + RNDIS_OID_802_3_RCV_OVERRUN, + RNDIS_OID_802_3_XMIT_UNDERRUN, + RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE, + RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST, + RNDIS_OID_802_3_XMIT_LATE_COLLISIONS, #endif /* RNDIS_OPTIONAL_STATS */ #ifdef RNDIS_PM @@ -160,20 +160,20 @@ static const u32 oid_supported_list [] = /* NDIS Functions */ -static int -gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, - rndis_resp_t *r) +static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, + unsigned buf_len, rndis_resp_t *r) { - int retval = -ENOTSUPP; - u32 length = 4; /* usually */ - __le32 *outbuf; - int i, count; - rndis_query_cmplt_type *resp; - struct net_device *net; - const struct net_device_stats *stats; + int retval = -ENOTSUPP; + u32 length = 4; /* usually */ + __le32 *outbuf; + int i, count; + rndis_query_cmplt_type *resp; + struct net_device *net; + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats; if (!r) return -ENOMEM; - resp = (rndis_query_cmplt_type *) r->buf; + resp = (rndis_query_cmplt_type *)r->buf; if (!resp) return -ENOMEM; @@ -189,147 +189,153 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, } /* response goes here, right after the header */ - outbuf = (__le32 *) &resp[1]; - resp->InformationBufferOffset = cpu_to_le32 (16); + outbuf = (__le32 *)&resp[1]; + resp->InformationBufferOffset = cpu_to_le32(16); net = rndis_per_dev_params[configNr].dev; - stats = dev_get_stats(net); + stats = dev_get_stats(net, &temp); switch (OID) { /* general oids (table 4-1) */ /* mandatory */ - case OID_GEN_SUPPORTED_LIST: - pr_debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__); - length = sizeof (oid_supported_list); - count = length / sizeof (u32); + case RNDIS_OID_GEN_SUPPORTED_LIST: + pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__); + length = sizeof(oid_supported_list); + count = length / sizeof(u32); for (i = 0; i < count; i++) - outbuf[i] = cpu_to_le32 (oid_supported_list[i]); + outbuf[i] = cpu_to_le32(oid_supported_list[i]); retval = 0; break; /* mandatory */ - case OID_GEN_HARDWARE_STATUS: - pr_debug("%s: OID_GEN_HARDWARE_STATUS\n", __func__); + case RNDIS_OID_GEN_HARDWARE_STATUS: + pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__); /* Bogus question! * Hardware must be ready to receive high level protocols. * BTW: * reddite ergo quae sunt Caesaris Caesari * et quae sunt Dei Deo! */ - *outbuf = cpu_to_le32 (0); + *outbuf = cpu_to_le32(0); retval = 0; break; /* mandatory */ - case OID_GEN_MEDIA_SUPPORTED: - pr_debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__); - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); + case RNDIS_OID_GEN_MEDIA_SUPPORTED: + pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__); + *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); retval = 0; break; /* mandatory */ - case OID_GEN_MEDIA_IN_USE: - pr_debug("%s: OID_GEN_MEDIA_IN_USE\n", __func__); + case RNDIS_OID_GEN_MEDIA_IN_USE: + pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__); /* one medium, one transport... (maybe you do it better) */ - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); + *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); retval = 0; break; /* mandatory */ - case OID_GEN_MAXIMUM_FRAME_SIZE: - pr_debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); - if (rndis_per_dev_params [configNr].dev) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].dev->mtu); + case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE: + pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); + if (rndis_per_dev_params[configNr].dev) { + *outbuf = cpu_to_le32( + rndis_per_dev_params[configNr].dev->mtu); retval = 0; } break; /* mandatory */ - case OID_GEN_LINK_SPEED: + case RNDIS_OID_GEN_LINK_SPEED: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_LINK_SPEED\n", __func__); - if (rndis_per_dev_params [configNr].media_state - == NDIS_MEDIA_STATE_DISCONNECTED) - *outbuf = cpu_to_le32 (0); + pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__); + if (rndis_per_dev_params[configNr].media_state + == RNDIS_MEDIA_STATE_DISCONNECTED) + *outbuf = cpu_to_le32(0); else - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].speed); + *outbuf = cpu_to_le32( + rndis_per_dev_params[configNr].speed); retval = 0; break; /* mandatory */ - case OID_GEN_TRANSMIT_BLOCK_SIZE: - pr_debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params [configNr].dev) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].dev->mtu); + case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE: + pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); + if (rndis_per_dev_params[configNr].dev) { + *outbuf = cpu_to_le32( + rndis_per_dev_params[configNr].dev->mtu); retval = 0; } break; /* mandatory */ - case OID_GEN_RECEIVE_BLOCK_SIZE: - pr_debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params [configNr].dev) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].dev->mtu); + case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE: + pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); + if (rndis_per_dev_params[configNr].dev) { + *outbuf = cpu_to_le32( + rndis_per_dev_params[configNr].dev->mtu); retval = 0; } break; /* mandatory */ - case OID_GEN_VENDOR_ID: - pr_debug("%s: OID_GEN_VENDOR_ID\n", __func__); - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].vendorID); + case RNDIS_OID_GEN_VENDOR_ID: + pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__); + *outbuf = cpu_to_le32( + rndis_per_dev_params[configNr].vendorID); retval = 0; break; /* mandatory */ - case OID_GEN_VENDOR_DESCRIPTION: - pr_debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__); - length = strlen (rndis_per_dev_params [configNr].vendorDescr); - memcpy (outbuf, - rndis_per_dev_params [configNr].vendorDescr, length); + case RNDIS_OID_GEN_VENDOR_DESCRIPTION: + pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__); + if (rndis_per_dev_params[configNr].vendorDescr) { + length = strlen(rndis_per_dev_params[configNr]. + vendorDescr); + memcpy(outbuf, + rndis_per_dev_params[configNr].vendorDescr, + length); + } else { + outbuf[0] = 0; + } retval = 0; break; - case OID_GEN_VENDOR_DRIVER_VERSION: - pr_debug("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__); + case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION: + pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__); /* Created as LE */ *outbuf = rndis_driver_version; retval = 0; break; /* mandatory */ - case OID_GEN_CURRENT_PACKET_FILTER: - pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__); - *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter); + case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: + pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__); + *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter); retval = 0; break; /* mandatory */ - case OID_GEN_MAXIMUM_TOTAL_SIZE: - pr_debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); + case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE: + pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); retval = 0; break; /* mandatory */ - case OID_GEN_MEDIA_CONNECT_STATUS: + case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] + pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); + *outbuf = cpu_to_le32(rndis_per_dev_params[configNr] .media_state); retval = 0; break; - case OID_GEN_PHYSICAL_MEDIUM: - pr_debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__); - *outbuf = cpu_to_le32 (0); + case RNDIS_OID_GEN_PHYSICAL_MEDIUM: + pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__); + *outbuf = cpu_to_le32(0); retval = 0; break; @@ -337,20 +343,20 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, * of MS-Windows expect OIDs that aren't specified there. Other * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! */ - case OID_GEN_MAC_OPTIONS: /* from WinME */ - pr_debug("%s: OID_GEN_MAC_OPTIONS\n", __func__); + case RNDIS_OID_GEN_MAC_OPTIONS: /* from WinME */ + pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__); *outbuf = cpu_to_le32( - NDIS_MAC_OPTION_RECEIVE_SERIALIZED - | NDIS_MAC_OPTION_FULL_DUPLEX); + RNDIS_MAC_OPTION_RECEIVE_SERIALIZED + | RNDIS_MAC_OPTION_FULL_DUPLEX); retval = 0; break; /* statistics OIDs (table 4-2) */ /* mandatory */ - case OID_GEN_XMIT_OK: + case RNDIS_OID_GEN_XMIT_OK: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_XMIT_OK\n", __func__); + pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->tx_packets - stats->tx_errors - stats->tx_dropped); @@ -359,9 +365,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_GEN_RCV_OK: + case RNDIS_OID_GEN_RCV_OK: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_RCV_OK\n", __func__); + pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->rx_packets - stats->rx_errors - stats->rx_dropped); @@ -370,9 +376,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_GEN_XMIT_ERROR: + case RNDIS_OID_GEN_XMIT_ERROR: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_XMIT_ERROR\n", __func__); + pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->tx_errors); retval = 0; @@ -380,9 +386,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_GEN_RCV_ERROR: + case RNDIS_OID_GEN_RCV_ERROR: if (rndis_debug > 1) - pr_debug("%s: OID_GEN_RCV_ERROR\n", __func__); + pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->rx_errors); retval = 0; @@ -390,8 +396,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_GEN_RCV_NO_BUFFER: - pr_debug("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); + case RNDIS_OID_GEN_RCV_NO_BUFFER: + pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->rx_dropped); retval = 0; @@ -401,23 +407,23 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* ieee802.3 OIDs (table 4-3) */ /* mandatory */ - case OID_802_3_PERMANENT_ADDRESS: - pr_debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__); - if (rndis_per_dev_params [configNr].dev) { + case RNDIS_OID_802_3_PERMANENT_ADDRESS: + pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__); + if (rndis_per_dev_params[configNr].dev) { length = ETH_ALEN; - memcpy (outbuf, - rndis_per_dev_params [configNr].host_mac, + memcpy(outbuf, + rndis_per_dev_params[configNr].host_mac, length); retval = 0; } break; /* mandatory */ - case OID_802_3_CURRENT_ADDRESS: - pr_debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__); - if (rndis_per_dev_params [configNr].dev) { + case RNDIS_OID_802_3_CURRENT_ADDRESS: + pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__); + if (rndis_per_dev_params[configNr].dev) { length = ETH_ALEN; - memcpy (outbuf, + memcpy(outbuf, rndis_per_dev_params [configNr].host_mac, length); retval = 0; @@ -425,30 +431,32 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_802_3_MULTICAST_LIST: - pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__); + case RNDIS_OID_802_3_MULTICAST_LIST: + pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__); /* Multicast base address only */ - *outbuf = cpu_to_le32 (0xE0000000); + *outbuf = cpu_to_le32(0xE0000000); retval = 0; break; /* mandatory */ - case OID_802_3_MAXIMUM_LIST_SIZE: - pr_debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); + case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE: + pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); /* Multicast base address only */ - *outbuf = cpu_to_le32 (1); + *outbuf = cpu_to_le32(1); retval = 0; break; - case OID_802_3_MAC_OPTIONS: - pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__); + case RNDIS_OID_802_3_MAC_OPTIONS: + pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__); + *outbuf = cpu_to_le32(0); + retval = 0; break; /* ieee802.3 statistics OIDs (table 4-4) */ /* mandatory */ - case OID_802_3_RCV_ERROR_ALIGNMENT: - pr_debug("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); + case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT: + pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); if (stats) { *outbuf = cpu_to_le32(stats->rx_frame_errors); retval = 0; @@ -456,16 +464,16 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; /* mandatory */ - case OID_802_3_XMIT_ONE_COLLISION: - pr_debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__); - *outbuf = cpu_to_le32 (0); + case RNDIS_OID_802_3_XMIT_ONE_COLLISION: + pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__); + *outbuf = cpu_to_le32(0); retval = 0; break; /* mandatory */ - case OID_802_3_XMIT_MORE_COLLISIONS: - pr_debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); - *outbuf = cpu_to_le32 (0); + case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS: + pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); + *outbuf = cpu_to_le32(0); retval = 0; break; @@ -476,22 +484,22 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, if (retval < 0) length = 0; - resp->InformationBufferLength = cpu_to_le32 (length); - r->length = length + sizeof *resp; - resp->MessageLength = cpu_to_le32 (r->length); + resp->InformationBufferLength = cpu_to_le32(length); + r->length = length + sizeof(*resp); + resp->MessageLength = cpu_to_le32(r->length); return retval; } -static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, - rndis_resp_t *r) +static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, + rndis_resp_t *r) { - rndis_set_cmplt_type *resp; - int i, retval = -ENOTSUPP; - struct rndis_params *params; + rndis_set_cmplt_type *resp; + int i, retval = -ENOTSUPP; + struct rndis_params *params; if (!r) return -ENOMEM; - resp = (rndis_set_cmplt_type *) r->buf; + resp = (rndis_set_cmplt_type *)r->buf; if (!resp) return -ENOMEM; @@ -506,9 +514,9 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, } } - params = &rndis_per_dev_params [configNr]; + params = &rndis_per_dev_params[configNr]; switch (OID) { - case OID_GEN_CURRENT_PACKET_FILTER: + case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: /* these NDIS_PACKET_TYPE_* bitflags are shared with * cdc_filter; it's not RNDIS-specific @@ -517,7 +525,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, * MULTICAST, ALL_MULTICAST, BROADCAST */ *params->filter = (u16)get_unaligned_le32(buf); - pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n", __func__, *params->filter); /* this call has a significant side effect: it's @@ -529,17 +537,17 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, params->state = RNDIS_DATA_INITIALIZED; netif_carrier_on(params->dev); if (netif_running(params->dev)) - netif_wake_queue (params->dev); + netif_wake_queue(params->dev); } else { params->state = RNDIS_INITIALIZED; - netif_carrier_off (params->dev); - netif_stop_queue (params->dev); + netif_carrier_off(params->dev); + netif_stop_queue(params->dev); } break; - case OID_802_3_MULTICAST_LIST: + case RNDIS_OID_802_3_MULTICAST_LIST: /* I think we can ignore this */ - pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__); + pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__); retval = 0; break; @@ -555,48 +563,47 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, * Response Functions */ -static int rndis_init_response (int configNr, rndis_init_msg_type *buf) +static int rndis_init_response(int configNr, rndis_init_msg_type *buf) { - rndis_init_cmplt_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + rndis_init_cmplt_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; if (!params->dev) return -ENOTSUPP; - r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); + r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); if (!r) return -ENOMEM; - resp = (rndis_init_cmplt_type *) r->buf; + resp = (rndis_init_cmplt_type *)r->buf; - resp->MessageType = cpu_to_le32 ( - REMOTE_NDIS_INITIALIZE_CMPLT); - resp->MessageLength = cpu_to_le32 (52); + resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C); + resp->MessageLength = cpu_to_le32(52); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); - resp->MajorVersion = cpu_to_le32 (RNDIS_MAJOR_VERSION); - resp->MinorVersion = cpu_to_le32 (RNDIS_MINOR_VERSION); - resp->DeviceFlags = cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); - resp->Medium = cpu_to_le32 (RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = cpu_to_le32 (1); - resp->MaxTransferSize = cpu_to_le32 ( + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); + resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION); + resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); + resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); + resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = cpu_to_le32(1); + resp->MaxTransferSize = cpu_to_le32( params->dev->mtu - + sizeof (struct ethhdr) - + sizeof (struct rndis_packet_msg_type) + + sizeof(struct ethhdr) + + sizeof(struct rndis_packet_msg_type) + 22); - resp->PacketAlignmentFactor = cpu_to_le32 (0); - resp->AFListOffset = cpu_to_le32 (0); - resp->AFListSize = cpu_to_le32 (0); + resp->PacketAlignmentFactor = cpu_to_le32(0); + resp->AFListOffset = cpu_to_le32(0); + resp->AFListSize = cpu_to_le32(0); params->resp_avail(params->v); return 0; } -static int rndis_query_response (int configNr, rndis_query_msg_type *buf) +static int rndis_query_response(int configNr, rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */ if (!params->dev) @@ -608,47 +615,46 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) * rndis_query_cmplt_type followed by data. * oid_supported_list is the largest data reply */ - r = rndis_add_response (configNr, - sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type)); + r = rndis_add_response(configNr, + sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); if (!r) return -ENOMEM; - resp = (rndis_query_cmplt_type *) r->buf; + resp = (rndis_query_cmplt_type *)r->buf; - resp->MessageType = cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); + resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), + if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID), le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *) buf, + + 8 + (u8 *)buf, le32_to_cpu(buf->InformationBufferLength), r)) { /* OID not supported */ - resp->Status = cpu_to_le32 ( - RNDIS_STATUS_NOT_SUPPORTED); - resp->MessageLength = cpu_to_le32 (sizeof *resp); - resp->InformationBufferLength = cpu_to_le32 (0); - resp->InformationBufferOffset = cpu_to_le32 (0); + resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); + resp->MessageLength = cpu_to_le32(sizeof *resp); + resp->InformationBufferLength = cpu_to_le32(0); + resp->InformationBufferOffset = cpu_to_le32(0); } else - resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; } -static int rndis_set_response (int configNr, rndis_set_msg_type *buf) +static int rndis_set_response(int configNr, rndis_set_msg_type *buf) { - u32 BufLength, BufOffset; - rndis_set_cmplt_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); + r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; - resp = (rndis_set_cmplt_type *) r->buf; + resp = (rndis_set_cmplt_type *)r->buf; - BufLength = le32_to_cpu (buf->InformationBufferLength); - BufOffset = le32_to_cpu (buf->InformationBufferOffset); + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); #ifdef VERBOSE_DEBUG pr_debug("%s: Length: %d\n", __func__, BufLength); @@ -662,59 +668,58 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) pr_debug("\n"); #endif - resp->MessageType = cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); - resp->MessageLength = cpu_to_le32 (16); + resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C); + resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID), - ((u8 *) buf) + 8 + BufOffset, BufLength, r)) - resp->Status = cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); + if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID), + ((u8 *)buf) + 8 + BufOffset, BufLength, r)) + resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); else - resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; } -static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) +static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) { - rndis_reset_cmplt_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); + r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; - resp = (rndis_reset_cmplt_type *) r->buf; + resp = (rndis_reset_cmplt_type *)r->buf; - resp->MessageType = cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); - resp->MessageLength = cpu_to_le32 (16); - resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C); + resp->MessageLength = cpu_to_le32(16); + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); /* resent information */ - resp->AddressingReset = cpu_to_le32 (1); + resp->AddressingReset = cpu_to_le32(1); params->resp_avail(params->v); return 0; } -static int rndis_keepalive_response (int configNr, - rndis_keepalive_msg_type *buf) +static int rndis_keepalive_response(int configNr, + rndis_keepalive_msg_type *buf) { - rndis_keepalive_cmplt_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ - r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); + r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); if (!r) return -ENOMEM; - resp = (rndis_keepalive_cmplt_type *) r->buf; + resp = (rndis_keepalive_cmplt_type *)r->buf; - resp->MessageType = cpu_to_le32 ( - REMOTE_NDIS_KEEPALIVE_CMPLT); - resp->MessageLength = cpu_to_le32 (16); + resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C); + resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; @@ -724,86 +729,89 @@ static int rndis_keepalive_response (int configNr, /* * Device to Host Comunication */ -static int rndis_indicate_status_msg (int configNr, u32 status) +static int rndis_indicate_status_msg(int configNr, u32 status) { - rndis_indicate_status_msg_type *resp; - rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; - r = rndis_add_response (configNr, - sizeof (rndis_indicate_status_msg_type)); + r = rndis_add_response(configNr, + sizeof(rndis_indicate_status_msg_type)); if (!r) return -ENOMEM; - resp = (rndis_indicate_status_msg_type *) r->buf; + resp = (rndis_indicate_status_msg_type *)r->buf; - resp->MessageType = cpu_to_le32 ( - REMOTE_NDIS_INDICATE_STATUS_MSG); - resp->MessageLength = cpu_to_le32 (20); - resp->Status = cpu_to_le32 (status); - resp->StatusBufferLength = cpu_to_le32 (0); - resp->StatusBufferOffset = cpu_to_le32 (0); + resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE); + resp->MessageLength = cpu_to_le32(20); + resp->Status = cpu_to_le32(status); + resp->StatusBufferLength = cpu_to_le32(0); + resp->StatusBufferOffset = cpu_to_le32(0); params->resp_avail(params->v); return 0; } -int rndis_signal_connect (int configNr) +int rndis_signal_connect(int configNr) { - rndis_per_dev_params [configNr].media_state - = NDIS_MEDIA_STATE_CONNECTED; - return rndis_indicate_status_msg (configNr, + rndis_per_dev_params[configNr].media_state + = RNDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(configNr, RNDIS_STATUS_MEDIA_CONNECT); } +EXPORT_SYMBOL_GPL(rndis_signal_connect); -int rndis_signal_disconnect (int configNr) +int rndis_signal_disconnect(int configNr) { - rndis_per_dev_params [configNr].media_state - = NDIS_MEDIA_STATE_DISCONNECTED; - return rndis_indicate_status_msg (configNr, + rndis_per_dev_params[configNr].media_state + = RNDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg(configNr, RNDIS_STATUS_MEDIA_DISCONNECT); } +EXPORT_SYMBOL_GPL(rndis_signal_disconnect); -void rndis_uninit (int configNr) +void rndis_uninit(int configNr) { u8 *buf; u32 length; if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; /* drain the response queue */ 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) +void rndis_set_host_mac(int configNr, const u8 *addr) { - rndis_per_dev_params [configNr].host_mac = addr; + rndis_per_dev_params[configNr].host_mac = addr; } +EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser */ -int rndis_msg_parser (u8 configNr, u8 *buf) +int rndis_msg_parser(u8 configNr, u8 *buf) { u32 MsgType, MsgLength; __le32 *tmp; - struct rndis_params *params; + struct rndis_params *params; if (!buf) return -ENOMEM; - tmp = (__le32 *) buf; + tmp = (__le32 *)buf; MsgType = get_unaligned_le32(tmp++); MsgLength = get_unaligned_le32(tmp++); if (configNr >= RNDIS_MAX_CONFIGS) return -ENOTSUPP; - params = &rndis_per_dev_params [configNr]; + params = &rndis_per_dev_params[configNr]; /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for * rx/tx statistics and link status, in addition to KEEPALIVE traffic @@ -812,43 +820,43 @@ int rndis_msg_parser (u8 configNr, u8 *buf) /* For USB: responses may take up to 10 seconds */ switch (MsgType) { - case REMOTE_NDIS_INITIALIZE_MSG: - pr_debug("%s: REMOTE_NDIS_INITIALIZE_MSG\n", - __func__ ); + case RNDIS_MSG_INIT: + pr_debug("%s: RNDIS_MSG_INIT\n", + __func__); params->state = RNDIS_INITIALIZED; - return rndis_init_response (configNr, - (rndis_init_msg_type *) buf); + return rndis_init_response(configNr, + (rndis_init_msg_type *)buf); - case REMOTE_NDIS_HALT_MSG: - pr_debug("%s: REMOTE_NDIS_HALT_MSG\n", - __func__ ); + case RNDIS_MSG_HALT: + pr_debug("%s: RNDIS_MSG_HALT\n", + __func__); params->state = RNDIS_UNINITIALIZED; if (params->dev) { - netif_carrier_off (params->dev); - netif_stop_queue (params->dev); + netif_carrier_off(params->dev); + netif_stop_queue(params->dev); } return 0; - case REMOTE_NDIS_QUERY_MSG: - return rndis_query_response (configNr, - (rndis_query_msg_type *) buf); + case RNDIS_MSG_QUERY: + return rndis_query_response(configNr, + (rndis_query_msg_type *)buf); - case REMOTE_NDIS_SET_MSG: - return rndis_set_response (configNr, - (rndis_set_msg_type *) buf); + case RNDIS_MSG_SET: + return rndis_set_response(configNr, + (rndis_set_msg_type *)buf); - case REMOTE_NDIS_RESET_MSG: - pr_debug("%s: REMOTE_NDIS_RESET_MSG\n", - __func__ ); - return rndis_reset_response (configNr, - (rndis_reset_msg_type *) buf); + case RNDIS_MSG_RESET: + pr_debug("%s: RNDIS_MSG_RESET\n", + __func__); + return rndis_reset_response(configNr, + (rndis_reset_msg_type *)buf); - case REMOTE_NDIS_KEEPALIVE_MSG: + case RNDIS_MSG_KEEPALIVE: /* For USB: host does this every 5 seconds */ if (rndis_debug > 1) - pr_debug("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", - __func__ ); - return rndis_keepalive_response (configNr, + pr_debug("%s: RNDIS_MSG_KEEPALIVE\n", + __func__); + return rndis_keepalive_response(configNr, (rndis_keepalive_msg_type *) buf); @@ -858,32 +866,15 @@ int rndis_msg_parser (u8 configNr, u8 *buf) * suspending itself. */ pr_warning("%s: unknown RNDIS message 0x%08X len %d\n", - __func__ , MsgType, MsgLength); - { - unsigned i; - for (i = 0; i < MsgLength; i += 16) { - pr_debug("%03d: " - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - " %02x %02x %02x %02x" - "\n", - i, - buf[i], buf [i+1], - buf[i+2], buf[i+3], - buf[i+4], buf [i+5], - buf[i+6], buf[i+7], - buf[i+8], buf [i+9], - buf[i+10], buf[i+11], - buf[i+12], buf [i+13], - buf[i+14], buf[i+15]); - } - } + __func__, MsgType, MsgLength); + print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET, + buf, MsgLength); break; } return -ENOTSUPP; } +EXPORT_SYMBOL_GPL(rndis_msg_parser); int rndis_register(void (*resp_avail)(void *v), void *v) { @@ -893,10 +884,10 @@ int rndis_register(void (*resp_avail)(void *v), void *v) return -EINVAL; for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - if (!rndis_per_dev_params [i].used) { - rndis_per_dev_params [i].used = 1; - rndis_per_dev_params [i].resp_avail = resp_avail; - rndis_per_dev_params [i].v = v; + if (!rndis_per_dev_params[i].used) { + rndis_per_dev_params[i].used = 1; + rndis_per_dev_params[i].resp_avail = resp_avail; + rndis_per_dev_params[i].v = v; pr_debug("%s: configNr = %d\n", __func__, i); return i; } @@ -905,16 +896,16 @@ int rndis_register(void (*resp_avail)(void *v), void *v) return -ENODEV; } +EXPORT_SYMBOL_GPL(rndis_register); -void rndis_deregister (int configNr) +void rndis_deregister(int configNr) { - pr_debug("%s: \n", __func__); + pr_debug("%s:\n", __func__); if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params [configNr].used = 0; - - 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) { @@ -923,76 +914,81 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) return -EINVAL; if (configNr >= RNDIS_MAX_CONFIGS) return -1; - rndis_per_dev_params [configNr].dev = dev; - rndis_per_dev_params [configNr].filter = cdc_filter; + rndis_per_dev_params[configNr].dev = dev; + rndis_per_dev_params[configNr].filter = cdc_filter; return 0; } +EXPORT_SYMBOL_GPL(rndis_set_param_dev); -int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) +int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) { pr_debug("%s:\n", __func__); if (!vendorDescr) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; - rndis_per_dev_params [configNr].vendorID = vendorID; - rndis_per_dev_params [configNr].vendorDescr = vendorDescr; + rndis_per_dev_params[configNr].vendorID = vendorID; + rndis_per_dev_params[configNr].vendorDescr = vendorDescr; return 0; } +EXPORT_SYMBOL_GPL(rndis_set_param_vendor); -int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) +int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) { pr_debug("%s: %u %u\n", __func__, medium, speed); if (configNr >= RNDIS_MAX_CONFIGS) return -1; - rndis_per_dev_params [configNr].medium = medium; - rndis_per_dev_params [configNr].speed = speed; + rndis_per_dev_params[configNr].medium = medium; + rndis_per_dev_params[configNr].speed = speed; return 0; } +EXPORT_SYMBOL_GPL(rndis_set_param_medium); -void rndis_add_hdr (struct sk_buff *skb) +void rndis_add_hdr(struct sk_buff *skb) { - struct rndis_packet_msg_type *header; + struct rndis_packet_msg_type *header; if (!skb) return; - header = (void *) skb_push (skb, sizeof *header); - memset (header, 0, sizeof *header); - header->MessageType = cpu_to_le32(REMOTE_NDIS_PACKET_MSG); + header = (void *)skb_push(skb, sizeof(*header)); + memset(header, 0, sizeof *header); + header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET); header->MessageLength = cpu_to_le32(skb->len); - header->DataOffset = cpu_to_le32 (36); - header->DataLength = cpu_to_le32(skb->len - sizeof *header); + 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) +void rndis_free_response(int configNr, u8 *buf) { - rndis_resp_t *r; - struct list_head *act, *tmp; + rndis_resp_t *r; + struct list_head *act, *tmp; - list_for_each_safe (act, tmp, - &(rndis_per_dev_params [configNr].resp_queue)) + list_for_each_safe(act, tmp, + &(rndis_per_dev_params[configNr].resp_queue)) { - r = list_entry (act, rndis_resp_t, list); + r = list_entry(act, rndis_resp_t, list); if (r && r->buf == buf) { - list_del (&r->list); - kfree (r); + list_del(&r->list); + kfree(r); } } } +EXPORT_SYMBOL_GPL(rndis_free_response); -u8 *rndis_get_next_response (int configNr, u32 *length) +u8 *rndis_get_next_response(int configNr, u32 *length) { - rndis_resp_t *r; - struct list_head *act, *tmp; + rndis_resp_t *r; + struct list_head *act, *tmp; if (!length) return NULL; - list_for_each_safe (act, tmp, - &(rndis_per_dev_params [configNr].resp_queue)) + list_for_each_safe(act, tmp, + &(rndis_per_dev_params[configNr].resp_queue)) { - r = list_entry (act, rndis_resp_t, list); + r = list_entry(act, rndis_resp_t, list); if (!r->send) { r->send = 1; *length = r->length; @@ -1002,44 +998,53 @@ 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) +static rndis_resp_t *rndis_add_response(int configNr, u32 length) { - rndis_resp_t *r; + rndis_resp_t *r; - /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */ - r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); + /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */ + r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC); if (!r) return NULL; - r->buf = (u8 *) (r + 1); + r->buf = (u8 *)(r + 1); r->length = length; r->send = 0; - list_add_tail (&r->list, - &(rndis_per_dev_params [configNr].resp_queue)); + list_add_tail(&r->list, + &(rndis_per_dev_params[configNr].resp_queue)); return r; } -int rndis_rm_hdr(struct sk_buff *skb) +int rndis_rm_hdr(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) { /* tmp points to a struct rndis_packet_msg_type */ - __le32 *tmp = (void *) skb->data; + __le32 *tmp = (void *)skb->data; /* MessageType, MessageLength */ - if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG) - != get_unaligned(tmp++)) + if (cpu_to_le32(RNDIS_MSG_PACKET) + != get_unaligned(tmp++)) { + dev_kfree_skb_any(skb); return -EINVAL; + } tmp++; /* DataOffset, DataLength */ - if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) + if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { + dev_kfree_skb_any(skb); return -EOVERFLOW; + } skb_trim(skb, get_unaligned_le32(tmp++)); + skb_queue_tail(list, skb); return 0; } +EXPORT_SYMBOL_GPL(rndis_rm_hdr); -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static int rndis_proc_show(struct seq_file *m, void *v) { @@ -1063,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", @@ -1072,9 +1077,9 @@ 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) + 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; @@ -1094,11 +1099,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, case '8': case '9': fl_speed = 1; - speed = speed*10 + c - '0'; + speed = speed * 10 + c - '0'; break; case 'C': case 'c': - rndis_signal_connect (p->confignr); + rndis_signal_connect(p->confignr); break; case 'D': case 'd': @@ -1118,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 = { @@ -1130,14 +1135,14 @@ static const struct file_operations rndis_proc_fops = { .write = rndis_proc_write, }; -#define NAME_TEMPLATE "driver/rndis-%03d" +#define NAME_TEMPLATE "driver/rndis-%03d" static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; -#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -int __init rndis_init (void) +int rndis_init(void) { u8 i; @@ -1145,41 +1150,40 @@ int __init rndis_init (void) #ifdef CONFIG_USB_GADGET_DEBUG_FILES char name [20]; - sprintf (name, NAME_TEMPLATE, i); - if (!(rndis_connect_state [i] - = proc_create_data(name, 0660, NULL, + sprintf(name, NAME_TEMPLATE, i); + rndis_connect_state[i] = proc_create_data(name, 0660, NULL, &rndis_proc_fops, - (void *)(rndis_per_dev_params + i)))) - { - pr_debug("%s :remove entries", __func__); + (void *)(rndis_per_dev_params + i)); + if (!rndis_connect_state[i]) { + pr_debug("%s: remove entries", __func__); while (i) { - sprintf (name, NAME_TEMPLATE, --i); - remove_proc_entry (name, NULL); + sprintf(name, NAME_TEMPLATE, --i); + remove_proc_entry(name, NULL); } pr_debug("\n"); return -EIO; } #endif - rndis_per_dev_params [i].confignr = i; - rndis_per_dev_params [i].used = 0; - rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; - rndis_per_dev_params [i].media_state - = NDIS_MEDIA_STATE_DISCONNECTED; - INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue)); + rndis_per_dev_params[i].confignr = i; + rndis_per_dev_params[i].used = 0; + rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; + rndis_per_dev_params[i].media_state + = RNDIS_MEDIA_STATE_DISCONNECTED; + INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); } return 0; } -void rndis_exit (void) +void rndis_exit(void) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; - char name [20]; + char name[20]; for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, NAME_TEMPLATE, i); - remove_proc_entry (name, NULL); + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); } #endif } diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index aac61dfe0f0..0f4abb4c377 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -15,58 +15,13 @@ #ifndef _LINUX_RNDIS_H #define _LINUX_RNDIS_H +#include <linux/rndis.h> +#include "u_ether.h" #include "ndis.h" #define RNDIS_MAXIMUM_FRAME_SIZE 1518 #define RNDIS_MAX_TOTAL_SIZE 1558 -/* Remote NDIS Versions */ -#define RNDIS_MAJOR_VERSION 1 -#define RNDIS_MINOR_VERSION 0 - -/* Status Values */ -#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ -#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ -#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ -#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ -#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ -#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ -/* For all not specified status messages: - * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx - */ - -/* Message Set for Connectionless (802.3) Devices */ -#define REMOTE_NDIS_PACKET_MSG 0x00000001U -#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ -#define REMOTE_NDIS_HALT_MSG 0x00000003U -#define REMOTE_NDIS_QUERY_MSG 0x00000004U -#define REMOTE_NDIS_SET_MSG 0x00000005U -#define REMOTE_NDIS_RESET_MSG 0x00000006U -#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U -#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U - -/* Message completion */ -#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U -#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U -#define REMOTE_NDIS_SET_CMPLT 0x80000005U -#define REMOTE_NDIS_RESET_CMPLT 0x80000006U -#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U - -/* Device Flags */ -#define RNDIS_DF_CONNECTIONLESS 0x00000001U -#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U - -#define RNDIS_MEDIUM_802_3 0x00000000U - -/* from drivers/net/sk98lin/h/skgepnmi.h */ -#define OID_PNP_CAPABILITIES 0xFD010100 -#define OID_PNP_SET_POWER 0xFD010101 -#define OID_PNP_QUERY_POWER 0xFD010102 -#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 -#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 -#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 - - typedef struct rndis_init_msg_type { __le32 MessageType; @@ -251,7 +206,8 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); void rndis_add_hdr (struct sk_buff *skb); -int rndis_rm_hdr (struct sk_buff *skb); +int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, + struct sk_buff_head *list); u8 *rndis_get_next_response (int configNr, u32 *length); void rndis_free_response (int configNr, u8 *buf); @@ -261,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 __devinit rndis_init (void); -void rndis_exit (void); - #endif /* _LINUX_RNDIS_H */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 00000000000..10c6a128250 --- /dev/null +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -0,0 +1,1369 @@ +/* linux/drivers/usb/gadget/s3c-hsudc.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S3C24XX USB 2.0 High-speed USB controller gadget driver + * + * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints. + * Each endpoint can be configured as either in or out endpoint. Endpoints + * can be configured for Bulk or Interrupt transfer mode. + * + * 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/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/prefetch.h> +#include <linux/platform_data/s3c-hsudc.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> + +#include <mach/regs-s3c2443-clock.h> + +#define S3C_HSUDC_REG(x) (x) + +/* Non-Indexed Registers */ +#define S3C_IR S3C_HSUDC_REG(0x00) /* Index Register */ +#define S3C_EIR S3C_HSUDC_REG(0x04) /* EP Intr Status */ +#define S3C_EIR_EP0 (1<<0) +#define S3C_EIER S3C_HSUDC_REG(0x08) /* EP Intr Enable */ +#define S3C_FAR S3C_HSUDC_REG(0x0c) /* Gadget Address */ +#define S3C_FNR S3C_HSUDC_REG(0x10) /* Frame Number */ +#define S3C_EDR S3C_HSUDC_REG(0x14) /* EP Direction */ +#define S3C_TR S3C_HSUDC_REG(0x18) /* Test Register */ +#define S3C_SSR S3C_HSUDC_REG(0x1c) /* System Status */ +#define S3C_SSR_DTZIEN_EN (0xff8f) +#define S3C_SSR_ERR (0xff80) +#define S3C_SSR_VBUSON (1 << 8) +#define S3C_SSR_HSP (1 << 4) +#define S3C_SSR_SDE (1 << 3) +#define S3C_SSR_RESUME (1 << 2) +#define S3C_SSR_SUSPEND (1 << 1) +#define S3C_SSR_RESET (1 << 0) +#define S3C_SCR S3C_HSUDC_REG(0x20) /* System Control */ +#define S3C_SCR_DTZIEN_EN (1 << 14) +#define S3C_SCR_RRD_EN (1 << 5) +#define S3C_SCR_SUS_EN (1 << 1) +#define S3C_SCR_RST_EN (1 << 0) +#define S3C_EP0SR S3C_HSUDC_REG(0x24) /* EP0 Status */ +#define S3C_EP0SR_EP0_LWO (1 << 6) +#define S3C_EP0SR_STALL (1 << 4) +#define S3C_EP0SR_TX_SUCCESS (1 << 1) +#define S3C_EP0SR_RX_SUCCESS (1 << 0) +#define S3C_EP0CR S3C_HSUDC_REG(0x28) /* EP0 Control */ +#define S3C_BR(_x) S3C_HSUDC_REG(0x60 + (_x * 4)) + +/* Indexed Registers */ +#define S3C_ESR S3C_HSUDC_REG(0x2c) /* EPn Status */ +#define S3C_ESR_FLUSH (1 << 6) +#define S3C_ESR_STALL (1 << 5) +#define S3C_ESR_LWO (1 << 4) +#define S3C_ESR_PSIF_ONE (1 << 2) +#define S3C_ESR_PSIF_TWO (2 << 2) +#define S3C_ESR_TX_SUCCESS (1 << 1) +#define S3C_ESR_RX_SUCCESS (1 << 0) +#define S3C_ECR S3C_HSUDC_REG(0x30) /* EPn Control */ +#define S3C_ECR_DUEN (1 << 7) +#define S3C_ECR_FLUSH (1 << 6) +#define S3C_ECR_STALL (1 << 1) +#define S3C_ECR_IEMS (1 << 0) +#define S3C_BRCR S3C_HSUDC_REG(0x34) /* Read Count */ +#define S3C_BWCR S3C_HSUDC_REG(0x38) /* Write Count */ +#define S3C_MPR S3C_HSUDC_REG(0x3c) /* Max Pkt Size */ + +#define WAIT_FOR_SETUP (0) +#define DATA_STATE_XMIT (1) +#define DATA_STATE_RECV (2) + +static const char * const s3c_hsudc_supply_names[] = { + "vdda", /* analog phy supply, 3.3V */ + "vddi", /* digital phy supply, 1.2V */ + "vddosc", /* oscillator supply, 1.8V - 3.3V */ +}; + +/** + * struct s3c_hsudc_ep - Endpoint representation used by driver. + * @ep: USB gadget layer representation of device endpoint. + * @name: Endpoint name (as required by ep autoconfiguration). + * @dev: Reference to the device controller to which this EP belongs. + * @desc: Endpoint descriptor obtained from the gadget driver. + * @queue: Transfer request queue for the endpoint. + * @stopped: Maintains state of endpoint, set if EP is halted. + * @bEndpointAddress: EP address (including direction bit). + * @fifo: Base address of EP FIFO. + */ +struct s3c_hsudc_ep { + struct usb_ep ep; + char name[20]; + struct s3c_hsudc *dev; + struct list_head queue; + u8 stopped; + u8 wedge; + u8 bEndpointAddress; + void __iomem *fifo; +}; + +/** + * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request. + * @req: Reference to USB gadget transfer request. + * @queue: Used for inserting this request to the endpoint request queue. + */ +struct s3c_hsudc_req { + struct usb_request req; + struct list_head queue; +}; + +/** + * struct s3c_hsudc - Driver's abstraction of the device controller. + * @gadget: Instance of usb_gadget which is referenced by gadget driver. + * @driver: Reference to currenty active gadget driver. + * @dev: The device reference used by probe function. + * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed). + * @regs: Remapped base address of controller's register space. + * irq: IRQ number used by the controller. + * uclk: Reference to the controller clock. + * ep0state: Current state of EP0. + * ep: List of endpoints supported by the controller. + */ +struct s3c_hsudc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct s3c24xx_hsudc_platdata *pd; + struct usb_phy *transceiver; + struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)]; + spinlock_t lock; + void __iomem *regs; + int irq; + struct clk *uclk; + int ep0state; + struct s3c_hsudc_ep ep[]; +}; + +#define ep_maxpacket(_ep) ((_ep)->ep.maxpacket) +#define ep_is_in(_ep) ((_ep)->bEndpointAddress & USB_DIR_IN) +#define ep_index(_ep) ((_ep)->bEndpointAddress & \ + USB_ENDPOINT_NUMBER_MASK) + +static const char driver_name[] = "s3c-udc"; +static const char ep0name[] = "ep0-control"; + +static inline struct s3c_hsudc_req *our_req(struct usb_request *req) +{ + return container_of(req, struct s3c_hsudc_req, req); +} + +static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct s3c_hsudc_ep, ep); +} + +static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct s3c_hsudc, gadget); +} + +static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr) +{ + ep_addr &= USB_ENDPOINT_NUMBER_MASK; + writel(ep_addr, hsudc->regs + S3C_IR); +} + +static inline void __orr32(void __iomem *ptr, u32 val) +{ + writel(readl(ptr) | val, ptr); +} + +static void s3c_hsudc_init_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + cfg = readl(S3C2443_URSTCON); + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + mdelay(1); + + cfg = readl(S3C2443_URSTCON); + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + + cfg = readl(S3C2443_PHYCTRL); + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); + writel(cfg, S3C2443_PHYCTRL); + + cfg = readl(S3C2443_PHYPWR); + cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN | + S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK | + S3C2443_PHYPWR_ANALOG_PD); + cfg |= S3C2443_PHYPWR_COMMON_ON; + writel(cfg, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON); + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | + S3C2443_UCLKCON_TCLKEN); + writel(cfg, S3C2443_UCLKCON); +} + +static void s3c_hsudc_uninit_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; + writel(cfg, S3C2443_UCLKCON); +} + +/** + * s3c_hsudc_complete_request - Complete a transfer request. + * @hsep: Endpoint to which the request belongs. + * @hsreq: Transfer request to be completed. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq, int status) +{ + unsigned int stopped = hsep->stopped; + struct s3c_hsudc *hsudc = hsep->dev; + + list_del_init(&hsreq->queue); + hsreq->req.status = status; + + if (!ep_index(hsep)) { + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + + hsep->stopped = 1; + spin_unlock(&hsudc->lock); + if (hsreq->req.complete != NULL) + hsreq->req.complete(&hsep->ep, &hsreq->req); + spin_lock(&hsudc->lock); + hsep->stopped = stopped; +} + +/** + * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint. + * @hsep: Endpoint for which queued requests have to be terminated. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) +{ + struct s3c_hsudc_req *hsreq; + + while (!list_empty(&hsep->queue)) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_complete_request(hsep, hsreq, status); + } +} + +/** + * s3c_hsudc_stop_activity - Stop activity on all endpoints. + * @hsudc: Device controller for which EP activity is to be stopped. + * + * All the endpoints are stopped and any pending transfer requests if any on + * the endpoint are terminated. + */ +static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep; + int epnum; + + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) { + hsep = &hsudc->ep[epnum]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + } +} + +/** + * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo. + * @hsudc: Device controller from which setup packet is to be read. + * @buf: The buffer into which the setup packet is read. + * + * The setup packet received in the EP0 fifo is read and stored into a + * given buffer address. + */ + +static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf) +{ + int count; + + count = readl(hsudc->regs + S3C_BRCR); + while (count--) + *buf++ = (u16)readl(hsudc->regs + S3C_BR(0)); + + writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR); +} + +/** + * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo. + * @hsep: Endpoint to which the data is to be written. + * @hsreq: Transfer request from which the next chunk of data is written. + * + * Write the next chunk of data from a transfer request to the endpoint FIFO. + * If the transfer request completes, 1 is returned, otherwise 0 is returned. + */ +static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + u16 *buf; + u32 max = ep_maxpacket(hsep); + u32 count, length; + bool is_last; + void __iomem *fifo = hsep->fifo; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetch(buf); + + length = hsreq->req.length - hsreq->req.actual; + length = min(length, max); + hsreq->req.actual += length; + + writel(length, hsep->dev->regs + S3C_BWCR); + for (count = 0; count < length; count += 2) + writel(*buf++, fifo); + + if (count != max) { + is_last = true; + } else { + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) + is_last = false; + else + is_last = true; + } + + if (is_last) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo. + * @hsep: Endpoint from which the data is to be read. + * @hsreq: Transfer request to which the next chunk of data read is written. + * + * Read the next chunk of data from the endpoint FIFO and a write it to the + * transfer request buffer. If the transfer request completes, 1 is returned, + * otherwise 0 is returned. + */ +static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + struct s3c_hsudc *hsudc = hsep->dev; + u32 csr, offset; + u16 *buf, word; + u32 buflen, rcnt, rlen; + void __iomem *fifo = hsep->fifo; + u32 is_short = 0; + + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + csr = readl(hsudc->regs + offset); + if (!(csr & S3C_ESR_RX_SUCCESS)) + return -EINVAL; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetchw(buf); + buflen = hsreq->req.length - hsreq->req.actual; + + rcnt = readl(hsudc->regs + S3C_BRCR); + rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2); + + hsreq->req.actual += min(rlen, buflen); + is_short = (rlen < hsep->ep.maxpacket); + + while (rcnt-- != 0) { + word = (u16)readl(fifo); + if (buflen) { + *buf++ = word; + buflen--; + } else { + hsreq->req.status = -EOVERFLOW; + } + } + + writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset); + + if (is_short || hsreq->req.actual == hsreq->req.length) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_epin_intr - Handle in-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a in-endpoint. The interrupts that are handled are + * stall and data transmit complete interrupt. + */ +static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl(hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_TX_SUCCESS) { + writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR); + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_write_fifo(hsep, hsreq); + } +} + +/** + * s3c_hsudc_epout_intr - Handle out-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a out-endpoint. The interrupts that are handled are + * stall, flush and data ready interrupt. + */ +static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl(hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_FLUSH) { + __orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH); + return; + } + + if (csr & S3C_ESR_RX_SUCCESS) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_read_fifo(hsep, hsreq); + } +} + +/** s3c_hsudc_set_halt - Set or clear a endpoint halt. + * @_ep: Endpoint on which halt has to be set or cleared. + * @value: 1 for setting halt on endpoint, 0 to clear halt. + * + * Set or clear endpoint halt. If halt is set, the endpoint is stopped. + * If halt is cleared, for in-endpoints, if there are any pending + * transfer requests, transfers are started. + */ +static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long irqflags; + u32 ecr; + u32 offset; + + if (value && ep_is_in(hsep) && !list_empty(&hsep->queue)) + return -EAGAIN; + + spin_lock_irqsave(&hsudc->lock, irqflags); + set_index(hsudc, ep_index(hsep)); + offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR; + ecr = readl(hsudc->regs + offset); + + if (value) { + ecr |= S3C_ECR_STALL; + if (ep_index(hsep)) + ecr |= S3C_ECR_FLUSH; + hsep->stopped = 1; + } else { + ecr &= ~S3C_ECR_STALL; + hsep->stopped = hsep->wedge = 0; + } + writel(ecr, hsudc->regs + offset); + + if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (hsreq) + s3c_hsudc_write_fifo(hsep, hsreq); + } + + spin_unlock_irqrestore(&hsudc->lock, irqflags); + return 0; +} + +/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored + * @_ep: Endpoint on which wedge has to be set. + * + * Sets the halt feature with the clear requests ignored. + */ +static int s3c_hsudc_set_wedge(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + + if (!hsep) + return -EINVAL; + + hsep->wedge = 1; + return usb_ep_set_halt(_ep); +} + +/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests. + * @_ep: Device controller on which the set/clear feature needs to be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle set feature or clear feature control requests on the control endpoint. + */ +static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; + + if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { + hsep = &hsudc->ep[ep_num]; + switch (le16_to_cpu(ctrl->wValue)) { + case USB_ENDPOINT_HALT: + if (set || (!set && !hsep->wedge)) + s3c_hsudc_set_halt(&hsep->ep, set); + return 0; + } + } + + return -ENOENT; +} + +/** + * s3c_hsudc_process_req_status - Handle get status control request. + * @hsudc: Device controller on which get status request has be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle get status control request received on control endpoint. + */ +static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0]; + struct s3c_hsudc_req hsreq; + struct s3c_hsudc_ep *hsep; + __le16 reply; + u8 epnum; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_INTERFACE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + hsep = &hsudc->ep[epnum]; + reply = cpu_to_le16(hsep->stopped ? 1 : 0); + break; + } + + INIT_LIST_HEAD(&hsreq.queue); + hsreq.req.length = 2; + hsreq.req.buf = &reply; + hsreq.req.actual = 0; + hsreq.req.complete = NULL; + s3c_hsudc_write_fifo(hsep0, &hsreq); +} + +/** + * s3c_hsudc_process_setup - Process control request received on endpoint 0. + * @hsudc: Device controller on which control request has been received. + * + * Read the control request received on endpoint 0, decode it and handle + * the request. + */ +static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct usb_ctrlrequest ctrl = {0}; + int ret; + + s3c_hsudc_nuke_ep(hsep, -EPROTO); + s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl); + + if (ctrl.bRequestType & USB_DIR_IN) { + hsep->bEndpointAddress |= USB_DIR_IN; + hsudc->ep0state = DATA_STATE_XMIT; + } else { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = DATA_STATE_RECV; + } + + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + hsudc->ep0state = WAIT_FOR_SETUP; + return; + + case USB_REQ_GET_STATUS: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_process_req_status(hsudc, &ctrl); + return; + + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_handle_reqfeat(hsudc, &ctrl); + hsudc->ep0state = WAIT_FOR_SETUP; + return; + } + + if (hsudc->driver) { + spin_unlock(&hsudc->lock); + ret = hsudc->driver->setup(&hsudc->gadget, &ctrl); + spin_lock(&hsudc->lock); + + if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = WAIT_FOR_SETUP; + } + + if (ret < 0) { + dev_err(hsudc->dev, "setup failed, returned %d\n", + ret); + s3c_hsudc_set_halt(&hsep->ep, 1); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + } +} + +/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt. + * @hsudc: Device controller on which endpoint 0 interrupt has occured. + * + * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur + * when a stall handshake is sent to host or data is sent/received on + * endpoint 0. + */ +static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct s3c_hsudc_req *hsreq; + u32 csr = readl(hsudc->regs + S3C_EP0SR); + u32 ecr; + + if (csr & S3C_EP0SR_STALL) { + ecr = readl(hsudc->regs + S3C_EP0CR); + ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); + writel(ecr, hsudc->regs + S3C_EP0CR); + + writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR); + hsep->stopped = 0; + + s3c_hsudc_nuke_ep(hsep, -ECONNABORTED); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + return; + } + + if (csr & S3C_EP0SR_TX_SUCCESS) { + writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR); + if (ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_write_fifo(hsep, hsreq); + } + } + + if (csr & S3C_EP0SR_RX_SUCCESS) { + if (hsudc->ep0state == WAIT_FOR_SETUP) + s3c_hsudc_process_setup(hsudc); + else { + if (!ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_read_fifo(hsep, hsreq); + } + } + } +} + +/** + * s3c_hsudc_ep_enable - Enable a endpoint. + * @_ep: The endpoint to be enabled. + * @desc: Endpoint descriptor. + * + * Enables a endpoint when called from the gadget driver. Endpoint stall if + * any is cleared, transfer type is configured and endpoint interrupt is + * enabled. + */ +static int s3c_hsudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 ecr = 0; + + hsep = our_ep(_ep); + if (!_ep || !desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || hsep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(hsep) < usb_endpoint_maxp(desc)) + return -EINVAL; + + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && usb_endpoint_maxp(desc) != ep_maxpacket(hsep)) + || !desc->wMaxPacketSize) + return -ERANGE; + + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN); + writel(ecr, hsudc->regs + S3C_ECR); + + hsep->stopped = hsep->wedge = 0; + hsep->ep.desc = desc; + hsep->ep.maxpacket = usb_endpoint_maxp(desc); + + s3c_hsudc_set_halt(_ep, 0); + __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_ep_disable - Disable a endpoint. + * @_ep: The endpoint to be disabled. + * @desc: Endpoint descriptor. + * + * Disables a endpoint when called from the gadget driver. + */ +static int s3c_hsudc_ep_disable(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + unsigned long flags; + + if (!_ep || !hsep->ep.desc) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + __clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + + hsep->ep.desc = NULL; + hsep->stopped = 1; + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_alloc_request - Allocate a new request. + * @_ep: Endpoint for which request is allocated (not used). + * @gfp_flags: Flags used for the allocation. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = kzalloc(sizeof(*hsreq), gfp_flags); + if (!hsreq) + return NULL; + + INIT_LIST_HEAD(&hsreq->queue); + return &hsreq->req; +} + +/** + * s3c_hsudc_free_request - Deallocate a request. + * @ep: Endpoint for which request is deallocated (not used). + * @_req: Request to be deallocated. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = our_req(_req); + WARN_ON(!list_empty(&hsreq->queue)); + kfree(hsreq); +} + +/** + * s3c_hsudc_queue - Queue a transfer request for the endpoint. + * @_ep: Endpoint for which the request is queued. + * @_req: Request to be queued. + * @gfp_flags: Not used. + * + * Start or enqueue a request for a endpoint when called from gadget driver. + */ +static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 offset; + u32 csr; + + hsreq = our_req(_req); + if ((!_req || !_req->complete || !_req->buf || + !list_empty(&hsreq->queue))) + return -EINVAL; + + hsep = our_ep(_ep); + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + set_index(hsudc, hsep->bEndpointAddress); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (!ep_index(hsep) && _req->length == 0) { + hsudc->ep0state = WAIT_FOR_SETUP; + s3c_hsudc_complete_request(hsep, hsreq, 0); + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; + } + + if (list_empty(&hsep->queue) && !hsep->stopped) { + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + if (ep_is_in(hsep)) { + csr = readl(hsudc->regs + offset); + if (!(csr & S3C_ESR_TX_SUCCESS) && + (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) + hsreq = NULL; + } else { + csr = readl(hsudc->regs + offset); + if ((csr & S3C_ESR_RX_SUCCESS) + && (s3c_hsudc_read_fifo(hsep, hsreq) == 1)) + hsreq = NULL; + } + } + + if (hsreq) + list_add_tail(&hsreq->queue, &hsep->queue); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint. + * @_ep: Endpoint from which the request is dequeued. + * @_req: Request to be dequeued. + * + * Dequeue a request from a endpoint when called from gadget driver. + */ +static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long flags; + + hsep = our_ep(_ep); + if (!_ep || hsep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + list_for_each_entry(hsreq, &hsep->queue, queue) { + if (&hsreq->req == _req) + break; + } + if (&hsreq->req != _req) { + spin_unlock_irqrestore(&hsudc->lock, flags); + return -EINVAL; + } + + set_index(hsudc, hsep->bEndpointAddress); + s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +static struct usb_ep_ops s3c_hsudc_ep_ops = { + .enable = s3c_hsudc_ep_enable, + .disable = s3c_hsudc_ep_disable, + .alloc_request = s3c_hsudc_alloc_request, + .free_request = s3c_hsudc_free_request, + .queue = s3c_hsudc_queue, + .dequeue = s3c_hsudc_dequeue, + .set_halt = s3c_hsudc_set_halt, + .set_wedge = s3c_hsudc_set_wedge, +}; + +/** + * s3c_hsudc_initep - Initialize a endpoint to default state. + * @hsudc - Reference to the device controller. + * @hsep - Endpoint to be initialized. + * @epnum - Address to be assigned to the endpoint. + * + * Initialize a endpoint with default configuration. + */ +static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, + struct s3c_hsudc_ep *hsep, int epnum) +{ + char *dir; + + if ((epnum % 2) == 0) { + dir = "out"; + } else { + dir = "in"; + hsep->bEndpointAddress = USB_DIR_IN; + } + + hsep->bEndpointAddress |= epnum; + if (epnum) + snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir); + else + snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name); + + INIT_LIST_HEAD(&hsep->queue); + INIT_LIST_HEAD(&hsep->ep.ep_list); + if (epnum) + list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list); + + hsep->dev = hsudc; + hsep->ep.name = hsep->name; + 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; + hsep->stopped = 0; + hsep->wedge = 0; + + set_index(hsudc, epnum); + writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR); +} + +/** + * s3c_hsudc_setup_ep - Configure all endpoints to default state. + * @hsudc: Reference to device controller. + * + * Configures all endpoints to default state. + */ +static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc) +{ + int epnum; + + hsudc->ep0state = WAIT_FOR_SETUP; + INIT_LIST_HEAD(&hsudc->gadget.ep_list); + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) + s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum); +} + +/** + * s3c_hsudc_reconfig - Reconfigure the device controller to default state. + * @hsudc: Reference to device controller. + * + * Reconfigures the device controller registers to a default state. + */ +static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc) +{ + writel(0xAA, hsudc->regs + S3C_EDR); + writel(1, hsudc->regs + S3C_EIER); + writel(0, hsudc->regs + S3C_TR); + writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | + S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); + writel(0, hsudc->regs + S3C_EP0CR); + + s3c_hsudc_setup_ep(hsudc); +} + +/** + * s3c_hsudc_irq - Interrupt handler for device controller. + * @irq: Not used. + * @_dev: Reference to the device controller. + * + * Interrupt handler for the device controller. This handler handles controller + * interrupts and endpoint interrupts. + */ +static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) +{ + struct s3c_hsudc *hsudc = _dev; + struct s3c_hsudc_ep *hsep; + u32 ep_intr; + u32 sys_status; + u32 ep_idx; + + spin_lock(&hsudc->lock); + + sys_status = readl(hsudc->regs + S3C_SSR); + ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF; + + if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) { + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; + } + + if (sys_status) { + if (sys_status & S3C_SSR_VBUSON) + writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_ERR) + writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_SDE) { + writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR); + hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + if (sys_status & S3C_SSR_SUSPEND) { + writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->suspend) + hsudc->driver->suspend(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESUME) { + writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->resume) + hsudc->driver->resume(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESET) { + writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR); + for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) { + hsep = &hsudc->ep[ep_idx]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ECONNRESET); + } + s3c_hsudc_reconfig(hsudc); + hsudc->ep0state = WAIT_FOR_SETUP; + } + } + + if (ep_intr & S3C_EIR_EP0) { + writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR); + set_index(hsudc, 0); + s3c_hsudc_handle_ep0_intr(hsudc); + } + + ep_intr >>= 1; + ep_idx = 1; + while (ep_intr) { + if (ep_intr & 1) { + hsep = &hsudc->ep[ep_idx]; + set_index(hsudc, ep_idx); + writel(1 << ep_idx, hsudc->regs + S3C_EIR); + if (ep_is_in(hsep)) + s3c_hsudc_epin_intr(hsudc, ep_idx); + else + s3c_hsudc_epout_intr(hsudc, ep_idx); + } + ep_intr >>= 1; + ep_idx++; + } + + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; +} + +static int s3c_hsudc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = to_hsudc(gadget); + int ret; + + if (!driver + || driver->max_speed < USB_SPEED_FULL + || !driver->setup) + return -EINVAL; + + if (!hsudc) + return -ENODEV; + + if (hsudc->driver) + return -EBUSY; + + hsudc->driver = driver; + + ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies), + hsudc->supplies); + if (ret != 0) { + dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret); + goto err_supplies; + } + + /* connect to bus through transceiver */ + if (!IS_ERR_OR_NULL(hsudc->transceiver)) { + ret = otg_set_peripheral(hsudc->transceiver->otg, + &hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: can't bind to transceiver\n", + hsudc->gadget.name); + goto err_otg; + } + } + + enable_irq(hsudc->irq); + dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); + + s3c_hsudc_reconfig(hsudc); + + pm_runtime_get_sync(hsudc->dev); + + s3c_hsudc_init_phy(); + if (hsudc->pd->gpio_init) + hsudc->pd->gpio_init(); + + return 0; +err_otg: + regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); +err_supplies: + hsudc->driver = NULL; + return ret; +} + +static int s3c_hsudc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = to_hsudc(gadget); + unsigned long flags; + + if (!hsudc) + return -ENODEV; + + if (!driver || driver != hsudc->driver) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + hsudc->driver = NULL; + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + s3c_hsudc_uninit_phy(); + + pm_runtime_put(hsudc->dev); + + if (hsudc->pd->gpio_uninit) + hsudc->pd->gpio_uninit(); + s3c_hsudc_stop_activity(hsudc); + spin_unlock_irqrestore(&hsudc->lock, flags); + + if (!IS_ERR_OR_NULL(hsudc->transceiver)) + (void) otg_set_peripheral(hsudc->transceiver->otg, NULL); + + disable_irq(hsudc->irq); + + regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); + + dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", + driver->driver.name); + return 0; +} + +static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc) +{ + return readl(hsudc->regs + S3C_FNR) & 0x3FF; +} + +static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) +{ + return s3c_hsudc_read_frameno(to_hsudc(gadget)); +} + +static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct s3c_hsudc *hsudc = to_hsudc(gadget); + + if (!hsudc) + return -ENODEV; + + if (!IS_ERR_OR_NULL(hsudc->transceiver)) + return usb_phy_set_power(hsudc->transceiver, mA); + + return -EOPNOTSUPP; +} + +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, + .vbus_draw = s3c_hsudc_vbus_draw, +}; + +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 = dev_get_platdata(&pdev->dev); + int ret, i; + + hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) + + sizeof(struct s3c_hsudc_ep) * pd->epnum, + GFP_KERNEL); + if (!hsudc) { + dev_err(dev, "cannot allocate memory\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, dev); + hsudc->dev = dev; + 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 = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies), + hsudc->supplies); + if (ret != 0) { + dev_err(dev, "failed to request supplies: %d\n", ret); + goto err_supplies; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + 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); + + hsudc->gadget.max_speed = USB_SPEED_HIGH; + hsudc->gadget.ops = &s3c_hsudc_gadget_ops; + hsudc->gadget.name = dev_name(dev); + hsudc->gadget.ep0 = &hsudc->ep[0].ep; + hsudc->gadget.is_otg = 0; + hsudc->gadget.is_a_peripheral = 0; + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + + s3c_hsudc_setup_ep(hsudc); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_res; + } + hsudc->irq = ret; + + ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0, + driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_res; + } + + hsudc->uclk = devm_clk_get(&pdev->dev, "usb-device"); + if (IS_ERR(hsudc->uclk)) { + dev_err(dev, "failed to find usb-device clock source\n"); + ret = PTR_ERR(hsudc->uclk); + goto err_res; + } + clk_enable(hsudc->uclk); + + local_irq_disable(); + + disable_irq(hsudc->irq); + local_irq_enable(); + + ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); + if (ret) + goto err_add_udc; + + pm_runtime_enable(dev); + + return 0; +err_add_udc: + clk_disable(hsudc->uclk); +err_res: + if (!IS_ERR_OR_NULL(hsudc->transceiver)) + usb_put_phy(hsudc->transceiver); + +err_supplies: + return ret; +} + +static struct platform_driver s3c_hsudc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "s3c-hsudc", + }, + .probe = s3c_hsudc_probe, +}; + +module_platform_driver(s3c_hsudc_driver); + +MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s3c-hsudc"); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 9a2b8920532..7987aa049fa 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -3,32 +3,23 @@ * * Samsung S3C24xx series on-chip full speed USB device controllers * - * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard * Additional cleanups by Ben Dooks <ben-linux@fluff.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ +#define pr_fmt(fmt) "s3c2410_udc: " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> @@ -37,6 +28,8 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/gpio.h> +#include <linux/prefetch.h> +#include <linux/io.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -45,23 +38,21 @@ #include <linux/usb/gadget.h> #include <asm/byteorder.h> -#include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/unaligned.h> #include <mach/irqs.h> #include <mach/hardware.h> #include <plat/regs-udc.h> -#include <plat/udc.h> +#include <linux/platform_data/usb-s3c2410_udc.h> #include "s3c2410_udc.h" #define DRIVER_DESC "S3C2410 USB Device Controller Gadget" #define DRIVER_VERSION "29 Apr 2007" -#define DRIVER_AUTHOR "Herbert Pötzl <herbert@13thfloor.at>, " \ +#define DRIVER_AUTHOR "Herbert Pötzl <herbert@13thfloor.at>, " \ "Arnaud Patard <arnaud.patard@rtp-net.org>" static const char gadget_name[] = "s3c2410_udc"; @@ -126,7 +117,8 @@ static int dprintk(int level, const char *fmt, ...) sizeof(printk_buf)-len, fmt, args); va_end(args); - return printk(KERN_DEBUG "%s", printk_buf); + pr_debug("%s", printk_buf); + return len; } #else static int dprintk(int level, const char *fmt, ...) @@ -136,10 +128,10 @@ static int dprintk(int level, const char *fmt, ...) #endif static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { - u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg; + u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; u32 ep_int_en_reg, usb_int_en_reg, ep0_csr; - u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2; - u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2; + u32 ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2; + u32 ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2; addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG); pwr_reg = udc_read(S3C2410_UDC_PWR_REG); @@ -175,10 +167,10 @@ static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) "EP2_I_CSR2 : 0x%04X\n" "EP2_O_CSR1 : 0x%04X\n" "EP2_O_CSR2 : 0x%04X\n", - addr_reg,pwr_reg,ep_int_reg,usb_int_reg, + addr_reg, pwr_reg, ep_int_reg, usb_int_reg, ep_int_en_reg, usb_int_en_reg, ep0_csr, - ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, - ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2 + ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2, + ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2 ); return 0; @@ -241,7 +233,7 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base) { udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); - udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY + udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY | S3C2410_UDC_EP0_CSR_DE), S3C2410_UDC_EP0_CSR_REG); } @@ -274,7 +266,7 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep, list_del_init(&req->queue); - if (likely (req->req.status == -EINPROGRESS)) + if (likely(req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; @@ -291,9 +283,9 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc, if (&ep->queue == NULL) return; - while (!list_empty (&ep->queue)) { + while (!list_empty(&ep->queue)) { struct s3c2410_request *req; - req = list_entry (ep->queue.next, struct s3c2410_request, + req = list_entry(ep->queue.next, struct s3c2410_request, queue); s3c2410_udc_done(ep, req, status); } @@ -400,10 +392,10 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, if (idx == 0) { /* Reset signal => no need to say 'data sent' */ - if (! (udc_read(S3C2410_UDC_USB_INT_REG) + if (!(udc_read(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET)) s3c2410_udc_set_ep0_de_in(base_addr); - ep->dev->ep0state=EP0_IDLE; + ep->dev->ep0state = EP0_IDLE; } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); @@ -417,7 +409,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, } else { if (idx == 0) { /* Reset signal => no need to say 'data sent' */ - if (! (udc_read(S3C2410_UDC_USB_INT_REG) + if (!(udc_read(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET)) s3c2410_udc_set_ep0_ipr(base_addr); } else { @@ -453,7 +445,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, u8 *buf; u32 ep_csr; unsigned bufferspace; - int is_last=1; + int is_last = 1; unsigned avail; int fifo_count = 0; u32 idx; @@ -521,7 +513,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, /* Only ep0 debug messages are interesting */ if (idx == 0) dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n", - __func__, fifo_count,is_last); + __func__, fifo_count, is_last); if (is_last) { if (idx == 0) { @@ -553,7 +545,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq) { - unsigned char *outbuf = (unsigned char*)crq; + unsigned char *outbuf = (unsigned char *)crq; int bytes_read = 0; udc_write(0, S3C2410_UDC_INDEX_REG); @@ -659,7 +651,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, switch (crq->bRequest) { case USB_REQ_SET_CONFIGURATION: - dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n"); + dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ...\n"); if (crq->bRequestType == USB_RECIP_DEVICE) { dev->req_config = 1; @@ -668,7 +660,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, break; case USB_REQ_SET_INTERFACE: - dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n"); + dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ...\n"); if (crq->bRequestType == USB_RECIP_INTERFACE) { dev->req_config = 1; @@ -677,7 +669,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, break; case USB_REQ_SET_ADDRESS: - dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n"); + dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ...\n"); if (crq->bRequestType == USB_RECIP_DEVICE) { tmp = crq->wValue & 0x7F; @@ -690,13 +682,12 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, break; case USB_REQ_GET_STATUS: - dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n"); + dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ...\n"); s3c2410_udc_clear_ep0_opr(base_addr); if (dev->req_std) { - if (!s3c2410_udc_get_status(dev, crq)) { + if (!s3c2410_udc_get_status(dev, crq)) return; - } } break; @@ -736,6 +727,10 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, else dev->ep0state = EP0_OUT_DATA_PHASE; + if (!dev->driver) + return; + + /* deliver the request to the gadget driver */ ret = dev->driver->setup(&dev->gadget, crq); if (ret < 0) { if (dev->req_config) { @@ -757,7 +752,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, /* deferred i/o == no response yet */ } else if (dev->req_pending) { dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n"); - dev->req_pending=0; + dev->req_pending = 0; } dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]); @@ -808,16 +803,14 @@ static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev) case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n"); - if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) { + if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) s3c2410_udc_write_fifo(ep, req); - } break; case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n"); - if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) { - s3c2410_udc_read_fifo(ep,req); - } + if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req) + s3c2410_udc_read_fifo(ep, req); break; case EP0_END_XFER: @@ -843,7 +836,7 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) u32 ep_csr1; u32 idx; - if (likely (!list_empty(&ep->queue))) + if (likely(!list_empty(&ep->queue))) req = list_entry(ep->queue.next, struct s3c2410_request, queue); else @@ -865,9 +858,8 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) return; } - if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) { - s3c2410_udc_write_fifo(ep,req); - } + if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) + s3c2410_udc_write_fifo(ep, req); } else { udc_write(idx, S3C2410_UDC_INDEX_REG); ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG); @@ -880,9 +872,8 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) return; } - if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { - s3c2410_udc_read_fifo(ep,req); - } + if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) + s3c2410_udc_read_fifo(ep, req); } } @@ -899,7 +890,7 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev) int pwr_reg; int ep0csr; int i; - u32 idx; + u32 idx, idx2; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); @@ -1014,6 +1005,20 @@ static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev) } } + /* what else causes this interrupt? a receive! who is it? */ + if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) { + for (i = 1; i < S3C2410_ENDPOINTS; i++) { + idx2 = udc_read(S3C2410_UDC_INDEX_REG); + udc_write(i, S3C2410_UDC_INDEX_REG); + + if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1) + s3c2410_udc_handle_ep(&dev->ep[i]); + + /* restore index */ + udc_write(idx2, S3C2410_UDC_INDEX_REG); + } + } + dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD); /* Restore old index */ @@ -1050,12 +1055,12 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, struct s3c2410_ep *ep; u32 max, tmp; unsigned long flags; - u32 csr1,csr2; + u32 csr1, csr2; u32 int_en_reg; ep = to_s3c2410_ep(_ep); - if (!_ep || !desc || ep->desc + if (!_ep || !desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; @@ -1064,11 +1069,11 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; - local_irq_save (flags); + local_irq_save(flags); _ep->maxpacket = max & 0x7ff; - ep->desc = desc; + ep->ep.desc = desc; ep->halted = 0; ep->bEndpointAddress = desc->bEndpointAddress; @@ -1110,11 +1115,11 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, /* print some debug message */ tmp = desc->bEndpointAddress; - dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", - _ep->name,ep->num, tmp, + dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", + _ep->name, ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max); - local_irq_restore (flags); + local_irq_restore(flags); s3c2410_udc_set_halt(_ep, 0); return 0; @@ -1129,7 +1134,7 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep) unsigned long flags; u32 int_en_reg; - if (!_ep || !ep->desc) { + if (!_ep || !ep->ep.desc) { dprintk(DEBUG_NORMAL, "%s not enabled\n", _ep ? ep->ep.name : NULL); return -EINVAL; @@ -1139,10 +1144,10 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep) dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); - ep->desc = NULL; + ep->ep.desc = NULL; ep->halted = 1; - s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); + s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN); /* disable irqs */ int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); @@ -1163,16 +1168,16 @@ s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { struct s3c2410_request *req; - dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags); + dprintk(DEBUG_VERBOSE, "%s(%p,%d)\n", __func__, _ep, mem_flags); if (!_ep) return NULL; - req = kzalloc (sizeof(struct s3c2410_request), mem_flags); + req = kzalloc(sizeof(struct s3c2410_request), mem_flags); if (!req) return NULL; - INIT_LIST_HEAD (&req->queue); + INIT_LIST_HEAD(&req->queue); return &req->req; } @@ -1187,10 +1192,10 @@ s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req) dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); - if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) + if (!ep || !_req || (!ep->ep.desc && _ep->name != ep0name)) return; - WARN_ON (!list_empty (&req->queue)); + WARN_ON(!list_empty(&req->queue)); kfree(req); } @@ -1207,18 +1212,18 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, int fifo_count = 0; unsigned long flags; - if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) { dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__); return -EINVAL; } dev = ep->dev; - if (unlikely (!dev->driver + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { return -ESHUTDOWN; } - local_irq_save (flags); + local_irq_save(flags); if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) { @@ -1226,7 +1231,7 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__); else { dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", - __func__, !_req->complete,!_req->buf, + __func__, !_req->complete, !_req->buf, !list_empty(&req->queue)); } @@ -1292,7 +1297,7 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, } /* pio or dma irq handler advances the queue. */ - if (likely (req != 0)) + if (likely(req)) list_add_tail(&req->queue, &ep->queue); local_irq_restore(flags); @@ -1322,11 +1327,11 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) udc = to_s3c2410_udc(ep->gadget); - local_irq_save (flags); + local_irq_save(flags); - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) { - list_del_init (&req->queue); + list_del_init(&req->queue); _req->status = -ECONNRESET; retval = 0; break; @@ -1341,7 +1346,7 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) s3c2410_udc_done(ep, req, -ECONNRESET); } - local_irq_restore (flags); + local_irq_restore(flags); return retval; } @@ -1355,12 +1360,12 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) unsigned long flags; u32 idx; - if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) { dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__); return -EINVAL; } - local_irq_save (flags); + local_irq_save(flags); idx = ep->bEndpointAddress & 0x7F; @@ -1369,7 +1374,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) s3c2410_udc_set_ep0_de_out(base_addr); } else { udc_write(idx, S3C2410_UDC_INDEX_REG); - ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) + ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG); @@ -1397,7 +1402,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) } ep->halted = value ? 1 : 0; - local_irq_restore (flags); + local_irq_restore(flags); return 0; } @@ -1464,7 +1469,9 @@ static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on) { dprintk(DEBUG_NORMAL, "%s()\n", __func__); - if (udc_info && udc_info->udc_command) { + if (udc_info && (udc_info->udc_command || + gpio_is_valid(udc_info->pullup_pin))) { + if (is_on) s3c2410_udc_enable(udc); else { @@ -1475,9 +1482,9 @@ static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on) } s3c2410_udc_disable(udc); } - } - else + } else { return -EOPNOTSUPP; + } return 0; } @@ -1532,6 +1539,11 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) return -ENOTSUPP; } +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, .wakeup = s3c2410_udc_wakeup, @@ -1539,8 +1551,36 @@ static const struct usb_gadget_ops s3c2410_ops = { .pullup = s3c2410_udc_pullup, .vbus_session = s3c2410_udc_vbus_session, .vbus_draw = s3c2410_vbus_draw, + .udc_start = s3c2410_udc_start, + .udc_stop = s3c2410_udc_stop, }; +static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd) +{ + if (!udc_info) + return; + + if (udc_info->udc_command) { + udc_info->udc_command(cmd); + } else if (gpio_is_valid(udc_info->pullup_pin)) { + int value; + + switch (cmd) { + case S3C2410_UDC_P_ENABLE: + value = 1; + break; + case S3C2410_UDC_P_DISABLE: + value = 0; + break; + default: + return; + } + value ^= udc_info->pullup_pin_inverted; + + gpio_set_value(udc_info->pullup_pin, value); + } +} + /*------------------------- gadget driver handling---------------------------*/ /* * s3c2410_udc_disable @@ -1562,8 +1602,7 @@ static void s3c2410_udc_disable(struct s3c2410_udc *dev) udc_write(0x1F, S3C2410_UDC_EP_INT_REG); /* Good bye, cruel world */ - if (udc_info && udc_info->udc_command) - udc_info->udc_command(S3C2410_UDC_P_DISABLE); + s3c2410_udc_command(S3C2410_UDC_P_DISABLE); /* Set speed to unknown */ dev->gadget.speed = USB_SPEED_UNKNOWN; @@ -1577,20 +1616,21 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev) u32 i; /* device/ep0 records init */ - INIT_LIST_HEAD (&dev->gadget.ep_list); - INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); dev->ep0state = EP0_IDLE; for (i = 0; i < S3C2410_ENDPOINTS; i++) { struct s3c2410_ep *ep = &dev->ep[i]; if (i != 0) - list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ep->dev = dev; - ep->desc = NULL; + ep->ep.desc = NULL; ep->halted = 0; - INIT_LIST_HEAD (&ep->queue); + INIT_LIST_HEAD(&ep->queue); + usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); } } @@ -1624,90 +1664,30 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev) udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG); /* time to say "hello, world" */ - if (udc_info && udc_info->udc_command) - udc_info->udc_command(S3C2410_UDC_P_ENABLE); + s3c2410_udc_command(S3C2410_UDC_P_ENABLE); } -/* - * usb_gadget_register_driver - */ -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +static int s3c2410_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct s3c2410_udc *udc = the_controller; - int retval; - - dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n", - driver->driver.name); - - /* Sanity checks */ - if (!udc) - return -ENODEV; - - if (udc->driver) - return -EBUSY; + struct s3c2410_udc *udc = to_s3c2410(g); - if (!driver->bind || !driver->setup - || driver->speed < USB_SPEED_FULL) { - printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", - driver->bind, driver->setup, driver->speed); - return -EINVAL; - } -#if defined(MODULE) - if (!driver->unbind) { - printk(KERN_ERR "Invalid driver: no unbind method\n"); - return -EINVAL; - } -#endif + dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name); /* Hook the driver */ udc->driver = driver; - udc->gadget.dev.driver = &driver->driver; - - /* Bind the driver */ - if ((retval = device_add(&udc->gadget.dev)) != 0) { - printk(KERN_ERR "Error in device_add() : %d\n",retval); - goto register_error; - } - - dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", - driver->driver.name); - - if ((retval = driver->bind (&udc->gadget)) != 0) { - 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; } -/* - * usb_gadget_unregister_driver - */ -int usb_gadget_unregister_driver(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_register_driver() '%s'\n", - driver->driver.name); - - if (driver->disconnect) - driver->disconnect(&udc->gadget); - - device_del(&udc->gadget.dev); udc->driver = NULL; /* Disable udc */ @@ -1830,8 +1810,8 @@ static int s3c2410_udc_probe(struct platform_device *pdev) memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE; } - spin_lock_init (&udc->lock); - udc_info = pdev->dev.platform_data; + spin_lock_init(&udc->lock); + udc_info = dev_get_platdata(&pdev->dev); rsrc_start = S3C2410_PA_USBDEV; rsrc_len = S3C24XX_SZ_USBDEV; @@ -1845,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); @@ -1857,7 +1833,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + 0, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1877,11 +1853,12 @@ 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; } retval = request_irq(irq, s3c2410_udc_vbus_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); @@ -1897,6 +1874,21 @@ static int s3c2410_udc_probe(struct platform_device *pdev) udc->vbus = 1; } + if (udc_info && !udc_info->udc_command && + gpio_is_valid(udc_info->pullup_pin)) { + + retval = gpio_request_one(udc_info->pullup_pin, + udc_info->vbus_pin_inverted ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + "udc pullup"); + if (retval) + goto err_vbus_irq; + } + + retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (retval) + goto err_add_udc; + if (s3c2410_udc_debugfs_root) { udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root, @@ -1909,6 +1901,13 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return 0; +err_add_udc: + if (udc_info && !udc_info->udc_command && + gpio_is_valid(udc_info->pullup_pin)) + gpio_free(udc_info->pullup_pin); +err_vbus_irq: + if (udc_info && udc_info->vbus_pin > 0) + free_irq(gpio_to_irq(udc_info->vbus_pin), udc); err_gpio_claim: if (udc_info && udc_info->vbus_pin > 0) gpio_free(udc_info->vbus_pin); @@ -1931,11 +1930,17 @@ static int s3c2410_udc_remove(struct platform_device *pdev) unsigned int irq; dev_dbg(&pdev->dev, "%s()\n", __func__); + if (udc->driver) return -EBUSY; + usb_del_gadget_udc(&udc->gadget); debugfs_remove(udc->regs_info); + if (udc_info && !udc_info->udc_command && + gpio_is_valid(udc_info->pullup_pin)) + gpio_free(udc_info->pullup_pin); + if (udc_info && udc_info->vbus_pin > 0) { irq = gpio_to_irq(udc_info->vbus_pin); free_irq(irq, udc); @@ -1946,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); @@ -1965,18 +1968,17 @@ static int s3c2410_udc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message) +static int +s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message) { - if (udc_info && udc_info->udc_command) - udc_info->udc_command(S3C2410_UDC_P_DISABLE); + s3c2410_udc_command(S3C2410_UDC_P_DISABLE); return 0; } static int s3c2410_udc_resume(struct platform_device *pdev) { - if (udc_info && udc_info->udc_command) - udc_info->udc_command(S3C2410_UDC_P_ENABLE); + s3c2410_udc_command(S3C2410_UDC_P_ENABLE); return 0; } @@ -1985,26 +1987,23 @@ static int s3c2410_udc_resume(struct platform_device *pdev) #define s3c2410_udc_resume NULL #endif -static struct platform_driver udc_driver_2410 = { - .driver = { - .name = "s3c2410-usbgadget", - .owner = THIS_MODULE, - }, - .probe = s3c2410_udc_probe, - .remove = s3c2410_udc_remove, - .suspend = s3c2410_udc_suspend, - .resume = s3c2410_udc_resume, +static const struct platform_device_id s3c_udc_ids[] = { + { "s3c2410-usbgadget", }, + { "s3c2440-usbgadget", }, + { } }; +MODULE_DEVICE_TABLE(platform, s3c_udc_ids); -static struct platform_driver udc_driver_2440 = { +static struct platform_driver udc_driver_24x0 = { .driver = { - .name = "s3c2440-usbgadget", + .name = "s3c24x0-usbgadget", .owner = THIS_MODULE, }, .probe = s3c2410_udc_probe, .remove = s3c2410_udc_remove, .suspend = s3c2410_udc_suspend, .resume = s3c2410_udc_resume, + .id_table = s3c_udc_ids, }; static int __init udc_init(void) @@ -2015,16 +2014,12 @@ static int __init udc_init(void) s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); if (IS_ERR(s3c2410_udc_debugfs_root)) { - printk(KERN_ERR "%s: debugfs dir creation failed %ld\n", + pr_err("%s: debugfs dir creation failed %ld\n", gadget_name, PTR_ERR(s3c2410_udc_debugfs_root)); s3c2410_udc_debugfs_root = NULL; } - retval = platform_driver_register(&udc_driver_2410); - if (retval) - goto err; - - retval = platform_driver_register(&udc_driver_2440); + retval = platform_driver_register(&udc_driver_24x0); if (retval) goto err; @@ -2037,14 +2032,10 @@ err: static void __exit udc_exit(void) { - platform_driver_unregister(&udc_driver_2410); - platform_driver_unregister(&udc_driver_2440); + platform_driver_unregister(&udc_driver_24x0); debugfs_remove(s3c2410_udc_debugfs_root); } -EXPORT_SYMBOL(usb_gadget_unregister_driver); -EXPORT_SYMBOL(usb_gadget_register_driver); - module_init(udc_init); module_exit(udc_exit); @@ -2052,5 +2043,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c2410-usbgadget"); -MODULE_ALIAS("platform:s3c2440-usbgadget"); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h index 9e0bece4f24..93bf225f196 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -2,23 +2,13 @@ * linux/drivers/usb/gadget/s3c2410_udc.h * Samsung on-chip full speed USB device controllers * - * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard * Additional cleanups by Ben Dooks <ben-linux@fluff.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _S3C2410_UDC_H @@ -29,7 +19,6 @@ struct s3c2410_ep { unsigned long last_io; /* jiffies timestamp */ struct usb_gadget *gadget; struct s3c2410_udc *dev; - const struct usb_endpoint_descriptor *desc; struct usb_ep ep; u8 num; @@ -106,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 f46a60962da..1f5f978d35d 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -11,8 +11,8 @@ */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> +#include <linux/module.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -29,25 +29,7 @@ #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 "composite.c" -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" - -#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. * @@ -61,15 +43,12 @@ /* string IDs are assigned dynamically */ -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_DESCRIPTION_IDX 2 - -static char manufacturer[50]; +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME, + [USB_GADGET_SERIAL_IDX].s = "", [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, { } /* end of list */ }; @@ -94,7 +73,7 @@ static struct usb_device_descriptor device_desc = { /* .bMaxPacketSize0 = f(hardware) */ .idVendor = cpu_to_le16(GS_VENDOR_ID), /* .idProduct = f(use_acm) */ - /* .bcdDevice = f(hardware) */ + .bcdDevice = cpu_to_le16(GS_VERSION_NUM), /* .iManufacturer = DYNAMIC */ /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, @@ -123,11 +102,11 @@ MODULE_AUTHOR("Al Borchers"); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); -static int use_acm = true; +static bool use_acm = true; module_param(use_acm, bool, 0); MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); -static int use_obex = false; +static bool use_obex = false; module_param(use_obex, bool, 0); MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no"); @@ -137,112 +116,127 @@ 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) */ - .bind = serial_bind_config, /* .bConfigurationValue = f(use_acm) */ /* .iConfiguration = DYNAMIC */ .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 gcnum; - struct usb_gadget *gadget = cdev->gadget; 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. */ - /* device description: manufacturer, product */ - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - - device_desc.iManufacturer = status; - - status = usb_string_id(cdev); + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; - strings_dev[STRING_PRODUCT_IDX].id = status; - - device_desc.iProduct = status; - - /* config description */ - status = usb_string_id(cdev); - if (status < 0) - goto fail; - strings_dev[STRING_DESCRIPTION_IDX].id = status; - + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + status = strings_dev[STRING_DESCRIPTION_IDX].id; serial_config_driver.iConfiguration = status; - /* set up other descriptors */ - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); - else { - /* this is so simple (for now, no altsettings) that it - * SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrcognized controllers -- don't panic. - * - * things like configuration and altsetting numbering - * can need hardware-specific attention though. - */ - pr_warning("gs_bind: controller '%s' not recognized\n", - gadget->name); - device_desc.bcdDevice = - cpu_to_le16(GS_VERSION_NUM | 0x0099); - } - if (gadget_is_otg(cdev->gadget)) { serial_config_driver.descriptors = otg_desc; serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } /* register our configuration */ - status = usb_add_config(cdev, &serial_config_driver); + if (use_acm) { + status = serial_register_ports(cdev, &serial_config_driver, + "acm"); + usb_ep_autoconfig_reset(cdev->gadget); + } else if (use_obex) + status = serial_register_ports(cdev, &serial_config_driver, + "obex"); + else { + status = serial_register_ports(cdev, &serial_config_driver, + "gser"); + } if (status < 0) goto fail; + usb_composite_overwrite_options(cdev, &coverwrite); INFO(cdev, "%s\n", GS_VERSION_NAME); return 0; fail: - gserial_cleanup(); return status; } -static struct usb_composite_driver gserial_driver = { +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) @@ -271,13 +265,12 @@ static int __init init(void) } strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - return usb_composite_register(&gserial_driver); + return usb_composite_probe(&gserial_driver); } 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 new file mode 100644 index 00000000000..648f9e489b3 --- /dev/null +++ b/drivers/usb/gadget/storage_common.c @@ -0,0 +1,504 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (mina86@mina86.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 file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_interface -- name of the interface + */ + +/* + * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers + * sets the number of pipeline buffers (length of the fsg_buffhd array). + * 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 "storage_common.h" + +/* There is only one interface. */ + +struct usb_interface_descriptor fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .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. + */ + +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; +EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc); + +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; +EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc); + +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); + + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; +EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc); + +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; +EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc); + + +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); + +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_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); + +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; +EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_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); + +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, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, + NULL, +}; +EXPORT_SYMBOL_GPL(fsg_ss_function); + + + /*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +void fsg_lun_close(struct fsg_lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} +EXPORT_SYMBOL_GPL(fsg_lun_close); + +int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + unsigned int blkbits; + unsigned int blksize; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + if (!ro) { + filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); + if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES) + ro = 1; + } + if (ro) + filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + 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; + } + + /* + * 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_mode & FMODE_CAN_READ)) { + LINFO(curlun, "file not readable: %s\n", filename); + goto out; + } + if (!(filp->f_mode & FMODE_CAN_WRITE)) + ro = 1; + + size = i_size_read(inode->i_mapping->host); + if (size < 0) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + + if (curlun->cdrom) { + blksize = 2048; + blkbits = 11; + } else if (inode->i_bdev) { + blksize = bdev_logical_block_size(inode->i_bdev); + blkbits = blksize_bits(blksize); + } else { + blksize = 512; + blkbits = 9; + } + + num_sectors = size >> blkbits; /* File size in logic-block-size blocks */ + min_sectors = 1; + if (curlun->cdrom) { + min_sectors = 300; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75) { + num_sectors = 256*60*75 - 1; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + if (fsg_lun_is_open(curlun)) + fsg_lun_close(curlun); + + curlun->blksize = blksize; + curlun->blkbits = blkbits; + curlun->ro = ro; + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + return 0; + +out: + fput(filp); + return rc; +} +EXPORT_SYMBOL_GPL(fsg_lun_open); + + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + + if (curlun->ro || !filp) + return 0; + return vfs_fsync(filp, 1); +} +EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub); + +void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} +EXPORT_SYMBOL_GPL(store_cdrom_address); + +/*-------------------------------------------------------------------------*/ + + +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); +} +EXPORT_SYMBOL_GPL(fsg_show_ro); + +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->nofua); +} +EXPORT_SYMBOL_GPL(fsg_show_nofua); + +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf) +{ + char *p; + ssize_t rc; + + down_read(filesem); + if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } + } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } + 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); + +/* + * 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; + bool ro; + + rc = strtobool(buf, &ro); + if (rc) + return rc; + + /* + * Allow the write-enable status to change only while the + * backing file is closed. + */ + down_read(filesem); + rc = _fsg_store_ro(curlun, ro); + if (!rc) + rc = count; + up_read(filesem); + + return rc; +} +EXPORT_SYMBOL_GPL(fsg_store_ro); + +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) +{ + bool nofua; + int ret; + + ret = strtobool(buf, &nofua); + if (ret) + return ret; + + /* Sync data when switching from async mode to sync */ + if (!nofua && curlun->nofua) + fsg_lun_fsync_sub(curlun); + + curlun->nofua = nofua; + + return count; +} +EXPORT_SYMBOL_GPL(fsg_store_nofua); + +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + int rc = 0; + + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; /* "Door is locked" */ + } + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; /* Ugh! */ + + /* Load new medium */ + down_write(filesem); + if (count > 0 && buf[0]) { + /* fsg_lun_open() will close existing file if any. */ + rc = fsg_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } else if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + 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 new file mode 100644 index 00000000000..6cdb7a534f2 --- /dev/null +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -0,0 +1,2473 @@ +/* Target based USB-Gadget + * + * UAS protocol handling, target callbacks, configfs handling, + * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling. + * + * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de> + * License: GPLv2 as published by FSF. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/configfs.h> +#include <linux/ctype.h> +#include <linux/usb/ch9.h> +#include <linux/usb/composite.h> +#include <linux/usb/gadget.h> +#include <linux/usb/storage.h> +#include <scsi/scsi.h> +#include <scsi/scsi_tcq.h> +#include <target/target_core_base.h> +#include <target/target_core_fabric.h> +#include <target/target_core_fabric_configfs.h> +#include <target/target_core_configfs.h> +#include <target/configfs_macros.h> +#include <asm/unaligned.h> + +#include "tcm_usb_gadget.h" + +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct target_fabric_configfs *usbg_fabric_configfs; + +static inline struct f_uas *to_f_uas(struct usb_function *f) +{ + return container_of(f, struct f_uas, function); +} + +static void usbg_cmd_release(struct kref *); + +static inline void usbg_cleanup_cmd(struct usbg_cmd *cmd) +{ + kref_put(&cmd->ref, usbg_cmd_release); +} + +/* Start bot.c code */ + +static int bot_enqueue_cmd_cbw(struct f_uas *fu) +{ + int ret; + + if (fu->flags & USBG_BOT_CMD_PEND) + return 0; + + ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC); + if (!ret) + fu->flags |= USBG_BOT_CMD_PEND; + return ret; +} + +static void bot_status_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct usbg_cmd *cmd = req->context; + struct f_uas *fu = cmd->fu; + + usbg_cleanup_cmd(cmd); + if (req->status < 0) { + pr_err("ERR %s(%d)\n", __func__, __LINE__); + return; + } + + /* CSW completed, wait for next CBW */ + bot_enqueue_cmd_cbw(fu); +} + +static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd) +{ + struct bulk_cs_wrap *csw = &fu->bot_status.csw; + int ret; + u8 *sense; + unsigned int csw_stat; + + csw_stat = cmd->csw_code; + + /* + * We can't send SENSE as a response. So we take ASC & ASCQ from our + * sense buffer and queue it and hope the host sends a REQUEST_SENSE + * command where it learns why we failed. + */ + sense = cmd->sense_iu.sense; + + csw->Tag = cmd->bot_tag; + csw->Status = csw_stat; + fu->bot_status.req->context = cmd; + ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC); + if (ret) + pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret); +} + +static void bot_err_compl(struct usb_ep *ep, struct usb_request *req) +{ + struct usbg_cmd *cmd = req->context; + struct f_uas *fu = cmd->fu; + + if (req->status < 0) + pr_err("ERR %s(%d)\n", __func__, __LINE__); + + if (cmd->data_len) { + if (cmd->data_len > ep->maxpacket) { + req->length = ep->maxpacket; + cmd->data_len -= ep->maxpacket; + } else { + req->length = cmd->data_len; + cmd->data_len = 0; + } + + usb_ep_queue(ep, req, GFP_ATOMIC); + return ; + } + bot_enqueue_sense_code(fu, cmd); +} + +static void bot_send_bad_status(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct bulk_cs_wrap *csw = &fu->bot_status.csw; + struct usb_request *req; + struct usb_ep *ep; + + csw->Residue = cpu_to_le32(cmd->data_len); + + if (cmd->data_len) { + if (cmd->is_read) { + ep = fu->ep_in; + req = fu->bot_req_in; + } else { + ep = fu->ep_out; + req = fu->bot_req_out; + } + + if (cmd->data_len > fu->ep_in->maxpacket) { + req->length = ep->maxpacket; + cmd->data_len -= ep->maxpacket; + } else { + req->length = cmd->data_len; + cmd->data_len = 0; + } + req->complete = bot_err_compl; + req->context = cmd; + req->buf = fu->cmd.buf; + usb_ep_queue(ep, req, GFP_KERNEL); + } else { + bot_enqueue_sense_code(fu, cmd); + } +} + +static int bot_send_status(struct usbg_cmd *cmd, bool moved_data) +{ + struct f_uas *fu = cmd->fu; + struct bulk_cs_wrap *csw = &fu->bot_status.csw; + int ret; + + if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) { + if (!moved_data && cmd->data_len) { + /* + * the host wants to move data, we don't. Fill / empty + * the pipe and then send the csw with reside set. + */ + cmd->csw_code = US_BULK_STAT_OK; + bot_send_bad_status(cmd); + return 0; + } + + csw->Tag = cmd->bot_tag; + csw->Residue = cpu_to_le32(0); + csw->Status = US_BULK_STAT_OK; + fu->bot_status.req->context = cmd; + + ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_KERNEL); + if (ret) + pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret); + } else { + cmd->csw_code = US_BULK_STAT_FAIL; + bot_send_bad_status(cmd); + } + return 0; +} + +/* + * Called after command (no data transfer) or after the write (to device) + * operation is completed + */ +static int bot_send_status_response(struct usbg_cmd *cmd) +{ + bool moved_data = false; + + if (!cmd->is_read) + moved_data = true; + return bot_send_status(cmd, moved_data); +} + +/* Read request completed, now we have to send the CSW */ +static void bot_read_compl(struct usb_ep *ep, struct usb_request *req) +{ + struct usbg_cmd *cmd = req->context; + + if (req->status < 0) + pr_err("ERR %s(%d)\n", __func__, __LINE__); + + bot_send_status(cmd, true); +} + +static int bot_send_read_response(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct se_cmd *se_cmd = &cmd->se_cmd; + struct usb_gadget *gadget = fuas_to_gadget(fu); + int ret; + + if (!cmd->data_len) { + cmd->csw_code = US_BULK_STAT_PHASE; + bot_send_bad_status(cmd); + return 0; + } + + if (!gadget->sg_supported) { + cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC); + if (!cmd->data_buf) + return -ENOMEM; + + sg_copy_to_buffer(se_cmd->t_data_sg, + se_cmd->t_data_nents, + cmd->data_buf, + se_cmd->data_length); + + fu->bot_req_in->buf = cmd->data_buf; + } else { + fu->bot_req_in->buf = NULL; + fu->bot_req_in->num_sgs = se_cmd->t_data_nents; + fu->bot_req_in->sg = se_cmd->t_data_sg; + } + + fu->bot_req_in->complete = bot_read_compl; + fu->bot_req_in->length = se_cmd->data_length; + fu->bot_req_in->context = cmd; + ret = usb_ep_queue(fu->ep_in, fu->bot_req_in, GFP_ATOMIC); + if (ret) + pr_err("%s(%d)\n", __func__, __LINE__); + return 0; +} + +static void usbg_data_write_cmpl(struct usb_ep *, struct usb_request *); +static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *); + +static int bot_send_write_request(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct se_cmd *se_cmd = &cmd->se_cmd; + struct usb_gadget *gadget = fuas_to_gadget(fu); + int ret; + + init_completion(&cmd->write_complete); + cmd->fu = fu; + + if (!cmd->data_len) { + cmd->csw_code = US_BULK_STAT_PHASE; + return -EINVAL; + } + + if (!gadget->sg_supported) { + cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL); + if (!cmd->data_buf) + return -ENOMEM; + + fu->bot_req_out->buf = cmd->data_buf; + } else { + fu->bot_req_out->buf = NULL; + fu->bot_req_out->num_sgs = se_cmd->t_data_nents; + fu->bot_req_out->sg = se_cmd->t_data_sg; + } + + fu->bot_req_out->complete = usbg_data_write_cmpl; + fu->bot_req_out->length = se_cmd->data_length; + fu->bot_req_out->context = cmd; + + ret = usbg_prepare_w_request(cmd, fu->bot_req_out); + if (ret) + goto cleanup; + ret = usb_ep_queue(fu->ep_out, fu->bot_req_out, GFP_KERNEL); + if (ret) + pr_err("%s(%d)\n", __func__, __LINE__); + + wait_for_completion(&cmd->write_complete); + target_execute_cmd(se_cmd); +cleanup: + return ret; +} + +static int bot_submit_command(struct f_uas *, void *, unsigned int); + +static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_uas *fu = req->context; + int ret; + + fu->flags &= ~USBG_BOT_CMD_PEND; + + if (req->status < 0) + return; + + ret = bot_submit_command(fu, req->buf, req->actual); + if (ret) + pr_err("%s(%d): %d\n", __func__, __LINE__, ret); +} + +static int bot_prepare_reqs(struct f_uas *fu) +{ + int ret; + + fu->bot_req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); + if (!fu->bot_req_in) + goto err; + + fu->bot_req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); + if (!fu->bot_req_out) + goto err_out; + + fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); + if (!fu->cmd.req) + goto err_cmd; + + fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); + if (!fu->bot_status.req) + goto err_sts; + + fu->bot_status.req->buf = &fu->bot_status.csw; + fu->bot_status.req->length = US_BULK_CS_WRAP_LEN; + fu->bot_status.req->complete = bot_status_complete; + fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN); + + fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL); + if (!fu->cmd.buf) + goto err_buf; + + fu->cmd.req->complete = bot_cmd_complete; + fu->cmd.req->buf = fu->cmd.buf; + fu->cmd.req->length = fu->ep_out->maxpacket; + fu->cmd.req->context = fu; + + ret = bot_enqueue_cmd_cbw(fu); + if (ret) + goto err_queue; + return 0; +err_queue: + kfree(fu->cmd.buf); + fu->cmd.buf = NULL; +err_buf: + usb_ep_free_request(fu->ep_in, fu->bot_status.req); +err_sts: + usb_ep_free_request(fu->ep_out, fu->cmd.req); + fu->cmd.req = NULL; +err_cmd: + usb_ep_free_request(fu->ep_out, fu->bot_req_out); + fu->bot_req_out = NULL; +err_out: + usb_ep_free_request(fu->ep_in, fu->bot_req_in); + fu->bot_req_in = NULL; +err: + pr_err("BOT: endpoint setup failed\n"); + return -ENOMEM; +} + +static void bot_cleanup_old_alt(struct f_uas *fu) +{ + if (!(fu->flags & USBG_ENABLED)) + return; + + usb_ep_disable(fu->ep_in); + usb_ep_disable(fu->ep_out); + + if (!fu->bot_req_in) + return; + + usb_ep_free_request(fu->ep_in, fu->bot_req_in); + usb_ep_free_request(fu->ep_out, fu->bot_req_out); + usb_ep_free_request(fu->ep_out, fu->cmd.req); + usb_ep_free_request(fu->ep_out, fu->bot_status.req); + + kfree(fu->cmd.buf); + + fu->bot_req_in = NULL; + fu->bot_req_out = NULL; + fu->cmd.req = NULL; + fu->bot_status.req = NULL; + fu->cmd.buf = NULL; +} + +static void bot_set_alt(struct f_uas *fu) +{ + struct usb_function *f = &fu->function; + struct usb_gadget *gadget = f->config->cdev->gadget; + int ret; + + fu->flags = USBG_IS_BOT; + + config_ep_by_speed(gadget, f, fu->ep_in); + ret = usb_ep_enable(fu->ep_in); + if (ret) + goto err_b_in; + + config_ep_by_speed(gadget, f, fu->ep_out); + ret = usb_ep_enable(fu->ep_out); + if (ret) + goto err_b_out; + + ret = bot_prepare_reqs(fu); + if (ret) + goto err_wq; + fu->flags |= USBG_ENABLED; + pr_info("Using the BOT protocol\n"); + return; +err_wq: + usb_ep_disable(fu->ep_out); +err_b_out: + usb_ep_disable(fu->ep_in); +err_b_in: + fu->flags = USBG_IS_BOT; +} + +static int usbg_bot_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_uas *fu = to_f_uas(f); + struct usb_composite_dev *cdev = f->config->cdev; + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + int luns; + u8 *ret_lun; + + switch (ctrl->bRequest) { + case US_BULK_GET_MAX_LUN: + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE)) + return -ENOTSUPP; + + if (w_length < 1) + return -EINVAL; + if (w_value != 0) + return -EINVAL; + luns = atomic_read(&fu->tpg->tpg_port_count); + if (!luns) { + pr_err("No LUNs configured?\n"); + return -EINVAL; + } + /* + * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be + * accessed. The upper limit is 0xf + */ + luns--; + if (luns > 0xf) { + pr_info_once("Limiting the number of luns to 16\n"); + luns = 0xf; + } + ret_lun = cdev->req->buf; + *ret_lun = luns; + cdev->req->length = 1; + return usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + break; + + case US_BULK_RESET_REQUEST: + /* XXX maybe we should remove previous requests for IN + OUT */ + bot_enqueue_cmd_cbw(fu); + return 0; + break; + } + return -ENOTSUPP; +} + +/* Start uas.c code */ + +static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream) +{ + /* We have either all three allocated or none */ + if (!stream->req_in) + return; + + usb_ep_free_request(fu->ep_in, stream->req_in); + usb_ep_free_request(fu->ep_out, stream->req_out); + usb_ep_free_request(fu->ep_status, stream->req_status); + + stream->req_in = NULL; + stream->req_out = NULL; + stream->req_status = NULL; +} + +static void uasp_free_cmdreq(struct f_uas *fu) +{ + usb_ep_free_request(fu->ep_cmd, fu->cmd.req); + kfree(fu->cmd.buf); + fu->cmd.req = NULL; + fu->cmd.buf = NULL; +} + +static void uasp_cleanup_old_alt(struct f_uas *fu) +{ + int i; + + if (!(fu->flags & USBG_ENABLED)) + return; + + usb_ep_disable(fu->ep_in); + usb_ep_disable(fu->ep_out); + usb_ep_disable(fu->ep_status); + usb_ep_disable(fu->ep_cmd); + + for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++) + uasp_cleanup_one_stream(fu, &fu->stream[i]); + uasp_free_cmdreq(fu); +} + +static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req); + +static int uasp_prepare_r_request(struct usbg_cmd *cmd) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct f_uas *fu = cmd->fu; + struct usb_gadget *gadget = fuas_to_gadget(fu); + struct uas_stream *stream = cmd->stream; + + if (!gadget->sg_supported) { + cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC); + if (!cmd->data_buf) + return -ENOMEM; + + sg_copy_to_buffer(se_cmd->t_data_sg, + se_cmd->t_data_nents, + cmd->data_buf, + se_cmd->data_length); + + stream->req_in->buf = cmd->data_buf; + } else { + stream->req_in->buf = NULL; + stream->req_in->num_sgs = se_cmd->t_data_nents; + stream->req_in->sg = se_cmd->t_data_sg; + } + + stream->req_in->complete = uasp_status_data_cmpl; + stream->req_in->length = se_cmd->data_length; + stream->req_in->context = cmd; + + cmd->state = UASP_SEND_STATUS; + return 0; +} + +static void uasp_prepare_status(struct usbg_cmd *cmd) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct sense_iu *iu = &cmd->sense_iu; + struct uas_stream *stream = cmd->stream; + + cmd->state = UASP_QUEUE_COMMAND; + iu->iu_id = IU_ID_STATUS; + iu->tag = cpu_to_be16(cmd->tag); + + /* + * iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where R U?); + */ + iu->len = cpu_to_be16(se_cmd->scsi_sense_length); + iu->status = se_cmd->scsi_status; + stream->req_status->context = cmd; + stream->req_status->length = se_cmd->scsi_sense_length + 16; + stream->req_status->buf = iu; + stream->req_status->complete = uasp_status_data_cmpl; +} + +static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) +{ + struct usbg_cmd *cmd = req->context; + struct uas_stream *stream = cmd->stream; + struct f_uas *fu = cmd->fu; + int ret; + + if (req->status < 0) + goto cleanup; + + switch (cmd->state) { + case UASP_SEND_DATA: + ret = uasp_prepare_r_request(cmd); + if (ret) + goto cleanup; + ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC); + if (ret) + pr_err("%s(%d) => %d\n", __func__, __LINE__, ret); + break; + + case UASP_RECEIVE_DATA: + ret = usbg_prepare_w_request(cmd, stream->req_out); + if (ret) + goto cleanup; + ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC); + if (ret) + pr_err("%s(%d) => %d\n", __func__, __LINE__, ret); + break; + + case UASP_SEND_STATUS: + uasp_prepare_status(cmd); + ret = usb_ep_queue(fu->ep_status, stream->req_status, + GFP_ATOMIC); + if (ret) + pr_err("%s(%d) => %d\n", __func__, __LINE__, ret); + break; + + case UASP_QUEUE_COMMAND: + usbg_cleanup_cmd(cmd); + usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + break; + + default: + BUG(); + } + return; + +cleanup: + usbg_cleanup_cmd(cmd); +} + +static int uasp_send_status_response(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct uas_stream *stream = cmd->stream; + struct sense_iu *iu = &cmd->sense_iu; + + iu->tag = cpu_to_be16(cmd->tag); + stream->req_status->complete = uasp_status_data_cmpl; + stream->req_status->context = cmd; + cmd->fu = fu; + uasp_prepare_status(cmd); + return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC); +} + +static int uasp_send_read_response(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct uas_stream *stream = cmd->stream; + struct sense_iu *iu = &cmd->sense_iu; + int ret; + + cmd->fu = fu; + + iu->tag = cpu_to_be16(cmd->tag); + if (fu->flags & USBG_USE_STREAMS) { + + ret = uasp_prepare_r_request(cmd); + if (ret) + goto out; + ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC); + if (ret) { + pr_err("%s(%d) => %d\n", __func__, __LINE__, ret); + kfree(cmd->data_buf); + cmd->data_buf = NULL; + } + + } else { + + iu->iu_id = IU_ID_READ_READY; + iu->tag = cpu_to_be16(cmd->tag); + + stream->req_status->complete = uasp_status_data_cmpl; + stream->req_status->context = cmd; + + cmd->state = UASP_SEND_DATA; + stream->req_status->buf = iu; + stream->req_status->length = sizeof(struct iu); + + ret = usb_ep_queue(fu->ep_status, stream->req_status, + GFP_ATOMIC); + if (ret) + pr_err("%s(%d) => %d\n", __func__, __LINE__, ret); + } +out: + return ret; +} + +static int uasp_send_write_request(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct se_cmd *se_cmd = &cmd->se_cmd; + struct uas_stream *stream = cmd->stream; + struct sense_iu *iu = &cmd->sense_iu; + int ret; + + init_completion(&cmd->write_complete); + cmd->fu = fu; + + iu->tag = cpu_to_be16(cmd->tag); + + if (fu->flags & USBG_USE_STREAMS) { + + ret = usbg_prepare_w_request(cmd, stream->req_out); + if (ret) + goto cleanup; + ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC); + if (ret) + pr_err("%s(%d)\n", __func__, __LINE__); + + } else { + + iu->iu_id = IU_ID_WRITE_READY; + iu->tag = cpu_to_be16(cmd->tag); + + stream->req_status->complete = uasp_status_data_cmpl; + stream->req_status->context = cmd; + + cmd->state = UASP_RECEIVE_DATA; + stream->req_status->buf = iu; + stream->req_status->length = sizeof(struct iu); + + ret = usb_ep_queue(fu->ep_status, stream->req_status, + GFP_ATOMIC); + if (ret) + pr_err("%s(%d)\n", __func__, __LINE__); + } + + wait_for_completion(&cmd->write_complete); + target_execute_cmd(se_cmd); +cleanup: + return ret; +} + +static int usbg_submit_command(struct f_uas *, void *, unsigned int); + +static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_uas *fu = req->context; + int ret; + + if (req->status < 0) + return; + + ret = usbg_submit_command(fu, req->buf, req->actual); + /* + * Once we tune for performance enqueue the command req here again so + * we can receive a second command while we processing this one. Pay + * attention to properly sync STAUS endpoint with DATA IN + OUT so you + * don't break HS. + */ + if (!ret) + return; + usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); +} + +static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream) +{ + stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); + if (!stream->req_in) + goto out; + + stream->req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); + if (!stream->req_out) + goto err_out; + + stream->req_status = usb_ep_alloc_request(fu->ep_status, GFP_KERNEL); + if (!stream->req_status) + goto err_sts; + + return 0; +err_sts: + usb_ep_free_request(fu->ep_status, stream->req_status); + stream->req_status = NULL; +err_out: + usb_ep_free_request(fu->ep_out, stream->req_out); + stream->req_out = NULL; +out: + return -ENOMEM; +} + +static int uasp_alloc_cmd(struct f_uas *fu) +{ + fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL); + if (!fu->cmd.req) + goto err; + + fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL); + if (!fu->cmd.buf) + goto err_buf; + + fu->cmd.req->complete = uasp_cmd_complete; + fu->cmd.req->buf = fu->cmd.buf; + fu->cmd.req->length = fu->ep_cmd->maxpacket; + fu->cmd.req->context = fu; + return 0; + +err_buf: + usb_ep_free_request(fu->ep_cmd, fu->cmd.req); +err: + return -ENOMEM; +} + +static void uasp_setup_stream_res(struct f_uas *fu, int max_streams) +{ + int i; + + for (i = 0; i < max_streams; i++) { + struct uas_stream *s = &fu->stream[i]; + + s->req_in->stream_id = i + 1; + s->req_out->stream_id = i + 1; + s->req_status->stream_id = i + 1; + } +} + +static int uasp_prepare_reqs(struct f_uas *fu) +{ + int ret; + int i; + int max_streams; + + if (fu->flags & USBG_USE_STREAMS) + max_streams = UASP_SS_EP_COMP_NUM_STREAMS; + else + max_streams = 1; + + for (i = 0; i < max_streams; i++) { + ret = uasp_alloc_stream_res(fu, &fu->stream[i]); + if (ret) + goto err_cleanup; + } + + ret = uasp_alloc_cmd(fu); + if (ret) + goto err_free_stream; + uasp_setup_stream_res(fu, max_streams); + + ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + if (ret) + goto err_free_stream; + + return 0; + +err_free_stream: + uasp_free_cmdreq(fu); + +err_cleanup: + if (i) { + do { + uasp_cleanup_one_stream(fu, &fu->stream[i - 1]); + i--; + } while (i); + } + pr_err("UASP: endpoint setup failed\n"); + return ret; +} + +static void uasp_set_alt(struct f_uas *fu) +{ + struct usb_function *f = &fu->function; + struct usb_gadget *gadget = f->config->cdev->gadget; + int ret; + + fu->flags = USBG_IS_UAS; + + if (gadget->speed == USB_SPEED_SUPER) + fu->flags |= USBG_USE_STREAMS; + + config_ep_by_speed(gadget, f, fu->ep_in); + ret = usb_ep_enable(fu->ep_in); + if (ret) + goto err_b_in; + + config_ep_by_speed(gadget, f, fu->ep_out); + ret = usb_ep_enable(fu->ep_out); + if (ret) + goto err_b_out; + + config_ep_by_speed(gadget, f, fu->ep_cmd); + ret = usb_ep_enable(fu->ep_cmd); + if (ret) + goto err_cmd; + config_ep_by_speed(gadget, f, fu->ep_status); + ret = usb_ep_enable(fu->ep_status); + if (ret) + goto err_status; + + ret = uasp_prepare_reqs(fu); + if (ret) + goto err_wq; + fu->flags |= USBG_ENABLED; + + pr_info("Using the UAS protocol\n"); + return; +err_wq: + usb_ep_disable(fu->ep_status); +err_status: + usb_ep_disable(fu->ep_cmd); +err_cmd: + usb_ep_disable(fu->ep_out); +err_b_out: + usb_ep_disable(fu->ep_in); +err_b_in: + fu->flags = 0; +} + +static int get_cmd_dir(const unsigned char *cdb) +{ + int ret; + + switch (cdb[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case SERVICE_ACTION_IN: + case MAINTENANCE_IN: + case PERSISTENT_RESERVE_IN: + case SECURITY_PROTOCOL_IN: + case ACCESS_CONTROL_IN: + case REPORT_LUNS: + case READ_BLOCK_LIMITS: + case READ_POSITION: + case READ_CAPACITY: + case READ_TOC: + case READ_FORMAT_CAPACITIES: + case REQUEST_SENSE: + ret = DMA_FROM_DEVICE; + break; + + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case MODE_SELECT: + case MODE_SELECT_10: + case WRITE_VERIFY: + case WRITE_VERIFY_12: + case PERSISTENT_RESERVE_OUT: + case MAINTENANCE_OUT: + case SECURITY_PROTOCOL_OUT: + case ACCESS_CONTROL_OUT: + ret = DMA_TO_DEVICE; + break; + case ALLOW_MEDIUM_REMOVAL: + case TEST_UNIT_READY: + case SYNCHRONIZE_CACHE: + case START_STOP: + case ERASE: + case REZERO_UNIT: + case SEEK_10: + case SPACE: + case VERIFY: + case WRITE_FILEMARKS: + ret = DMA_NONE; + break; + default: + pr_warn("target: Unknown data direction for SCSI Opcode " + "0x%02x\n", cdb[0]); + ret = -EINVAL; + } + return ret; +} + +static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req) +{ + struct usbg_cmd *cmd = req->context; + struct se_cmd *se_cmd = &cmd->se_cmd; + + if (req->status < 0) { + pr_err("%s() state %d transfer failed\n", __func__, cmd->state); + goto cleanup; + } + + if (req->num_sgs == 0) { + sg_copy_from_buffer(se_cmd->t_data_sg, + se_cmd->t_data_nents, + cmd->data_buf, + se_cmd->data_length); + } + + complete(&cmd->write_complete); + return; + +cleanup: + usbg_cleanup_cmd(cmd); +} + +static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct f_uas *fu = cmd->fu; + struct usb_gadget *gadget = fuas_to_gadget(fu); + + if (!gadget->sg_supported) { + cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC); + if (!cmd->data_buf) + return -ENOMEM; + + req->buf = cmd->data_buf; + } else { + req->buf = NULL; + req->num_sgs = se_cmd->t_data_nents; + req->sg = se_cmd->t_data_sg; + } + + req->complete = usbg_data_write_cmpl; + req->length = se_cmd->data_length; + req->context = cmd; + return 0; +} + +static int usbg_send_status_response(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + struct f_uas *fu = cmd->fu; + + if (fu->flags & USBG_IS_BOT) + return bot_send_status_response(cmd); + else + return uasp_send_status_response(cmd); +} + +static int usbg_send_write_request(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + struct f_uas *fu = cmd->fu; + + if (fu->flags & USBG_IS_BOT) + return bot_send_write_request(cmd); + else + return uasp_send_write_request(cmd); +} + +static int usbg_send_read_response(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + struct f_uas *fu = cmd->fu; + + if (fu->flags & USBG_IS_BOT) + return bot_send_read_response(cmd); + else + return uasp_send_read_response(cmd); +} + +static void usbg_cmd_work(struct work_struct *work) +{ + struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); + struct se_cmd *se_cmd; + struct tcm_usbg_nexus *tv_nexus; + struct usbg_tpg *tpg; + int dir; + + se_cmd = &cmd->se_cmd; + tpg = cmd->fu->tpg; + tv_nexus = tpg->tpg_nexus; + dir = get_cmd_dir(cmd->cmd_buf); + if (dir < 0) { + transport_init_se_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense); + goto out; + } + + if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, + cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, + 0, cmd->prio_attr, dir, TARGET_SCF_UNKNOWN_SIZE) < 0) + goto out; + + return; + +out: + transport_send_check_condition_and_sense(se_cmd, + TCM_UNSUPPORTED_SCSI_OPCODE, 1); + usbg_cleanup_cmd(cmd); +} + +static int usbg_submit_command(struct f_uas *fu, + void *cmdbuf, unsigned int len) +{ + struct command_iu *cmd_iu = cmdbuf; + struct usbg_cmd *cmd; + struct usbg_tpg *tpg; + struct se_cmd *se_cmd; + struct tcm_usbg_nexus *tv_nexus; + u32 cmd_len; + int ret; + + if (cmd_iu->iu_id != IU_ID_COMMAND) { + pr_err("Unsupported type %d\n", cmd_iu->iu_id); + return -EINVAL; + } + + cmd = kzalloc(sizeof *cmd, GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->fu = fu; + + /* XXX until I figure out why I can't free in on complete */ + kref_init(&cmd->ref); + kref_get(&cmd->ref); + + tpg = fu->tpg; + cmd_len = (cmd_iu->len & ~0x3) + 16; + if (cmd_len > USBG_MAX_CMD) + goto err; + + memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); + + cmd->tag = be16_to_cpup(&cmd_iu->tag); + if (fu->flags & USBG_USE_STREAMS) { + if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS) + goto err; + if (!cmd->tag) + cmd->stream = &fu->stream[0]; + else + cmd->stream = &fu->stream[cmd->tag - 1]; + } else { + cmd->stream = &fu->stream[0]; + } + + tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) { + pr_err("Missing nexus, ignoring command\n"); + goto err; + } + + switch (cmd_iu->prio_attr & 0x7) { + case UAS_HEAD_TAG: + cmd->prio_attr = MSG_HEAD_TAG; + break; + case UAS_ORDERED_TAG: + cmd->prio_attr = MSG_ORDERED_TAG; + break; + case UAS_ACA: + cmd->prio_attr = MSG_ACA_TAG; + break; + default: + pr_debug_once("Unsupported prio_attr: %02x.\n", + cmd_iu->prio_attr); + case UAS_SIMPLE_TAG: + cmd->prio_attr = MSG_SIMPLE_TAG; + break; + } + + se_cmd = &cmd->se_cmd; + cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); + + INIT_WORK(&cmd->work, usbg_cmd_work); + ret = queue_work(tpg->workqueue, &cmd->work); + if (ret < 0) + goto err; + + return 0; +err: + kfree(cmd); + return -EINVAL; +} + +static void bot_cmd_work(struct work_struct *work) +{ + struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); + struct se_cmd *se_cmd; + struct tcm_usbg_nexus *tv_nexus; + struct usbg_tpg *tpg; + int dir; + + se_cmd = &cmd->se_cmd; + tpg = cmd->fu->tpg; + tv_nexus = tpg->tpg_nexus; + dir = get_cmd_dir(cmd->cmd_buf); + if (dir < 0) { + transport_init_se_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense); + goto out; + } + + if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, + cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, + cmd->data_len, cmd->prio_attr, dir, 0) < 0) + goto out; + + return; + +out: + transport_send_check_condition_and_sense(se_cmd, + TCM_UNSUPPORTED_SCSI_OPCODE, 1); + usbg_cleanup_cmd(cmd); +} + +static int bot_submit_command(struct f_uas *fu, + void *cmdbuf, unsigned int len) +{ + struct bulk_cb_wrap *cbw = cmdbuf; + struct usbg_cmd *cmd; + struct usbg_tpg *tpg; + struct se_cmd *se_cmd; + struct tcm_usbg_nexus *tv_nexus; + u32 cmd_len; + int ret; + + if (cbw->Signature != cpu_to_le32(US_BULK_CB_SIGN)) { + pr_err("Wrong signature on CBW\n"); + return -EINVAL; + } + if (len != 31) { + pr_err("Wrong length for CBW\n"); + return -EINVAL; + } + + cmd_len = cbw->Length; + if (cmd_len < 1 || cmd_len > 16) + return -EINVAL; + + cmd = kzalloc(sizeof *cmd, GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->fu = fu; + + /* XXX until I figure out why I can't free in on complete */ + kref_init(&cmd->ref); + kref_get(&cmd->ref); + + tpg = fu->tpg; + + memcpy(cmd->cmd_buf, cbw->CDB, cmd_len); + + cmd->bot_tag = cbw->Tag; + + tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) { + pr_err("Missing nexus, ignoring command\n"); + goto err; + } + + cmd->prio_attr = MSG_SIMPLE_TAG; + se_cmd = &cmd->se_cmd; + cmd->unpacked_lun = cbw->Lun; + cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0; + cmd->data_len = le32_to_cpu(cbw->DataTransferLength); + + INIT_WORK(&cmd->work, bot_cmd_work); + ret = queue_work(tpg->workqueue, &cmd->work); + if (ret < 0) + goto err; + + return 0; +err: + kfree(cmd); + return -EINVAL; +} + +/* Start fabric.c code */ + +static int usbg_check_true(struct se_portal_group *se_tpg) +{ + return 1; +} + +static int usbg_check_false(struct se_portal_group *se_tpg) +{ + return 0; +} + +static char *usbg_get_fabric_name(void) +{ + return "usb_gadget"; +} + +static u8 usbg_get_fabric_proto_ident(struct se_portal_group *se_tpg) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + struct usbg_tport *tport = tpg->tport; + u8 proto_id; + + switch (tport->tport_proto_id) { + case SCSI_PROTOCOL_SAS: + default: + proto_id = sas_get_fabric_proto_ident(se_tpg); + break; + } + + return proto_id; +} + +static char *usbg_get_fabric_wwn(struct se_portal_group *se_tpg) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + struct usbg_tport *tport = tpg->tport; + + return &tport->tport_name[0]; +} + +static u16 usbg_get_tag(struct se_portal_group *se_tpg) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + return tpg->tport_tpgt; +} + +static u32 usbg_get_default_depth(struct se_portal_group *se_tpg) +{ + return 1; +} + +static u32 usbg_get_pr_transport_id( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code, + unsigned char *buf) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + struct usbg_tport *tport = tpg->tport; + int ret = 0; + + switch (tport->tport_proto_id) { + case SCSI_PROTOCOL_SAS: + default: + ret = sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); + break; + } + + return ret; +} + +static u32 usbg_get_pr_transport_id_len( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + struct usbg_tport *tport = tpg->tport; + int ret = 0; + + switch (tport->tport_proto_id) { + case SCSI_PROTOCOL_SAS: + default: + ret = sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); + break; + } + + return ret; +} + +static char *usbg_parse_pr_out_transport_id( + struct se_portal_group *se_tpg, + const char *buf, + u32 *out_tid_len, + char **port_nexus_ptr) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + struct usbg_tport *tport = tpg->tport; + char *tid = NULL; + + switch (tport->tport_proto_id) { + case SCSI_PROTOCOL_SAS: + default: + tid = sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); + } + + return tid; +} + +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) + return NULL; + + return &nacl->se_node_acl; +} + +static void usbg_release_fabric_acl( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl) +{ + struct usbg_nacl *nacl = container_of(se_nacl, + struct usbg_nacl, se_node_acl); + kfree(nacl); +} + +static u32 usbg_tpg_get_inst_index(struct se_portal_group *se_tpg) +{ + return 1; +} + +static void usbg_cmd_release(struct kref *ref) +{ + struct usbg_cmd *cmd = container_of(ref, struct usbg_cmd, + ref); + + transport_generic_free_cmd(&cmd->se_cmd, 0); +} + +static void usbg_release_cmd(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + kfree(cmd->data_buf); + kfree(cmd); + return; +} + +static int usbg_shutdown_session(struct se_session *se_sess) +{ + return 0; +} + +static void usbg_close_session(struct se_session *se_sess) +{ + return; +} + +static u32 usbg_sess_get_index(struct se_session *se_sess) +{ + return 0; +} + +/* + * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be + */ +static int usbg_write_pending_status(struct se_cmd *se_cmd) +{ + return 0; +} + +static void usbg_set_default_node_attrs(struct se_node_acl *nacl) +{ + return; +} + +static u32 usbg_get_task_tag(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + struct f_uas *fu = cmd->fu; + + if (fu->flags & USBG_IS_BOT) + return le32_to_cpu(cmd->bot_tag); + else + return cmd->tag; +} + +static int usbg_get_cmd_state(struct se_cmd *se_cmd) +{ + return 0; +} + +static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) +{ +} + +static void usbg_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + +static const char *usbg_check_wwn(const char *name) +{ + const char *n; + unsigned int len; + + n = strstr(name, "naa."); + if (!n) + return NULL; + n += 4; + len = strlen(n); + if (len == 0 || len > USBG_NAMELEN - 1) + return NULL; + return n; +} + +static struct se_node_acl *usbg_make_nodeacl( + struct se_portal_group *se_tpg, + struct config_group *group, + const char *name) +{ + struct se_node_acl *se_nacl, *se_nacl_new; + struct usbg_nacl *nacl; + u64 wwpn = 0; + u32 nexus_depth; + const char *wnn_name; + + wnn_name = usbg_check_wwn(name); + if (!wnn_name) + return ERR_PTR(-EINVAL); + se_nacl_new = usbg_alloc_fabric_acl(se_tpg); + if (!(se_nacl_new)) + return ERR_PTR(-ENOMEM); + + nexus_depth = 1; + /* + * se_nacl_new may be released by core_tpg_add_initiator_node_acl() + * when converting a NodeACL from demo mode -> explict + */ + se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new, + name, nexus_depth); + if (IS_ERR(se_nacl)) { + usbg_release_fabric_acl(se_tpg, se_nacl_new); + return se_nacl; + } + /* + * Locate our struct usbg_nacl and set the FC Nport WWPN + */ + nacl = container_of(se_nacl, struct usbg_nacl, se_node_acl); + nacl->iport_wwpn = wwpn; + snprintf(nacl->iport_name, sizeof(nacl->iport_name), "%s", name); + return se_nacl; +} + +static void usbg_drop_nodeacl(struct se_node_acl *se_acl) +{ + struct usbg_nacl *nacl = container_of(se_acl, + struct usbg_nacl, se_node_acl); + core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1); + kfree(nacl); +} + +struct usbg_tpg *the_only_tpg_I_currently_have; + +static struct se_portal_group *usbg_make_tpg( + struct se_wwn *wwn, + struct config_group *group, + const char *name) +{ + struct usbg_tport *tport = container_of(wwn, struct usbg_tport, + tport_wwn); + struct usbg_tpg *tpg; + unsigned long tpgt; + int ret; + + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); + if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX) + return ERR_PTR(-EINVAL); + if (the_only_tpg_I_currently_have) { + pr_err("Until the gadget framework can't handle multiple\n"); + pr_err("gadgets, you can't do this here.\n"); + return ERR_PTR(-EBUSY); + } + + tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL); + 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); + if (!tpg->workqueue) { + kfree(tpg); + return NULL; + } + + tpg->tport = tport; + tpg->tport_tpgt = tpgt; + + ret = core_tpg_register(&usbg_fabric_configfs->tf_ops, wwn, + &tpg->se_tpg, tpg, + TRANSPORT_TPG_TYPE_NORMAL); + if (ret < 0) { + destroy_workqueue(tpg->workqueue); + kfree(tpg); + return NULL; + } + the_only_tpg_I_currently_have = tpg; + return &tpg->se_tpg; +} + +static void usbg_drop_tpg(struct se_portal_group *se_tpg) +{ + struct usbg_tpg *tpg = container_of(se_tpg, + struct usbg_tpg, se_tpg); + + core_tpg_deregister(se_tpg); + destroy_workqueue(tpg->workqueue); + kfree(tpg); + the_only_tpg_I_currently_have = NULL; +} + +static struct se_wwn *usbg_make_tport( + struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) +{ + struct usbg_tport *tport; + const char *wnn_name; + u64 wwpn = 0; + + wnn_name = usbg_check_wwn(name); + if (!wnn_name) + return ERR_PTR(-EINVAL); + + tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL); + if (!(tport)) + return ERR_PTR(-ENOMEM); + tport->tport_wwpn = wwpn; + snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name); + return &tport->tport_wwn; +} + +static void usbg_drop_tport(struct se_wwn *wwn) +{ + struct usbg_tport *tport = container_of(wwn, + struct usbg_tport, tport_wwn); + kfree(tport); +} + +/* + * If somebody feels like dropping the version property, go ahead. + */ +static ssize_t usbg_wwn_show_attr_version( + struct target_fabric_configfs *tf, + char *page) +{ + return sprintf(page, "usb-gadget fabric module\n"); +} +TF_WWN_ATTR_RO(usbg, version); + +static struct configfs_attribute *usbg_wwn_attrs[] = { + &usbg_wwn_version.attr, + NULL, +}; + +static ssize_t tcm_usbg_tpg_show_enable( + struct se_portal_group *se_tpg, + char *page) +{ + struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); + + return snprintf(page, PAGE_SIZE, "%u\n", tpg->gadget_connect); +} + +static int usbg_attach(struct usbg_tpg *); +static void usbg_detach(struct usbg_tpg *); + +static ssize_t tcm_usbg_tpg_store_enable( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); + unsigned long op; + ssize_t ret; + + ret = kstrtoul(page, 0, &op); + if (ret < 0) + return -EINVAL; + if (op > 1) + return -EINVAL; + + if (op && tpg->gadget_connect) + goto out; + if (!op && !tpg->gadget_connect) + goto out; + + if (op) { + ret = usbg_attach(tpg); + if (ret) + goto out; + } else { + usbg_detach(tpg); + } + tpg->gadget_connect = op; +out: + return count; +} +TF_TPG_BASE_ATTR(tcm_usbg, enable, S_IRUGO | S_IWUSR); + +static ssize_t tcm_usbg_tpg_show_nexus( + struct se_portal_group *se_tpg, + char *page) +{ + struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); + struct tcm_usbg_nexus *tv_nexus; + ssize_t ret; + + mutex_lock(&tpg->tpg_mutex); + tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) { + ret = -ENODEV; + goto out; + } + ret = snprintf(page, PAGE_SIZE, "%s\n", + tv_nexus->tvn_se_sess->se_node_acl->initiatorname); +out: + mutex_unlock(&tpg->tpg_mutex); + return ret; +} + +static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name) +{ + struct se_portal_group *se_tpg; + struct tcm_usbg_nexus *tv_nexus; + int ret; + + mutex_lock(&tpg->tpg_mutex); + if (tpg->tpg_nexus) { + ret = -EEXIST; + pr_debug("tpg->tpg_nexus already exists\n"); + goto err_unlock; + } + se_tpg = &tpg->se_tpg; + + ret = -ENOMEM; + tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL); + if (!tv_nexus) + goto err_unlock; + tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL); + if (IS_ERR(tv_nexus->tvn_se_sess)) + goto err_free; + + /* + * Since we are running in 'demo mode' this call with generate a + * struct se_node_acl for the tcm_vhost struct se_portal_group with + * the SCSI Initiator port name of the passed configfs group 'name'. + */ + tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl( + se_tpg, name); + if (!tv_nexus->tvn_se_sess->se_node_acl) { + pr_debug("core_tpg_check_initiator_node_acl() failed" + " for %s\n", name); + goto err_session; + } + /* + * Now register the TCM vHost virtual I_T Nexus as active with the + * call to __transport_register_session() + */ + __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, + tv_nexus->tvn_se_sess, tv_nexus); + tpg->tpg_nexus = tv_nexus; + mutex_unlock(&tpg->tpg_mutex); + return 0; + +err_session: + transport_free_session(tv_nexus->tvn_se_sess); +err_free: + kfree(tv_nexus); +err_unlock: + mutex_unlock(&tpg->tpg_mutex); + return ret; +} + +static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg) +{ + struct se_session *se_sess; + struct tcm_usbg_nexus *tv_nexus; + int ret = -ENODEV; + + mutex_lock(&tpg->tpg_mutex); + tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) + goto out; + + se_sess = tv_nexus->tvn_se_sess; + if (!se_sess) + goto out; + + if (atomic_read(&tpg->tpg_port_count)) { + ret = -EPERM; + pr_err("Unable to remove Host I_T Nexus with" + " active TPG port count: %d\n", + atomic_read(&tpg->tpg_port_count)); + goto out; + } + + pr_debug("Removing I_T Nexus to Initiator Port: %s\n", + tv_nexus->tvn_se_sess->se_node_acl->initiatorname); + /* + * Release the SCSI I_T Nexus to the emulated vHost Target Port + */ + transport_deregister_session(tv_nexus->tvn_se_sess); + tpg->tpg_nexus = NULL; + + kfree(tv_nexus); + ret = 0; +out: + mutex_unlock(&tpg->tpg_mutex); + return ret; +} + +static ssize_t tcm_usbg_tpg_store_nexus( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); + unsigned char i_port[USBG_NAMELEN], *ptr; + int ret; + + if (!strncmp(page, "NULL", 4)) { + ret = tcm_usbg_drop_nexus(tpg); + return (!ret) ? count : ret; + } + if (strlen(page) >= USBG_NAMELEN) { + pr_err("Emulated NAA Sas Address: %s, exceeds" + " max: %d\n", page, USBG_NAMELEN); + return -EINVAL; + } + snprintf(i_port, USBG_NAMELEN, "%s", page); + + ptr = strstr(i_port, "naa."); + if (!ptr) { + pr_err("Missing 'naa.' prefix\n"); + return -EINVAL; + } + + if (i_port[strlen(i_port) - 1] == '\n') + i_port[strlen(i_port) - 1] = '\0'; + + ret = tcm_usbg_make_nexus(tpg, &i_port[4]); + if (ret < 0) + return ret; + return count; +} +TF_TPG_BASE_ATTR(tcm_usbg, nexus, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *usbg_base_attrs[] = { + &tcm_usbg_tpg_enable.attr, + &tcm_usbg_tpg_nexus.attr, + NULL, +}; + +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(); + return 0; +} + +static void usbg_port_unlink(struct se_portal_group *se_tpg, + struct se_lun *se_lun) +{ + struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); + + atomic_dec(&tpg->tpg_port_count); + smp_mb__after_atomic(); +} + +static int usbg_check_stop_free(struct se_cmd *se_cmd) +{ + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, + se_cmd); + + kref_put(&cmd->ref, usbg_cmd_release); + return 1; +} + +static struct target_core_fabric_ops usbg_ops = { + .get_fabric_name = usbg_get_fabric_name, + .get_fabric_proto_ident = usbg_get_fabric_proto_ident, + .tpg_get_wwn = usbg_get_fabric_wwn, + .tpg_get_tag = usbg_get_tag, + .tpg_get_default_depth = usbg_get_default_depth, + .tpg_get_pr_transport_id = usbg_get_pr_transport_id, + .tpg_get_pr_transport_id_len = usbg_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = usbg_parse_pr_out_transport_id, + .tpg_check_demo_mode = usbg_check_true, + .tpg_check_demo_mode_cache = usbg_check_false, + .tpg_check_demo_mode_write_protect = usbg_check_false, + .tpg_check_prod_mode_write_protect = usbg_check_false, + .tpg_alloc_fabric_acl = usbg_alloc_fabric_acl, + .tpg_release_fabric_acl = usbg_release_fabric_acl, + .tpg_get_inst_index = usbg_tpg_get_inst_index, + .release_cmd = usbg_release_cmd, + .shutdown_session = usbg_shutdown_session, + .close_session = usbg_close_session, + .sess_get_index = usbg_sess_get_index, + .sess_get_initiator_sid = NULL, + .write_pending = usbg_send_write_request, + .write_pending_status = usbg_write_pending_status, + .set_default_node_attributes = usbg_set_default_node_attrs, + .get_task_tag = usbg_get_task_tag, + .get_cmd_state = usbg_get_cmd_state, + .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, + .fabric_drop_wwn = usbg_drop_tport, + .fabric_make_tpg = usbg_make_tpg, + .fabric_drop_tpg = usbg_drop_tpg, + .fabric_post_link = usbg_port_link, + .fabric_pre_unlink = usbg_port_unlink, + .fabric_make_np = NULL, + .fabric_drop_np = NULL, + .fabric_make_nodeacl = usbg_make_nodeacl, + .fabric_drop_nodeacl = usbg_drop_nodeacl, +}; + +static int usbg_register_configfs(void) +{ + struct target_fabric_configfs *fabric; + int ret; + + fabric = target_fabric_configfs_init(THIS_MODULE, "usb_gadget"); + if (IS_ERR(fabric)) { + printk(KERN_ERR "target_fabric_configfs_init() failed\n"); + return PTR_ERR(fabric); + } + + fabric->tf_ops = usbg_ops; + 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" + " for usb-gadget\n"); + return ret; + } + usbg_fabric_configfs = fabric; + return 0; +}; + +static void usbg_deregister_configfs(void) +{ + if (!(usbg_fabric_configfs)) + return; + + target_fabric_configfs_deregister(usbg_fabric_configfs); + usbg_fabric_configfs = NULL; +}; + +/* Start gadget.c code */ + +static struct usb_interface_descriptor bot_intf_desc = { + .bLength = sizeof(bot_intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bAlternateSetting = USB_G_ALT_INT_BBB, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, + .bInterfaceProtocol = USB_PR_BULK, +}; + +static struct usb_interface_descriptor uasp_intf_desc = { + .bLength = sizeof(uasp_intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 4, + .bAlternateSetting = USB_G_ALT_INT_UAS, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, + .bInterfaceProtocol = USB_PR_UAS, +}; + +static struct usb_endpoint_descriptor uasp_bi_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor uasp_fs_bi_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = { + .bLength = sizeof(uasp_bi_pipe_desc), + .bDescriptorType = USB_DT_PIPE_USAGE, + .bPipeID = DATA_IN_PIPE_ID, +}; + +static struct usb_endpoint_descriptor uasp_ss_bi_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = { + .bLength = sizeof(uasp_bi_ep_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, + .wBytesPerInterval = 0, +}; + +static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = { + .bLength = sizeof(bot_bi_ep_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, +}; + +static struct usb_endpoint_descriptor uasp_bo_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor uasp_fs_bo_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = { + .bLength = sizeof(uasp_bo_pipe_desc), + .bDescriptorType = USB_DT_PIPE_USAGE, + .bPipeID = DATA_OUT_PIPE_ID, +}; + +static struct usb_endpoint_descriptor uasp_ss_bo_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(0x400), +}; + +static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = { + .bLength = sizeof(uasp_bo_ep_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, +}; + +static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = { + .bLength = sizeof(bot_bo_ep_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_endpoint_descriptor uasp_status_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor uasp_fs_status_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = { + .bLength = sizeof(uasp_status_pipe_desc), + .bDescriptorType = USB_DT_PIPE_USAGE, + .bPipeID = STATUS_PIPE_ID, +}; + +static struct usb_endpoint_descriptor uasp_ss_status_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = { + .bLength = sizeof(uasp_status_in_ep_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, +}; + +static struct usb_endpoint_descriptor uasp_cmd_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor uasp_fs_cmd_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = { + .bLength = sizeof(uasp_cmd_pipe_desc), + .bDescriptorType = USB_DT_PIPE_USAGE, + .bPipeID = CMD_PIPE_ID, +}; + +static struct usb_endpoint_descriptor uasp_ss_cmd_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor uasp_cmd_comp_desc = { + .bLength = sizeof(uasp_cmd_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *uasp_fs_function_desc[] = { + (struct usb_descriptor_header *) &bot_intf_desc, + (struct usb_descriptor_header *) &uasp_fs_bi_desc, + (struct usb_descriptor_header *) &uasp_fs_bo_desc, + + (struct usb_descriptor_header *) &uasp_intf_desc, + (struct usb_descriptor_header *) &uasp_fs_bi_desc, + (struct usb_descriptor_header *) &uasp_bi_pipe_desc, + (struct usb_descriptor_header *) &uasp_fs_bo_desc, + (struct usb_descriptor_header *) &uasp_bo_pipe_desc, + (struct usb_descriptor_header *) &uasp_fs_status_desc, + (struct usb_descriptor_header *) &uasp_status_pipe_desc, + (struct usb_descriptor_header *) &uasp_fs_cmd_desc, + (struct usb_descriptor_header *) &uasp_cmd_pipe_desc, + NULL, +}; + +static struct usb_descriptor_header *uasp_hs_function_desc[] = { + (struct usb_descriptor_header *) &bot_intf_desc, + (struct usb_descriptor_header *) &uasp_bi_desc, + (struct usb_descriptor_header *) &uasp_bo_desc, + + (struct usb_descriptor_header *) &uasp_intf_desc, + (struct usb_descriptor_header *) &uasp_bi_desc, + (struct usb_descriptor_header *) &uasp_bi_pipe_desc, + (struct usb_descriptor_header *) &uasp_bo_desc, + (struct usb_descriptor_header *) &uasp_bo_pipe_desc, + (struct usb_descriptor_header *) &uasp_status_desc, + (struct usb_descriptor_header *) &uasp_status_pipe_desc, + (struct usb_descriptor_header *) &uasp_cmd_desc, + (struct usb_descriptor_header *) &uasp_cmd_pipe_desc, + NULL, +}; + +static struct usb_descriptor_header *uasp_ss_function_desc[] = { + (struct usb_descriptor_header *) &bot_intf_desc, + (struct usb_descriptor_header *) &uasp_ss_bi_desc, + (struct usb_descriptor_header *) &bot_bi_ep_comp_desc, + (struct usb_descriptor_header *) &uasp_ss_bo_desc, + (struct usb_descriptor_header *) &bot_bo_ep_comp_desc, + + (struct usb_descriptor_header *) &uasp_intf_desc, + (struct usb_descriptor_header *) &uasp_ss_bi_desc, + (struct usb_descriptor_header *) &uasp_bi_ep_comp_desc, + (struct usb_descriptor_header *) &uasp_bi_pipe_desc, + (struct usb_descriptor_header *) &uasp_ss_bo_desc, + (struct usb_descriptor_header *) &uasp_bo_ep_comp_desc, + (struct usb_descriptor_header *) &uasp_bo_pipe_desc, + (struct usb_descriptor_header *) &uasp_ss_status_desc, + (struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc, + (struct usb_descriptor_header *) &uasp_status_pipe_desc, + (struct usb_descriptor_header *) &uasp_ss_cmd_desc, + (struct usb_descriptor_header *) &uasp_cmd_comp_desc, + (struct usb_descriptor_header *) &uasp_cmd_pipe_desc, + NULL, +}; + +#define UAS_VENDOR_ID 0x0525 /* NetChip */ +#define UAS_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + +static struct usb_device_descriptor usbg_device_desc = { + .bLength = sizeof(usbg_device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .idVendor = cpu_to_le16(UAS_VENDOR_ID), + .idProduct = cpu_to_le16(UAS_PRODUCT_ID), + .bNumConfigurations = 1, +}; + +static struct usb_string usbg_us_strings[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor", + [USB_GADGET_PRODUCT_IDX].s = "Target Product", + [USB_GADGET_SERIAL_IDX].s = "000000000001", + [USB_G_STR_CONFIG].s = "default config", + [USB_G_STR_INT_UAS].s = "USB Attached SCSI", + [USB_G_STR_INT_BBB].s = "Bulk Only Transport", + { }, +}; + +static struct usb_gadget_strings usbg_stringtab = { + .language = 0x0409, + .strings = usbg_us_strings, +}; + +static struct usb_gadget_strings *usbg_strings[] = { + &usbg_stringtab, + NULL, +}; + +static int guas_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + +static struct usb_configuration usbg_config_driver = { + .label = "Linux Target", + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +static void give_back_ep(struct usb_ep **pep) +{ + struct usb_ep *ep = *pep; + if (!ep) + return; + ep->driver_data = NULL; +} + +static int usbg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_uas *fu = to_f_uas(f); + struct usb_gadget *gadget = c->cdev->gadget; + struct usb_ep *ep; + int iface; + int ret; + + iface = usb_interface_id(c, f); + if (iface < 0) + return iface; + + bot_intf_desc.bInterfaceNumber = iface; + uasp_intf_desc.bInterfaceNumber = iface; + fu->iface = iface; + ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc, + &uasp_bi_ep_comp_desc); + if (!ep) + goto ep_fail; + + ep->driver_data = fu; + fu->ep_in = ep; + + ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc, + &uasp_bo_ep_comp_desc); + if (!ep) + goto ep_fail; + ep->driver_data = fu; + fu->ep_out = ep; + + ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc, + &uasp_status_in_ep_comp_desc); + if (!ep) + goto ep_fail; + ep->driver_data = fu; + fu->ep_status = ep; + + ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc, + &uasp_cmd_comp_desc); + if (!ep) + goto ep_fail; + ep->driver_data = fu; + fu->ep_cmd = ep; + + /* Assume endpoint addresses are the same for both speeds */ + uasp_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; + uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; + uasp_status_desc.bEndpointAddress = + uasp_ss_status_desc.bEndpointAddress; + uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + + uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; + uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; + uasp_fs_status_desc.bEndpointAddress = + uasp_ss_status_desc.bEndpointAddress; + uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, uasp_fs_function_desc, + uasp_hs_function_desc, uasp_ss_function_desc); + if (ret) + goto ep_fail; + + return 0; +ep_fail: + pr_err("Can't claim all required eps\n"); + + give_back_ep(&fu->ep_in); + give_back_ep(&fu->ep_out); + give_back_ep(&fu->ep_status); + give_back_ep(&fu->ep_cmd); + return -ENOTSUPP; +} + +static void usbg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_uas *fu = to_f_uas(f); + + usb_free_all_descriptors(f); + kfree(fu); +} + +struct guas_setup_wq { + struct work_struct work; + struct f_uas *fu; + unsigned int alt; +}; + +static void usbg_delayed_set_alt(struct work_struct *wq) +{ + struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, + work); + struct f_uas *fu = work->fu; + int alt = work->alt; + + kfree(work); + + if (fu->flags & USBG_IS_BOT) + bot_cleanup_old_alt(fu); + if (fu->flags & USBG_IS_UAS) + uasp_cleanup_old_alt(fu); + + if (alt == USB_G_ALT_INT_BBB) + bot_set_alt(fu); + else if (alt == USB_G_ALT_INT_UAS) + uasp_set_alt(fu); + usb_composite_setup_continue(fu->function.config->cdev); +} + +static int usbg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_uas *fu = to_f_uas(f); + + if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { + struct guas_setup_wq *work; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return -ENOMEM; + INIT_WORK(&work->work, usbg_delayed_set_alt); + work->fu = fu; + work->alt = alt; + schedule_work(&work->work); + return USB_GADGET_DELAYED_STATUS; + } + return -EOPNOTSUPP; +} + +static void usbg_disable(struct usb_function *f) +{ + struct f_uas *fu = to_f_uas(f); + + if (fu->flags & USBG_IS_UAS) + uasp_cleanup_old_alt(fu); + else if (fu->flags & USBG_IS_BOT) + bot_cleanup_old_alt(fu); + fu->flags = 0; +} + +static int usbg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_uas *fu = to_f_uas(f); + + if (!(fu->flags & USBG_IS_BOT)) + return -EOPNOTSUPP; + + return usbg_bot_setup(f, ctrl); +} + +static int usbg_cfg_bind(struct usb_configuration *c) +{ + struct f_uas *fu; + int ret; + + fu = kzalloc(sizeof(*fu), GFP_KERNEL); + if (!fu) + return -ENOMEM; + fu->function.name = "Target Function"; + fu->function.bind = usbg_bind; + fu->function.unbind = usbg_unbind; + fu->function.set_alt = usbg_set_alt; + fu->function.setup = usbg_setup; + fu->function.disable = usbg_disable; + fu->tpg = the_only_tpg_I_currently_have; + + bot_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_BBB].id; + uasp_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_UAS].id; + + ret = usb_add_function(c, &fu->function); + if (ret) + goto err; + + return 0; +err: + kfree(fu); + return ret; +} + +static int usb_target_bind(struct usb_composite_dev *cdev) +{ + int ret; + + ret = usb_string_ids_tab(cdev, usbg_us_strings); + if (ret) + return ret; + + usbg_device_desc.iManufacturer = + usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id; + usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id; + usbg_device_desc.iSerialNumber = + usbg_us_strings[USB_GADGET_SERIAL_IDX].id; + usbg_config_driver.iConfiguration = + usbg_us_strings[USB_G_STR_CONFIG].id; + + ret = usb_add_config(cdev, &usbg_config_driver, + usbg_cfg_bind); + if (ret) + return ret; + usb_composite_overwrite_options(cdev, &coverwrite); + return 0; +} + +static __refdata struct usb_composite_driver usbg_driver = { + .name = "g_target", + .dev = &usbg_device_desc, + .strings = usbg_strings, + .max_speed = USB_SPEED_SUPER, + .bind = usb_target_bind, + .unbind = guas_unbind, +}; + +static int usbg_attach(struct usbg_tpg *tpg) +{ + return usb_composite_probe(&usbg_driver); +} + +static void usbg_detach(struct usbg_tpg *tpg) +{ + usb_composite_unregister(&usbg_driver); +} + +static int __init usb_target_gadget_init(void) +{ + int ret; + + ret = usbg_register_configfs(); + return ret; +} +module_init(usb_target_gadget_init); + +static void __exit usb_target_gadget_exit(void) +{ + usbg_deregister_configfs(); +} +module_exit(usb_target_gadget_exit); + +MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); +MODULE_DESCRIPTION("usb-gadget fabric"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h new file mode 100644 index 00000000000..8289219925b --- /dev/null +++ b/drivers/usb/gadget/tcm_usb_gadget.h @@ -0,0 +1,145 @@ +#ifndef __TARGET_USB_GADGET_H__ +#define __TARGET_USB_GADGET_H__ + +#include <linux/kref.h> +/* #include <linux/usb/uas.h> */ +#include <linux/usb/composite.h> +#include <linux/usb/uas.h> +#include <linux/usb/storage.h> +#include <scsi/scsi.h> +#include <target/target_core_base.h> +#include <target/target_core_fabric.h> + +#define USBG_NAMELEN 32 + +#define fuas_to_gadget(f) (f->function.config->cdev->gadget) +#define UASP_SS_EP_COMP_LOG_STREAMS 4 +#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS) + +enum { + USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX, + USB_G_STR_INT_UAS, + USB_G_STR_INT_BBB, +}; + +#define USB_G_ALT_INT_BBB 0 +#define USB_G_ALT_INT_UAS 1 + +struct usbg_nacl { + /* Binary World Wide unique Port Name for SAS Initiator port */ + u64 iport_wwpn; + /* ASCII formatted WWPN for Sas Initiator port */ + char iport_name[USBG_NAMELEN]; + /* Returned by usbg_make_nodeacl() */ + struct se_node_acl se_node_acl; +}; + +struct tcm_usbg_nexus { + struct se_session *tvn_se_sess; +}; + +struct usbg_tpg { + struct mutex tpg_mutex; + /* SAS port target portal group tag for TCM */ + u16 tport_tpgt; + /* Pointer back to usbg_tport */ + struct usbg_tport *tport; + struct workqueue_struct *workqueue; + /* Returned by usbg_make_tpg() */ + struct se_portal_group se_tpg; + u32 gadget_connect; + struct tcm_usbg_nexus *tpg_nexus; + atomic_t tpg_port_count; +}; + +struct usbg_tport { + /* SCSI protocol the tport is providing */ + u8 tport_proto_id; + /* Binary World Wide unique Port Name for SAS Target port */ + u64 tport_wwpn; + /* ASCII formatted WWPN for SAS Target port */ + char tport_name[USBG_NAMELEN]; + /* Returned by usbg_make_tport() */ + struct se_wwn tport_wwn; +}; + +enum uas_state { + UASP_SEND_DATA, + UASP_RECEIVE_DATA, + UASP_SEND_STATUS, + UASP_QUEUE_COMMAND, +}; + +#define USBG_MAX_CMD 64 +struct usbg_cmd { + /* common */ + u8 cmd_buf[USBG_MAX_CMD]; + u32 data_len; + struct work_struct work; + int unpacked_lun; + struct se_cmd se_cmd; + void *data_buf; /* used if no sg support available */ + struct f_uas *fu; + struct completion write_complete; + struct kref ref; + + /* UAS only */ + u16 tag; + u16 prio_attr; + struct sense_iu sense_iu; + enum uas_state state; + struct uas_stream *stream; + + /* BOT only */ + __le32 bot_tag; + unsigned int csw_code; + unsigned is_read:1; + +}; + +struct uas_stream { + struct usb_request *req_in; + struct usb_request *req_out; + struct usb_request *req_status; +}; + +struct usbg_cdb { + struct usb_request *req; + void *buf; +}; + +struct bot_status { + struct usb_request *req; + struct bulk_cs_wrap csw; +}; + +struct f_uas { + struct usbg_tpg *tpg; + struct usb_function function; + u16 iface; + + u32 flags; +#define USBG_ENABLED (1 << 0) +#define USBG_IS_UAS (1 << 1) +#define USBG_USE_STREAMS (1 << 2) +#define USBG_IS_BOT (1 << 3) +#define USBG_BOT_CMD_PEND (1 << 4) + + struct usbg_cdb cmd; + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + /* UAS */ + struct usb_ep *ep_status; + struct usb_ep *ep_cmd; + struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS]; + + /* BOT */ + struct bot_status bot_status; + struct usb_request *bot_req_in; + struct usb_request *bot_req_out; +}; + +extern struct usbg_tpg *the_only_tpg_I_currently_have; + +#endif 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 4007770f7ed..97b027724ee 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -9,25 +9,18 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> +#include <linux/module.h> +#include <linux/gfp.h> #include <linux/device.h> #include <linux/ctype.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> +#include <linux/if_vlan.h> #include "u_ether.h" @@ -37,8 +30,9 @@ * one (!) network link through the USB gadget stack, normally "usb0". * * The control and data models are handled by the function driver which - * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. - * That includes all descriptor and endpoint management. + * connects to this code; such as CDC Ethernet (ECM or EEM), + * "CDC Subset", or RNDIS. That includes all descriptor and endpoint + * management. * * Link level addressing is handled by this component using module * parameters; if no such parameters are provided, random link level @@ -56,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; @@ -68,9 +61,15 @@ struct eth_dev { struct list_head tx_reqs, rx_reqs; atomic_t tx_qlen; + struct sk_buff_head rx_frames; + + unsigned qmult; + unsigned header_len; - struct sk_buff *(*wrap)(struct sk_buff *skb); - int (*unwrap)(struct sk_buff *skb); + struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); + int (*unwrap)(struct gether *, + struct sk_buff *skb, + struct sk_buff_head *list); struct work_struct work; @@ -79,6 +78,7 @@ struct eth_dev { bool zlp; u8 host_mac[ETH_ALEN]; + u8 dev_mac[ETH_ALEN]; }; /*-------------------------------------------------------------------------*/ @@ -87,21 +87,11 @@ struct eth_dev { #define DEFAULT_QLEN 2 /* double buffering by default */ - -#ifdef CONFIG_USB_GADGET_DUALSPEED - -static unsigned qmult = 5; -module_param(qmult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); - -#else /* full speed (low speed doesn't do bulk) */ -#define qmult 1 -#endif - -/* for dual-speed hardware, use deeper queues at highspeed */ -static inline int qlen(struct usb_gadget *gadget) +/* for dual-speed hardware, use deeper queues at high/super speed */ +static inline int qlen(struct usb_gadget *gadget, unsigned qmult) { - if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) + if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || + gadget->speed == USB_SPEED_SUPER)) return qmult * DEFAULT_QLEN; else return DEFAULT_QLEN; @@ -167,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: @@ -181,7 +171,7 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) * - ... probably more ethtool ops */ -static struct ethtool_ops ops = { +static const struct ethtool_ops ops = { .get_drvinfo = eth_get_drvinfo, .get_link = ethtool_op_get_link, }; @@ -235,6 +225,9 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) size += out->maxpacket - 1; size -= size % out->maxpacket; + if (dev->port_usb->is_fixed) + size = max_t(size_t, size, dev->port_usb->fixed_out_len); + skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); if (skb == NULL) { DBG(dev, "no rx skb\n"); @@ -269,7 +262,7 @@ enomem: static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context; + struct sk_buff *skb = req->context, *skb2; struct eth_dev *dev = ep->driver_data; int status = req->status; @@ -278,26 +271,47 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) /* normal completion */ case 0: skb_put(skb, req->actual); - if (dev->unwrap) - status = dev->unwrap(skb); - if (status < 0 - || ETH_HLEN > skb->len - || skb->len > ETH_FRAME_LEN) { - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - DBG(dev, "rx length %d\n", skb->len); - break; - } - skb->protocol = eth_type_trans(skb, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb->len; + if (dev->unwrap) { + unsigned long flags; - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb); + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + status = dev->unwrap(dev->port_usb, + skb, + &dev->rx_frames); + } else { + dev_kfree_skb_any(skb); + status = -ENOTCONN; + } + spin_unlock_irqrestore(&dev->lock, flags); + } else { + skb_queue_tail(&dev->rx_frames, skb); + } skb = NULL; + + skb2 = skb_dequeue(&dev->rx_frames); + while (skb2) { + if (status < 0 + || ETH_HLEN > skb2->len + || skb2->len > VLAN_ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb2->len); + dev_kfree_skb_any(skb2); + goto next_frame; + } + skb2->protocol = eth_type_trans(skb2, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb2->len; + + /* no buffer copies needed, unless hardware can't + * use skb buffers. + */ + status = netif_rx(skb2); +next_frame: + skb2 = skb_dequeue(&dev->rx_frames); + } break; /* software-driven interface shutdown */ @@ -465,7 +479,8 @@ static inline int is_promisc(u16 cdc_filter) return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; } -static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) +static netdev_tx_t eth_start_xmit(struct sk_buff *skb, + struct net_device *net) { struct eth_dev *dev = netdev_priv(net); int length = skb->len; @@ -487,7 +502,7 @@ static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) if (!in) { dev_kfree_skb_any(skb); - return 0; + return NETDEV_TX_OK; } /* apply outgoing CDC or RNDIS filters */ @@ -506,7 +521,7 @@ static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; if (!(cdc_filter & type)) { dev_kfree_skb_any(skb); - return 0; + return NETDEV_TX_OK; } } /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ @@ -520,7 +535,7 @@ static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) */ if (list_empty(&dev->tx_reqs)) { spin_unlock_irqrestore(&dev->req_lock, flags); - return 1; + return NETDEV_TX_BUSY; } req = container_of(dev->tx_reqs.next, struct usb_request, list); @@ -536,34 +551,43 @@ static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) * or there's not enough space for extra headers we need */ if (dev->wrap) { - struct sk_buff *skb_new; + unsigned long flags; - skb_new = dev->wrap(skb); - if (!skb_new) + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + skb = dev->wrap(dev->port_usb, skb); + spin_unlock_irqrestore(&dev->lock, flags); + if (!skb) goto drop; - dev_kfree_skb_any(skb); - skb = skb_new; length = skb->len; } req->buf = skb->data; req->context = skb; req->complete = tx_complete; + /* NCM requires no zlp if transfer is dwNtbInMaxSize */ + if (dev->port_usb->is_fixed && + length == dev->port_usb->fixed_in_len && + (length % in->maxpacket) == 0) + req->zero = 0; + else + req->zero = 1; + /* use zlp framing on tx for strict CDC-Ether conformance, * though any robust network rx path ignores extra padding. * and some hardware doesn't like to write zlps. */ - req->zero = 1; - if (!dev->zlp && (length % in->maxpacket) == 0) + if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) length++; req->length = length; - /* throttle highspeed IRQ rate back slightly */ + /* throttle high/super speed IRQ rate back slightly */ if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) - ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH || + dev->gadget->speed == USB_SPEED_SUPER) + ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0) : 0; retval = usb_ep_queue(in, req, GFP_ATOMIC); @@ -577,16 +601,16 @@ static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) } if (retval) { + dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; - dev_kfree_skb_any(skb); spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->req_lock, flags); } - return 0; + return NETDEV_TX_OK; } /*-------------------------------------------------------------------------*/ @@ -638,6 +662,8 @@ static int eth_stop(struct net_device *net) spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb) { struct gether *link = dev->port_usb; + const struct usb_endpoint_descriptor *in; + const struct usb_endpoint_descriptor *out; if (link->close) link->close(link); @@ -651,12 +677,16 @@ static int eth_stop(struct net_device *net) * their own pace; the network stack can handle old packets. * For the moment we leave this here, since it works. */ + in = link->in_ep->desc; + out = link->out_ep->desc; usb_ep_disable(link->in_ep); usb_ep_disable(link->out_ep); if (netif_carrier_ok(net)) { DBG(dev, "host still using in/out endpoints\n"); - usb_ep_enable(link->in_ep, link->in); - usb_ep_enable(link->out_ep, link->out); + link->in_ep->desc = in; + link->out_ep->desc = out; + usb_ep_enable(link->in_ep); + usb_ep_enable(link->out_ep); } } spin_unlock_irqrestore(&dev->lock, flags); @@ -666,28 +696,7 @@ 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 u8 __init nibble(unsigned char c) -{ - if (isdigit(c)) - return c - '0'; - c = toupper(c); - if (isxdigit(c)) - return 10 + c - 'A'; - return 0; -} - -static int __init get_ether_addr(const char *str, u8 *dev_addr) +static int get_ether_addr(const char *str, u8 *dev_addr) { if (str) { unsigned i; @@ -697,18 +706,27 @@ static int __init get_ether_addr(const char *str, u8 *dev_addr) if ((*str == '.') || (*str == ':')) str++; - num = nibble(*str++) << 4; - num |= (nibble(*str++)); + num = hex_to_bin(*str++) << 4; + num |= hex_to_bin(*str++); dev_addr [i] = num; } if (is_valid_ether_addr(dev_addr)) return 0; } - random_ether_addr(dev_addr); + eth_random_addr(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, @@ -719,31 +737,35 @@ static const struct net_device_ops eth_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static struct device_type gadget_type = { + .name = "gadget", +}; + /** - * gether_setup - initialize one ethernet-over-usb link + * gether_setup_name - initialize one ethernet-over-usb link * @g: gadget to associated with these links * @ethaddr: NULL, or a buffer in which the ethernet address of the * host side of the link is recorded + * @netname: name for network device (for example, "usb") * Context: may sleep * * This sets up the single network link that may be exported by a * gadget driver using this framework. The link layer addresses are * set up using module parameters. * - * Returns negative errno, or zero on success + * Returns an eth_dev pointer on success, or an ERR_PTR on failure. */ -int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +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); @@ -752,9 +774,12 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) 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; - strcpy(net->name, "usb%d"); + dev->qmult = qmult; + snprintf(net->name, sizeof(net->name), "%s%%d", netname); if (get_ether_addr(dev_addr, net->dev_addr)) dev_warn(&g->dev, @@ -768,31 +793,211 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) net->netdev_ops = ð_netdev_ops; - SET_ETHTOOL_OPS(net, &ops); - - /* two kinds of host-initiated state changes: - * - iff DATA transfer is active, carrier is "on" - * - tx queueing enabled if open *and* carrier is "on" - */ - netif_stop_queue(net); - netif_carrier_off(net); + net->ethtool_ops = &ops; dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); + SET_NETDEV_DEVTYPE(net, &gadget_type); status = register_netdev(net); 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" + * - tx queueing enabled if open *and* carrier is "on" + */ + netif_carrier_off(net); + } + sa.sa_family = net->type; + memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN); + rtnl_lock(); + status = dev_set_mac_address(net, &sa); + rtnl_unlock(); + if (status) + pr_warn("cannot set self ethernet address: %d\n", status); + else + INFO(dev, "MAC %pM\n", dev->dev_mac); + return status; } +EXPORT_SYMBOL_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 @@ -800,20 +1005,16 @@ int __init gether_setup(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); - free_netdev(the_dev->net); - - /* assuming we used keventd, it must quiesce too */ - flush_scheduled_work(); - - 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 @@ -833,14 +1034,14 @@ 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) return ERR_PTR(-EINVAL); link->in_ep->driver_data = dev; - result = usb_ep_enable(link->in_ep, link->in); + result = usb_ep_enable(link->in_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", link->in_ep->name, result); @@ -848,7 +1049,7 @@ struct net_device *gether_connect(struct gether *link) } link->out_ep->driver_data = dev; - result = usb_ep_enable(link->out_ep, link->out); + result = usb_ep_enable(link->out_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", link->out_ep->name, result); @@ -856,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; @@ -868,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); @@ -894,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 @@ -918,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 @@ -938,7 +1143,7 @@ void gether_disconnect(struct gether *link) } spin_unlock(&dev->req_lock); link->in_ep->driver_data = NULL; - link->in = NULL; + link->in_ep->desc = NULL; usb_ep_disable(link->out_ep); spin_lock(&dev->req_lock); @@ -953,7 +1158,7 @@ void gether_disconnect(struct gether *link) } spin_unlock(&dev->req_lock); link->out_ep->driver_data = NULL; - link->out = NULL; + link->out_ep->desc = NULL; /* finish forgetting about this USB link episode */ dev->header_len = 0; @@ -962,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 0d1f7ae3b07..0f0290acea7 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __U_ETHER_H @@ -30,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 @@ -52,20 +64,21 @@ struct gether { struct usb_ep *in_ep; struct usb_ep *out_ep; - /* descriptors match device speed at gether_connect() time */ - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - bool is_zlp_ok; u16 cdc_filter; - /* hooks for added framing, as needed for RNDIS and EEM. - * we currently don't support multiple frames per SKB. - */ + /* hooks for added framing, as needed for RNDIS and EEM. */ u32 header_len; - struct sk_buff *(*wrap)(struct sk_buff *skb); - int (*unwrap)(struct sk_buff *skb); + /* NCM requires fixed size bundles */ + bool is_fixed; + u32 fixed_out_len; + u32 fixed_in_len; + struct sk_buff *(*wrap)(struct gether *port, + struct sk_buff *skb); + int (*unwrap)(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list); /* called on network open/close */ void (*open)(struct gether *); @@ -77,10 +90,165 @@ struct gether { |USB_CDC_PACKET_TYPE_PROMISCUOUS \ |USB_CDC_PACKET_TYPE_DIRECTED) +/* variant of gether_setup that allows customizing network device name */ +struct eth_dev *gether_setup_name(struct usb_gadget *g, + 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 */ -int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); -void gether_cleanup(void); +/* gether_setup - initialize one ethernet-over-usb link + * @g: gadget to associated with these links + * @ethaddr: NULL, or a buffer in which the ethernet address of the + * host side of the link is recorded + * Context: may sleep + * + * This sets up the single network link that may be exported by a + * gadget driver using this framework. The link layer addresses are + * set up using module parameters. + * + * Returns 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 struct net_device *gether_setup_default(void) +{ + return gether_setup_name_default("usb"); +} + +/** + * gether_set_gadget - initialize one ethernet-over-usb link with a gadget + * @net: device representing this link + * @g: the gadget to initialize with + * + * This associates one ethernet-over-usb link with a gadget. + */ +void gether_set_gadget(struct net_device *net, struct usb_gadget *g); + +/** + * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address + * @net: device representing this link + * @dev_addr: eth address of this device + * + * This sets the device-side Ethernet address of this ethernet-over-usb link + * if dev_addr is correct. + * Returns negative errno if the new address is incorrect. + */ +int gether_set_dev_addr(struct net_device *net, const char *dev_addr); + +/** + * gether_get_dev_addr - get an ethernet-over-usb link eth address + * @net: device representing this link + * @dev_addr: place to store device's eth address + * @len: length of the @dev_addr buffer + * + * This gets the device-side Ethernet address of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len); + +/** + * gether_set_host_addr - initialize an ethernet-over-usb link with host address + * @net: device representing this link + * @host_addr: eth address of the host + * + * This sets the host-side Ethernet address of this ethernet-over-usb link + * if host_addr is correct. + * Returns negative errno if the new address is incorrect. + */ +int gether_set_host_addr(struct net_device *net, const char *host_addr); + +/** + * gether_get_host_addr - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_addr: place to store eth address of the host + * @len: length of the @host_addr buffer + * + * This gets the host-side Ethernet address of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_host_addr(struct net_device *net, char *host_addr, int len); + +/** + * gether_get_host_addr_cdc - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_addr: place to store eth address of the host + * @len: length of the @host_addr buffer + * + * This gets the CDC formatted host-side Ethernet address of this + * ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len); + +/** + * gether_get_host_addr_u8 - get an ethernet-over-usb link host address + * @net: device representing this link + * @host_mac: place to store the eth address of the host + * + * This gets the binary formatted host-side Ethernet address of this + * ethernet-over-usb link. + */ +void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]); + +/** + * gether_set_qmult - initialize an ethernet-over-usb link with a multiplier + * @net: device representing this link + * @qmult: queue multiplier + * + * This sets the queue length multiplier of this ethernet-over-usb link. + * For higher speeds use longer queues. + */ +void gether_set_qmult(struct net_device *net, unsigned qmult); + +/** + * gether_get_qmult - get an ethernet-over-usb link multiplier + * @net: device representing this link + * + * This gets the queue length multiplier of this ethernet-over-usb link. + */ +unsigned gether_get_qmult(struct net_device *net); + +/** + * gether_get_ifname - get an ethernet-over-usb link interface name + * @net: device representing this link + * @name: place to store the interface name + * @len: length of the @name buffer + * + * This gets the interface name of this ethernet-over-usb link. + * Returns zero on success, else negative errno. + */ +int gether_get_ifname(struct net_device *net, char *name, int len); + +void gether_cleanup(struct eth_dev *dev); /* connect/disconnect is handled by individual functions */ struct net_device *gether_connect(struct gether *); @@ -92,13 +260,6 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) if (!gadget_supports_altsettings(gadget)) return false; - /* SA1100 can do ECM, *without* status endpoint ... but we'll - * only use it in non-ECM mode for backwards compatibility - * (and since we currently require a status endpoint) - */ - if (gadget_is_sa1100(gadget)) - return false; - /* Everything else is *presumably* fine ... but this is a bit * chancy, so be **CERTAIN** there are no hardware issues with * your controller. Add it above if it can't handle CDC. @@ -106,22 +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]); - -#ifdef CONFIG_USB_ETH_RNDIS - -int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); - -#else - -static inline int -rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) -{ - return 0; -} - -#endif - #endif /* __U_ETHER_H */ diff --git a/drivers/usb/gadget/u_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 0a4d99ab40d..ad0aca81200 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -18,11 +18,15 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/interrupt.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/module.h> #include "u_serial.h" @@ -32,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 @@ -91,23 +96,24 @@ struct gs_buf { * (and thus for each /dev/ node). */ struct gs_port { + struct tty_port port; spinlock_t port_lock; /* guard port_* access */ struct gserial *port_usb; - struct tty_struct *port_tty; - unsigned open_count; bool openclose; /* open/close in progress */ u8 port_num; - wait_queue_head_t close_wait; /* wait for last close */ - struct list_head read_pool; + int read_started; + int read_allocated; struct list_head read_queue; unsigned n_read; struct tasklet_struct push; struct list_head write_pool; + int write_started; + int write_allocated; struct gs_buf port_write_buf; wait_queue_head_t drain_wait; /* wait while writes drain */ @@ -115,24 +121,25 @@ 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 */ #ifdef VERBOSE_DEBUG +#ifndef pr_vdebug #define pr_vdebug(fmt, arg...) \ pr_debug(fmt, ##arg) +#endif /* pr_vdebug */ #else +#ifndef pr_vdebug #define pr_vdebug(fmt, arg...) \ ({ if (0) pr_debug(fmt, ##arg); }) +#endif /* pr_vdebug */ #endif /*-------------------------------------------------------------------------*/ @@ -301,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 @@ -312,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 @@ -361,6 +370,9 @@ __acquires(&port->port_lock) struct usb_request *req; int len; + if (port->write_started >= QUEUE_SIZE) + break; + req = list_entry(pool->next, struct usb_request, list); len = gs_send_packet(port, req->buf, in->maxpacket); if (len == 0) { @@ -371,6 +383,7 @@ __acquires(&port->port_lock) req->length = len; list_del(&req->list); + req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", port->port_num, len, *((u8 *)req->buf), @@ -394,13 +407,15 @@ __acquires(&port->port_lock) break; } + port->write_started++; + /* abort immediately after disconnect */ if (!port->port_usb) break; } - if (do_tty_wake && port->port_tty) - tty_wakeup(port->port_tty); + if (do_tty_wake && port->port.tty) + tty_wakeup(port->port.tty); return status; } @@ -415,7 +430,6 @@ __acquires(&port->port_lock) { struct list_head *pool = &port->read_pool; struct usb_ep *out = port->port_usb->out; - unsigned started = 0; while (!list_empty(pool)) { struct usb_request *req; @@ -423,10 +437,13 @@ __acquires(&port->port_lock) struct tty_struct *tty; /* no more rx if closed */ - tty = port->port_tty; + tty = port->port.tty; if (!tty) break; + if (port->read_started >= QUEUE_SIZE) + break; + req = list_entry(pool->next, struct usb_request, list); list_del(&req->list); req->length = out->maxpacket; @@ -444,13 +461,13 @@ __acquires(&port->port_lock) list_add(&req->list, pool); break; } - started++; + port->read_started++; /* abort immediately after disconnect */ if (!port->port_usb) break; } - return started; + return port->read_started; } /* @@ -473,18 +490,14 @@ static void gs_rx_push(unsigned long _port) /* hand any queued data to the tty */ spin_lock_irq(&port->port_lock); - tty = port->port_tty; + tty = port->port.tty; while (!list_empty(queue)) { struct usb_request *req; req = list_first_entry(queue, struct usb_request, list); - /* discard data if tty was closed */ - if (!tty) - goto recycle; - /* leave data queued if tty was rx throttled */ - if (test_bit(TTY_THROTTLED, &tty->flags)) + if (tty && test_bit(TTY_THROTTLED, &tty->flags)) break; switch (req->status) { @@ -517,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) { @@ -530,22 +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; this is immediate with low_latency, and - * may trigger callbacks to this driver ... so drop the spinlock. + /* 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) { - spin_unlock_irq(&port->port_lock); - tty_flip_buffer_push(tty); - wake_up_interruptible(&tty->read_wait); - spin_lock_irq(&port->port_lock); - - /* tty may have been closed */ - tty = port->port_tty; - } + if (do_push) + tty_flip_buffer_push(&port->port); /* We want our data queue to become empty ASAP, keeping data @@ -590,6 +598,7 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) spin_lock(&port->port_lock); list_add(&req->list, &port->write_pool); + port->write_started--; switch (req->status) { default: @@ -611,7 +620,8 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) spin_unlock(&port->port_lock); } -static void gs_free_requests(struct usb_ep *ep, struct list_head *head) +static void gs_free_requests(struct usb_ep *ep, struct list_head *head, + int *allocated) { struct usb_request *req; @@ -619,25 +629,31 @@ static void gs_free_requests(struct usb_ep *ep, struct list_head *head) req = list_entry(head->next, struct usb_request, list); list_del(&req->list); gs_free_req(ep, req); + if (allocated) + (*allocated)--; } } static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, - void (*fn)(struct usb_ep *, struct usb_request *)) + void (*fn)(struct usb_ep *, struct usb_request *), + int *allocated) { int i; struct usb_request *req; + int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE; /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't * do quite that many this time, don't fail ... we just won't * be as speedy as we might otherwise be. */ - for (i = 0; i < QUEUE_SIZE; i++) { + for (i = 0; i < n; i++) { req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); if (!req) return list_empty(head) ? -ENOMEM : 0; req->complete = fn; list_add_tail(&req->list, head); + if (allocated) + (*allocated)++; } return 0; } @@ -664,14 +680,15 @@ static int gs_start_io(struct gs_port *port) * configurations may use different endpoints with a given port; * and high speed vs full speed changes packet sizes too. */ - status = gs_alloc_requests(ep, head, gs_read_complete); + status = gs_alloc_requests(ep, head, gs_read_complete, + &port->read_allocated); if (status) return status; status = gs_alloc_requests(port->port_usb->in, &port->write_pool, - gs_write_complete); + gs_write_complete, &port->write_allocated); if (status) { - gs_free_requests(ep, head); + gs_free_requests(ep, head, &port->read_allocated); return status; } @@ -681,10 +698,11 @@ static int gs_start_io(struct gs_port *port) /* unblock any pending writes into our circular buffer */ if (started) { - tty_wakeup(port->port_tty); + tty_wakeup(port->port.tty); } else { - gs_free_requests(ep, head); - gs_free_requests(port->port_usb->in, &port->write_pool); + gs_free_requests(ep, head, &port->read_allocated); + gs_free_requests(port->port_usb->in, &port->write_pool, + &port->write_allocated); status = -EIO; } @@ -706,9 +724,6 @@ static int gs_open(struct tty_struct *tty, struct file *file) struct gs_port *port; int status; - if (port_num < 0 || port_num >= n_ports) - return -ENXIO; - do { mutex_lock(&ports[port_num].lock); port = ports[port_num].port; @@ -718,9 +733,9 @@ static int gs_open(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); /* already open? Great. */ - if (port->open_count) { + if (port->port.count) { status = 0; - port->open_count++; + port->port.count++; /* currently opening/closing? wait ... */ } else if (port->openclose) { @@ -777,16 +792,11 @@ static int gs_open(struct tty_struct *tty, struct file *file) /* REVISIT maybe wait for "carrier detect" */ tty->driver_data = port; - port->port_tty = tty; + port->port.tty = tty; - port->open_count = 1; + port->port.count = 1; port->openclose = false; - /* low_latency means ldiscs work in tasklet context, without - * needing a workqueue schedule ... easier to keep up. - */ - tty->low_latency = 1; - /* if connected, start the I/O stream */ if (port->port_usb) { struct gserial *gser = port->port_usb; @@ -826,11 +836,11 @@ static void gs_close(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); - if (port->open_count != 1) { - if (port->open_count == 0) + if (port->port.count != 1) { + if (port->port.count == 0) WARN_ON(1); else - --port->open_count; + --port->port.count; goto exit; } @@ -840,7 +850,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) * and sleep if necessary */ port->openclose = true; - port->open_count = 0; + port->port.count = 0; gser = port->port_usb; if (gser && gser->disconnect) @@ -868,14 +878,14 @@ static void gs_close(struct tty_struct *tty, struct file *file) gs_buf_clear(&port->port_write_buf); tty->driver_data = NULL; - port->port_tty = NULL; + port->port.tty = NULL; port->openclose = false; pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); - wake_up_interruptible(&port->close_wait); + wake_up(&port->port.close_wait); exit: spin_unlock_irq(&port->port_lock); } @@ -906,7 +916,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) unsigned long flags; int status; - pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n", port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); @@ -1014,17 +1024,26 @@ static const struct tty_operations gs_tty_ops = { static struct tty_driver *gs_tty_driver; -static int __init +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); - init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->drain_wait); tasklet_init(&port->push, gs_rx_push, (unsigned long) port); @@ -1037,108 +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 __init gserial_setup(struct usb_gadget *g, unsigned count) -{ - unsigned i; - struct usb_cdc_line_coding coding; - int status; - - if (count == 0 || count > N_PORTS) - return -EINVAL; - - gs_tty_driver = alloc_tty_driver(count); - if (!gs_tty_driver) - return -ENOMEM; - - gs_tty_driver->owner = THIS_MODULE; - gs_tty_driver->driver_name = "g_serial"; - gs_tty_driver->name = PREFIX; - /* uses dynamically assigned dev_t values */ - - gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gs_tty_driver->init_termios = tty_std_termios; - - /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on - * MS-Windows. Otherwise, most of these flags shouldn't affect - * anything unless we were to actually hook up to a serial line. - */ - gs_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - gs_tty_driver->init_termios.c_ispeed = 9600; - gs_tty_driver->init_termios.c_ospeed = 9600; - - coding.dwDTERate = cpu_to_le32(9600); - coding.bCharFormat = 8; - coding.bParityType = USB_CDC_NO_PARITY; - coding.bDataBits = USB_CDC_1_STOP_BITS; - - tty_set_operations(gs_tty_driver, &gs_tty_ops); - - /* make devices be openable */ - for (i = 0; i < count; i++) { - mutex_init(&ports[i].lock); - status = gs_port_alloc(i, &coding); - if (status) { - count = i; - goto fail; - } - } - n_ports = count; - - /* export the driver ... */ - status = tty_register_driver(gs_tty_driver); - if (status) { - put_tty_driver(gs_tty_driver); - pr_err("%s: cannot register, err %d\n", - __func__, status); - goto fail; - } - - /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - for (i = 0; i < count; i++) { - struct device *tty_dev; - - tty_dev = tty_register_device(gs_tty_driver, i, &g->dev); - if (IS_ERR(tty_dev)) - pr_warning("%s: no classdev for port %d, err %ld\n", - __func__, i, PTR_ERR(tty_dev)); - } - - pr_debug("%s: registered %d ttyGS* device%s\n", __func__, - count, (count == 1) ? "" : "s"); - - return status; -fail: - while (count--) - kfree(ports[count].port); - put_tty_driver(gs_tty_driver); - gs_tty_driver = NULL; - return status; +out: + mutex_unlock(&ports[port_num].lock); + return ret; } static int gs_closed(struct gs_port *port) @@ -1146,58 +1066,82 @@ static int gs_closed(struct gs_port *port) int cond; spin_lock_irq(&port->port_lock); - cond = (port->open_count == 0) && !port->openclose; + cond = (port->port.count == 0) && !port->openclose; spin_unlock_irq(&port->port_lock); return cond; } -/** - * gserial_cleanup - remove TTY-over-USB driver and devices - * Context: may sleep - * - * This is called to free all resources allocated by @gserial_setup(). - * Accordingly, it may need to wait until some open /dev/ files have - * closed. - * - * The caller must have issued @gserial_disconnect() for any ports - * that had previously been connected, so that there is never any - * I/O pending when it's called. - */ -void gserial_cleanup(void) +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->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; - 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); - gs_tty_driver = NULL; + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ + + 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)); - pr_debug("%s: cleaned up ttyGS* support\n", __func__); + 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 @@ -1214,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. @@ -1226,19 +1170,26 @@ 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, gser->in_desc); + status = usb_ep_enable(gser->in); if (status < 0) return status; gser->in->driver_data = port; - status = usb_ep_enable(gser->out, gser->out_desc); + status = usb_ep_enable(gser->out); if (status < 0) goto fail_out; gser->out->driver_data = port; @@ -1258,7 +1209,7 @@ int gserial_connect(struct gserial *gser, u8 port_num) /* if it's already open, start I/O ... and notify the serial * protocol about open/close status (connect/disconnect). */ - if (port->open_count) { + if (port->port.count) { pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); gs_start_io(port); if (gser->connect) @@ -1277,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 @@ -1305,10 +1256,10 @@ void gserial_disconnect(struct gserial *gser) port->port_usb = NULL; gser->ioport = NULL; - if (port->open_count > 0 || port->openclose) { + if (port->port.count > 0 || port->openclose) { wake_up_interruptible(&port->drain_wait); - if (port->port_tty) - tty_hangup(port->port_tty); + if (port->port.tty) + tty_hangup(port->port.tty); } spin_unlock_irqrestore(&port->port_lock, flags); @@ -1321,10 +1272,76 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); - if (port->open_count == 0 && !port->openclose) + if (port->port.count == 0 && !port->openclose) gs_buf_free(&port->port_write_buf); - gs_free_requests(gser->out, &port->read_pool); - gs_free_requests(gser->out, &port->read_queue); - gs_free_requests(gser->in, &port->write_pool); + gs_free_requests(gser->out, &port->read_pool, NULL); + gs_free_requests(gser->out, &port->read_queue, NULL); + gs_free_requests(gser->in, &port->write_pool, NULL); + + port->read_allocated = port->read_started = + port->write_allocated = port->write_started = 0; + 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 300f0ed9475..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. @@ -35,8 +42,6 @@ struct gserial { struct usb_ep *in; struct usb_ep *out; - struct usb_endpoint_descriptor *in_desc; - struct usb_endpoint_descriptor *out_desc; /* REVISIT avoid this CDC-ACM support harder ... */ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ @@ -51,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 new file mode 100644 index 00000000000..7a55fea4343 --- /dev/null +++ b/drivers/usb/gadget/u_uac1.c @@ -0,0 +1,330 @@ +/* + * u_uac1.c -- ALSA audio utilities for Gadget stack + * + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/random.h> +#include <linux/syscalls.h> + +#include "u_uac1.h" + +/* + * This component encapsulates the ALSA devices for USB audio gadget + */ + +#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +#define FILE_CONTROL "/dev/snd/controlC0" + +static char *fn_play = FILE_PCM_PLAYBACK; +module_param(fn_play, charp, S_IRUGO); +MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); + +static char *fn_cap = FILE_PCM_CAPTURE; +module_param(fn_cap, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); + +static char *fn_cntl = FILE_CONTROL; +module_param(fn_cntl, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cntl, "Control device file name"); + +/*-------------------------------------------------------------------------*/ + +/** + * Some ALSA internal helper functions + */ +static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) +{ + struct snd_interval t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + if (hw_is_mask(var)) { + struct snd_mask *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set( + hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + struct snd_interval *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + struct snd_interval t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} +/*-------------------------------------------------------------------------*/ + +/** + * Set default hardware params + */ +static int playback_default_hw_params(struct gaudio_snd_dev *snd) +{ + struct snd_pcm_substream *substream = snd->substream; + struct snd_pcm_hw_params *params; + snd_pcm_sframes_t result; + + /* + * SNDRV_PCM_ACCESS_RW_INTERLEAVED, + * SNDRV_PCM_FORMAT_S16_LE + * CHANNELS: 2 + * RATE: 48000 + */ + snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + snd->format = SNDRV_PCM_FORMAT_S16_LE; + snd->channels = 2; + snd->rate = 48000; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + _snd_pcm_hw_params_any(params); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, + snd->access, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, + snd->format, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, + snd->channels, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, + snd->rate, 0); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); + + result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); + if (result < 0) { + ERROR(snd->card, + "Preparing sound card failed: %d\n", (int)result); + kfree(params); + return result; + } + + /* Store the hardware parameters */ + snd->access = params_access(params); + snd->format = params_format(params); + snd->channels = params_channels(params); + snd->rate = params_rate(params); + + kfree(params); + + INFO(snd->card, + "Hardware params: access %x, format %x, channels %d, rate %d\n", + snd->access, snd->format, snd->channels, snd->rate); + + return 0; +} + +/** + * Playback audio buffer data by ALSA PCM device + */ +static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) +{ + struct gaudio_snd_dev *snd = &card->playback; + struct snd_pcm_substream *substream = snd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + mm_segment_t old_fs; + ssize_t result; + snd_pcm_sframes_t frames; + +try_again: + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + result = snd_pcm_kernel_ioctl(substream, + SNDRV_PCM_IOCTL_PREPARE, NULL); + if (result < 0) { + ERROR(card, "Preparing sound card failed: %d\n", + (int)result); + return result; + } + } + + frames = bytes_to_frames(runtime, count); + old_fs = get_fs(); + set_fs(KERNEL_DS); + 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); + goto try_again; + } + set_fs(old_fs); + + return 0; +} + +static int u_audio_get_playback_channels(struct gaudio *card) +{ + return card->playback.channels; +} + +static int u_audio_get_playback_rate(struct gaudio *card) +{ + return card->playback.rate; +} + +/** + * Open ALSA PCM and control device files + * Initial the PCM or control device + */ +static int gaudio_open_snd_dev(struct gaudio *card) +{ + struct snd_pcm_file *pcm_file; + struct gaudio_snd_dev *snd; + + if (!card) + return -ENODEV; + + /* Open control device */ + snd = &card->control; + snd->filp = filp_open(fn_cntl, O_RDWR, 0); + if (IS_ERR(snd->filp)) { + int ret = PTR_ERR(snd->filp); + ERROR(card, "unable to open sound control device file: %s\n", + fn_cntl); + snd->filp = NULL; + return ret; + } + snd->card = card; + + /* Open PCM playback device and setup substream */ + 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; + snd->card = card; + playback_default_hw_params(snd); + + /* Open PCM capture device and setup substream */ + snd = &card->capture; + snd->filp = filp_open(fn_cap, O_RDONLY, 0); + if (IS_ERR(snd->filp)) { + ERROR(card, "No such PCM capture device: %s\n", fn_cap); + snd->substream = NULL; + snd->card = NULL; + snd->filp = NULL; + } else { + pcm_file = snd->filp->private_data; + snd->substream = pcm_file->substream; + snd->card = card; + } + + return 0; +} + +/** + * Close ALSA PCM and control device files + */ +static int gaudio_close_snd_dev(struct gaudio *gau) +{ + struct gaudio_snd_dev *snd; + + /* Close control device */ + snd = &gau->control; + if (snd->filp) + filp_close(snd->filp, NULL); + + /* Close PCM playback device and setup substream */ + snd = &gau->playback; + if (snd->filp) + filp_close(snd->filp, NULL); + + /* Close PCM capture device and setup substream */ + snd = &gau->capture; + if (snd->filp) + filp_close(snd->filp, NULL); + + return 0; +} + +static struct gaudio *the_card; +/** + * gaudio_setup - setup ALSA interface and preparing for USB transfer + * + * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. + * + * Returns negative errno, or zero on success + */ +int __init gaudio_setup(struct gaudio *card) +{ + int ret; + + ret = gaudio_open_snd_dev(card); + if (ret) + ERROR(card, "we need at least one control device\n"); + else if (!the_card) + the_card = card; + + return ret; + +} + +/** + * gaudio_cleanup - remove ALSA device interface + * + * This is called to free all resources allocated by @gaudio_setup(). + */ +void gaudio_cleanup(void) +{ + if (the_card) { + gaudio_close_snd_dev(the_card); + the_card = NULL; + } +} + diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/u_uac1.h new file mode 100644 index 00000000000..18c2e729faf --- /dev/null +++ b/drivers/usb/gadget/u_uac1.h @@ -0,0 +1,56 @@ +/* + * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities + * + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __U_AUDIO_H +#define __U_AUDIO_H + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/usb/audio.h> +#include <linux/usb/composite.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "gadget_chips.h" + +/* + * This represents the USB side of an audio card device, managed by a USB + * function which provides control and stream interfaces. + */ + +struct gaudio_snd_dev { + struct gaudio *card; + struct file *filp; + struct snd_pcm_substream *substream; + int access; + int format; + int channels; + int rate; +}; + +struct gaudio { + struct usb_function func; + struct usb_gadget *gadget; + + /* ALSA sound device interfaces */ + struct gaudio_snd_dev control; + struct gaudio_snd_dev playback; + struct gaudio_snd_dev capture; + + /* TODO */ +}; + +int gaudio_setup(struct gaudio *card); +void gaudio_cleanup(void); + +#endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c new file mode 100644 index 00000000000..b0d98172bc0 --- /dev/null +++ b/drivers/usb/gadget/udc-core.c @@ -0,0 +1,585 @@ +/** + * udc.c - Core UDC Framework + * + * Copyright (C) 2010 Texas Instruments + * Author: Felipe Balbi <balbi@ti.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 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#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> + +/** + * struct usb_udc - describes one usb device controller + * @driver - the gadget driver pointer. For use by the class code + * @dev - the child device to the actual controller + * @gadget - the gadget. For use by the class code + * @list - for use by the udc class driver + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; +}; + +static struct class *udc_class; +static LIST_HEAD(udc_list); +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) +{ + if (req->length == 0) + return 0; + + if (req->num_sgs) { + int mapped; + + mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mapped == 0) { + dev_err(&gadget->dev, "failed to map SGs\n"); + return -EFAULT; + } + + req->num_mapped_sgs = mapped; + } else { + req->dma = dma_map_single(&gadget->dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(&gadget->dev, req->dma)) { + dev_err(&gadget->dev, "failed to map buffer\n"); + return -EFAULT; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->num_mapped_sgs) { + dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->num_mapped_sgs = 0; + } else { + dma_unmap_single(&gadget->dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +#endif /* CONFIG_HAS_DMA */ + +/* ------------------------------------------------------------------------- */ + +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) +{ + 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 + * @gadget: The gadget we want to get started + * @driver: The driver we want to bind to @gadget + * + * 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_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + return gadget->ops->udc_start(gadget, 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 + * + * 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_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + gadget->ops->udc_stop(gadget, driver); +} + +/** + * usb_udc_release - release the usb_udc struct + * @dev: the dev member within usb_udc + * + * This is called by driver's core in order to free memory once the last + * reference is released. + */ +static void usb_udc_release(struct device *dev) +{ + struct usb_udc *udc; + + udc = container_of(dev, struct usb_udc, dev); + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(udc); +} + +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_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_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + struct usb_udc *udc; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + 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; + udc->dev.groups = usb_udc_attr_groups; + udc->dev.parent = parent; + ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); + if (ret) + goto err3; + + udc->gadget = gadget; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + + ret = device_add(&udc->dev); + if (ret) + goto err4; + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + + mutex_unlock(&udc_lock); + + return 0; + +err4: + list_del(&udc->list); + mutex_unlock(&udc_lock); + +err3: + put_device(&udc->dev); + +err2: + put_device(&gadget->dev); + kfree(udc); + +err1: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * 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) +{ + 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) +{ + dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", + udc->gadget->name); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + + 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; +} + +/** + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. + * + * This, will call usb_gadget_unregister_driver() if + * the @udc is still busy. + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + struct usb_udc *udc = NULL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; + + dev_err(gadget->dev.parent, "gadget not registered.\n"); + mutex_unlock(&udc_lock); + + return; + +found: + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + list_del(&udc->list); + mutex_unlock(&udc_lock); + + if (udc->driver) + 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; + int ret; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + /* For now we take the first one */ + if (!udc->driver) + goto found; + } + + pr_debug("couldn't find an available UDC\n"); + mutex_unlock(&udc_lock); + return -ENODEV; +found: + ret = udc_bind_to_driver(udc, driver); + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + if (!driver || !driver->unbind) + return -EINVAL; + + mutex_lock(&udc_lock); + 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; + } + + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +/* ------------------------------------------------------------------------- */ + +static ssize_t usb_udc_srp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + + if (sysfs_streq(buf, "1")) + usb_gadget_wakeup(udc->gadget); + + return n; +} +static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); + +static ssize_t usb_udc_softconn_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + + if (sysfs_streq(buf, "connect")) { + usb_gadget_udc_start(udc->gadget, udc->driver); + usb_gadget_connect(udc->gadget); + } else if (sysfs_streq(buf, "disconnect")) { + usb_gadget_disconnect(udc->gadget); + usb_gadget_udc_stop(udc->gadget, udc->driver); + } else { + dev_err(dev, "unsupported command '%s'\n", buf); + return -EINVAL; + } + + return n; +} +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 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_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 name##_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 snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ +} \ +static DEVICE_ATTR_RO(name) + +static USB_UDC_ATTR(is_otg); +static USB_UDC_ATTR(is_a_peripheral); +static USB_UDC_ATTR(b_hnp_enable); +static USB_UDC_ATTR(a_hnp_support); +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, + + &dev_attr_is_otg.attr, + &dev_attr_is_a_peripheral.attr, + &dev_attr_b_hnp_enable.attr, + &dev_attr_a_hnp_support.attr, + &dev_attr_a_alt_hnp_support.attr, + NULL, +}; + +static const struct attribute_group usb_udc_attr_group = { + .attrs = usb_udc_attrs, +}; + +static const struct attribute_group *usb_udc_attr_groups[] = { + &usb_udc_attr_group, + NULL, +}; + +static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + int ret; + + ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_NAME\n"); + return ret; + } + + if (udc->driver) { + ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", + udc->driver->function); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); + return ret; + } + } + + return 0; +} + +static int __init usb_udc_init(void) +{ + udc_class = class_create(THIS_MODULE, "udc"); + if (IS_ERR(udc_class)) { + pr_err("failed to create udc class --> %ld\n", + PTR_ERR(udc_class)); + return PTR_ERR(udc_class); + } + + udc_class->dev_uevent = usb_udc_uevent; + return 0; +} +subsys_initcall(usb_udc_init); + +static void __exit usb_udc_exit(void) +{ + class_destroy(udc_class); +} +module_exit(usb_udc_exit); + +MODULE_DESCRIPTION("UDC Framework"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 58c4d37d312..73a4dfba0ed 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -9,86 +9,21 @@ #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/module.h> #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> #include <linux/usb/gadget.h> -#include <asm/unaligned.h> - - -static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) -{ - int count = 0; - u8 c; - u16 uchar; - - /* this insists on correct encodings, though not minimal ones. - * BUT it currently rejects legit 4-byte UTF-8 code points, - * which need surrogate pairs. (Unicode 3.1 can use them.) - */ - while (len != 0 && (c = (u8) *s++) != 0) { - if (unlikely(c & 0x80)) { - // 2-byte sequence: - // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx - if ((c & 0xe0) == 0xc0) { - uchar = (c & 0x1f) << 6; - - c = (u8) *s++; - if ((c & 0xc0) != 0x80) - goto fail; - c &= 0x3f; - uchar |= c; - - // 3-byte sequence (most CJKV characters): - // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx - } else if ((c & 0xf0) == 0xe0) { - uchar = (c & 0x0f) << 12; - - c = (u8) *s++; - if ((c & 0xc0) != 0x80) - goto fail; - c &= 0x3f; - uchar |= c << 6; - - c = (u8) *s++; - if ((c & 0xc0) != 0x80) - goto fail; - c &= 0x3f; - uchar |= c; - - /* no bogus surrogates */ - if (0xd800 <= uchar && uchar <= 0xdfff) - goto fail; - - // 4-byte sequence (surrogate pairs, currently rare): - // 11101110wwwwzzzzyy + 110111yyyyxxxxxx - // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx - // (uuuuu = wwww + 1) - // FIXME accept the surrogate code points (only) - - } else - goto fail; - } else - uchar = c; - put_unaligned_le16(uchar, cp++); - count++; - len--; - } - return count; -fail: - return -1; -} - /** * usb_gadget_get_string - fill out a string descriptor * @table: of c strings encoded using UTF-8 * @id: string id, from low byte of wValue in get string descriptor - * @buf: at least 256 bytes + * @buf: at least 256 bytes, must be 16-bit aligned * * Finds the UTF-8 string matching the ID, and converts it into a * string descriptor in utf16-le. @@ -125,12 +60,12 @@ usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) /* string descriptors have length, tag, then UTF16-LE text */ len = min ((size_t) 126, strlen (s->s)); - memset (buf + 2, 0, 2 * len); /* zero all the bytes */ - len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); + len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN, + (wchar_t *) &buf[2], 126); if (len < 0) return -EINVAL; buf [0] = (len + 1) * 2; buf [1] = USB_DT_STRING; return buf [0]; } - +EXPORT_SYMBOL_GPL(usb_gadget_get_string); diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h new file mode 100644 index 00000000000..7a9111de805 --- /dev/null +++ b/drivers/usb/gadget/uvc.h @@ -0,0 +1,202 @@ +/* + * uvc_gadget.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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. + */ + +#ifndef _UVC_GADGET_H_ +#define _UVC_GADGET_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> +#include <linux/usb/ch9.h> + +#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0) +#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0) +#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1) +#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2) +#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3) +#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4) +#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5) +#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5) + +struct uvc_request_data +{ + __s32 length; + __u8 data[60]; +}; + +struct uvc_event +{ + union { + enum usb_device_speed speed; + struct usb_ctrlrequest req; + struct uvc_request_data data; + }; +}; + +#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data) + +#define UVC_INTF_CONTROL 0 +#define UVC_INTF_STREAMING 1 + +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + +#ifdef __KERNEL__ + +#include <linux/usb.h> /* For usb_endpoint_* */ +#include <linux/usb/gadget.h> +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-device.h> + +#include "uvc_queue.h" + +#define UVC_TRACE_PROBE (1 << 0) +#define UVC_TRACE_DESCR (1 << 1) +#define UVC_TRACE_CONTROL (1 << 2) +#define UVC_TRACE_FORMAT (1 << 3) +#define UVC_TRACE_CAPTURE (1 << 4) +#define UVC_TRACE_CALLS (1 << 5) +#define UVC_TRACE_IOCTL (1 << 6) +#define UVC_TRACE_FRAME (1 << 7) +#define UVC_TRACE_SUSPEND (1 << 8) +#define UVC_TRACE_STATUS (1 << 9) + +#define UVC_WARN_MINMAX 0 +#define UVC_WARN_PROBE_DEF 1 + +extern unsigned int uvc_gadget_trace_param; + +#define uvc_trace(flag, msg...) \ + do { \ + if (uvc_gadget_trace_param & flag) \ + printk(KERN_DEBUG "uvcvideo: " msg); \ + } while (0) + +#define uvc_warn_once(dev, warn, msg...) \ + do { \ + if (!test_and_set_bit(warn, &dev->warnings)) \ + printk(KERN_INFO "uvcvideo: " msg); \ + } while (0) + +#define uvc_printk(level, msg...) \ + printk(level "uvcvideo: " msg) + +/* ------------------------------------------------------------------------ + * Driver specific constants + */ + +#define DRIVER_VERSION "0.1.0" +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) + +#define UVC_NUM_REQUESTS 4 +#define UVC_MAX_REQUEST_SIZE 64 +#define UVC_MAX_EVENTS 4 + +/* ------------------------------------------------------------------------ + * Structures + */ + +struct uvc_video +{ + struct usb_ep *ep; + + /* Frame parameters */ + u8 bpp; + u32 fcc; + unsigned int width; + unsigned int height; + unsigned int imagesize; + + /* Requests */ + unsigned int req_size; + struct usb_request *req[UVC_NUM_REQUESTS]; + __u8 *req_buffer[UVC_NUM_REQUESTS]; + struct list_head req_free; + spinlock_t req_lock; + + void (*encode) (struct usb_request *req, struct uvc_video *video, + struct uvc_buffer *buf); + + /* Context data used by the completion handler */ + __u32 payload_size; + __u32 max_payload_size; + + struct uvc_video_queue queue; + unsigned int fid; +}; + +enum uvc_state +{ + UVC_STATE_DISCONNECTED, + UVC_STATE_CONNECTED, + UVC_STATE_STREAMING, +}; + +struct uvc_device +{ + struct video_device *vdev; + struct v4l2_device v4l2_dev; + enum uvc_state state; + struct usb_function func; + struct uvc_video video; + + /* Descriptors */ + struct { + const struct uvc_descriptor_header * const *fs_control; + const struct uvc_descriptor_header * const *ss_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; + } desc; + + unsigned int control_intf; + struct usb_ep *control_ep; + struct usb_request *control_req; + void *control_buf; + + unsigned int streaming_intf; + + /* Events */ + unsigned int event_length; + unsigned int event_setup_out : 1; +}; + +static inline struct uvc_device *to_uvc(struct usb_function *f) +{ + return container_of(f, struct uvc_device, func); +} + +struct uvc_file_handle +{ + struct v4l2_fh vfh; + struct uvc_video *device; +}; + +#define to_uvc_file_handle(handle) \ + container_of(handle, struct uvc_file_handle, vfh) + +/* ------------------------------------------------------------------------ + * 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); +extern void uvc_function_disconnect(struct uvc_device *uvc); + +#endif /* __KERNEL__ */ + +#endif /* _UVC_GADGET_H_ */ + diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c new file mode 100644 index 00000000000..1c29bc954db --- /dev/null +++ b/drivers/usb/gadget/uvc_queue.c @@ -0,0 +1,407 @@ +/* + * uvc_queue.c -- USB Video Class driver - Buffers management + * + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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/atomic.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> + +#include <media/v4l2-common.h> +#include <media/videobuf2-vmalloc.h> + +#include "uvc.h" + +/* ------------------------------------------------------------------------ + * Video buffers queue management. + * + * Video queues is initialized by uvc_queue_init(). The function performs + * basic initialization of the uvc_video_queue struct and never fails. + * + * 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. + */ + +/* ----------------------------------------------------------------------------- + * videobuf2 queue operations + */ + +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[]) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); + + if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) + *nbuffers = UVC_MAX_VIDEO_BUFFERS; + + *nplanes = 1; + + sizes[0] = video->imagesize; + + return 0; +} + +static int uvc_buffer_prepare(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); + + 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; + } + + if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) + return -ENODEV; + + 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); + + return 0; +} + +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; + + spin_lock_irqsave(&queue->irqlock, flags); + + 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); + } + + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +static void uvc_wait_prepare(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_unlock(&queue->mutex); +} + +static void uvc_wait_finish(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_lock(&queue->mutex); +} + +static struct vb2_ops uvc_queue_qops = { + .queue_setup = uvc_queue_setup, + .buf_prepare = uvc_buffer_prepare, + .buf_queue = uvc_buffer_queue, + .wait_prepare = uvc_wait_prepare, + .wait_finish = uvc_wait_finish, +}; + +static int uvc_queue_init(struct uvc_video_queue *queue, + enum v4l2_buf_type type) +{ + int ret; + + 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; + + mutex_init(&queue->mutex); + spin_lock_init(&queue->irqlock); + INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = 0; + + 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); +} + +/* + * Allocate the video buffers. + */ +static int uvc_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_reqbufs(&queue->queue, rb); + mutex_unlock(&queue->mutex); + + return ret ? ret : rb->count; +} + +static int uvc_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_querybuf(&queue->queue, buf); + mutex_unlock(&queue->mutex); + + return ret; +} + +static int uvc_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) +{ + unsigned long flags; + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_qbuf(&queue->queue, buf); + if (ret < 0) + goto done; + + spin_lock_irqsave(&queue->irqlock, flags); + ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; + queue->flags &= ~UVC_QUEUE_PAUSED; + spin_unlock_irqrestore(&queue->irqlock, flags); + +done: + mutex_unlock(&queue->mutex); + return ret; +} + +/* + * 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 *buf, int nonblocking) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_dqbuf(&queue->queue, buf, nonblocking); + mutex_unlock(&queue->mutex); + + return ret; +} + +/* + * Poll the video queue. + * + * 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) +{ + unsigned int ret; + + mutex_lock(&queue->mutex); + ret = vb2_poll(&queue->queue, file, wait); + mutex_unlock(&queue->mutex); + + return ret; +} + +static int uvc_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma) +{ + int ret; + + mutex_lock(&queue->mutex); + ret = vb2_mmap(&queue->queue, vma); + mutex_unlock(&queue->mutex); + + return ret; +} + +#ifndef CONFIG_MMU +/* + * Get unmapped area. + * + * NO-MMU arch need this function to make mmap() work correctly. + */ +static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + +/* + * Cancel the video buffers queue. + * + * Cancelling the queue marks all buffers on the irq queue as erroneous, + * wakes them up and removes them from the queue. + * + * If the disconnect parameter is set, further calls to uvc_queue_buffer will + * fail with -ENODEV. + * + * This function acquires the irq spinlock and can be called from interrupt + * context. + */ +static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +{ + struct uvc_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&queue->irqlock, flags); + while (!list_empty(&queue->irqqueue)) { + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + list_del(&buf->queue); + buf->state = UVC_BUF_STATE_ERROR; + 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 + * could result in an interruptible wait in uvc_dequeue_buffer. Do not + * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED + * state outside the queue code. + */ + if (disconnect) + queue->flags |= UVC_QUEUE_DISCONNECTED; + spin_unlock_irqrestore(&queue->irqlock, flags); +} + +/* + * Enable or disable the video buffers queue. + * + * The queue must be enabled before starting video acquisition and must be + * disabled after stopping it. This ensures that the video buffers queue + * state can be properly initialized before buffers are accessed from the + * interrupt handler. + * + * Enabling the video queue initializes parameters (such as sequence number, + * sync pattern, ...). If the queue is already enabled, return -EBUSY. + * + * Disabling the video queue cancels the queue and removes all buffers from + * the main queue. + * + * This function can't be called from interrupt context. Use + * uvc_queue_cancel() instead. + */ +static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +{ + unsigned long flags; + int ret = 0; + + mutex_lock(&queue->mutex); + if (enable) { + ret = vb2_streamon(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; + + queue->sequence = 0; + queue->buf_used = 0; + } else { + ret = vb2_streamoff(&queue->queue, queue->queue.type); + if (ret < 0) + goto done; + + spin_lock_irqsave(&queue->irqlock, flags); + INIT_LIST_HEAD(&queue->irqqueue); + + /* + * 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: + mutex_unlock(&queue->mutex); + return ret; +} + +/* called with &queue_irqlock held.. */ +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->length != buf->bytesused) { + buf->state = UVC_BUF_STATE_QUEUED; + vb2_set_plane_payload(&buf->buf, 0, 0); + return buf; + } + + list_del(&buf->queue); + if (!list_empty(&queue->irqqueue)) + nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + nextbuf = NULL; + + 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); + + return nextbuf; +} + +static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue) +{ + struct uvc_buffer *buf = NULL; + + if (!list_empty(&queue->irqqueue)) + buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, + queue); + else + queue->flags |= UVC_QUEUE_PAUSED; + + return buf; +} + diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h new file mode 100644 index 00000000000..8e76ce982f1 --- /dev/null +++ b/drivers/usb/gadget/uvc_queue.h @@ -0,0 +1,63 @@ +#ifndef _UVC_QUEUE_H_ +#define _UVC_QUEUE_H_ + +#ifdef __KERNEL__ + +#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) +/* Maximum number of video buffers. */ +#define UVC_MAX_VIDEO_BUFFERS 32 + +/* ------------------------------------------------------------------------ + * Structures. + */ + +enum uvc_buffer_state { + UVC_BUF_STATE_IDLE = 0, + UVC_BUF_STATE_QUEUED = 1, + UVC_BUF_STATE_ACTIVE = 2, + UVC_BUF_STATE_DONE = 3, + UVC_BUF_STATE_ERROR = 4, +}; + +struct uvc_buffer { + struct vb2_buffer buf; + struct list_head queue; + + enum uvc_buffer_state state; + void *mem; + unsigned int length; + unsigned int bytesused; +}; + +#define UVC_QUEUE_DISCONNECTED (1 << 0) +#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1) +#define UVC_QUEUE_PAUSED (1 << 2) + +struct uvc_video_queue { + struct vb2_queue queue; + struct mutex mutex; /* Protects queue */ + + unsigned int flags; + __u32 sequence; + + unsigned int buf_used; + + spinlock_t irqlock; /* Protects flags and irqqueue */ + struct list_head irqqueue; +}; + +static inline int uvc_queue_streaming(struct uvc_video_queue *queue) +{ + return vb2_is_streaming(&queue->queue); +} + +#endif /* __KERNEL__ */ + +#endif /* _UVC_QUEUE_H_ */ + diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c new file mode 100644 index 00000000000..ad48e81155e --- /dev/null +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -0,0 +1,365 @@ +/* + * uvc_v4l2.c -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> + +#include "uvc.h" +#include "uvc_queue.h" + +/* -------------------------------------------------------------------------- + * Requests handling + */ + +static int +uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) +{ + struct usb_composite_dev *cdev = uvc->func.config->cdev; + struct usb_request *req = uvc->control_req; + + if (data->length < 0) + return usb_ep_set_halt(cdev->gadget->ep0); + + req->length = min_t(unsigned int, uvc->event_length, data->length); + req->zero = data->length < uvc->event_length; + + memcpy(req->buf, data->data, req->length); + + return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL); +} + +/* -------------------------------------------------------------------------- + * V4L2 + */ + +struct uvc_format +{ + u8 bpp; + u32 fcc; +}; + +static struct uvc_format uvc_formats[] = { + { 16, V4L2_PIX_FMT_YUYV }, + { 0, V4L2_PIX_FMT_MJPEG }, +}; + +static int +uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) +{ + fmt->fmt.pix.pixelformat = video->fcc; + fmt->fmt.pix.width = video->width; + fmt->fmt.pix.height = video->height; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = video->bpp * video->width / 8; + fmt->fmt.pix.sizeimage = video->imagesize; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int +uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) +{ + struct uvc_format *format; + unsigned int imagesize; + unsigned int bpl; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) { + format = &uvc_formats[i]; + if (format->fcc == fmt->fmt.pix.pixelformat) + break; + } + + if (i == ARRAY_SIZE(uvc_formats)) { + printk(KERN_INFO "Unsupported format 0x%08x.\n", + fmt->fmt.pix.pixelformat); + return -EINVAL; + } + + bpl = format->bpp * fmt->fmt.pix.width / 8; + imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage; + + video->fcc = format->fcc; + video->bpp = format->bpp; + video->width = fmt->fmt.pix.width; + video->height = fmt->fmt.pix.height; + video->imagesize = imagesize; + + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = bpl; + fmt->fmt.pix.sizeimage = imagesize; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int +uvc_v4l2_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, vdev); + v4l2_fh_add(&handle->vfh); + + handle->device = &uvc->video; + file->private_data = &handle->vfh; + + uvc_function_connect(uvc); + return 0; +} + +static int +uvc_v4l2_release(struct file *file) +{ + 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); + struct uvc_video *video = handle->device; + + uvc_function_disconnect(uvc); + + uvc_video_enable(video, 0); + uvc_free_buffers(&video->queue); + + file->private_data = NULL; + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); + + return 0; +} + +static long +uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + 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); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + struct uvc_video *video = &uvc->video; + int ret = 0; + + switch (cmd) { + /* Query capabilities */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof *cap); + 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; + break; + } + + /* Get & Set format */ + case VIDIOC_G_FMT: + { + struct v4l2_format *fmt = arg; + + if (fmt->type != video->queue.queue.type) + return -EINVAL; + + return uvc_v4l2_get_format(video, fmt); + } + + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = arg; + + if (fmt->type != video->queue.queue.type) + return -EINVAL; + + return uvc_v4l2_set_format(video, fmt); + } + + /* Buffers & streaming */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + + if (rb->type != video->queue.queue.type) + return -EINVAL; + + ret = uvc_alloc_buffers(&video->queue, rb); + if (ret < 0) + return ret; + + ret = 0; + break; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + + return uvc_query_buffer(&video->queue, buf); + } + + case VIDIOC_QBUF: + if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0) + return ret; + + return uvc_video_pump(video); + + case VIDIOC_DQBUF: + return uvc_dequeue_buffer(&video->queue, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + int *type = arg; + + if (*type != video->queue.queue.type) + return -EINVAL; + + /* 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.queue.type) + return -EINVAL; + + return uvc_video_enable(video, 0); + } + + /* Events */ + case VIDIOC_DQEVENT: + { + struct v4l2_event *event = arg; + + ret = v4l2_event_dequeue(&handle->vfh, event, + file->f_flags & O_NONBLOCK); + if (ret == 0 && event->type == UVC_EVENT_SETUP) { + struct uvc_event *uvc_event = (void *)&event->u.data; + + /* Tell the complete callback to generate an event for + * the next request that will be enqueued by + * uvc_event_write. + */ + uvc->event_setup_out = + !(uvc_event->req.bRequestType & USB_DIR_IN); + uvc->event_length = uvc_event->req.wLength; + } + + return ret; + } + + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) + return -EINVAL; + + return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); + } + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_event_unsubscribe(&handle->vfh, arg); + + case UVCIOC_SEND_RESPONSE: + ret = uvc_send_response(uvc, arg); + break; + + default: + return -ENOIOCTLCMD; + } + + return ret; +} + +static long +uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); +} + +static int +uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + + return uvc_queue_mmap(&uvc->video.queue, vma); +} + +static unsigned int +uvc_v4l2_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + + return 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 uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff); +} +#endif + +static struct v4l2_file_operations uvc_v4l2_fops = { + .owner = THIS_MODULE, + .open = uvc_v4l2_open, + .release = uvc_v4l2_release, + .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 new file mode 100644 index 00000000000..71e896d4c5a --- /dev/null +++ b/drivers/usb/gadget/uvc_video.c @@ -0,0 +1,394 @@ +/* + * uvc_video.c -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <media/v4l2-dev.h> + +#include "uvc.h" +#include "uvc_queue.h" + +/* -------------------------------------------------------------------------- + * Video codecs + */ + +static int +uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, + u8 *data, int len) +{ + data[0] = 2; + data[1] = UVC_STREAM_EOH | video->fid; + + if (buf->bytesused - video->queue.buf_used <= len - 2) + data[1] |= UVC_STREAM_EOF; + + return 2; +} + +static int +uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, + u8 *data, int len) +{ + struct uvc_video_queue *queue = &video->queue; + unsigned int nbytes; + void *mem; + + /* Copy video data to the USB buffer. */ + mem = buf->mem + queue->buf_used; + nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); + + memcpy(data, mem, nbytes); + queue->buf_used += nbytes; + + return nbytes; +} + +static void +uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, + struct uvc_buffer *buf) +{ + void *mem = req->buf; + int len = video->req_size; + int ret; + + /* Add a header at the beginning of the payload. */ + if (video->payload_size == 0) { + ret = uvc_video_encode_header(video, buf, mem, len); + video->payload_size += ret; + mem += ret; + len -= ret; + } + + /* Process video data. */ + len = min((int)(video->max_payload_size - video->payload_size), len); + ret = uvc_video_encode_data(video, buf, mem, len); + + video->payload_size += ret; + len -= ret; + + req->length = video->req_size - len; + req->zero = video->payload_size == video->max_payload_size; + + 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); + video->fid ^= UVC_STREAM_FID; + + video->payload_size = 0; + } + + if (video->payload_size == video->max_payload_size || + buf->bytesused == video->queue.buf_used) + video->payload_size = 0; +} + +static void +uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, + struct uvc_buffer *buf) +{ + void *mem = req->buf; + int len = video->req_size; + int ret; + + /* Add the header. */ + ret = uvc_video_encode_header(video, buf, mem, len); + mem += ret; + len -= ret; + + /* Process video data. */ + ret = uvc_video_encode_data(video, buf, mem, len); + len -= ret; + + req->length = video->req_size - len; + + 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); + video->fid ^= UVC_STREAM_FID; + } +} + +/* -------------------------------------------------------------------------- + * Request handling + */ + +/* + * I somehow feel that synchronisation won't be easy to achieve here. We have + * three events that control USB requests submission: + * + * - USB request completion: the completion handler will resubmit the request + * if a video buffer is available. + * + * - USB interface setting selection: in response to a SET_INTERFACE request, + * the handler will start streaming if a video buffer is available and if + * video is not currently streaming. + * + * - V4L2 buffer queueing: the driver will start streaming if video is not + * currently streaming. + * + * Race conditions between those 3 events might lead to deadlocks or other + * nasty side effects. + * + * The "video currently streaming" condition can't be detected by the irqqueue + * being empty, as a request can still be in flight. A separate "queue paused" + * flag is thus needed. + * + * The paused flag will be set when we try to retrieve the irqqueue head if the + * queue is empty, and cleared when we queue a buffer. + * + * The USB request completion handler will get the buffer at the irqqueue head + * under protection of the queue spinlock. If the queue is empty, the streaming + * paused flag will be set. Right after releasing the spinlock a userspace + * application can queue a buffer. The flag will then cleared, and the ioctl + * handler will restart the video stream. + */ +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; + + switch (req->status) { + case 0: + break; + + 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; + } + + spin_lock_irqsave(&video->queue.irqlock, flags); + buf = uvc_queue_head(&video->queue); + if (buf == NULL) { + spin_unlock_irqrestore(&video->queue.irqlock, flags); + goto requeue; + } + + video->encode(req, video, buf); + + if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) { + printk(KERN_INFO "Failed to queue request (%d).\n", ret); + usb_ep_set_halt(ep); + spin_unlock_irqrestore(&video->queue.irqlock, flags); + goto requeue; + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + + return; + +requeue: + spin_lock_irqsave(&video->req_lock, flags); + list_add_tail(&req->list, &video->req_free); + spin_unlock_irqrestore(&video->req_lock, flags); +} + +static int +uvc_video_free_requests(struct uvc_video *video) +{ + unsigned int i; + + for (i = 0; i < UVC_NUM_REQUESTS; ++i) { + if (video->req[i]) { + usb_ep_free_request(video->ep, video->req[i]); + video->req[i] = NULL; + } + + if (video->req_buffer[i]) { + kfree(video->req_buffer[i]); + video->req_buffer[i] = NULL; + } + } + + INIT_LIST_HEAD(&video->req_free); + video->req_size = 0; + return 0; +} + +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(req_size, GFP_KERNEL); + if (video->req_buffer[i] == NULL) + goto error; + + video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (video->req[i] == NULL) + goto error; + + video->req[i]->buf = video->req_buffer[i]; + video->req[i]->length = 0; + 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 = req_size; + + return 0; + +error: + uvc_video_free_requests(video); + return ret; +} + +/* -------------------------------------------------------------------------- + * Video streaming + */ + +/* + * uvc_video_pump - Pump video data into the USB requests + * + * This function fills the available USB requests (listed in req_free) with + * video data from the queued buffers. + */ +static int +uvc_video_pump(struct uvc_video *video) +{ + struct usb_request *req; + struct uvc_buffer *buf; + unsigned long flags; + int ret; + + /* FIXME TODO Race between uvc_video_pump and requests completion + * handler ??? + */ + + while (1) { + /* Retrieve the first available USB request, protected by the + * request lock. + */ + spin_lock_irqsave(&video->req_lock, flags); + if (list_empty(&video->req_free)) { + spin_unlock_irqrestore(&video->req_lock, flags); + return 0; + } + req = list_first_entry(&video->req_free, struct usb_request, + list); + list_del(&req->list); + spin_unlock_irqrestore(&video->req_lock, flags); + + /* Retrieve the first available video buffer and fill the + * request, protected by the video queue irqlock. + */ + spin_lock_irqsave(&video->queue.irqlock, flags); + buf = uvc_queue_head(&video->queue); + if (buf == NULL) { + spin_unlock_irqrestore(&video->queue.irqlock, flags); + break; + } + + video->encode(req, video, buf); + + /* Queue the USB request */ + 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); + break; + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + } + + spin_lock_irqsave(&video->req_lock, flags); + list_add_tail(&req->list, &video->req_free); + spin_unlock_irqrestore(&video->req_lock, flags); + return 0; +} + +/* + * Enable or disable the video stream. + */ +static int +uvc_video_enable(struct uvc_video *video, int enable) +{ + unsigned int i; + int ret; + + if (video->ep == NULL) { + printk(KERN_INFO "Video enable failed, device is " + "uninitialized.\n"); + return -ENODEV; + } + + if (!enable) { + for (i = 0; i < UVC_NUM_REQUESTS; ++i) + usb_ep_dequeue(video->ep, video->req[i]); + + uvc_video_free_requests(video); + uvc_queue_enable(&video->queue, 0); + return 0; + } + + if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + return ret; + + if ((ret = uvc_video_alloc_requests(video)) < 0) + return ret; + + if (video->max_payload_size) { + video->encode = uvc_video_encode_bulk; + video->payload_size = 0; + } else + video->encode = uvc_video_encode_isoc; + + return uvc_video_pump(video); +} + +/* + * Initialize the UVC video stream. + */ +static int +uvc_video_init(struct uvc_video *video) +{ + INIT_LIST_HEAD(&video->req_free); + spin_lock_init(&video->req_lock); + + video->fcc = V4L2_PIX_FMT_YUYV; + video->bpp = 16; + video->width = 320; + video->height = 240; + video->imagesize = 320 * 240 * 2; + + /* Initialize the video buffers queue. */ + uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return 0; +} + diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c new file mode 100644 index 00000000000..8cef1e658c2 --- /dev/null +++ b/drivers/usb/gadget/webcam.c @@ -0,0 +1,412 @@ +/* + * webcam.c -- USB webcam gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.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> +#include <linux/device.h> +#include <linux/usb/video.h> + +#include "f_uvc.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 "uvc_queue.c" +#include "uvc_video.c" +#include "uvc_v4l2.c" +#include "f_uvc.c" + +USB_GADGET_COMPOSITE_OPTIONS(); +/* -------------------------------------------------------------------------- + * Device descriptor + */ + +#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */ +#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */ +#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */ + +static char webcam_vendor_label[] = "Linux Foundation"; +static char webcam_product_label[] = "Webcam gadget"; +static char webcam_config_label[] = "Video"; + +/* string IDs are assigned dynamically */ + +#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX + +static struct usb_string webcam_strings[] = { + [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label, + [USB_GADGET_PRODUCT_IDX].s = webcam_product_label, + [USB_GADGET_SERIAL_IDX].s = "", + [STRING_DESCRIPTION_IDX].s = webcam_config_label, + { } +}; + +static struct usb_gadget_strings webcam_stringtab = { + .language = 0x0409, /* en-us */ + .strings = webcam_strings, +}; + +static struct usb_gadget_strings *webcam_device_strings[] = { + &webcam_stringtab, + NULL, +}; + +static struct usb_device_descriptor webcam_device_descriptor = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, + .bMaxPacketSize0 = 0, /* dynamic */ + .idVendor = cpu_to_le16(WEBCAM_VENDOR_ID), + .idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID), + .bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD), + .iManufacturer = 0, /* dynamic */ + .iProduct = 0, /* dynamic */ + .iSerialNumber = 0, /* dynamic */ + .bNumConfigurations = 0, /* dynamic */ +}; + +DECLARE_UVC_HEADER_DESCRIPTOR(1); + +static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { + .bLength = UVC_DT_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_HEADER, + .bcdUVC = cpu_to_le16(0x0100), + .wTotalLength = 0, /* dynamic */ + .dwClockFrequency = cpu_to_le32(48000000), + .bInCollection = 0, /* dynamic */ + .baInterfaceNr[0] = 0, /* dynamic */ +}; + +static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { + .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, + .bTerminalID = 1, + .wTerminalType = cpu_to_le16(0x0201), + .bAssocTerminal = 0, + .iTerminal = 0, + .wObjectiveFocalLengthMin = cpu_to_le16(0), + .wObjectiveFocalLengthMax = cpu_to_le16(0), + .wOcularFocalLength = cpu_to_le16(0), + .bControlSize = 3, + .bmControls[0] = 2, + .bmControls[1] = 0, + .bmControls[2] = 0, +}; + +static const struct uvc_processing_unit_descriptor uvc_processing = { + .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, + .bUnitID = 2, + .bSourceID = 1, + .wMaxMultiplier = cpu_to_le16(16*1024), + .bControlSize = 2, + .bmControls[0] = 1, + .bmControls[1] = 0, + .iProcessing = 0, +}; + +static const struct uvc_output_terminal_descriptor uvc_output_terminal = { + .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, + .bTerminalID = 3, + .wTerminalType = cpu_to_le16(0x0101), + .bAssocTerminal = 0, + .bSourceID = 2, + .iTerminal = 0, +}; + +DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2); + +static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { + .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_INPUT_HEADER, + .bNumFormats = 2, + .wTotalLength = 0, /* dynamic */ + .bEndpointAddress = 0, /* dynamic */ + .bmInfo = 0, + .bTerminalLink = 3, + .bStillCaptureMethod = 0, + .bTriggerSupport = 0, + .bTriggerUsage = 0, + .bControlSize = 1, + .bmaControls[0][0] = 0, + .bmaControls[1][0] = 4, +}; + +static const struct uvc_format_uncompressed uvc_format_yuv = { + .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 1, + .bNumFrameDescriptors = 2, + .guidFormat = + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}, + .bBitsPerPixel = 16, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterfaceFlags = 0, + .bCopyProtect = 0, +}; + +DECLARE_UVC_FRAME_UNCOMPRESSED(1); +DECLARE_UVC_FRAME_UNCOMPRESSED(3); + +static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 1, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), + .wHeight = cpu_to_le16(360), + .dwMinBitRate = cpu_to_le32(18432000), + .dwMaxBitRate = cpu_to_le32(55296000), + .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), + .dwDefaultFrameInterval = cpu_to_le32(666666), + .bFrameIntervalType = 3, + .dwFrameInterval[0] = cpu_to_le32(666666), + .dwFrameInterval[1] = cpu_to_le32(1000000), + .dwFrameInterval[2] = cpu_to_le32(5000000), +}; + +static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 2, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), + .wHeight = cpu_to_le16(720), + .dwMinBitRate = cpu_to_le32(29491200), + .dwMaxBitRate = cpu_to_le32(29491200), + .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), + .dwDefaultFrameInterval = cpu_to_le32(5000000), + .bFrameIntervalType = 1, + .dwFrameInterval[0] = cpu_to_le32(5000000), +}; + +static const struct uvc_format_mjpeg uvc_format_mjpg = { + .bLength = UVC_DT_FORMAT_MJPEG_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 2, + .bNumFrameDescriptors = 2, + .bmFlags = 0, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, + .bAspectRatioY = 0, + .bmInterfaceFlags = 0, + .bCopyProtect = 0, +}; + +DECLARE_UVC_FRAME_MJPEG(1); +DECLARE_UVC_FRAME_MJPEG(3); + +static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 1, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), + .wHeight = cpu_to_le16(360), + .dwMinBitRate = cpu_to_le32(18432000), + .dwMaxBitRate = cpu_to_le32(55296000), + .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), + .dwDefaultFrameInterval = cpu_to_le32(666666), + .bFrameIntervalType = 3, + .dwFrameInterval[0] = cpu_to_le32(666666), + .dwFrameInterval[1] = cpu_to_le32(1000000), + .dwFrameInterval[2] = cpu_to_le32(5000000), +}; + +static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 2, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), + .wHeight = cpu_to_le16(720), + .dwMinBitRate = cpu_to_le32(29491200), + .dwMaxBitRate = cpu_to_le32(29491200), + .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), + .dwDefaultFrameInterval = cpu_to_le32(5000000), + .bFrameIntervalType = 1, + .dwFrameInterval[0] = cpu_to_le32(5000000), +}; + +static const struct uvc_color_matching_descriptor uvc_color_matching = { + .bLength = UVC_DT_COLOR_MATCHING_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_COLORFORMAT, + .bColorPrimaries = 1, + .bTransferCharacteristics = 1, + .bMatrixCoefficients = 4, +}; + +static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { + (const struct uvc_descriptor_header *) &uvc_control_header, + (const struct uvc_descriptor_header *) &uvc_camera_terminal, + (const struct uvc_descriptor_header *) &uvc_processing, + (const struct uvc_descriptor_header *) &uvc_output_terminal, + NULL, +}; + +static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { + (const struct uvc_descriptor_header *) &uvc_control_header, + (const struct uvc_descriptor_header *) &uvc_camera_terminal, + (const struct uvc_descriptor_header *) &uvc_processing, + (const struct uvc_descriptor_header *) &uvc_output_terminal, + NULL, +}; + +static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + +static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + +static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, +}; + +/* -------------------------------------------------------------------------- + * USB configuration + */ + +static int __init +webcam_config_bind(struct usb_configuration *c) +{ + return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, + uvc_fs_streaming_cls, uvc_hs_streaming_cls, + uvc_ss_streaming_cls); +} + +static struct usb_configuration webcam_config_driver = { + .label = webcam_config_label, + .bConfigurationValue = 1, + .iConfiguration = 0, /* dynamic */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, +}; + +static int /* __init_or_exit */ +webcam_unbind(struct usb_composite_dev *cdev) +{ + return 0; +} + +static int __init +webcam_bind(struct usb_composite_dev *cdev) +{ + int ret; + + /* Allocate string descriptor numbers ... note that string contents + * can be overridden by the composite_dev glue. + */ + ret = usb_string_ids_tab(cdev, webcam_strings); + if (ret < 0) + goto error; + webcam_device_descriptor.iManufacturer = + webcam_strings[USB_GADGET_MANUFACTURER_IDX].id; + webcam_device_descriptor.iProduct = + webcam_strings[USB_GADGET_PRODUCT_IDX].id; + webcam_config_driver.iConfiguration = + webcam_strings[STRING_DESCRIPTION_IDX].id; + + /* Register our configuration. */ + if ((ret = usb_add_config(cdev, &webcam_config_driver, + webcam_config_bind)) < 0) + goto error; + + usb_composite_overwrite_options(cdev, &coverwrite); + INFO(cdev, "Webcam Video Gadget\n"); + return 0; + +error: + webcam_unbind(cdev); + return ret; +} + +/* -------------------------------------------------------------------------- + * Driver + */ + +static __refdata struct usb_composite_driver webcam_driver = { + .name = "g_webcam", + .dev = &webcam_device_descriptor, + .strings = webcam_device_strings, + .max_speed = USB_SPEED_SUPER, + .bind = webcam_bind, + .unbind = webcam_unbind, +}; + +static int __init +webcam_init(void) +{ + return usb_composite_probe(&webcam_driver); +} + +static void __exit +webcam_cleanup(void) +{ + usb_composite_unregister(&webcam_driver); +} + +module_init(webcam_init); +module_exit(webcam_cleanup); + +MODULE_AUTHOR("Laurent Pinchart"); +MODULE_DESCRIPTION("Webcam Video Gadget"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1.0"); + diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 2d772401b7a..134f354ede6 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -8,18 +8,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - /* * 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. @@ -50,48 +40,36 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.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 "composite.c" -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" - -#include "f_sourcesink.c" -#include "f_loopback.c" - /*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_VERSION "Cinco de Mayo 2008" static const char longname[] = "Gadget Zero"; -unsigned buflen = 4096; -module_param(buflen, uint, 0); - /* * Normally the "loopback" configuration is second (index 1) so * it's not the default. Here's where to change that order, to * work better with hosts where config changes are problematic or * controllers (like original superh) that only support one config. */ -static int loopdefault = 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. @@ -113,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 = { @@ -142,27 +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 */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 -#define STRING_SERIAL_IDX 2 - -static char manufacturer[50]; - /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; +#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[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = longname, - [STRING_SERIAL_IDX].s = serial, + [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 */ }; @@ -178,50 +168,6 @@ static struct usb_gadget_strings *dev_strings[] = { /*-------------------------------------------------------------------------*/ -struct usb_request *alloc_ep_req(struct usb_ep *ep) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = buflen; - req->buf = kmalloc(buflen, 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) -{ - disable_ep(cdev, in); - disable_ep(cdev, out); -} - -/*-------------------------------------------------------------------------*/ - static struct timer_list autoresume_timer; static void zero_autoresume(unsigned long _c) @@ -249,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__); } @@ -263,84 +217,194 @@ 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) { - int gcnum; - struct usb_gadget *gadget = cdev->gadget; - int id; + struct f_ss_opts *ss_opts; + struct f_lb_opts *lb_opts; + int status; /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. */ - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_dev[STRING_MANUFACTURER_IDX].id = id; - device_desc.iManufacturer = id; - - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_dev[STRING_PRODUCT_IDX].id = id; - device_desc.iProduct = id; - - id = usb_string_id(cdev); - if (id < 0) - return id; - strings_dev[STRING_SERIAL_IDX].id = id; - device_desc.iSerialNumber = id; + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + return status; + + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id; 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); - if (!gadget_is_sh(gadget)) - 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); - if (!gadget_is_sh(gadget)) - 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; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - else { - /* gadget zero is so simple (for now, no altsettings) that - * it SHOULD NOT have problems with bulk-capable hardware. - * so just warn about unrcognized controllers -- don't panic. - * - * things like configuration and altsetting numbering - * can need hardware-specific attention though. - */ - pr_warning("%s: controller '%s' not recognized\n", - longname, gadget->name); - device_desc.bcdDevice = cpu_to_le16(0x9999); - } + 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); - snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - 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; } -static struct usb_composite_driver zero_driver = { +static __refdata struct usb_composite_driver zero_driver = { .name = "zero", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, .bind = zero_bind, .unbind = zero_unbind, .suspend = zero_suspend, @@ -352,7 +416,7 @@ MODULE_LICENSE("GPL"); static int __init init(void) { - return usb_composite_register(&zero_driver); + return usb_composite_probe(&zero_driver); } module_init(init); |
