aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig934
-rw-r--r--drivers/usb/gadget/Makefile92
-rw-r--r--drivers/usb/gadget/acm_ms.c284
-rw-r--r--drivers/usb/gadget/amd5536udc.c399
-rw-r--r--drivers/usb/gadget/amd5536udc.h15
-rw-r--r--drivers/usb/gadget/at91_udc.c492
-rw-r--r--drivers/usb/gadget/at91_udc.h18
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c542
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.h12
-rw-r--r--drivers/usb/gadget/audio.c190
-rw-r--r--drivers/usb/gadget/bcm63xx_udc.c2436
-rw-r--r--drivers/usb/gadget/cdc2.c160
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c2832
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.h195
-rw-r--r--drivers/usb/gadget/composite.c1559
-rw-r--r--drivers/usb/gadget/config.c74
-rw-r--r--drivers/usb/gadget/configfs.c1574
-rw-r--r--drivers/usb/gadget/configfs.h19
-rw-r--r--drivers/usb/gadget/dbgp.c434
-rw-r--r--drivers/usb/gadget/dummy_hcd.c2302
-rw-r--r--drivers/usb/gadget/epautoconf.c238
-rw-r--r--drivers/usb/gadget/ether.c300
-rw-r--r--drivers/usb/gadget/f_acm.c359
-rw-r--r--drivers/usb/gadget/f_ecm.c450
-rw-r--r--drivers/usb/gadget/f_eem.c662
-rw-r--r--drivers/usb/gadget/f_fs.c3023
-rw-r--r--drivers/usb/gadget/f_hid.c763
-rw-r--r--drivers/usb/gadget/f_loopback.c337
-rw-r--r--drivers/usb/gadget/f_mass_storage.c3668
-rw-r--r--drivers/usb/gadget/f_mass_storage.h166
-rw-r--r--drivers/usb/gadget/f_midi.c986
-rw-r--r--drivers/usb/gadget/f_ncm.c1438
-rw-r--r--drivers/usb/gadget/f_obex.c268
-rw-r--r--drivers/usb/gadget/f_phonet.c349
-rw-r--r--drivers/usb/gadget/f_rndis.c567
-rw-r--r--drivers/usb/gadget/f_serial.c255
-rw-r--r--drivers/usb/gadget/f_sourcesink.c967
-rw-r--r--drivers/usb/gadget/f_subset.c324
-rw-r--r--drivers/usb/gadget/f_uac1.c768
-rw-r--r--drivers/usb/gadget/f_uac2.c1354
-rw-r--r--drivers/usb/gadget/f_uvc.c836
-rw-r--r--drivers/usb/gadget/f_uvc.h27
-rw-r--r--drivers/usb/gadget/file_storage.c4278
-rw-r--r--drivers/usb/gadget/fotg210-udc.c1216
-rw-r--r--drivers/usb/gadget/fotg210.h253
-rw-r--r--drivers/usb/gadget/fsl_mxc_udc.c123
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c449
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.h28
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_usb2_udc.c)1041
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h57
-rw-r--r--drivers/usb/gadget/functions.c116
-rw-r--r--drivers/usb/gadget/fusb300_udc.c1501
-rw-r--r--drivers/usb/gadget/fusb300_udc.h678
-rw-r--r--drivers/usb/gadget/g_ffs.c582
-rw-r--r--drivers/usb/gadget/g_zero.h62
-rw-r--r--drivers/usb/gadget/gadget_chips.h233
-rw-r--r--drivers/usb/gadget/gmidi.c1302
-rw-r--r--drivers/usb/gadget/goku_udc.c279
-rw-r--r--drivers/usb/gadget/goku_udc.h2
-rw-r--r--drivers/usb/gadget/gr_udc.c2236
-rw-r--r--drivers/usb/gadget/gr_udc.h220
-rw-r--r--drivers/usb/gadget/hid.c266
-rw-r--r--drivers/usb/gadget/imx_udc.c1568
-rw-r--r--drivers/usb/gadget/imx_udc.h359
-rw-r--r--drivers/usb/gadget/inode.c255
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.c2151
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.h259
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c3425
-rw-r--r--drivers/usb/gadget/m66592-udc.c462
-rw-r--r--drivers/usb/gadget/m66592-udc.h134
-rw-r--r--drivers/usb/gadget/mass_storage.c276
-rw-r--r--drivers/usb/gadget/multi.c521
-rw-r--r--drivers/usb/gadget/mv_u3d.h320
-rw-r--r--drivers/usb/gadget/mv_u3d_core.c2070
-rw-r--r--drivers/usb/gadget/mv_udc.h313
-rw-r--r--drivers/usb/gadget/mv_udc_core.c2423
-rw-r--r--drivers/usb/gadget/ncm.c221
-rw-r--r--drivers/usb/gadget/ndis.h170
-rw-r--r--drivers/usb/gadget/net2272.c2710
-rw-r--r--drivers/usb/gadget/net2272.h601
-rw-r--r--drivers/usb/gadget/net2280.c253
-rw-r--r--drivers/usb/gadget/net2280.h9
-rw-r--r--drivers/usb/gadget/nokia.c360
-rw-r--r--drivers/usb/gadget/omap_udc.c589
-rw-r--r--drivers/usb/gadget/omap_udc.h3
-rw-r--r--drivers/usb/gadget/pch_udc.c3248
-rw-r--r--drivers/usb/gadget/printer.c758
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c309
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h22
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c383
-rw-r--r--drivers/usb/gadget/pxa27x_udc.h28
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c2029
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h290
-rw-r--r--drivers/usb/gadget/rndis.c822
-rw-r--r--drivers/usb/gadget/rndis.h55
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c1369
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c387
-rw-r--r--drivers/usb/gadget/s3c2410_udc.h14
-rw-r--r--drivers/usb/gadget/serial.c189
-rw-r--r--drivers/usb/gadget/storage_common.c504
-rw-r--r--drivers/usb/gadget/storage_common.h225
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.c2473
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.h145
-rw-r--r--drivers/usb/gadget/u_ecm.h36
-rw-r--r--drivers/usb/gadget/u_eem.h36
-rw-r--r--drivers/usb/gadget/u_ether.c474
-rw-r--r--drivers/usb/gadget/u_ether.h233
-rw-r--r--drivers/usb/gadget/u_ether_configfs.h164
-rw-r--r--drivers/usb/gadget/u_f.c32
-rw-r--r--drivers/usb/gadget/u_f.h52
-rw-r--r--drivers/usb/gadget/u_fs.h263
-rw-r--r--drivers/usb/gadget/u_gether.h36
-rw-r--r--drivers/usb/gadget/u_ncm.h36
-rw-r--r--drivers/usb/gadget/u_os_desc.h90
-rw-r--r--drivers/usb/gadget/u_phonet.h14
-rw-r--r--drivers/usb/gadget/u_rndis.h46
-rw-r--r--drivers/usb/gadget/u_serial.c463
-rw-r--r--drivers/usb/gadget/u_serial.h16
-rw-r--r--drivers/usb/gadget/u_uac1.c330
-rw-r--r--drivers/usb/gadget/u_uac1.h56
-rw-r--r--drivers/usb/gadget/udc-core.c585
-rw-r--r--drivers/usb/gadget/usbstring.c77
-rw-r--r--drivers/usb/gadget/uvc.h202
-rw-r--r--drivers/usb/gadget/uvc_queue.c407
-rw-r--r--drivers/usb/gadget/uvc_queue.h63
-rw-r--r--drivers/usb/gadget/uvc_v4l2.c365
-rw-r--r--drivers/usb/gadget/uvc_video.c394
-rw-r--r--drivers/usb/gadget/webcam.c412
-rw-r--r--drivers/usb/gadget/zero.c344
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 *)&reg);
-}
-
-/**
- * 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, &eth_config_driver);
+ status = usb_add_config(cdev, &eth_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(&eth_driver);
+ return usb_composite_probe(&eth_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, &current->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 *) &ether_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(&copy_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(&copy_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, &current->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(&regs->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(&regs->int_status), &next, &size);
- dump_intmask("int_enable", readl(&regs->int_enable), &next, &size);
+ dump_intmask(m, "int_status", readl(&regs->int_status));
+ dump_intmask(m, "int_enable", readl(&regs->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(&regs->DataSet),
readl(&regs->EPxSingle), readl(&regs->EPxBCS),
readl(&regs->UsbState),
- readl(&regs->address));
- size -= t;
- next += t;
+ readl(&regs->address)) < 0)
+ goto done;
tmp = readl(&regs->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(&current_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 = &eth_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 = &eth_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);