aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig543
-rw-r--r--drivers/usb/gadget/Makefile48
-rw-r--r--drivers/usb/gadget/acm_ms.c190
-rw-r--r--drivers/usb/gadget/amd5536udc.c272
-rw-r--r--drivers/usb/gadget/amd5536udc.h4
-rw-r--r--drivers/usb/gadget/at91_udc.c230
-rw-r--r--drivers/usb/gadget/at91_udc.h5
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c508
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.h11
-rw-r--r--drivers/usb/gadget/audio.c93
-rw-r--r--drivers/usb/gadget/bcm63xx_udc.c2436
-rw-r--r--drivers/usb/gadget/cdc2.c147
-rw-r--r--drivers/usb/gadget/ci13xxx_msm.c126
-rw-r--r--drivers/usb/gadget/ci13xxx_pci.c176
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c2994
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.h227
-rw-r--r--drivers/usb/gadget/composite.c1025
-rw-r--r--drivers/usb/gadget/config.c43
-rw-r--r--drivers/usb/gadget/configfs.c1574
-rw-r--r--drivers/usb/gadget/configfs.h19
-rw-r--r--drivers/usb/gadget/dbgp.c25
-rw-r--r--drivers/usb/gadget/dummy_hcd.c1000
-rw-r--r--drivers/usb/gadget/epautoconf.c58
-rw-r--r--drivers/usb/gadget/ether.c245
-rw-r--r--drivers/usb/gadget/f_acm.c262
-rw-r--r--drivers/usb/gadget/f_ecm.c292
-rw-r--r--drivers/usb/gadget/f_eem.c244
-rw-r--r--drivers/usb/gadget/f_fs.c1642
-rw-r--r--drivers/usb/gadget/f_hid.c228
-rw-r--r--drivers/usb/gadget/f_loopback.c271
-rw-r--r--drivers/usb/gadget/f_mass_storage.c1534
-rw-r--r--drivers/usb/gadget/f_mass_storage.h166
-rw-r--r--drivers/usb/gadget/f_midi.c46
-rw-r--r--drivers/usb/gadget/f_ncm.c316
-rw-r--r--drivers/usb/gadget/f_obex.c224
-rw-r--r--drivers/usb/gadget/f_phonet.c203
-rw-r--r--drivers/usb/gadget/f_rndis.c378
-rw-r--r--drivers/usb/gadget/f_serial.c222
-rw-r--r--drivers/usb/gadget/f_sourcesink.c905
-rw-r--r--drivers/usb/gadget/f_subset.c271
-rw-r--r--drivers/usb/gadget/f_uac1.c (renamed from drivers/usb/gadget/f_audio.c)53
-rw-r--r--drivers/usb/gadget/f_uac2.c1354
-rw-r--r--drivers/usb/gadget/f_uvc.c326
-rw-r--r--drivers/usb/gadget/f_uvc.h10
-rw-r--r--drivers/usb/gadget/file_storage.c3676
-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.c118
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c394
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.h5
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c463
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h23
-rw-r--r--drivers/usb/gadget/functions.c116
-rw-r--r--drivers/usb/gadget/fusb300_udc.c136
-rw-r--r--drivers/usb/gadget/fusb300_udc.h5
-rw-r--r--drivers/usb/gadget/g_ffs.c431
-rw-r--r--drivers/usb/gadget/g_zero.h62
-rw-r--r--drivers/usb/gadget/gadget_chips.h93
-rw-r--r--drivers/usb/gadget/gmidi.c63
-rw-r--r--drivers/usb/gadget/goku_udc.c255
-rw-r--r--drivers/usb/gadget/goku_udc.h5
-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.c64
-rw-r--r--drivers/usb/gadget/imx_udc.c1595
-rw-r--r--drivers/usb/gadget/imx_udc.h351
-rw-r--r--drivers/usb/gadget/inode.c122
-rw-r--r--drivers/usb/gadget/langwell_udc.c3465
-rw-r--r--drivers/usb/gadget/langwell_udc.h224
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c3425
-rw-r--r--drivers/usb/gadget/m66592-udc.c117
-rw-r--r--drivers/usb/gadget/m66592-udc.h8
-rw-r--r--drivers/usb/gadget/mass_storage.c160
-rw-r--r--drivers/usb/gadget/multi.c349
-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.h6
-rw-r--r--drivers/usb/gadget/mv_udc_core.c405
-rw-r--r--drivers/usb/gadget/ncm.c115
-rw-r--r--drivers/usb/gadget/ndis.h164
-rw-r--r--drivers/usb/gadget/net2272.c94
-rw-r--r--drivers/usb/gadget/net2280.c105
-rw-r--r--drivers/usb/gadget/nokia.c319
-rw-r--r--drivers/usb/gadget/omap_udc.c556
-rw-r--r--drivers/usb/gadget/omap_udc.h3
-rw-r--r--drivers/usb/gadget/pch_udc.c451
-rw-r--r--drivers/usb/gadget/printer.c650
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c172
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h6
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c142
-rw-r--r--drivers/usb/gadget/pxa27x_udc.h5
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c106
-rw-r--r--drivers/usb/gadget/r8a66597-udc.h8
-rw-r--r--drivers/usb/gadget/rndis.c317
-rw-r--r--drivers/usb/gadget/rndis.h52
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c3475
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c144
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c242
-rw-r--r--drivers/usb/gadget/s3c2410_udc.h2
-rw-r--r--drivers/usb/gadget/serial.c187
-rw-r--r--drivers/usb/gadget/storage_common.c762
-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.c306
-rw-r--r--drivers/usb/gadget/u_ether.h200
-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.c389
-rw-r--r--drivers/usb/gadget/u_serial.h14
-rw-r--r--drivers/usb/gadget/u_uac1.c (renamed from drivers/usb/gadget/u_audio.c)15
-rw-r--r--drivers/usb/gadget/u_uac1.h (renamed from drivers/usb/gadget/u_audio.h)2
-rw-r--r--drivers/usb/gadget/udc-core.c326
-rw-r--r--drivers/usb/gadget/usbstring.c4
-rw-r--r--drivers/usb/gadget/uvc.h11
-rw-r--r--drivers/usb/gadget/uvc_queue.c546
-rw-r--r--drivers/usb/gadget/uvc_queue.h32
-rw-r--r--drivers/usb/gadget/uvc_v4l2.c75
-rw-r--r--drivers/usb/gadget/uvc_video.c31
-rw-r--r--drivers/usb/gadget/webcam.c73
-rw-r--r--drivers/usb/gadget/zero.c330
130 files changed, 31800 insertions, 25676 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 7ecb68a6741..ba18e9c110c 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -58,6 +58,20 @@ config USB_GADGET_DEBUG
trying to track down. Never enable these messages for a
production build.
+config USB_GADGET_VERBOSE
+ bool "Verbose debugging Messages (DEVELOPMENT)"
+ depends on USB_GADGET_DEBUG
+ help
+ Many controller and gadget drivers will print verbose debugging
+ messages if you use this option to ask for those messages.
+
+ Avoid enabling these messages, even if you're actively
+ debugging such a driver. Many drivers will emit so many
+ messages that the driver timings are affected, which will
+ either create new failure modes or remove the one you're
+ trying to track down. Never enable these messages for a
+ production build.
+
config USB_GADGET_DEBUG_FILES
boolean "Debugging information files (DEVELOPMENT)"
depends on PROC_FS
@@ -123,13 +137,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
# - discrete ones (including all PCI-only controllers)
# - debug/dummy gadget+hcd is last.
#
-choice
- prompt "USB Peripheral Controller"
- 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
@@ -137,7 +145,7 @@ choice
config USB_AT91
tristate "Atmel AT91 USB Device Port"
- depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
+ 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
@@ -147,21 +155,41 @@ config USB_AT91
dynamically linked module called "at91_udc" and force all
gadget drivers to also be dynamically linked.
+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_ATMEL_USBA
tristate "Atmel USBA"
- select USB_GADGET_DUALSPEED
- depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+ depends on AVR32 || ARCH_AT91
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+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_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
- select USB_GADGET_DUALSPEED
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
@@ -173,16 +201,32 @@ config USB_FSL_USB2
config USB_FUSB300
tristate "Faraday FUSB300 USB Peripheral Controller"
- depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
+ depends on !PHYS_ADDR_T_64BIT && HAS_DMA
help
Faraday usb device controller FUSB300 driver
+config USB_FOTG210_UDC
+ depends on HAS_DMA
+ tristate "Faraday FOTG210 USB Peripheral Controller"
+ help
+ Faraday USB2.0 OTG controller which can be configured as
+ high speed or full speed USB device. This driver supppors
+ Bulk Transfer so far.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fotg210_udc".
+
+config USB_GR_UDC
+ tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
+ depends on HAS_DMA
+ help
+ Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
+ VHDL IP core library.
+
config USB_OMAP
tristate "OMAP USB Device Controller"
- depends on ARCH_OMAP
- select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG
- select USB_OTG_UTILS if ARCH_OMAP
+ 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
@@ -197,7 +241,6 @@ config USB_OMAP
config USB_PXA25X
tristate "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
- select USB_OTG_UTILS
help
Intel's PXA 25x series XScale ARM-5TE processors include
an integrated full speed USB 1.1 device controller. The
@@ -222,7 +265,7 @@ config USB_PXA25X_SMALL
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
+ 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.
@@ -235,7 +278,6 @@ config USB_R8A66597
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS
- select USB_GADGET_DUALSPEED
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -247,8 +289,6 @@ config USB_RENESAS_USBHS_UDC
config USB_PXA27X
tristate "PXA 27x"
- depends on ARCH_PXA && (PXA27x || PXA3xx)
- select USB_OTG_UTILS
help
Intel's PXA 27x series XScale ARM v5TE processors include
an integrated full speed USB 1.1 device controller.
@@ -260,31 +300,9 @@ config USB_PXA27X
dynamically linked module called "pxa27x_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_S3C_HSOTG
- tristate "S3C HS/OtG USB Device controller"
- depends on S3C_DEV_USB_HSOTG
- select USB_GADGET_DUALSPEED
- help
- The Samsung S3C64XX USB2.0 high-speed gadget controller
- integrated into the S3C64XX series SoC.
-
-config USB_IMX
- tristate "Freescale i.MX1 USB Peripheral Controller"
- depends on ARCH_MXC
- help
- Freescale's i.MX1 includes an integrated full speed
- USB 1.1 device controller.
-
- It has Six fixed-function endpoints, as well as endpoint
- zero (for control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "imx_udc" and force all
- gadget drivers to also be dynamically linked.
-
config USB_S3C2410
tristate "S3C2410 USB Device Controller"
- depends on ARCH_S3C2410
+ 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
@@ -299,8 +317,7 @@ config USB_S3C2410_DEBUG
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
- depends on ARCH_S3C2410
- select USB_GADGET_DUALSPEED
+ depends on ARCH_S3C24XX
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
integrated with dual speed USB 2.0 device controller. It has
@@ -310,28 +327,25 @@ config USB_S3C_HSUDC
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
- select USB_GADGET_DUALSPEED
+ depends on HAS_DMA
help
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
full speed USB peripheral.
+config USB_MV_U3D
+ depends on HAS_DMA
+ tristate "MARVELL PXA2128 USB 3.0 controller"
+ help
+ MARVELL PXA2128 Processor series include a super speed USB3.0 device
+ controller, which support super speed USB peripheral.
+
#
# Controllers available in both integrated and discrete versions
#
-# musb builds in ../musb along with host support
-config USB_GADGET_MUSB_HDRC
- tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
- depends on USB_MUSB_HDRC
- select USB_GADGET_DUALSPEED
- help
- This OTG-capable silicon IP is used in dual designs including
- the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
-
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -348,7 +362,6 @@ config USB_M66592
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
@@ -373,21 +386,8 @@ config USB_FSL_QE
Set CONFIG_USB_GADGET to "m" to build this driver as a
dynamically linked module called "fsl_qe_udc".
-config USB_CI13XXX_PCI
- tristate "MIPS USB CI13xxx PCI UDC"
- depends on PCI
- select USB_GADGET_DUALSPEED
- help
- MIPS USB IP core family device controller
- Currently it only supports IP part number CI13412
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "ci13xxx_udc" and force all
- gadget drivers to also be dynamically linked.
-
config USB_NET2272
tristate "PLX NET2272"
- select USB_GADGET_DUALSPEED
help
PLX NET2272 is a USB peripheral controller which supports
both full and high speed USB 2.0 data transfers.
@@ -400,7 +400,7 @@ config USB_NET2272
config USB_NET2272_DMA
boolean "Support external DMA controller"
- depends on USB_NET2272
+ depends on USB_NET2272 && HAS_DMA
help
The NET2272 part can optionally support an external DMA
controller, but your board has to have support in the
@@ -411,7 +411,6 @@ config USB_NET2272_DMA
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.
@@ -438,26 +437,9 @@ config USB_GOKU
dynamically linked module called "goku_udc" and to force all
gadget drivers to also be dynamically linked.
-config USB_LANGWELL
- tristate "Intel Langwell USB Device Controller"
- depends on PCI
- depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
- help
- Intel Langwell USB Device Controller is a High-Speed USB
- On-The-Go device controller.
-
- The number of programmable endpoints is different through
- controller revision.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "langwell_udc" and force all
- gadget drivers to also be dynamically linked.
-
config USB_EG20T
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
@@ -477,23 +459,6 @@ config USB_EG20T
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
-config USB_CI13XXX_MSM
- tristate "MIPS USB CI13xxx for MSM"
- depends on ARCH_MSM
- select USB_GADGET_DUALSPEED
- select USB_MSM_OTG
- help
- MSM SoC has chipidea USB controller. This driver uses
- ci13xxx_udc core.
- This driver depends on OTG driver for PHY initialization,
- clock management, powering up VBUS, and power management.
- This driver is not supported on boards like trout which
- has an external PHY.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "ci13xxx_msm" and force all
- gadget drivers to also be dynamically linked.
-
#
# LAST -- dummy/emulated controller
#
@@ -501,8 +466,6 @@ config USB_CI13XXX_MSM
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
@@ -525,20 +488,60 @@ config USB_DUMMY_HCD
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
# first and will be selected by default.
-endchoice
-
-# Selected by UDC drivers that support high-speed operation.
-config USB_GADGET_DUALSPEED
- bool
-
-# Selected by UDC drivers that support super-speed opperation
-config USB_GADGET_SUPERSPEED
- bool
- depends on USB_GADGET_DUALSPEED
+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"
default USB_ETH
@@ -560,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
@@ -595,23 +751,41 @@ config USB_ZERO_HNPTEST
one serve as the USB host instead (in the "B-Host" role).
config USB_AUDIO
- tristate "Audio Gadget (EXPERIMENTAL)"
+ tristate "Audio Gadget"
depends on SND
+ select USB_LIBCOMPOSITE
select SND_PCM
help
- Gadget Audio is compatible with USB Audio Class specification 1.0.
- It will include at least one AudioControl interface, zero or more
- AudioStream interface and zero or more MIDIStream interface.
-
- Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
- playback or capture audio stream.
+ 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 one of
@@ -647,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,
@@ -665,6 +841,8 @@ config USB_ETH_RNDIS
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
@@ -681,19 +859,21 @@ config USB_ETH_EEM
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 diffferent
+ 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
@@ -701,15 +881,13 @@ 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_FUNCTIONFS
- tristate "Function Filesystem (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ 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
@@ -728,6 +906,9 @@ config USB_FUNCTIONFS
config USB_FUNCTIONFS_ETH
bool "Include configuration with CDC ECM (Ethernet)"
depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
help
Include a configuration with CDC ECM function (Ethernet) and the
Function Filesystem.
@@ -735,6 +916,8 @@ config USB_FUNCTIONFS_ETH
config USB_FUNCTIONFS_RNDIS
bool "Include configuration with RNDIS (Ethernet)"
depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_RNDIS
help
Include a configuration with RNDIS function (Ethernet) and the Filesystem.
@@ -745,48 +928,42 @@ config USB_FUNCTIONFS_GENERIC
Include a configuration with the Function Filesystem alone with
no Ethernet interface.
-config USB_FILE_STORAGE
- tristate "File-backed Storage Gadget (DEPRECATED)"
- depends on BLOCK
- 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.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_file_storage".
-
- NOTE: This driver is deprecated. Its replacement is the
- Mass Storage Gadget.
-
-config USB_FILE_STORAGE_TEST
- bool "File-backed Storage Gadget testing version"
- depends on USB_FILE_STORAGE
- default n
- 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.
-
config USB_MASS_STORAGE
tristate "Mass Storage Gadget"
depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_F_MASS_STORAGE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
- This driver is an updated replacement for the deprecated
- File-backed Storage Gadget (g_file_storage).
+ This driver is a replacement for now removed File-backed
+ Storage Gadget (g_file_storage).
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
+ 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
@@ -805,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
@@ -820,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
@@ -833,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.
@@ -850,6 +1036,13 @@ config USB_CDC_COMPOSITE
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.
@@ -860,6 +1053,10 @@ config USB_G_NOKIA
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.
@@ -868,9 +1065,14 @@ config USB_G_ACM_MS
dynamically linked module called "g_acm_ms".
config USB_G_MULTI
- tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
+ 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
@@ -889,6 +1091,7 @@ config USB_G_MULTI
config USB_G_MULTI_RNDIS
bool "RNDIS + CDC Serial + Storage configuration"
depends on USB_G_MULTI
+ select USB_F_RNDIS
default y
help
This option enables a configuration with RNDIS, CDC Serial and
@@ -902,6 +1105,7 @@ config USB_G_MULTI_CDC
bool "CDC Ethernet + CDC Serial + Storage configuration"
depends on USB_G_MULTI
default n
+ select USB_F_ECM
help
This option enables a configuration with CDC Ethernet (ECM), CDC
Serial and Mass Storage functions available in the Multifunction
@@ -909,8 +1113,11 @@ config USB_G_MULTI_CDC
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).
@@ -921,8 +1128,11 @@ config USB_G_HID
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.
@@ -943,6 +1153,7 @@ config USB_G_DBGP_PRINTK
config USB_G_DBGP_SERIAL
depends on USB_G_DBGP
+ select USB_U_SERIAL
bool "serial"
help
Userland can interact using /dev/ttyGSxxx.
@@ -954,6 +1165,8 @@ endif
config USB_G_WEBCAM
tristate "USB Webcam Gadget"
depends on VIDEO_DEV
+ select USB_LIBCOMPOSITE
+ select VIDEOBUF2_VMALLOC
help
The Webcam Gadget acts as a composite USB Audio and Video Class
device. It provides a userspace API to process UVC control requests
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index b7f6eefc392..49514ea60a9 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,36 +1,68 @@
#
# USB peripheral controller drivers
#
-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
+libcomposite-y := usbstring.o config.o epautoconf.o
+libcomposite-y += composite.o functions.o configfs.o u_f.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
-obj-$(CONFIG_USB_IMX) += imx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
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_PCI) += ci13xxx_pci.o
-obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
-obj-$(CONFIG_USB_LANGWELL) += langwell_udc.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_CI13XXX_MSM) += ci13xxx_msm.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
@@ -41,7 +73,6 @@ g_ether-y := ether.o
g_serial-y := serial.o
g_midi-y := gmidi.o
gadgetfs-y := inode.o
-g_file_storage-y := file_storage.o
g_mass_storage-y := mass_storage.o
g_printer-y := printer.o
g_cdc-y := cdc2.o
@@ -52,13 +83,13 @@ 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_FUNCTIONFS) += g_ffs.o
-obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.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
@@ -71,3 +102,4 @@ 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
index fdb7aec3bd0..a252444cc0a 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/acm_ms.c
@@ -15,7 +15,7 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
+#include <linux/module.h>
#include "u_serial.h"
@@ -31,25 +31,10 @@
#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
-/*-------------------------------------------------------------------------*/
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-#include "u_serial.c"
-#include "f_acm.c"
-#include "f_mass_storage.c"
+#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -89,17 +74,11 @@ 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 = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -116,17 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
-FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#else
-static struct fsg_common fsg_common;
+/*
+ * 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)) {
@@ -134,16 +131,38 @@ static int __init acm_ms_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
+ opts = fsg_opts_from_func_inst(fi_msg);
- status = acm_bind_config(c, 0);
- if (status < 0)
- return status;
+ f_acm = usb_get_function(f_acm_inst);
+ if (IS_ERR(f_acm))
+ return PTR_ERR(f_acm);
+
+ f_msg = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg)) {
+ status = PTR_ERR(f_msg);
+ goto put_acm;
+ }
- status = fsg_bind_config(c->cdev, c, &fsg_common);
+ status = usb_add_function(c, f_acm);
if (status < 0)
- return status;
+ goto put_msg;
+
+ status = fsg_common_run_thread(opts->common);
+ if (status)
+ goto remove_acm;
+
+ status = usb_add_function(c, f_msg);
+ if (status)
+ goto remove_acm;
return 0;
+remove_acm:
+ usb_remove_function(c, f_acm);
+put_msg:
+ usb_put_function(f_msg);
+put_acm:
+ usb_put_function(f_acm);
+ return status;
}
static struct usb_configuration acm_ms_config_driver = {
@@ -157,85 +176,94 @@ static struct usb_configuration acm_ms_config_driver = {
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_opts *opts;
+ struct fsg_config config;
int status;
- void *retp;
- /* set up serial link layer */
- status = gserial_setup(cdev->gadget, 1);
- if (status < 0)
- return status;
+ f_acm_inst = usb_get_function_instance("acm");
+ if (IS_ERR(f_acm_inst))
+ return PTR_ERR(f_acm_inst);
- /* set up mass storage function */
- retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
- if (IS_ERR(retp)) {
- status = PTR_ERR(retp);
- goto fail0;
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg)) {
+ status = PTR_ERR(fi_msg);
+ goto fail_get_msg;
}
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized; trying %s\n",
- gadget->name,
- acm_ms_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
+ /* 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.
*/
-
- /* 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;
+ goto fail_string_ids;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
if (status < 0)
- goto fail1;
+ goto fail_string_ids;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
- fsg_common_put(&fsg_common);
return 0;
/* error recovery */
-fail1:
- fsg_common_put(&fsg_common);
-fail0:
- gserial_cleanup();
+fail_string_ids:
+ fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(opts->common);
+fail:
+ usb_put_function_instance(fi_msg);
+fail_get_msg:
+ usb_put_function_instance(f_acm_inst);
return status;
}
static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
{
- gserial_cleanup();
-
+ usb_put_function(f_msg);
+ usb_put_function_instance(fi_msg);
+ usb_put_function(f_acm);
+ usb_put_function_instance(f_acm_inst);
return 0;
}
-static struct usb_composite_driver acm_ms_driver = {
+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),
};
@@ -245,7 +273,7 @@ MODULE_LICENSE("GPL v2");
static int __init init(void)
{
- return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
+ return usb_composite_probe(&acm_ms_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index c16ff55a74e..41b062eb4de 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -29,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>
@@ -40,7 +40,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -54,7 +53,6 @@
#include <linux/prefetch.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
/* gadget stack */
@@ -140,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",
@@ -204,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");
}
@@ -335,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;
@@ -444,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);
@@ -490,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);
@@ -569,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);
@@ -639,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++) {
@@ -660,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;
}
@@ -675,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) {
@@ -831,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;
@@ -897,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;
@@ -949,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;
}
@@ -1006,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 */
@@ -1024,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,
@@ -1085,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);
@@ -1095,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",
@@ -1151,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 */
@@ -1219,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;
}
@@ -1285,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;
@@ -1345,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;
@@ -1429,15 +1399,16 @@ static int udc_wakeup(struct usb_gadget *gadget)
return 0;
}
-static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int amd5536_stop(struct usb_gadget_driver *driver);
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int amd5536_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
/* gadget operations */
static const struct usb_gadget_ops udc_ops = {
.wakeup = udc_wakeup,
.get_frame = udc_get_frame,
- .start = amd5536_start,
- .stop = amd5536_stop,
+ .udc_start = amd5536_udc_start,
+ .udc_stop = amd5536_udc_stop,
};
/* Setups endpoint parameters, adds endpoints to linked list */
@@ -1479,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;
@@ -1504,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);
@@ -1540,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++) {
@@ -1570,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) {
/*
@@ -1596,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);
}
/*
@@ -1670,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]);
- }
}
@@ -1746,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
@@ -1760,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
@@ -1907,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);
}
@@ -1951,26 +1916,14 @@ static int setup_ep0(struct udc *dev)
}
/* Called by gadget driver to register itself */
-static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int amd5536_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct udc *dev = udc;
- int retval;
+ struct udc *dev = to_amd5536_udc(g);
u32 tmp;
- if (!driver || !bind || !driver->setup
- || driver->max_speed < USB_SPEED_HIGH)
- return -EINVAL;
- if (!dev)
- return -ENODEV;
- if (dev->driver)
- return -EBUSY;
-
driver->driver.bus = NULL;
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
-
- retval = bind(&dev->gadget);
/* Some gadget drivers use both ep0 directions.
* NOTE: to gadget driver, ep0 is just one endpoint...
@@ -1978,14 +1931,6 @@ static int amd5536_start(struct usb_gadget_driver *driver,
dev->ep[UDC_EP0OUT_IX].ep.driver_data =
dev->ep[UDC_EP0IN_IX].ep.driver_data;
- if (retval) {
- DBG(dev, "binding to %s returning %d\n",
- driver->driver.name, retval);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
-
/* get ready for ep0 traffic */
setup_ep0(dev);
@@ -2007,14 +1952,9 @@ __acquires(dev->lock)
{
int tmp;
- if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
/* empty queues and init hardware */
udc_basic_init(dev);
+
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
empty_req_queue(&dev->ep[tmp]);
@@ -2022,24 +1962,18 @@ __acquires(dev->lock)
}
/* Called by gadget driver to unregister itself */
-static int amd5536_stop(struct usb_gadget_driver *driver)
+static int amd5536_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct udc *dev = udc;
- unsigned long flags;
+ struct udc *dev = to_amd5536_udc(g);
+ unsigned long flags;
u32 tmp;
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver || !driver->unbind)
- return -EINVAL;
-
spin_lock_irqsave(&dev->lock, flags);
udc_mask_unused_interrupts(dev);
shutdown(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
- driver->unbind(&dev->gadget);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
/* set SD */
@@ -2047,9 +1981,6 @@ static int amd5536_stop(struct usb_gadget_driver *driver)
tmp |= AMD_BIT(UDC_DEVCTL_SD);
writel(tmp, &dev->regs->ctl);
-
- DBG(dev, "%s: unregistered\n", driver->driver.name);
-
return 0;
}
@@ -2104,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);
- }
}
}
}
@@ -2131,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 occurred - 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);
@@ -2294,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;
@@ -2318,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 */
@@ -2348,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 occurred - DESPTR = %08lx \n",
+ "BNA ep%din occurred - DESPTR = %08lx\n",
ep->num,
(unsigned long) readl(&ep->regs->desptr));
@@ -2361,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 occurred - DESPTR = %08lx \n",
+ "HE ep%dn occurred - DESPTR = %08lx\n",
ep->num, (unsigned long) readl(&ep->regs->desptr));
/* clear HE */
@@ -2427,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 */
@@ -2581,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);
- }
}
}
@@ -2697,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:
@@ -2723,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 */
@@ -3154,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);
}
@@ -3274,7 +3197,7 @@ 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;
@@ -3293,7 +3216,7 @@ static int udc_pci_probe(
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;
@@ -3319,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)
@@ -3347,7 +3268,6 @@ static int udc_probe(struct udc *dev)
dev->gadget.ops = &udc_ops;
dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.dev.release = gadget_release;
dev->gadget.name = name;
dev->gadget.max_speed = USB_SPEED_HIGH;
@@ -3371,17 +3291,11 @@ static int udc_probe(struct udc *dev)
"driver version: %s(for Geode5536 B1)\n", tmp);
udc = dev;
- retval = usb_add_gadget_udc(&udc->pdev->dev, &dev->gadget);
+ retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
+ gadget_release);
if (retval)
goto finished;
- retval = device_register(&dev->gadget.dev);
- if (retval) {
- usb_del_gadget_udc(&dev->gadget);
- put_device(&dev->gadget.dev);
- goto finished;
- }
-
/* timer init */
init_timer(&udc_timer);
udc_timer.function = udc_timer_function;
@@ -3444,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 f87e29c6532..6744d3b8310 100644
--- a/drivers/usb/gadget/amd5536udc.h
+++ b/drivers/usb/gadget/amd5536udc.h
@@ -472,7 +472,6 @@ struct udc_request {
/* flags */
unsigned dma_going : 1,
- dma_mapping : 1,
dma_done : 1;
/* phys. address */
dma_addr_t td_phys;
@@ -512,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,
@@ -564,6 +562,8 @@ struct udc {
u16 cur_alt;
};
+#define to_amd5536_udc(g) (container_of((g), struct udc, gadget))
+
/* setup request data */
union udc_setup_data {
u32 data[2];
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 143a7256b59..cfd18bcca72 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -21,7 +21,6 @@
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
@@ -29,18 +28,19 @@
#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/prefetch.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"
@@ -50,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.)
*
@@ -211,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);
}
}
@@ -220,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 = {
@@ -468,14 +468,13 @@ 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 *udc = 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 = usb_endpoint_maxp(desc)) == 0
|| maxpacket > ep->maxpacket) {
@@ -483,6 +482,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
return -EINVAL;
}
+ udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
DBG("bogus device state\n");
return -ESHUTDOWN;
@@ -529,7 +529,6 @@ ok:
tmp |= AT91_UDP_EPEDS;
__raw_writel(tmp, ep->creg);
- ep->desc = desc;
ep->ep.maxpacket = maxpacket;
/*
@@ -557,7 +556,7 @@ static int at91_ep_disable (struct usb_ep * _ep)
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 */
@@ -616,7 +615,7 @@ 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;
}
@@ -831,10 +830,10 @@ 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);
/* initialize one queue per endpoint */
INIT_LIST_HEAD(&ep->queue);
@@ -870,8 +869,13 @@ static void clk_on(struct at91_udc *udc)
if (udc->clocked)
return;
udc->clocked = 1;
- clk_enable(udc->iclk);
- clk_enable(udc->fclk);
+
+ if (IS_ENABLED(CONFIG_COMMON_CLK)) {
+ clk_set_rate(udc->uclk, 48000000);
+ clk_prepare_enable(udc->uclk);
+ }
+ clk_prepare_enable(udc->iclk);
+ clk_prepare_enable(udc->fclk);
}
static void clk_off(struct at91_udc *udc)
@@ -880,8 +884,10 @@ static void clk_off(struct at91_udc *udc)
return;
udc->clocked = 0;
udc->gadget.speed = USB_SPEED_UNKNOWN;
- clk_disable(udc->fclk);
- clk_disable(udc->iclk);
+ clk_disable_unprepare(udc->fclk);
+ clk_disable_unprepare(udc->iclk);
+ if (IS_ENABLED(CONFIG_COMMON_CLK))
+ clk_disable_unprepare(udc->uclk);
}
/*
@@ -910,9 +916,9 @@ static void pullup(struct at91_udc *udc, int is_on)
} 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);
@@ -928,9 +934,9 @@ static void pullup(struct at91_udc *udc, int is_on)
} 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);
}
@@ -976,18 +982,18 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
return 0;
}
-static int at91_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int at91_stop(struct usb_gadget_driver *driver);
-
+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,
- .start = at91_start,
- .stop = at91_stop,
+ .udc_start = at91_start,
+ .udc_stop = at91_stop,
/*
* VBUS-powered devices may also also want to support bigger
@@ -1170,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) {
@@ -1195,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)
@@ -1216,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)
@@ -1621,72 +1627,36 @@ static void at91_vbus_timer(unsigned long data)
* bus such as i2c or spi which may sleep, so schedule some work
* to read the vbus gpio
*/
- if (!work_pending(&udc->vbus_timer_work))
- schedule_work(&udc->vbus_timer_work);
+ schedule_work(&udc->vbus_timer_work);
}
-static int at91_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int at91_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct at91_udc *udc = &controller;
- int retval;
- unsigned long flags;
-
- if (!driver
- || driver->max_speed < USB_SPEED_FULL
- || !bind
- || !driver->setup) {
- DBG("bad parameter.\n");
- return -EINVAL;
- }
-
- if (udc->driver) {
- DBG("UDC already has a gadget driver\n");
- return -EBUSY;
- }
+ struct at91_udc *udc;
+ udc = container_of(gadget, struct at91_udc, gadget);
udc->driver = driver;
- udc->gadget.dev.driver = &driver->driver;
- dev_set_drvdata(&udc->gadget.dev, &driver->driver);
+ udc->gadget.dev.of_node = udc->pdev->dev.of_node;
udc->enabled = 1;
udc->selfpowered = 1;
- retval = bind(&udc->gadget);
- if (retval) {
- DBG("bind() returned %d\n", retval);
- udc->driver = NULL;
- udc->gadget.dev.driver = NULL;
- dev_set_drvdata(&udc->gadget.dev, NULL);
- udc->enabled = 0;
- udc->selfpowered = 0;
- return retval;
- }
-
- spin_lock_irqsave(&udc->lock, flags);
- pullup(udc, 1);
- spin_unlock_irqrestore(&udc->lock, flags);
-
DBG("bound to %s\n", driver->driver.name);
return 0;
}
-static int at91_stop(struct usb_gadget_driver *driver)
+static int at91_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct at91_udc *udc = &controller;
+ struct at91_udc *udc;
unsigned long flags;
- if (!driver || driver != udc->driver || !driver->unbind)
- return -EINVAL;
-
+ 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);
spin_unlock_irqrestore(&udc->lock, flags);
- driver->unbind(&udc->gadget);
- udc->gadget.dev.driver = NULL;
- dev_set_drvdata(&udc->gadget.dev, NULL);
udc->driver = NULL;
DBG("unbound from %s\n", driver->driver.name);
@@ -1706,29 +1676,39 @@ static void at91udc_shutdown(struct platform_device *dev)
spin_unlock_irqrestore(&udc->lock, flags);
}
-static int __init at91udc_probe(struct platform_device *pdev)
+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 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;
@@ -1741,14 +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 (gpio_is_valid(udc->board.pullup_pin)) {
+ if (!gpio_is_valid(udc->board.pullup_pin)) {
DBG("no D+ pullup?\n");
retval = -ENODEV;
goto fail0;
@@ -1786,26 +1770,24 @@ 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;
- }
-
- retval = device_register(&udc->gadget.dev);
- if (retval < 0) {
- put_device(&udc->gadget.dev);
- goto fail0b;
+ goto fail1;
}
/* don't do anything until we have both gadget driver and VBUS */
- clk_enable(udc->iclk);
+ retval = clk_prepare_enable(udc->iclk);
+ if (retval)
+ goto fail1;
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
/* Clear all pending interrupts - UDP may be used by bootloader. */
at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff);
- clk_disable(udc->iclk);
+ clk_disable_unprepare(udc->iclk);
/* request UDC and maybe VBUS irqs */
udc->udp_irq = platform_get_irq(pdev, 0);
@@ -1837,8 +1819,8 @@ static int __init at91udc_probe(struct platform_device *pdev)
mod_timer(&udc->vbus_timer,
jiffies + VBUS_POLL_TIMEOUT);
} else {
- if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
- 0, driver_name, udc)) {
+ 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;
@@ -1860,15 +1842,19 @@ static int __init at91udc_probe(struct platform_device *pdev)
return 0;
fail4:
if (gpio_is_valid(udc->board.vbus_pin) && !udc->board.vbus_polled)
- free_irq(udc->board.vbus_pin, udc);
+ free_irq(gpio_to_irq(udc->board.vbus_pin), udc);
fail3:
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())
@@ -1898,12 +1884,10 @@ static int __exit at91udc_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
remove_debug_file(udc);
if (gpio_is_valid(udc->board.vbus_pin)) {
- free_irq(udc->board.vbus_pin, udc);
+ 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())
@@ -1914,6 +1898,8 @@ static int __exit at91udc_remove(struct platform_device *pdev)
clk_put(udc->iclk);
clk_put(udc->fclk);
+ if (IS_ENABLED(CONFIG_COMMON_CLK))
+ clk_put(udc->uclk);
return 0;
}
@@ -1970,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,
@@ -1978,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 3c0315b86ac..01752466338 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/at91_udc.h
@@ -105,9 +105,6 @@ struct at91_ep {
unsigned is_in:1;
unsigned is_iso:1;
unsigned fifo_bank:1;
-
- const struct usb_endpoint_descriptor
- *desc;
};
/*
@@ -129,7 +126,7 @@ struct at91_udc {
unsigned active_suspend:1;
u8 addr;
struct at91_udc_data board;
- struct clk *iclk, *fclk;
+ struct clk *iclk, *fclk, *uclk;
struct platform_device *pdev;
struct proc_dir_entry *pde;
void __iomem *udp_baseaddr;
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index e2fb6d583bd..76023ce449a 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -21,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>
@@ -93,7 +91,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
- mutex_lock(&file->f_dentry->d_inode->i_mutex);
+ mutex_lock(&file_inode(file)->i_mutex);
list_for_each_entry_safe(req, tmp_req, queue, queue) {
len = snprintf(tmpbuf, sizeof(tmpbuf),
"%8p %08x %c%c%c %5d %c%c%c\n",
@@ -120,7 +118,7 @@ static ssize_t queue_dbg_read(struct file *file, char __user *buf,
nbytes -= len;
buf += len;
}
- mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+ mutex_unlock(&file_inode(file)->i_mutex);
return actual;
}
@@ -168,13 +166,13 @@ out:
static ssize_t regs_dbg_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = file_inode(file);
int ret;
mutex_lock(&inode->i_mutex);
ret = simple_read_from_buffer(buf, nbytes, ppos,
file->private_data,
- file->f_dentry->d_inode->i_size);
+ file_inode(file)->i_size);
mutex_unlock(&inode->i_mutex);
return ret;
@@ -328,16 +326,16 @@ static int vbus_is_present(struct usba_udc *udc)
#if defined(CONFIG_ARCH_AT91SAM9RL)
-#include <mach/at91_pmc.h>
+#include <linux/clk/at91_pmc.h>
static void toggle_bias(int is_on)
{
- unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR);
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
if (is_on)
- at91_sys_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
else
- at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
}
#else
@@ -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",
@@ -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,16 +985,16 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
return 0;
}
-static int atmel_usba_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int atmel_usba_stop(struct usb_gadget_driver *driver);
-
+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,
- .start = atmel_usba_start,
- .stop = atmel_usba_stop,
+ .udc_start = atmel_usba_start,
+ .udc_stop = atmel_usba_stop,
};
static struct usb_endpoint_descriptor usba_ep0_desc = {
@@ -1034,16 +1012,13 @@ static void nop_release(struct device *dev)
}
-static struct usba_udc the_udc = {
- .gadget = {
- .ops = &usba_udc_ops,
- .ep_list = LIST_HEAD_INIT(the_udc.gadget.ep_list),
- .max_speed = USB_SPEED_HIGH,
- .name = "atmel_usba_udc",
- .dev = {
- .init_name = "gadget",
- .release = nop_release,
- },
+static struct usb_gadget usba_gadget_template = {
+ .ops = &usba_udc_ops,
+ .max_speed = USB_SPEED_HIGH,
+ .name = "atmel_usba_udc",
+ .dev = {
+ .init_name = "gadget",
+ .release = nop_release,
},
};
@@ -1070,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);
@@ -1088,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)
@@ -1167,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,
@@ -1185,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
@@ -1686,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]);
}
}
@@ -1711,7 +1686,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
reset_all_endpoints(udc);
if (udc->gadget.speed != USB_SPEED_UNKNOWN
- && udc->driver->disconnect) {
+ && udc->driver && udc->driver->disconnect) {
udc->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock(&udc->lock);
udc->driver->disconnect(&udc->gadget);
@@ -1725,8 +1700,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
DBG(DBG_BUS, "%s bus reset detected\n",
usb_speed_string(udc->gadget.speed));
- ep0 = &usba_ep[0];
- ep0->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)
@@ -1794,35 +1769,26 @@ out:
return IRQ_HANDLED;
}
-static int atmel_usba_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+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 = 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);
@@ -1841,23 +1807,14 @@ static int atmel_usba_start(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;
}
-static int atmel_usba_stop(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));
@@ -1870,46 +1827,187 @@ static int atmel_usba_stop(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;
}
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;
@@ -1918,99 +2016,57 @@ static int __init usba_udc_probe(struct platform_device *pdev)
udc->vbus_pin = -ENODEV;
ret = -ENOMEM;
- udc->regs = ioremap(regs->start, resource_size(regs));
+ udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
if (!udc->regs) {
dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");
- goto err_map_regs;
+ return ret;
}
dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",
(unsigned long)regs->start, udc->regs);
- udc->fifo = ioremap(fifo->start, resource_size(fifo));
+ udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo));
if (!udc->fifo) {
dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");
- goto err_map_fifo;
+ return ret;
}
dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",
(unsigned long)fifo->start, udc->fifo);
- device_initialize(&udc->gadget.dev);
- udc->gadget.dev.parent = &pdev->dev;
- udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
-
platform_set_drvdata(pdev, udc);
/* Make sure we start from a clean slate */
- clk_enable(pclk);
+ ret = clk_prepare_enable(pclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
+ return ret;
+ }
toggle_bias(0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
- clk_disable(pclk);
-
- usba_ep = kzalloc(sizeof(struct usba_ep) * pdata->num_ep,
- GFP_KERNEL);
- if (!usba_ep)
- goto err_alloc_ep;
-
- the_udc.gadget.ep0 = &usba_ep[0].ep;
-
- INIT_LIST_HEAD(&usba_ep[0].ep.ep_list);
- usba_ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0);
- usba_ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0);
- usba_ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0);
- usba_ep[0].ep.ops = &usba_ep_ops;
- usba_ep[0].ep.name = pdata->ep[0].name;
- usba_ep[0].ep.maxpacket = pdata->ep[0].fifo_size;
- usba_ep[0].udc = &the_udc;
- INIT_LIST_HEAD(&usba_ep[0].queue);
- usba_ep[0].fifo_size = pdata->ep[0].fifo_size;
- usba_ep[0].nr_banks = pdata->ep[0].nr_banks;
- usba_ep[0].index = pdata->ep[0].index;
- usba_ep[0].can_dma = pdata->ep[0].can_dma;
- usba_ep[0].can_isoc = pdata->ep[0].can_isoc;
-
- for (i = 1; i < pdata->num_ep; i++) {
- struct usba_ep *ep = &usba_ep[i];
+ clk_disable_unprepare(pclk);
- ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
- ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
- ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
- ep->ep.ops = &usba_ep_ops;
- ep->ep.name = pdata->ep[i].name;
- ep->ep.maxpacket = pdata->ep[i].fifo_size;
- ep->udc = &the_udc;
- INIT_LIST_HEAD(&ep->queue);
- ep->fifo_size = pdata->ep[i].fifo_size;
- ep->nr_banks = pdata->ep[i].nr_banks;
- ep->index = pdata->ep[i].index;
- ep->can_dma = pdata->ep[i].can_dma;
- ep->can_isoc = pdata->ep[i].can_isoc;
+ if (pdev->dev.of_node)
+ udc->usba_ep = atmel_udc_of_init(pdev, udc);
+ else
+ udc->usba_ep = usba_udc_pdata(pdev, udc);
- list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- }
+ if (IS_ERR(udc->usba_ep))
+ return PTR_ERR(udc->usba_ep);
- ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc);
+ ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0,
+ "atmel_usba_udc", udc);
if (ret) {
dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n",
irq, ret);
- goto err_request_irq;
+ return ret;
}
udc->irq = irq;
- ret = device_add(&udc->gadget.dev);
- if (ret) {
- dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret);
- goto err_device_add;
- }
-
- if (gpio_is_valid(pdata->vbus_pin)) {
- if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) {
- udc->vbus_pin = pdata->vbus_pin;
- udc->vbus_pin_inverted = pdata->vbus_pin_inverted;
-
- ret = request_irq(gpio_to_irq(udc->vbus_pin),
+ if (gpio_is_valid(udc->vbus_pin)) {
+ if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
+ ret = devm_request_irq(&pdev->dev,
+ gpio_to_irq(udc->vbus_pin),
usba_vbus_irq, 0,
"atmel_usba_udc", udc);
if (ret) {
- gpio_free(udc->vbus_pin);
udc->vbus_pin = -ENODEV;
dev_warn(&udc->pdev->dev,
"failed to request vbus irq; "
@@ -2026,90 +2082,50 @@ static int __init usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret)
- goto err_add_udc;
+ return ret;
usba_init_debugfs(udc);
- for (i = 1; i < pdata->num_ep; i++)
- usba_ep_init_debugfs(udc, &usba_ep[i]);
+ for (i = 1; i < udc->num_ep; i++)
+ usba_ep_init_debugfs(udc, &udc->usba_ep[i]);
return 0;
-
-err_add_udc:
- if (gpio_is_valid(pdata->vbus_pin)) {
- free_irq(gpio_to_irq(udc->vbus_pin), udc);
- gpio_free(udc->vbus_pin);
- }
-
- device_unregister(&udc->gadget.dev);
-
-err_device_add:
- free_irq(irq, udc);
-err_request_irq:
- kfree(usba_ep);
-err_alloc_ep:
- iounmap(udc->fifo);
-err_map_fifo:
- iounmap(udc->regs);
-err_map_regs:
- clk_put(hclk);
-err_get_hclk:
- clk_put(pclk);
-
- platform_set_drvdata(pdev, NULL);
-
- return ret;
}
static int __exit usba_udc_remove(struct platform_device *pdev)
{
struct usba_udc *udc;
int i;
- struct usba_platform_data *pdata = pdev->dev.platform_data;
udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
- for (i = 1; i < pdata->num_ep; i++)
- usba_ep_cleanup_debugfs(&usba_ep[i]);
+ for (i = 1; i < udc->num_ep; i++)
+ usba_ep_cleanup_debugfs(&udc->usba_ep[i]);
usba_cleanup_debugfs(udc);
- if (gpio_is_valid(udc->vbus_pin)) {
- free_irq(gpio_to_irq(udc->vbus_pin), udc);
- gpio_free(udc->vbus_pin);
- }
-
- free_irq(udc->irq, udc);
- kfree(usba_ep);
- iounmap(udc->fifo);
- iounmap(udc->regs);
- clk_put(udc->hclk);
- clk_put(udc->pclk);
-
- device_unregister(&udc->gadget.dev);
-
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_udc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9rl-udc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+#endif
+
static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove),
.driver = {
.name = "atmel_usba_udc",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(atmel_udc_dt_ids),
},
};
-static int __init udc_init(void)
-{
- return platform_driver_probe(&udc_driver, usba_udc_probe);
-}
-module_init(udc_init);
-
-static void __exit udc_exit(void)
-{
- platform_driver_unregister(&udc_driver);
-}
-module_exit(udc_exit);
+module_platform_driver_probe(udc_driver, usba_udc_probe);
MODULE_DESCRIPTION("Atmel USBA UDC driver");
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/atmel_usba_udc.h
index 88a2e07a11a..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;
@@ -324,8 +317,10 @@ struct usba_udc {
int irq;
int vbus_pin;
int vbus_pin_inverted;
+ int num_ep;
struct clk *pclk;
struct clk *hclk;
+ struct usba_ep *usba_ep;
u16 devstatus;
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index 9d89ae4765a..231b0efe8fd 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -12,29 +12,41 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
-#include "u_audio.h"
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include "gadget_chips.h"
#define DRIVER_DESC "Linux USB Audio Gadget"
-#define DRIVER_VERSION "Dec 18, 2008"
+#define DRIVER_VERSION "Feb 2, 2012"
-/*-------------------------------------------------------------------------*/
+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"
+/* 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,
+};
-#include "u_audio.c"
-#include "f_audio.c"
+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
/*-------------------------------------------------------------------------*/
@@ -54,9 +66,15 @@ static struct usb_device_descriptor device_desc = {
.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
@@ -108,45 +126,27 @@ static struct usb_configuration audio_config_driver = {
.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 gcnum;
int status;
- gcnum = usb_gadget_controller_number(cdev->gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- ERROR(cdev, "controller '%s' not recognized; trying %s\n",
- cdev->gadget->name,
- audio_config_driver.label);
- device_desc.bcdDevice =
- __constant_cpu_to_le16(0x0300 | 0x0099);
- }
-
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- cdev->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;
+ 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;
@@ -157,21 +157,24 @@ fail:
static int __exit audio_unbind(struct usb_composite_dev *cdev)
{
+#ifdef CONFIG_GADGET_UAC1
gaudio_cleanup();
+#endif
return 0;
}
-static struct usb_composite_driver audio_driver = {
+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, audio_bind);
+ return usb_composite_probe(&audio_driver);
}
module_init(init);
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 725550f06fa..e126b6b248e 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -11,11 +11,11 @@
*/
#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"
@@ -33,24 +33,9 @@
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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.
- */
+USB_GADGET_COMPOSITE_OPTIONS();
-#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();
/*-------------------------------------------------------------------------*/
@@ -92,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 */
};
@@ -114,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.
@@ -130,15 +113,35 @@ static int __init cdc_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- status = ecm_bind_config(c, hostaddr);
- if (status < 0)
- return status;
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm)) {
+ status = PTR_ERR(f_ecm);
+ goto err_get_ecm;
+ }
- status = acm_bind_config(c, 0);
- if (status < 0)
- return status;
+ status = usb_add_function(c, f_ecm);
+ if (status)
+ goto err_add_ecm;
+ f_acm = usb_get_function(fi_serial);
+ if (IS_ERR(f_acm)) {
+ status = PTR_ERR(f_acm);
+ goto err_get_acm;
+ }
+
+ status = usb_add_function(c, f_acm);
+ if (status)
+ goto err_add_acm;
return 0;
+
+err_add_acm:
+ usb_put_function(f_acm);
+err_get_acm:
+ usb_remove_function(c, f_ecm);
+err_add_ecm:
+ usb_put_function(f_ecm);
+err_get_ecm:
+ return status;
}
static struct usb_configuration cdc_config_driver = {
@@ -152,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)) {
@@ -162,81 +165,69 @@ 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, 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),
};
@@ -246,7 +237,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&cdc_driver, cdc_bind);
+ return usb_composite_probe(&cdc_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
deleted file mode 100644
index 1fc612914c5..00000000000
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
- * only version 2 as published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/usb/msm_hsusb_hw.h>
-#include <linux/usb/ulpi.h>
-
-#include "ci13xxx_udc.c"
-
-#define MSM_USB_BASE (udc->regs)
-
-static irqreturn_t msm_udc_irq(int irq, void *data)
-{
- return udc_irq();
-}
-
-static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
-{
- struct device *dev = udc->gadget.dev.parent;
- int val;
-
- switch (event) {
- case CI13XXX_CONTROLLER_RESET_EVENT:
- dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
- writel(0, USB_AHBBURST);
- writel(0, USB_AHBMODE);
- break;
- case CI13XXX_CONTROLLER_STOPPED_EVENT:
- dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
- /*
- * Put the transceiver in non-driving mode. Otherwise host
- * may not detect soft-disconnection.
- */
- val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL);
- val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
- val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
- otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
- break;
- default:
- dev_dbg(dev, "unknown ci13xxx_udc event\n");
- break;
- }
-}
-
-static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
- .name = "ci13xxx_msm",
- .flags = CI13XXX_REGS_SHARED |
- CI13XXX_REQUIRE_TRANSCEIVER |
- CI13XXX_PULLUP_ON_VBUS |
- CI13XXX_DISABLE_STREAMING,
-
- .notify_event = ci13xxx_msm_notify_event,
-};
-
-static int ci13xxx_msm_probe(struct platform_device *pdev)
-{
- struct resource *res;
- void __iomem *regs;
- int irq;
- int ret;
-
- dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get platform resource mem\n");
- return -ENXIO;
- }
-
- regs = ioremap(res->start, resource_size(res));
- if (!regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -ENOMEM;
- }
-
- ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
- if (ret < 0) {
- dev_err(&pdev->dev, "udc_probe failed\n");
- goto iounmap;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "IRQ not found\n");
- ret = -ENXIO;
- goto udc_remove;
- }
-
- ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "request_irq failed\n");
- goto udc_remove;
- }
-
- pm_runtime_no_callbacks(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
-
- return 0;
-
-udc_remove:
- udc_remove();
-iounmap:
- iounmap(regs);
-
- return ret;
-}
-
-static struct platform_driver ci13xxx_msm_driver = {
- .probe = ci13xxx_msm_probe,
- .driver = { .name = "msm_hsusb", },
-};
-MODULE_ALIAS("platform:msm_hsusb");
-
-static int __init ci13xxx_msm_init(void)
-{
- return platform_driver_register(&ci13xxx_msm_driver);
-}
-module_init(ci13xxx_msm_init);
-
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_pci.c b/drivers/usb/gadget/ci13xxx_pci.c
deleted file mode 100644
index 883ab5e832d..00000000000
--- a/drivers/usb/gadget/ci13xxx_pci.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * ci13xxx_pci.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.
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include "ci13xxx_udc.c"
-
-/* driver name */
-#define UDC_DRIVER_NAME "ci13xxx_pci"
-
-/******************************************************************************
- * 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();
-}
-
-static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
- .name = UDC_DRIVER_NAME,
-};
-
-/**
- * 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(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
- 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.c b/drivers/usb/gadget/ci13xxx_udc.c
deleted file mode 100644
index 27e31371842..00000000000
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ /dev/null
@@ -1,2994 +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
- *
- * 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/slab.h>
-#include <linux/pm_runtime.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/otg.h>
-
-#include "ci13xxx_udc.h"
-
-
-/******************************************************************************
- * DEFINE
- *****************************************************************************/
-
-#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-
-/* ctrl register bank access */
-static DEFINE_SPINLOCK(udc_lock);
-
-/* control endpoint description */
-static const struct usb_endpoint_descriptor
-ctrl_endpt_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
- .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
-};
-
-static const struct usb_endpoint_descriptor
-ctrl_endpt_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .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;
-
-/* MSM specific */
-#define ABS_AHBBURST (0x0090UL)
-#define ABS_AHBMODE (0x0098UL)
-/* 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);
-}
-
-static int ep_to_bit(int n)
-{
- int fill = 16 - hw_ep_max / 2;
-
- if (n >= hw_ep_max / 2)
- n += fill;
-
- return n;
-}
-
-/**
- * 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);
-}
-
-static int hw_device_init(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);
-
- reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
- hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */
-
- if (hw_ep_max == 0 || hw_ep_max > ENDPT_MAX)
- return -ENODEV;
-
- /* setup lock mode ? */
-
- /* ENDPTSETUPSTAT is '0' by default */
-
- /* HCSPARAMS.bf.ppc SHOULD BE zero for device */
-
- return 0;
-}
-/**
- * hw_device_reset: resets chip (execute without interruption)
- * @base: register base address
- *
- * This function returns an error code
- */
-static int hw_device_reset(struct ci13xxx *udc)
-{
- /* 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 */
-
-
- if (udc->udc_driver->notify_event)
- udc->udc_driver->notify_event(udc,
- CI13XXX_CONTROLLER_RESET_EVENT);
-
- if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING)
- hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
-
- /* 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;
- }
-
- 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_test_and_clear_setup_status: test & clear setup status (execute without
- * interruption)
- * @n: endpoint number
- *
- * This function returns setup status
- */
-static int hw_test_and_clear_setup_status(int n)
-{
- n = ep_to_bit(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);
-
- 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: endpoint number
- *
- * This function returns complete status
- */
-static int hw_test_and_clear_complete(int n)
-{
- n = ep_to_bit(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, "max_speed = %d\n",
- gadget->max_speed);
- /* TODO: Scheduled for removal in 3.8. */
- n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n",
- gadget_is_dualspeed(gadget));
- 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->max_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/2; i++) {
- struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
- struct ci13xxx_ep *mEpTx = &udc->ci13xxx_ep[i + hw_ep_max/2];
- n += scnprintf(buf + n, PAGE_SIZE - n,
- "EP=%02i: RX=%08X TX=%08X\n",
- i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.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 *)mEpRx->qh.ptr + j),
- *((u32 *)mEpTx->qh.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
- */
-#define DUMP_ENTRIES 512
-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;
- 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;
- }
-
- dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
- if (!dump) {
- dev_err(dev, "%s: out of memory\n", __func__);
- return 0;
- }
-
- spin_lock_irqsave(udc->lock, flags);
- k = hw_register_read(dump, DUMP_ENTRIES);
- 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]);
- }
- kfree(dump);
-
- 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, 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++)
- list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
- {
- req = list_entry(ptr, struct ci13xxx_req, queue);
-
- n += scnprintf(buf + n, PAGE_SIZE - n,
- "EP=%02i: TD=%08X %s\n",
- i % hw_ep_max/2, (u32)req->dma,
- ((i < hw_ep_max/2) ? "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;
- int ret = 0;
- unsigned length = mReq->req.length;
-
- trace("%p, %p", mEp, mReq);
-
- /* don't queue twice */
- if (mReq->req.status == -EALREADY)
- return -EALREADY;
-
- mReq->req.status = -EALREADY;
- if (length && mReq->req.dma == DMA_ADDR_INVALID) {
- mReq->req.dma = \
- dma_map_single(mEp->device, mReq->req.buf,
- length, mEp->dir ? DMA_TO_DEVICE :
- DMA_FROM_DEVICE);
- if (mReq->req.dma == 0)
- return -ENOMEM;
-
- mReq->map = 1;
- }
-
- if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) {
- mReq->zptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC,
- &mReq->zdma);
- if (mReq->zptr == NULL) {
- if (mReq->map) {
- dma_unmap_single(mEp->device, mReq->req.dma,
- length, mEp->dir ? DMA_TO_DEVICE :
- DMA_FROM_DEVICE);
- mReq->req.dma = DMA_ADDR_INVALID;
- mReq->map = 0;
- }
- return -ENOMEM;
- }
- memset(mReq->zptr, 0, sizeof(*mReq->zptr));
- mReq->zptr->next = TD_TERMINATE;
- mReq->zptr->token = TD_STATUS_ACTIVE;
- if (!mReq->req.no_interrupt)
- mReq->zptr->token |= TD_IOC;
- }
- /*
- * TD configuration
- * TODO - handle requests which spawns into several TDs
- */
- memset(mReq->ptr, 0, sizeof(*mReq->ptr));
- mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES);
- mReq->ptr->token &= TD_TOTAL_BYTES;
- mReq->ptr->token |= TD_STATUS_ACTIVE;
- if (mReq->zptr) {
- mReq->ptr->next = mReq->zdma;
- } else {
- mReq->ptr->next = TD_TERMINATE;
- if (!mReq->req.no_interrupt)
- mReq->ptr->token |= TD_IOC;
- }
- mReq->ptr->page[0] = mReq->req.dma;
- for (i = 1; i < 5; i++)
- mReq->ptr->page[i] =
- (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
-
- if (!list_empty(&mEp->qh.queue)) {
- struct ci13xxx_req *mReqPrev;
- int n = hw_ep_bit(mEp->num, mEp->dir);
- int tmp_stat;
-
- mReqPrev = list_entry(mEp->qh.queue.prev,
- struct ci13xxx_req, queue);
- if (mReqPrev->zptr)
- mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
- else
- mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
- wmb();
- if (hw_cread(CAP_ENDPTPRIME, BIT(n)))
- goto done;
- do {
- hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
- tmp_stat = hw_cread(CAP_ENDPTSTAT, BIT(n));
- } while (!hw_cread(CAP_USBCMD, USBCMD_ATDTW));
- hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, 0);
- if (tmp_stat)
- goto done;
- }
-
- /* QH configuration */
- mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
- mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */
- mEp->qh.ptr->cap |= QH_ZLT;
-
- wmb(); /* synchronize before ep prime */
-
- ret = hw_ep_prime(mEp->num, mEp->dir,
- mEp->type == USB_ENDPOINT_XFER_CONTROL);
-done:
- return ret;
-}
-
-/**
- * _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 ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
- return -EBUSY;
-
- if (mReq->zptr) {
- if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
- return -EBUSY;
- dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
- mReq->zptr = NULL;
- }
-
- 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 = DMA_ADDR_INVALID;
- mReq->map = 0;
- }
-
- mReq->req.status = mReq->ptr->token & TD_STATUS;
- 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.queue)) {
-
- /* pop oldest request */
- struct ci13xxx_req *mReq = \
- list_entry(mEp->qh.queue.next,
- struct ci13xxx_req, queue);
- list_del_init(&mReq->queue);
- mReq->req.status = -ESHUTDOWN;
-
- if (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
- */
-static int _gadget_stop_activity(struct usb_gadget *gadget)
-{
- struct usb_ep *ep;
- struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
- unsigned long flags;
-
- trace("%p", gadget);
-
- if (gadget == NULL)
- return -EINVAL;
-
- spin_lock_irqsave(udc->lock, flags);
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- udc->remote_wakeup = 0;
- udc->suspended = 0;
- spin_unlock_irqrestore(udc->lock, flags);
-
- /* flush all endpoints */
- gadget_for_each_ep(ep, gadget) {
- usb_ep_fifo_flush(ep);
- }
- usb_ep_fifo_flush(&udc->ep0out.ep);
- usb_ep_fifo_flush(&udc->ep0in.ep);
-
- udc->driver->disconnect(gadget);
-
- /* make sure to disable all endpoints */
- gadget_for_each_ep(ep, gadget) {
- usb_ep_disable(ep);
- }
-
- if (udc->status != NULL) {
- usb_ep_free_request(&udc->ep0in.ep, udc->status);
- udc->status = NULL;
- }
-
- 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)
-{
- int retval;
-
- trace("%p", udc);
-
- if (udc == NULL) {
- err("EINVAL");
- return;
- }
-
- dbg_event(0xFF, "BUS RST", 0);
-
- spin_unlock(udc->lock);
- retval = _gadget_stop_activity(&udc->gadget);
- if (retval)
- goto done;
-
- retval = hw_usb_reset();
- if (retval)
- goto done;
-
- udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC);
- if (udc->status == NULL)
- 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
- * @udc: udc struct
- * @setup: setup request packet
- *
- * This function returns an error code
- */
-static int isr_get_status_response(struct ci13xxx *udc,
- struct usb_ctrlrequest *setup)
-__releases(mEp->lock)
-__acquires(mEp->lock)
-{
- struct ci13xxx_ep *mEp = &udc->ep0in;
- 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) {
- /* Assume that device is bus powered for now. */
- *((u16 *)req->buf) = _udc->remote_wakeup << 1;
- 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_complete: setup_status request complete function
- * @ep: endpoint
- * @req: request handled
- *
- * Caller must release lock. Put the port in test mode if test mode
- * feature is selected.
- */
-static void
-isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct ci13xxx *udc = req->context;
- unsigned long flags;
-
- trace("%p, %p", ep, req);
-
- spin_lock_irqsave(udc->lock, flags);
- if (udc->test_mode)
- hw_port_test_set(udc->test_mode);
- spin_unlock_irqrestore(udc->lock, flags);
-}
-
-/**
- * isr_setup_status_phase: queues the status phase of a setup transation
- * @udc: udc struct
- *
- * This function returns an error code
- */
-static int isr_setup_status_phase(struct ci13xxx *udc)
-__releases(mEp->lock)
-__acquires(mEp->lock)
-{
- int retval;
- struct ci13xxx_ep *mEp;
-
- trace("%p", udc);
-
- mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in;
- udc->status->context = udc;
- udc->status->complete = isr_setup_status_complete;
-
- spin_unlock(mEp->lock);
- retval = usb_ep_queue(&mEp->ep, udc->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, *mReqTemp;
- struct ci13xxx_ep *mEpTemp = mEp;
- int uninitialized_var(retval);
-
- trace("%p", mEp);
-
- if (list_empty(&mEp->qh.queue))
- return -EINVAL;
-
- list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
- queue) {
- retval = _hardware_dequeue(mEp, mReq);
- if (retval < 0)
- break;
- list_del_init(&mReq->queue);
- dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
- if (mReq->req.complete != NULL) {
- spin_unlock(mEp->lock);
- if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
- mReq->req.length)
- mEpTemp = &_udc->ep0in;
- mReq->req.complete(&mEpTemp->ep, &mReq->req);
- spin_lock(mEp->lock);
- }
- }
-
- if (retval == -EBUSY)
- retval = 0;
- if (retval < 0)
- dbg_event(_usb_addr(mEp), "DONE", retval);
-
- 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;
- u8 tmode = 0;
-
- 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, dir, err = -EINVAL;
- struct usb_ctrlrequest req;
-
- if (mEp->desc == NULL)
- continue; /* not configured */
-
- if (hw_test_and_clear_complete(i)) {
- err = isr_tr_complete_low(mEp);
- if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
- if (err > 0) /* needs status phase */
- err = isr_setup_status_phase(udc);
- 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;
- }
-
- /*
- * Flush data and handshake transactions of previous
- * setup packet.
- */
- _ep_nuke(&udc->ep0out);
- _ep_nuke(&udc->ep0in);
-
- /* read_setup_packet */
- do {
- hw_test_and_set_setup_guard();
- memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
- } while (!hw_test_and_clear_setup_guard());
-
- type = req.bRequestType;
-
- udc->ep0_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) {
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- dir = num & USB_ENDPOINT_DIR_MASK;
- num &= USB_ENDPOINT_NUMBER_MASK;
- if (dir) /* TX */
- num += hw_ep_max/2;
- 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(udc);
- } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
- le16_to_cpu(req.wValue) ==
- USB_DEVICE_REMOTE_WAKEUP) {
- if (req.wLength != 0)
- break;
- udc->remote_wakeup = 0;
- err = isr_setup_status_phase(udc);
- } else {
- goto delegate;
- }
- 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(udc, &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(udc);
- break;
- case USB_REQ_SET_FEATURE:
- if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
- le16_to_cpu(req.wValue) ==
- USB_ENDPOINT_HALT) {
- if (req.wLength != 0)
- break;
- num = le16_to_cpu(req.wIndex);
- dir = num & USB_ENDPOINT_DIR_MASK;
- num &= USB_ENDPOINT_NUMBER_MASK;
- if (dir) /* TX */
- num += hw_ep_max/2;
-
- spin_unlock(udc->lock);
- err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
- spin_lock(udc->lock);
- if (!err)
- isr_setup_status_phase(udc);
- } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
- if (req.wLength != 0)
- break;
- switch (le16_to_cpu(req.wValue)) {
- case USB_DEVICE_REMOTE_WAKEUP:
- udc->remote_wakeup = 1;
- err = isr_setup_status_phase(udc);
- break;
- case USB_DEVICE_TEST_MODE:
- tmode = le16_to_cpu(req.wIndex) >> 8;
- switch (tmode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
- udc->test_mode = tmode;
- err = isr_setup_status_phase(
- udc);
- break;
- default:
- break;
- }
- default:
- goto delegate;
- }
- } else {
- goto delegate;
- }
- break;
- default:
-delegate:
- if (req.wLength == 0) /* no data phase */
- udc->ep0_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 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.queue))
- warn("enabling a non-empty endpoint!");
-
- mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX;
- mEp->num = usb_endpoint_num(desc);
- mEp->type = usb_endpoint_type(desc);
-
- mEp->ep.maxpacket = usb_endpoint_maxp(desc);
-
- dbg_event(_usb_addr(mEp), "ENABLE", 0);
-
- mEp->qh.ptr->cap = 0;
-
- if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
- mEp->qh.ptr->cap |= QH_IOS;
- else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
- mEp->qh.ptr->cap &= ~QH_MULT;
- else
- mEp->qh.ptr->cap &= ~QH_ZLT;
-
- mEp->qh.ptr->cap |=
- (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
- mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */
-
- /*
- * Enable endpoints in the HW other than ep0 as ep0
- * is always enabled
- */
- if (mEp->num)
- retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type);
-
- 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;
-
- trace("%p, %i", ep, gfp_flags);
-
- if (ep == NULL) {
- err("EINVAL");
- return NULL;
- }
-
- mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
- if (mReq != NULL) {
- INIT_LIST_HEAD(&mReq->queue);
- mReq->req.dma = DMA_ADDR_INVALID;
-
- 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);
-
- 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) {
- if (req->length)
- mEp = (_udc->ep0_dir == RX) ?
- &_udc->ep0out : &_udc->ep0in;
- if (!list_empty(&mEp->qh.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 * CI13XXX_PAGE_SIZE)) {
- req->length = (4 * CI13XXX_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;
-
- retval = _hardware_enqueue(mEp, mReq);
-
- if (retval == -EALREADY) {
- dbg_event(_usb_addr(mEp), "QUEUE", retval);
- retval = 0;
- }
- if (!retval)
- list_add_tail(&mReq->queue, &mEp->qh.queue);
-
- 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 || mReq->req.status != -EALREADY ||
- mEp->desc == NULL || list_empty(&mReq->queue) ||
- list_empty(&mEp->qh.queue))
- return -EINVAL;
-
- spin_lock_irqsave(mEp->lock, flags);
-
- dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
-
- hw_ep_flush(mEp->num, mEp->dir);
-
- /* pop request */
- list_del_init(&mReq->queue);
- 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 = DMA_ADDR_INVALID;
- mReq->map = 0;
- }
- req->status = -ECONNRESET;
-
- if (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.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
- *****************************************************************************/
-static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
-{
- struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
- unsigned long flags;
- int gadget_ready = 0;
-
- if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
- return -EOPNOTSUPP;
-
- spin_lock_irqsave(udc->lock, flags);
- udc->vbus_active = is_active;
- if (udc->driver)
- gadget_ready = 1;
- spin_unlock_irqrestore(udc->lock, flags);
-
- if (gadget_ready) {
- if (is_active) {
- pm_runtime_get_sync(&_gadget->dev);
- hw_device_reset(udc);
- hw_device_state(udc->ep0out.qh.dma);
- } else {
- hw_device_state(0);
- if (udc->udc_driver->notify_event)
- udc->udc_driver->notify_event(udc,
- CI13XXX_CONTROLLER_STOPPED_EVENT);
- _gadget_stop_activity(&udc->gadget);
- pm_runtime_put_sync(&_gadget->dev);
- }
- }
-
- return 0;
-}
-
-static int ci13xxx_wakeup(struct usb_gadget *_gadget)
-{
- struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
- unsigned long flags;
- int ret = 0;
-
- trace();
-
- spin_lock_irqsave(udc->lock, flags);
- if (!udc->remote_wakeup) {
- ret = -EOPNOTSUPP;
- trace("remote wakeup feature is not enabled\n");
- goto out;
- }
- if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
- ret = -EINVAL;
- trace("port is not suspended\n");
- goto out;
- }
- hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
-out:
- spin_unlock_irqrestore(udc->lock, flags);
- return ret;
-}
-
-static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
-{
- struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
-
- if (udc->transceiver)
- return otg_set_power(udc->transceiver, mA);
- return -ENOTSUPP;
-}
-
-static int ci13xxx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int ci13xxx_stop(struct usb_gadget_driver *driver);
-/**
- * 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 = {
- .vbus_session = ci13xxx_vbus_session,
- .wakeup = ci13xxx_wakeup,
- .vbus_draw = ci13xxx_vbus_draw,
- .start = ci13xxx_start,
- .stop = ci13xxx_stop,
-};
-
-/**
- * ci13xxx_start: register a gadget driver
- * @driver: the driver being registered
- * @bind: the driver's bind callback
- *
- * Check ci13xxx_start() at <linux/usb/gadget.h> for details.
- * Interrupts are enabled here.
- */
-static int ci13xxx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
-{
- struct ci13xxx *udc = _udc;
- unsigned long flags;
- int i, j;
- int retval = -ENOMEM;
-
- trace("%p", driver);
-
- if (driver == NULL ||
- bind == NULL ||
- driver->setup == NULL ||
- driver->disconnect == 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, CI13XXX_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, CI13XXX_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->gadget.dev.driver = NULL;
-
- retval = 0;
- for (i = 0; i < hw_ep_max/2; i++) {
- for (j = RX; j <= TX; j++) {
- int k = i + j * hw_ep_max/2;
- struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k];
-
- scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i,
- (j == TX) ? "in" : "out");
-
- 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;
-
- INIT_LIST_HEAD(&mEp->qh.queue);
- spin_unlock_irqrestore(udc->lock, flags);
- mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL,
- &mEp->qh.dma);
- spin_lock_irqsave(udc->lock, flags);
- if (mEp->qh.ptr == NULL)
- retval = -ENOMEM;
- else
- memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr));
-
- /* skip ep0 out and in endpoints */
- if (i == 0)
- continue;
-
- list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list);
- }
- }
- if (retval)
- goto done;
- spin_unlock_irqrestore(udc->lock, flags);
- udc->ep0out.ep.desc = &ctrl_endpt_out_desc;
- retval = usb_ep_enable(&udc->ep0out.ep);
- if (retval)
- return retval;
-
- udc->ep0in.ep.desc = &ctrl_endpt_in_desc;
- retval = usb_ep_enable(&udc->ep0in.ep);
- if (retval)
- return retval;
- spin_lock_irqsave(udc->lock, flags);
-
- udc->gadget.ep0 = &udc->ep0in.ep;
- /* bind gadget */
- driver->driver.bus = NULL;
- udc->gadget.dev.driver = &driver->driver;
-
- spin_unlock_irqrestore(udc->lock, flags);
- retval = bind(&udc->gadget); /* MAY SLEEP */
- spin_lock_irqsave(udc->lock, flags);
-
- if (retval) {
- udc->gadget.dev.driver = NULL;
- goto done;
- }
-
- udc->driver = driver;
- pm_runtime_get_sync(&udc->gadget.dev);
- if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
- if (udc->vbus_active) {
- if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
- hw_device_reset(udc);
- } else {
- pm_runtime_put_sync(&udc->gadget.dev);
- goto done;
- }
- }
-
- retval = hw_device_state(udc->ep0out.qh.dma);
- if (retval)
- pm_runtime_put_sync(&udc->gadget.dev);
-
- done:
- spin_unlock_irqrestore(udc->lock, flags);
- return retval;
-}
-
-/**
- * ci13xxx_stop: unregister a gadget driver
- *
- * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details
- */
-static int ci13xxx_stop(struct usb_gadget_driver *driver)
-{
- struct ci13xxx *udc = _udc;
- unsigned long i, flags;
-
- trace("%p", driver);
-
- if (driver == NULL ||
- driver->unbind == NULL ||
- driver->setup == NULL ||
- driver->disconnect == NULL ||
- driver != udc->driver)
- return -EINVAL;
-
- spin_lock_irqsave(udc->lock, flags);
-
- if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
- udc->vbus_active) {
- hw_device_state(0);
- if (udc->udc_driver->notify_event)
- udc->udc_driver->notify_event(udc,
- CI13XXX_CONTROLLER_STOPPED_EVENT);
- spin_unlock_irqrestore(udc->lock, flags);
- _gadget_stop_activity(&udc->gadget);
- spin_lock_irqsave(udc->lock, flags);
- pm_runtime_put(&udc->gadget.dev);
- }
-
- /* unbind gadget */
- spin_unlock_irqrestore(udc->lock, flags);
- driver->unbind(&udc->gadget); /* MAY SLEEP */
- spin_lock_irqsave(udc->lock, flags);
-
- udc->gadget.dev.driver = NULL;
-
- /* free resources */
- for (i = 0; i < hw_ep_max; i++) {
- struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
-
- if (!list_empty(&mEp->ep.ep_list))
- list_del_init(&mEp->ep.ep_list);
-
- if (mEp->qh.ptr != NULL)
- dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma);
- }
-
- udc->gadget.ep0 = NULL;
- 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;
-}
-
-/******************************************************************************
- * 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);
-
- if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
- if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
- USBMODE_CM_DEVICE) {
- spin_unlock(udc->lock);
- return IRQ_NONE;
- }
- }
- 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 (udc->suspended && udc->driver->resume) {
- spin_unlock(udc->lock);
- udc->driver->resume(&udc->gadget);
- spin_lock(udc->lock);
- udc->suspended = 0;
- }
- }
- if (USBi_UEI & intr)
- isr_statistics.uei++;
- if (USBi_UI & intr) {
- isr_statistics.ui++;
- isr_tr_complete_handler(udc);
- }
- if (USBi_SLI & intr) {
- if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
- udc->driver->suspend) {
- udc->suspended = 1;
- spin_unlock(udc->lock);
- udc->driver->suspend(&udc->gadget);
- spin_lock(udc->lock);
- }
- 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 ci13xxx_udc_driver *driver, struct device *dev,
- void __iomem *regs)
-{
- struct ci13xxx *udc;
- int retval = 0;
-
- trace("%p, %p, %p", dev, regs, driver->name);
-
- if (dev == NULL || regs == NULL || driver == NULL ||
- driver->name == NULL)
- return -EINVAL;
-
- udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
- if (udc == NULL)
- return -ENOMEM;
-
- udc->lock = &udc_lock;
- udc->regs = regs;
- udc->udc_driver = driver;
-
- udc->gadget.ops = &usb_gadget_ops;
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- udc->gadget.max_speed = USB_SPEED_HIGH;
- udc->gadget.is_otg = 0;
- udc->gadget.name = driver->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.coherent_dma_mask = dev->coherent_dma_mask;
- udc->gadget.dev.parent = dev;
- udc->gadget.dev.release = udc_release;
-
- retval = hw_device_init(regs);
- if (retval < 0)
- goto free_udc;
-
- udc->transceiver = otg_get_transceiver();
-
- if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
- if (udc->transceiver == NULL) {
- retval = -ENODEV;
- goto free_udc;
- }
- }
-
- if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
- retval = hw_device_reset(udc);
- if (retval)
- goto put_transceiver;
- }
-
- retval = device_register(&udc->gadget.dev);
- if (retval) {
- put_device(&udc->gadget.dev);
- goto put_transceiver;
- }
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- retval = dbg_create_files(&udc->gadget.dev);
-#endif
- if (retval)
- goto unreg_device;
-
- if (udc->transceiver) {
- retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
- if (retval)
- goto remove_dbg;
- }
-
- retval = usb_add_gadget_udc(dev, &udc->gadget);
- if (retval)
- goto remove_trans;
-
- pm_runtime_no_callbacks(&udc->gadget.dev);
- pm_runtime_enable(&udc->gadget.dev);
-
- _udc = udc;
- return retval;
-
-remove_trans:
- if (udc->transceiver) {
- otg_set_peripheral(udc->transceiver, &udc->gadget);
- otg_put_transceiver(udc->transceiver);
- }
-
- err("error = %i", retval);
-remove_dbg:
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- dbg_remove_files(&udc->gadget.dev);
-#endif
-unreg_device:
- device_unregister(&udc->gadget.dev);
-put_transceiver:
- if (udc->transceiver)
- otg_put_transceiver(udc->transceiver);
-free_udc:
- 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;
- }
- usb_del_gadget_udc(&udc->gadget);
-
- if (udc->transceiver) {
- otg_set_peripheral(udc->transceiver, &udc->gadget);
- otg_put_transceiver(udc->transceiver);
- }
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- dbg_remove_files(&udc->gadget.dev);
-#endif
- device_unregister(&udc->gadget.dev);
-
- kfree(udc);
- _udc = NULL;
-}
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
deleted file mode 100644
index f4871e1fac5..00000000000
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ /dev/null
@@ -1,227 +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 CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
-#define ENDPT_MAX (32)
-#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)
-#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
- /* 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;
- struct ci13xxx_td *zptr;
- dma_addr_t zdma;
-};
-
-/* 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;
- int wedge;
-
- /* global resources */
- spinlock_t *lock;
- struct device *device;
- struct dma_pool *td_pool;
-};
-
-struct ci13xxx;
-struct ci13xxx_udc_driver {
- const char *name;
- unsigned long flags;
-#define CI13XXX_REGS_SHARED BIT(0)
-#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
-#define CI13XXX_PULLUP_ON_VBUS BIT(2)
-#define CI13XXX_DISABLE_STREAMING BIT(3)
-
-#define CI13XXX_CONTROLLER_RESET_EVENT 0
-#define CI13XXX_CONTROLLER_STOPPED_EVENT 1
- void (*notify_event) (struct ci13xxx *udc, unsigned event);
-};
-
-/* CI13XXX UDC descriptor & global resources */
-struct ci13xxx {
- spinlock_t *lock; /* ctrl register bank access */
- void __iomem *regs; /* registers address space */
-
- struct dma_pool *qh_pool; /* DMA pool for queue heads */
- struct dma_pool *td_pool; /* DMA pool for transfer descs */
- struct usb_request *status; /* ep0 status request */
-
- struct usb_gadget gadget; /* USB slave device */
- struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
- u32 ep0_dir; /* ep0 direction */
-#define ep0out ci13xxx_ep[0]
-#define ep0in ci13xxx_ep[hw_ep_max / 2]
- u8 remote_wakeup; /* Is remote wakeup feature
- enabled by the host? */
- u8 suspended; /* suspended by the host */
- u8 test_mode; /* the selected test mode */
-
- struct usb_gadget_driver *driver; /* 3rd party gadget driver */
- struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
- int vbus_active; /* is VBUS active */
- struct otg_transceiver *transceiver; /* Transceiver struct */
-};
-
-/******************************************************************************
- * 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)
-#define USBCMD_ATDTW BIT(14)
-
-/* 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_FPR BIT(6)
-#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)
-#define USBMODE_SDIS BIT(4)
-
-/* 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 baaebf2830f..f8015193205 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -21,6 +21,24 @@
#include <linux/usb/composite.h>
#include <asm/unaligned.h>
+#include "u_os_desc.h"
+
+/**
+ * struct usb_os_string - represents OS String to be reported by a gadget
+ * @bLength: total length of the entire descritor, always 0x12
+ * @bDescriptorType: USB_DT_STRING
+ * @qwSignature: the OS String proper
+ * @bMS_VendorCode: code used by the host for subsequent requests
+ * @bPad: not used, must be zero
+ */
+struct usb_os_string {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 qwSignature[OS_STRING_QW_SIGN_LEN];
+ __u8 bMS_VendorCode;
+ __u8 bPad;
+} __packed;
+
/*
* The code in this file is utility code, used to build a gadget driver
* from one or more "function" drivers, one or more "configuration"
@@ -28,44 +46,12 @@
* with the relevant device-wide data.
*/
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ 1024
-
-static struct usb_composite_driver *composite;
-static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
-
-/* Some systems will need runtime overrides for the product identifiers
- * published in the device descriptor, either numbers or strings or both.
- * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
- */
-
-static ushort idVendor;
-module_param(idVendor, ushort, 0);
-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-
-static ushort idProduct;
-module_param(idProduct, ushort, 0);
-MODULE_PARM_DESC(idProduct, "USB Product ID");
-
-static ushort bcdDevice;
-module_param(bcdDevice, ushort, 0);
-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-
-static char *iManufacturer;
-module_param(iManufacturer, charp, 0);
-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-
-static char *iProduct;
-module_param(iProduct, charp, 0);
-MODULE_PARM_DESC(iProduct, "USB Product string");
-
-static char *iSerialNumber;
-module_param(iSerialNumber, charp, 0);
-MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
-
-static char composite_manufacturer[50];
+static struct usb_gadget_strings **get_containers_gs(
+ struct usb_gadget_string_container *uc)
+{
+ return (struct usb_gadget_strings **)uc->stash;
+}
-/*-------------------------------------------------------------------------*/
/**
* next_ep_desc() - advance to the next EP descriptor
* @t: currect pointer within descriptor array
@@ -117,6 +103,7 @@ 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;
@@ -144,7 +131,7 @@ int config_ep_by_speed(struct usb_gadget *g,
}
/* else: fall through */
default:
- speed_desc = f->descriptors;
+ speed_desc = f->fs_descriptors;
}
/* find descriptors */
for_each_ep_desc(speed_desc, d_spd) {
@@ -180,15 +167,18 @@ ep_found:
_ep->mult = comp_desc->bmAttributes & 0x3;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
- _ep->maxburst = comp_desc->bMaxBurst;
+ _ep->maxburst = comp_desc->bMaxBurst + 1;
break;
default:
- /* Do nothing for control endpoints */
+ 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
@@ -234,7 +224,7 @@ int 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;
@@ -247,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
@@ -283,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
@@ -297,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;
@@ -309,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
@@ -348,16 +354,37 @@ int 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;
@@ -367,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) {
@@ -391,7 +418,7 @@ static int config_buf(struct usb_configuration *config,
descriptors = f->hs_descriptors;
break;
default:
- descriptors = f->descriptors;
+ descriptors = f->fs_descriptors;
}
if (!descriptors)
@@ -413,6 +440,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
{
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c;
+ struct list_head *pos;
u8 type = w_value >> 8;
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
@@ -431,7 +459,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
/* This is a lookup by config *INDEX* */
w_value &= 0xff;
- list_for_each_entry(c, &cdev->configs, list) {
+
+ pos = &cdev->configs;
+ c = cdev->os_desc_config;
+ if (c)
+ goto check_config;
+
+ while ((pos = pos->next) != &cdev->configs) {
+ c = list_entry(pos, typeof(*c), list);
+
+ /* skip OS Descriptors config which is handled separately */
+ if (c == cdev->os_desc_config)
+ continue;
+
+check_config:
/* ignore configs that won't work at this speed */
switch (speed) {
case USB_SPEED_SUPER:
@@ -584,6 +625,7 @@ static void reset_config(struct usb_composite_dev *cdev)
bitmap_zero(f->endpoints, 32);
}
cdev->config = NULL;
+ cdev->delayed_status = 0;
}
static int set_config(struct usb_composite_dev *cdev,
@@ -624,6 +666,7 @@ static int set_config(struct usb_composite_dev *cdev,
if (!c)
goto done;
+ usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
cdev->config = c;
/* Initialize all interfaces by setting them to altsetting zero. */
@@ -648,7 +691,7 @@ static int set_config(struct usb_composite_dev *cdev,
descriptors = f->hs_descriptors;
break;
default:
- descriptors = f->descriptors;
+ descriptors = f->fs_descriptors;
}
for (; *descriptors; ++descriptors) {
@@ -684,7 +727,7 @@ static int set_config(struct usb_composite_dev *cdev,
}
/* when we return, be sure our power usage is valid */
- power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+ power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
done:
usb_gadget_vbus_draw(gadget, power);
if (result >= 0 && cdev->delayed_status)
@@ -692,6 +735,31 @@ done:
return result;
}
+int usb_add_config_only(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ struct usb_configuration *c;
+
+ if (!config->bConfigurationValue)
+ return -EINVAL;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue)
+ return -EBUSY;
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+ memset(config->interface, 0, sizeof(config->interface));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_add_config_only);
+
/**
* usb_add_config() - add a configuration to a device.
* @cdev: wraps the USB gadget
@@ -712,31 +780,33 @@ int usb_add_config(struct usb_composite_dev *cdev,
int (*bind)(struct usb_configuration *))
{
int status = -EINVAL;
- struct usb_configuration *c;
+
+ if (!bind)
+ goto done;
DBG(cdev, "adding config #%u '%s'/%p\n",
config->bConfigurationValue,
config->label, config);
- if (!config->bConfigurationValue || !bind)
+ status = usb_add_config_only(cdev, config);
+ if (status)
goto done;
- /* Prevent duplicate configuration identifiers */
- list_for_each_entry(c, &cdev->configs, list) {
- if (c->bConfigurationValue == config->bConfigurationValue) {
- status = -EBUSY;
- goto done;
- }
- }
-
- config->cdev = cdev;
- list_add_tail(&config->list, &cdev->configs);
-
- INIT_LIST_HEAD(&config->functions);
- config->next_interface_id = 0;
-
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 {
@@ -773,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);
+}
/*-------------------------------------------------------------------------*/
@@ -785,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) {
@@ -825,10 +943,11 @@ 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;
- const char *str;
/* Yes, not only is USB's I18N support probably more than most
* folk will ever care about ... also, it's all supported here.
@@ -858,6 +977,12 @@ static int get_string(struct usb_composite_dev *cdev,
collect_langs(sp, s->wData);
}
}
+ list_for_each_entry(uc, &cdev->gstrings, list) {
+ struct usb_gadget_strings **sp;
+
+ sp = get_containers_gs(uc);
+ collect_langs(sp, s->wData);
+ }
for (len = 0; len <= 126 && s->wData[len]; len++)
continue;
@@ -868,24 +993,26 @@ static int get_string(struct usb_composite_dev *cdev,
return s->bLength;
}
- /* Otherwise, look up and return a specified string. First
- * check if the string has not been overridden.
- */
- if (cdev->manufacturer_override == id)
- str = iManufacturer ?: composite->iManufacturer ?:
- composite_manufacturer;
- else if (cdev->product_override == id)
- str = iProduct ?: composite->iProduct;
- else if (cdev->serial_override == id)
- str = iSerialNumber;
- else
- str = NULL;
- if (str) {
- struct usb_gadget_strings strings = {
- .language = language,
- .strings = &(struct usb_string) { 0xff, str }
- };
- return usb_gadget_get_string(&strings, 0xff, buf);
+ 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
@@ -939,6 +1066,7 @@ int usb_string_id(struct usb_composite_dev *cdev)
}
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(usb_string_id);
/**
* usb_string_ids() - allocate unused string IDs in batch
@@ -970,6 +1098,120 @@ int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
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
@@ -998,7 +1240,7 @@ int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
c->next_string_id += n;
return next + 1;
}
-
+EXPORT_SYMBOL_GPL(usb_string_ids_n);
/*-------------------------------------------------------------------------*/
@@ -1010,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
@@ -1017,7 +1409,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
* housekeeping for the gadget function we're implementing. Most of
* the work is in config and function specific setup.
*/
-static int
+int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
@@ -1219,6 +1611,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
default:
unknown:
+ /*
+ * OS descriptors handling
+ */
+ if (cdev->use_os_string && cdev->os_desc_config &&
+ (ctrl->bRequest & USB_TYPE_VENDOR) &&
+ ctrl->bRequest == cdev->b_vendor_code) {
+ struct usb_request *req;
+ struct usb_configuration *os_desc_cfg;
+ u8 *buf;
+ int interface;
+ int count = 0;
+
+ req = cdev->os_desc_req;
+ req->complete = composite_setup_complete;
+ buf = req->buf;
+ os_desc_cfg = cdev->os_desc_config;
+ memset(buf, 0, w_length);
+ buf[5] = 0x01;
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ if (w_index != 0x4 || (w_value >> 8))
+ break;
+ buf[6] = w_index;
+ if (w_length == 0x10) {
+ /* Number of ext compat interfaces */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ value = w_length;
+ } else {
+ /* "extended compatibility ID"s */
+ count = count_ext_compat(os_desc_cfg);
+ buf[8] = count;
+ count *= 24; /* 24 B/ext compat desc */
+ count += 16; /* header */
+ put_unaligned_le32(count, buf);
+ buf += 16;
+ fill_ext_compat(os_desc_cfg, buf);
+ value = w_length;
+ }
+ break;
+ case USB_RECIP_INTERFACE:
+ if (w_index != 0x5 || (w_value >> 8))
+ break;
+ interface = w_value & 0xFF;
+ buf[6] = w_index;
+ if (w_length == 0x0A) {
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+
+ value = w_length;
+ } else {
+ count = count_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le16(count, buf + 8);
+ count = len_ext_prop(os_desc_cfg,
+ interface);
+ put_unaligned_le32(count, buf);
+ buf += 10;
+ value = fill_ext_prop(os_desc_cfg,
+ interface, buf);
+ if (value < 0)
+ return value;
+
+ value = w_length;
+ }
+ break;
+ }
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ return value;
+ }
+
VDBG(cdev,
"non-core control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
@@ -1256,8 +1733,22 @@ unknown:
struct usb_configuration *c;
c = cdev->config;
- if (c && c->setup)
+ if (!c)
+ goto done;
+
+ /* try current config's setup */
+ if (c->setup) {
value = c->setup(c, ctrl);
+ goto done;
+ }
+
+ /* try the only function in the current config */
+ if (!list_is_singular(&c->functions))
+ goto done;
+ f = list_first_entry(&c->functions, struct usb_function,
+ list);
+ if (f->setup)
+ value = f->setup(f, ctrl);
}
goto done;
@@ -1284,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;
@@ -1295,27 +1786,24 @@ static void composite_disconnect(struct usb_gadget *gadget)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
- if (composite->disconnect)
- composite->disconnect(cdev);
+ if (cdev->driver->disconnect)
+ cdev->driver->disconnect(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
}
/*-------------------------------------------------------------------------*/
-static ssize_t composite_show_suspended(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t suspended_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
struct usb_composite_dev *cdev = get_gadget_data(gadget);
return sprintf(buf, "%d\n", cdev->suspended);
}
+static DEVICE_ATTR_RO(suspended);
-static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
-
-static void
-composite_unbind(struct usb_gadget *gadget)
+static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
@@ -1328,80 +1816,85 @@ 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);
- }
- device_remove_file(&gadget->dev, &dev_attr_suspended);
+ composite_dev_cleanup(cdev);
+
+ kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
- composite = NULL;
}
-static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
+static void composite_unbind(struct usb_gadget *gadget)
{
- if (!*desc) {
- int ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- WARNING(cdev, "failed to override string ID\n");
- else
- *desc = ret;
- }
-
- return *desc;
+ __composite_unbind(gadget, true);
}
-static int composite_bind(struct usb_gadget *gadget)
+static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
+ const struct usb_device_descriptor *old)
{
- struct usb_composite_dev *cdev;
- int status = -ENOMEM;
+ __le16 idVendor;
+ __le16 idProduct;
+ __le16 bcdDevice;
+ u8 iSerialNumber;
+ u8 iManufacturer;
+ u8 iProduct;
- cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
- if (!cdev)
- return status;
+ /*
+ * 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;
+}
- spin_lock_init(&cdev->lock);
- cdev->gadget = gadget;
- set_gadget_data(gadget, cdev);
- INIT_LIST_HEAD(&cdev->configs);
+int composite_dev_prepare(struct usb_composite_driver *composite,
+ struct usb_composite_dev *cdev)
+{
+ 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;
/*
@@ -1416,63 +1909,105 @@ static int composite_bind(struct usb_gadget *gadget)
* we force endpoints to start unassigned; few controller
* drivers will zero ep->driver_data.
*/
- usb_ep_autoconfig_reset(cdev->gadget);
+ usb_ep_autoconfig_reset(gadget);
+ return 0;
+fail_dev:
+ kfree(cdev->req->buf);
+fail:
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ cdev->req = NULL;
+ return ret;
+}
+
+int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
+ struct usb_ep *ep0)
+{
+ int ret = 0;
+
+ cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL);
+ if (!cdev->os_desc_req) {
+ ret = PTR_ERR(cdev->os_desc_req);
+ goto end;
+ }
+
+ /* OS feature descriptor length <= 4kB */
+ cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
+ if (!cdev->os_desc_req->buf) {
+ ret = PTR_ERR(cdev->os_desc_req->buf);
+ kfree(cdev->os_desc_req);
+ goto end;
+ }
+ cdev->os_desc_req->complete = composite_setup_complete;
+end:
+ return ret;
+}
+
+void composite_dev_cleanup(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget_string_container *uc, *tmp;
+
+ list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) {
+ list_del(&uc->list);
+ kfree(uc);
+ }
+ if (cdev->os_desc_req) {
+ kfree(cdev->os_desc_req->buf);
+ usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
+ }
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(cdev->gadget->ep0, cdev->req);
+ }
+ cdev->next_string_id = 0;
+ device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *gdriver)
+{
+ struct usb_composite_dev *cdev;
+ struct usb_composite_driver *composite = to_cdriver(gdriver);
+ int status = -ENOMEM;
+
+ cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+ if (!cdev)
+ return status;
+
+ spin_lock_init(&cdev->lock);
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+ INIT_LIST_HEAD(&cdev->gstrings);
+
+ status = composite_dev_prepare(composite, cdev);
+ if (status)
+ goto fail;
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
- status = composite_gadget_bind(cdev);
+ status = composite->bind(cdev);
if (status < 0)
goto fail;
- cdev->desc = *composite->dev;
-
- /* 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);
-
- /* string overrides */
- if (iManufacturer || !cdev->desc.iManufacturer) {
- if (!iManufacturer && !composite->iManufacturer &&
- !*composite_manufacturer)
- snprintf(composite_manufacturer,
- sizeof composite_manufacturer,
- "%s %s with %s",
- init_utsname()->sysname,
- init_utsname()->release,
- gadget->name);
-
- cdev->manufacturer_override =
- override_id(cdev, &cdev->desc.iManufacturer);
+ if (cdev->use_os_string) {
+ status = composite_os_desc_req_prepare(cdev, gadget->ep0);
+ if (status)
+ goto fail;
}
- if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
- cdev->product_override =
- override_id(cdev, &cdev->desc.iProduct);
-
- if (iSerialNumber)
- cdev->serial_override =
- override_id(cdev, &cdev->desc.iSerialNumber);
+ update_unchanged_dev_desc(&cdev->desc, composite->dev);
/* has userspace failed to provide a serial number? */
if (composite->needs_serial && !cdev->desc.iSerialNumber)
WARNING(cdev, "userspace failed to provide iSerialNumber\n");
- /* finish up */
- status = device_create_file(&gadget->dev, &dev_attr_suspended);
- if (status)
- goto fail;
-
INFO(cdev, "%s ready\n", composite->name);
return 0;
fail:
- composite_unbind(gadget);
+ __composite_unbind(gadget, false);
return status;
}
@@ -1494,8 +2029,8 @@ 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;
@@ -1507,24 +2042,24 @@ composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
- u8 maxpower;
+ u16 maxpower;
/* REVISIT: should we have config level
* suspend/resume callbacks?
*/
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->bMaxPower;
+ maxpower = cdev->config->MaxPower;
usb_gadget_vbus_draw(gadget, maxpower ?
- (2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
+ maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
}
cdev->suspended = 0;
@@ -1532,13 +2067,8 @@ composite_resume(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
-static struct usb_gadget_driver composite_driver = {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- .max_speed = USB_SPEED_SUPER,
-#else
- .max_speed = USB_SPEED_HIGH,
-#endif
-
+static const struct usb_gadget_driver composite_driver_template = {
+ .bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
@@ -1555,10 +2085,7 @@ static struct usb_gadget_driver composite_driver = {
/**
* usb_composite_probe() - register a composite driver
* @driver: the driver to register
- * @bind: the callback used to allocate resources that are shared across the
- * whole device, such as string IDs, and add its configurations using
- * @usb_add_config(). This may fail by returning a negative errno
- * value; it should return zero on successful initialization.
+ *
* Context: single threaded during gadget setup
*
* This function is used to register drivers using the composite driver
@@ -1571,25 +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 usb_composite_probe(struct usb_composite_driver *driver,
- int (*bind)(struct usb_composite_dev *cdev))
+int usb_composite_probe(struct usb_composite_driver *driver)
{
- if (!driver || !driver->dev || !bind || composite)
+ struct usb_gadget_driver *gadget_driver;
+
+ if (!driver || !driver->dev || !driver->bind)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
- if (!driver->iProduct)
- driver->iProduct = driver->name;
- composite_driver.function = (char *) driver->name;
- composite_driver.driver.name = driver->name;
- composite_driver.max_speed =
- min_t(u8, composite_driver.max_speed, driver->max_speed);
- composite = driver;
- composite_gadget_bind = bind;
-
- return usb_gadget_probe_driver(&composite_driver, composite_bind);
+
+ 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
@@ -1600,10 +2128,9 @@ int usb_composite_probe(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
@@ -1640,4 +2167,60 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
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 7542a72ce51..34e12fc52c2 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -12,13 +12,14 @@
#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
@@ -53,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
@@ -106,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
@@ -155,4 +157,41 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
return ret;
}
+EXPORT_SYMBOL_GPL(usb_copy_descriptors);
+
+int usb_assign_descriptors(struct usb_function *f,
+ struct usb_descriptor_header **fs,
+ struct usb_descriptor_header **hs,
+ struct usb_descriptor_header **ss)
+{
+ 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;
+ }
+ 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
index 19d7bb0df75..986fc511a2e 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/dbgp.c
@@ -13,12 +13,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-/* See comments in "zero.c" */
-#include "epautoconf.c"
-
-#ifdef CONFIG_USB_G_DBGP_SERIAL
-#include "u_serial.c"
-#endif
+#include "u_serial.h"
#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
@@ -236,6 +231,10 @@ static void dbgp_unbind(struct usb_gadget *gadget)
gadget->ep0->driver_data = NULL;
}
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+static unsigned char tty_line;
+#endif
+
static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
{
int stp;
@@ -273,7 +272,7 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
dbgp.serial->in->desc = &i_desc;
dbgp.serial->out->desc = &o_desc;
- if (gserial_setup(gadget, 1) < 0) {
+ if (gserial_alloc_line(&tty_line)) {
stp = 3;
goto fail_3;
}
@@ -292,7 +291,8 @@ fail_1:
return -ENODEV;
}
-static int __init dbgp_bind(struct usb_gadget *gadget)
+static int __init dbgp_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
int err, stp;
@@ -381,7 +381,7 @@ static int dbgp_setup(struct usb_gadget *gadget,
#ifdef CONFIG_USB_G_DBGP_PRINTK
err = dbgp_enable_ep();
#else
- err = gserial_connect(dbgp.serial, 0);
+ err = gserial_connect(dbgp.serial, tty_line);
#endif
if (err < 0)
goto fail;
@@ -402,9 +402,10 @@ fail:
return err;
}
-static struct usb_gadget_driver dbgp_driver = {
+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,
@@ -416,14 +417,14 @@ static struct usb_gadget_driver dbgp_driver = {
static int __init dbgp_init(void)
{
- return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind);
+ 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_cleanup();
+ gserial_free_line(tty_line);
#endif
}
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index db815c2da7e..2b54955d316 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -39,41 +39,44 @@
#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>
-
#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 */
@@ -83,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 {
@@ -94,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);
}
/*-------------------------------------------------------------------------*/
@@ -121,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",
@@ -137,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)
@@ -147,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;
};
@@ -166,6 +173,8 @@ struct dummy_hcd {
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;
@@ -178,12 +187,12 @@ struct dummy {
/*
* 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;
@@ -210,14 +219,14 @@ static inline struct device *dummy_dev(struct dummy_hcd *dum)
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_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget)
@@ -229,36 +238,33 @@ static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget)
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;
@@ -268,8 +274,8 @@ 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 */
}
@@ -404,8 +410,8 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
#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;
@@ -413,11 +419,11 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
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);
+ dum = ep_to_dummy(ep);
if (!dum->driver)
return -ESHUTDOWN;
@@ -441,10 +447,10 @@ 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) {
@@ -466,7 +472,7 @@ 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) {
@@ -486,8 +492,8 @@ 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) {
@@ -510,14 +516,22 @@ 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) {
+ switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_BULK:
val = "bulk";
break;
@@ -530,8 +544,8 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
default:
val = "ctrl";
break;
- }; val; }),
- max);
+ } 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.
@@ -542,67 +556,63 @@ 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;
@@ -611,49 +621,48 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
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);
+ 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.
@@ -661,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;
@@ -671,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;
}
@@ -707,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;
@@ -755,15 +764,15 @@ 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_hcd *dum_hcd;
@@ -786,11 +795,11 @@ static int dummy_wakeup (struct usb_gadget *_gadget)
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_hcd(_gadget))->dum;
+ dum = gadget_to_dummy_hcd(_gadget)->dum;
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
@@ -798,22 +807,15 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
return 0;
}
-static void dummy_udc_udpate_ep0(struct dummy *dum)
+static void dummy_udc_update_ep0(struct dummy *dum)
{
- u32 i;
-
- if (dum->gadget.speed == USB_SPEED_SUPER) {
- for (i = 0; i < DUMMY_ENDPOINTS; i++)
- dum->ep[i].ep.max_streams = 0x10;
+ if (dum->gadget.speed == USB_SPEED_SUPER)
dum->ep[0].ep.maxpacket = 9;
- } else {
- for (i = 0; i < DUMMY_ENDPOINTS; i++)
- dum->ep[i].ep.max_streams = 0;
+ else
dum->ep[0].ep.maxpacket = 64;
- }
}
-static int dummy_pullup (struct usb_gadget *_gadget, int value)
+static int dummy_pullup(struct usb_gadget *_gadget, int value)
{
struct dummy_hcd *dum_hcd;
struct dummy *dum;
@@ -829,7 +831,7 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value)
dum->driver->max_speed);
else
dum->gadget.speed = USB_SPEED_FULL;
- dummy_udc_udpate_ep0(dum);
+ dummy_udc_update_ep0(dum);
if (dum->gadget.speed < dum->driver->max_speed)
dev_dbg(udc_dev(dum), "This device can perform faster"
@@ -838,10 +840,10 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value)
}
dum_hcd = gadget_to_dummy_hcd(_gadget);
- spin_lock_irqsave (&dum->lock, flags);
+ spin_lock_irqsave(&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum_hcd);
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum->lock, flags);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
return 0;
@@ -864,16 +866,16 @@ static const struct usb_gadget_ops dummy_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 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);
/*-------------------------------------------------------------------------*/
@@ -908,7 +910,7 @@ static int dummy_udc_start(struct usb_gadget *g,
dum->devstatus = 0;
dum->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);
return 0;
}
@@ -919,12 +921,12 @@ static int dummy_udc_stop(struct usb_gadget *g,
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
struct dummy *dum = dum_hcd->dum;
- dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
- driver->driver.name);
+ if (driver)
+ dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
+ driver->driver.name);
dum->driver = NULL;
- dummy_pullup(&dum->gadget, 0);
return 0;
}
@@ -932,12 +934,6 @@ static int dummy_udc_stop(struct usb_gadget *g,
/* The gadget structure is stored inside the hcd structure and will be
* released along with it. */
-static void
-dummy_gadget_release (struct device *dev)
-{
- return;
-}
-
static void init_dummy_udc_hw(struct dummy *dum)
{
int i;
@@ -953,7 +949,8 @@ static void init_dummy_udc_hw(struct dummy *dum)
list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list);
ep->halted = ep->wedged = ep->already_seen =
ep->setup_stage = 0;
- ep->ep.maxpacket = ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.max_streams = 16;
ep->last_io = jiffies;
ep->gadget = &dum->gadget;
ep->desc = NULL;
@@ -969,31 +966,24 @@ static void init_dummy_udc_hw(struct dummy *dum)
#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.max_speed = USB_SPEED_SUPER;
- dev_set_name(&dum->gadget.dev, "gadget");
dum->gadget.dev.parent = &pdev->dev;
- dum->gadget.dev.release = dummy_gadget_release;
- rc = device_register (&dum->gadget.dev);
- if (rc < 0) {
- put_device(&dum->gadget.dev);
- return rc;
- }
-
init_dummy_udc_hw(dum);
rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget);
if (rc < 0)
goto err_udc;
- rc = device_create_file (&dum->gadget.dev, &dev_attr_function);
+ rc = device_create_file(&dum->gadget.dev, &dev_attr_function);
if (rc < 0)
goto err_dev;
platform_set_drvdata(pdev, dum);
@@ -1002,18 +992,15 @@ static int dummy_udc_probe (struct platform_device *pdev)
err_dev:
usb_del_gadget_udc(&dum->gadget);
err_udc:
- device_unregister(&dum->gadget.dev);
return rc;
}
-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);
+ device_remove_file(&dum->gadget.dev, &dev_attr_function);
usb_del_gadget_udc(&dum->gadget);
- platform_set_drvdata (pdev, NULL);
- device_remove_file (&dum->gadget.dev, &dev_attr_function);
- device_unregister (&dum->gadget.dev);
return 0;
}
@@ -1061,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.
@@ -1073,7 +1070,82 @@ 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
@@ -1083,16 +1155,21 @@ static int dummy_urb_enqueue (
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;
+ }
+
rc = usb_hcd_link_urb_to_ep(hcd, urb);
if (rc) {
kfree(urbp);
@@ -1107,7 +1184,7 @@ static int dummy_urb_enqueue (
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 */
@@ -1139,20 +1216,91 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
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).
*
@@ -1162,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;
@@ -1186,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.
@@ -1238,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;
@@ -1259,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;
@@ -1273,7 +1417,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
limit += limit * tmp;
}
if (dum->gadget.speed == USB_SPEED_SUPER) {
- switch (ep->desc->bmAttributes & 0x03) {
+ switch (usb_endpoint_type(ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* Sec. 4.4.8.2 USB3.0 Spec */
limit = 3 * 16 * 1024 * 8;
@@ -1295,7 +1439,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
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;
@@ -1303,9 +1447,9 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
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;
@@ -1535,19 +1679,19 @@ static void dummy_timer(unsigned long _dum_hcd)
/* 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_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:
@@ -1564,7 +1708,7 @@ restart:
goto return_urb;
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
@@ -1575,7 +1719,7 @@ 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) {
@@ -1590,7 +1734,7 @@ 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;
}
@@ -1604,21 +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;
- setup = *(struct usb_ctrlrequest*) urb->setup_packet;
+ 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;
}
@@ -1638,10 +1782,10 @@ restart:
* 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) */
@@ -1653,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;
@@ -1665,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;
@@ -1680,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;
}
@@ -1696,15 +1839,15 @@ 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_hcd_to_hcd(dum_hcd), urb);
- spin_unlock (&dum->lock);
+ spin_unlock(&dum->lock);
usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status);
- spin_lock (&dum->lock);
+ spin_lock(&dum->lock);
goto restart;
}
@@ -1717,7 +1860,7 @@ return_urb:
mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1));
}
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock_irqrestore(&dum->lock, flags);
}
/*-------------------------------------------------------------------------*/
@@ -1729,7 +1872,7 @@ 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_hcd *dum_hcd;
unsigned long flags;
@@ -1753,13 +1896,34 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
dum_hcd->port_status);
retval = 1;
if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED)
- usb_hcd_resume_root_hub (hcd);
+ usb_hcd_resume_root_hub(hcd);
}
done:
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
ss_hub_descriptor(struct usb_hub_descriptor *desc)
{
@@ -1772,10 +1936,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
desc->u.ss.DeviceRemovable = 0xffff;
}
-static inline void
-hub_descriptor (struct usb_hub_descriptor *desc)
+static inline void hub_descriptor(struct usb_hub_descriptor *desc)
{
- memset (desc, 0, sizeof *desc);
+ memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
desc->wHubCharacteristics = cpu_to_le16(0x0001);
@@ -1784,7 +1947,7 @@ hub_descriptor (struct usb_hub_descriptor *desc)
desc->u.hs.DeviceRemovable[1] = 0xff;
}
-static int dummy_hub_control (
+static int dummy_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
@@ -1851,8 +2014,20 @@ static int dummy_hub_control (
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)
@@ -1894,8 +2069,8 @@ static int dummy_hub_control (
}
}
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);
+ ((__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;
@@ -2029,15 +2204,15 @@ error:
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
if ((dum_hcd->port_status & PORT_C_MASK) != 0)
- usb_hcd_poll_rh_status (hcd);
+ 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_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_hcd->dum->lock);
dum_hcd->rh_state = DUMMY_RH_SUSPENDED;
@@ -2047,12 +2222,12 @@ static int dummy_bus_suspend (struct usb_hcd *hcd)
return 0;
}
-static int dummy_bus_resume (struct usb_hcd *hcd)
+static int dummy_bus_resume(struct usb_hcd *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_hcd->dum->lock);
if (!HCD_HW_ACCESSIBLE(hcd)) {
@@ -2070,55 +2245,54 @@ static int dummy_bus_resume (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-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:
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
s = "ls";
break;
- case USB_SPEED_FULL:
+ case USB_SPEED_FULL:
s = "fs";
break;
- case USB_SPEED_HIGH:
+ case USB_SPEED_HIGH:
s = "hs";
break;
- case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER:
s = "ss";
break;
- default:
+ default:
s = "?";
break;
- }; s; }),
- ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "",
+ } s; }),
+ ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
({ char *s; \
- switch (usb_pipetype (urb->pipe)) { \
- case PIPE_CONTROL: \
+ switch (usb_pipetype(urb->pipe)) { \
+ case PIPE_CONTROL: \
s = ""; \
break; \
- case PIPE_BULK: \
+ case PIPE_BULK: \
s = "-bulk"; \
break; \
- case PIPE_INTERRUPT: \
+ case PIPE_INTERRUPT: \
s = "-int"; \
break; \
- default: \
+ default: \
s = "-iso"; \
break; \
- }; s;}),
+ } s; }),
urb->actual_length, urb->transfer_buffer_length);
}
-static ssize_t
-show_urbs (struct device *dev, struct device_attribute *attr, 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 usb_hcd *hcd = dev_get_drvdata(dev);
struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
struct urbp *urbp;
size_t size = 0;
@@ -2128,7 +2302,7 @@ show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
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;
}
@@ -2136,7 +2310,7 @@ show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
return size;
}
-static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
+static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
@@ -2144,6 +2318,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd)
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;
@@ -2189,11 +2364,11 @@ static int dummy_start(struct usb_hcd *hcd)
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(hcd))->dum;
+ 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");
@@ -2201,16 +2376,20 @@ static void dummy_stop (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-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);
+ 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)) {
- the_controller.hs_hcd = hcd_to_dummy_hcd(hcd);
- the_controller.hs_hcd->dum = &the_controller;
+ 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
@@ -2219,8 +2398,8 @@ static int dummy_setup(struct usb_hcd *hcd)
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_HIGH;
} else {
- the_controller.ss_hcd = hcd_to_dummy_hcd(hcd);
- the_controller.ss_hcd->dum = &the_controller;
+ 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;
}
@@ -2228,27 +2407,82 @@ static int dummy_setup(struct usb_hcd *hcd)
}
/* Change a group of bulk endpoints to support multiple stream IDs */
-int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
+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)
{
- if (hcd->speed != HCD_USB3)
- dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
- "%s() - ERROR! Not supported for USB2.0 roothub\n",
- __func__);
- return 0;
+ 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;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */
-int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
+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)
{
- if (hcd->speed != HCD_USB3)
- dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)),
- "%s() - ERROR! Not supported for USB2.0 roothub\n",
- __func__);
- return 0;
+ 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 = {
@@ -2262,13 +2496,13 @@ static struct hc_driver dummy_hcd = {
.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,
@@ -2278,11 +2512,13 @@ static struct hc_driver dummy_hcd = {
static int dummy_hcd_probe(struct platform_device *pdev)
{
+ 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));
if (!mod_data.is_super_speed)
dummy_hcd.flags = HCD_USB2;
@@ -2292,10 +2528,8 @@ static int dummy_hcd_probe(struct platform_device *pdev)
hs_hcd->has_tt = 1;
retval = usb_add_hcd(hs_hcd, 0, 0);
- if (retval != 0) {
- usb_put_hcd(hs_hcd);
- return retval;
- }
+ if (retval)
+ goto put_usb2_hcd;
if (mod_data.is_super_speed) {
ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
@@ -2314,8 +2548,10 @@ static int dummy_hcd_probe(struct platform_device *pdev)
put_usb3_hcd:
usb_put_hcd(ss_hcd);
dealloc_usb2_hcd:
+ usb_remove_hcd(hs_hcd);
+put_usb2_hcd:
usb_put_hcd(hs_hcd);
- the_controller.hs_hcd = the_controller.ss_hcd = NULL;
+ dum->hs_hcd = dum->ss_hcd = NULL;
return retval;
}
@@ -2323,7 +2559,7 @@ static int dummy_hcd_remove(struct platform_device *pdev)
{
struct dummy *dum;
- dum = (hcd_to_dummy_hcd(platform_get_drvdata(pdev)))->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));
@@ -2333,21 +2569,21 @@ static int dummy_hcd_remove(struct platform_device *pdev)
usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd));
- the_controller.hs_hcd = NULL;
- the_controller.ss_hcd = NULL;
+ dum->hs_hcd = NULL;
+ dum->ss_hcd = 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_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);
+ 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");
@@ -2357,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;
}
@@ -2381,80 +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;
if (!mod_data.is_high_speed && mod_data.is_super_speed)
return -EINVAL;
- 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.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;
- if (!the_controller.hs_hcd ||
- (!the_controller.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_hcd_pdev[i]);
+ if (retval < 0) {
+ i--;
+ while (i >= 0)
+ platform_device_del(the_hcd_pdev[i--]);
+ goto err_add_hcd;
+ }
}
- retval = platform_device_add(the_udc_pdev);
- if (retval < 0)
- goto err_add_udc;
- if (!platform_get_drvdata(the_udc_pdev)) {
- /*
- * The udc was added successfully but its probe function failed
- * for some reason.
- */
- retval = -EINVAL;
- goto err_probe_udc;
+ 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:
- platform_device_del(the_udc_pdev);
+ 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 e0e6375ef5d..0567cca1465 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -10,7 +10,7 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
@@ -22,17 +22,6 @@
#include "gadget_chips.h"
-
-/* we must assign addresses for configurable endpoints (like net2280) */
-static unsigned epnum;
-
-// #define MANY_ENDPOINTS
-#ifdef MANY_ENDPOINTS
-/* more than 15 configurable endpoints */
-static unsigned in_epnum;
-#endif
-
-
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
@@ -68,7 +57,7 @@ ep_matches (
return 0;
/* only support ep0 for portable CONTROL traffic */
- type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ type = usb_endpoint_type(desc);
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
@@ -139,7 +128,7 @@ ep_matches (
* and wants to know the maximum possible, provide the info.
*/
if (desc->wMaxPacketSize == 0)
- desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
@@ -155,7 +144,7 @@ ep_matches (
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
- if (ep->maxpacket < max)
+ if (ep->maxpacket_limit < max)
return 0;
if (!gadget_is_dualspeed(gadget) && max > 1023)
return 0;
@@ -176,21 +165,19 @@ 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) && !ep_comp) {
- int size = ep->maxpacket;
+ int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
@@ -275,24 +262,24 @@ struct usb_ep *usb_ep_autoconfig_ss(
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
- return ep;
+ goto found_ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
- return ep;
+ 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, ep_comp))
- return ep;
+ 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,
ep_comp))
- return ep;
+ goto found_ep;
}
#ifdef CONFIG_BLACKFIN
@@ -311,19 +298,24 @@ struct usb_ep *usb_ep_autoconfig_ss(
} else
ep = NULL;
if (ep && ep_matches(gadget, ep, desc, ep_comp))
- return ep;
+ 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, ep_comp))
- return ep;
+ 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
@@ -363,7 +355,7 @@ struct usb_ep *usb_ep_autoconfig(
{
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
-
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
@@ -381,9 +373,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
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 a28f6ffcd0f..c1c113ef950 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -14,8 +14,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
+#include <linux/netdevice.h>
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
@@ -93,30 +92,22 @@ static inline bool has_rndis(void)
#endif
}
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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 <linux/module.h>
-#include "f_ecm.c"
-#include "f_subset.c"
+#include "u_ecm.h"
+#include "u_gether.h"
#ifdef USB_ETH_RNDIS
-#include "f_rndis.c"
-#include "rndis.c"
+#include "u_rndis.h"
+#include "rndis.h"
+#else
+#define rndis_borrow_net(...) do {} while (0)
#endif
-#include "f_eem.c"
-#include "u_ether.c"
+#include "u_eem.h"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
+
+USB_ETHERNET_MODULE_PARAMETERS();
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
@@ -195,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 */
};
@@ -219,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;
/*-------------------------------------------------------------------------*/
@@ -230,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)) {
@@ -237,7 +233,15 @@ static int __init rndis_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- return rndis_bind_config(c, hostaddr);
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ status = usb_add_function(c, f_rndis);
+ if (status < 0)
+ usb_put_function(f_rndis);
+
+ return status;
}
static struct usb_configuration rndis_config_driver = {
@@ -262,6 +266,8 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
*/
static int __init eth_do_config(struct usb_configuration *c)
{
+ int status = 0;
+
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
@@ -269,12 +275,38 @@ static int __init eth_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- if (use_eem)
- return eem_bind_config(c);
- else if (can_support_ecm(c->cdev->gadget))
- return ecm_bind_config(c, hostaddr);
- else
- return geth_bind_config(c, hostaddr);
+ if (use_eem) {
+ f_eem = usb_get_function(fi_eem);
+ if (IS_ERR(f_eem))
+ return PTR_ERR(f_eem);
+
+ status = usb_add_function(c, f_eem);
+ if (status < 0)
+ usb_put_function(f_eem);
+
+ return status;
+ } else if (can_support_ecm(c->cdev->gadget)) {
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ status = usb_add_function(c, f_ecm);
+ if (status < 0)
+ usb_put_function(f_ecm);
+
+ return status;
+ } else {
+ f_geth = usb_get_function(fi_geth);
+ if (IS_ERR(f_geth))
+ return PTR_ERR(f_geth);
+
+ status = usb_add_function(c, f_geth);
+ if (status < 0)
+ usb_put_function(f_geth);
+
+ return status;
+ }
+
}
static struct usb_configuration eth_config_driver = {
@@ -288,26 +320,51 @@ 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 (use_eem) {
/* EEM */
+ fi_eem = usb_get_function_instance("eem");
+ if (IS_ERR(fi_eem))
+ return PTR_ERR(fi_eem);
+
+ eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
+
+ net = eem_opts->net;
+
eth_config_driver.label = "CDC Ethernet (EEM)";
device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
- } else if (can_support_ecm(cdev->gadget)) {
+ } else if (can_support_ecm(gadget)) {
/* ECM */
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+ net = ecm_opts->net;
+
eth_config_driver.label = "CDC Ethernet (ECM)";
} else {
/* CDC Subset */
+
+ fi_geth = usb_get_function_instance("geth");
+ if (IS_ERR(fi_geth))
+ return PTR_ERR(fi_geth);
+
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+
+ net = geth_opts->net;
+
eth_config_driver.label = "CDC Subset/SAFE";
device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
@@ -316,83 +373,105 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
}
+ gether_set_qmult(net, qmult);
+ if (!gether_set_host_addr(net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+
if (has_rndis()) {
/* RNDIS plus ECM-or-Subset */
+ gether_set_gadget(net, cdev->gadget);
+ status = gether_register_netdev(net);
+ if (status)
+ goto fail;
+
+ if (use_eem)
+ eem_opts->bound = true;
+ else if (can_support_ecm(gadget))
+ ecm_opts->bound = true;
+ else
+ geth_opts->bound = true;
+
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ status = PTR_ERR(fi_rndis);
+ goto fail;
+ }
+
+ rndis_borrow_net(fi_rndis, net);
+
device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
device_desc.bNumConfigurations = 2;
}
- 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);
- }
-
-
/* 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,
rndis_do_config);
if (status < 0)
- goto fail;
+ goto fail1;
}
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),
};
@@ -402,7 +481,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&eth_driver, eth_bind);
+ 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 3f8849339ad..ab1065afbbd 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -5,7 +5,7 @@
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
* Copyright (C) 2009 by Samsung Electronics
- * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ * 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,
@@ -16,7 +16,9 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
+#include <linux/err.h>
#include "u_serial.h"
#include "gadget_chips.h"
@@ -87,7 +89,7 @@ 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: */
@@ -167,7 +169,7 @@ static struct usb_endpoint_descriptor acm_fs_notify_desc = {
.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 = {
@@ -199,14 +201,13 @@ static struct usb_descriptor_header *acm_fs_function[] = {
};
/* high speed support: */
-
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 = {
@@ -237,6 +238,42 @@ static struct usb_descriptor_header *acm_hs_function[] = {
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
@@ -248,7 +285,7 @@ static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data",
[ACM_IAD_IDX ].s = "CDC Serial",
- { /* ZEROES END LIST */ },
+ { } /* end of list */
};
static struct usb_gadget_strings acm_string_table = {
@@ -570,9 +607,23 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_acm *acm = func_to_acm(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep;
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string IDs, and patch descriptors */
+ us = usb_gstrings_attach(cdev, acm_strings,
+ ARRAY_SIZE(acm_string_defs));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
+ acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
+ acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
+
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
@@ -623,29 +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 */
- f->descriptors = usb_copy_descriptors(acm_fs_function);
- if (!f->descriptors)
- goto fail;
-
/* 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 */
- f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
- }
+ 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);
@@ -668,82 +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)
{
- /* everything else is *probably* fine ... */
- return true;
+ struct f_acm *acm = func_to_acm(f);
+
+ kfree(acm);
}
-/**
- * acm_bind_config - add a CDC ACM function to a configuration
- * @c: the configuration to support the CDC ACM instance
- * @port_num: /dev/ttyGS* port this interface will use
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gserial_setup() with enough ports to
- * handle all the ones it binds. Caller is also responsible
- * for calling @gserial_cleanup() before module unload.
- */
-int acm_bind_config(struct usb_configuration *c, u8 port_num)
+static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
- struct f_acm *acm;
- int status;
-
- if (!can_support_cdc(c))
- return -EINVAL;
+ struct f_serial_opts *opts;
+ struct f_acm *acm;
- /* REVISIT might want instance-specific strings to help
- * distinguish instances ...
- */
-
- /* maybe allocate device-global string IDs, and patch descriptors */
- if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- acm_string_defs[ACM_CTRL_IDX].id = status;
-
- acm_control_interface_desc.iInterface = status;
-
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- acm_string_defs[ACM_DATA_IDX].id = status;
-
- acm_data_interface_desc.iInterface = status;
-
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- acm_string_defs[ACM_IAD_IDX].id = status;
-
- acm_iad_descriptor.iFunction = 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;
@@ -752,13 +752,97 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num)
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
- acm->port.func.unbind = acm_unbind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
acm->port.func.disable = acm_disable;
- status = usb_add_function(c, &acm->port.func);
- if (status)
- kfree(acm);
- return status;
+ opts = container_of(fi, struct f_serial_opts, func_inst);
+ acm->port_num = opts->port_num;
+ acm->port.func.unbind = acm_unbind;
+ acm->port.func.free_func = acm_free_func;
+
+ return &acm->port.func;
+}
+
+static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_serial_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_serial_opts);
+static ssize_t f_acm_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct f_serial_opts *opts = to_f_serial_opts(item);
+ struct f_serial_opts_attribute *f_serial_opts_attr =
+ container_of(attr, struct f_serial_opts_attribute, attr);
+ ssize_t ret = 0;
+
+ if (f_serial_opts_attr->show)
+ ret = f_serial_opts_attr->show(opts, page);
+ return ret;
+}
+
+static void acm_attr_release(struct config_item *item)
+{
+ struct f_serial_opts *opts = to_f_serial_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations acm_item_ops = {
+ .release = acm_attr_release,
+ .show_attribute = f_acm_attr_show,
+};
+
+static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page)
+{
+ return sprintf(page, "%u\n", opts->port_num);
+}
+
+static struct f_serial_opts_attribute f_acm_port_num =
+ __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show);
+
+
+static struct configfs_attribute *acm_attrs[] = {
+ &f_acm_port_num.attr,
+ NULL,
+};
+
+static struct config_item_type acm_func_type = {
+ .ct_item_ops = &acm_item_ops,
+ .ct_attrs = acm_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void acm_free_instance(struct usb_function_instance *fi)
+{
+ struct f_serial_opts *opts;
+
+ opts = container_of(fi, struct f_serial_opts, func_inst);
+ gserial_free_line(opts->port_num);
+ kfree(opts);
+}
+
+static struct usb_function_instance *acm_alloc_instance(void)
+{
+ struct f_serial_opts *opts;
+ int ret;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ opts->func_inst.free_func_inst = acm_free_instance;
+ ret = gserial_alloc_line(&opts->port_num);
+ if (ret) {
+ kfree(opts);
+ return ERR_PTR(ret);
+ }
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &acm_func_type);
+ return &opts->func_inst;
}
+DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 11c07cb7d33..798760fa7e7 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -14,10 +14,13 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ecm.h"
/*
@@ -91,12 +94,26 @@ 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_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,
@@ -178,7 +195,7 @@ static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
.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 = {
@@ -199,6 +216,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc = {
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,
@@ -224,7 +242,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
.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 = {
@@ -247,6 +265,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc = {
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,
@@ -272,7 +291,7 @@ static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
.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_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
@@ -314,6 +333,7 @@ static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
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,
@@ -337,8 +357,9 @@ static struct usb_descriptor_header *ecm_ss_function[] = {
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 */
};
@@ -666,14 +687,49 @@ 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;
@@ -724,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;
-
/* 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;
- }
-
- if (gadget_is_superspeed(c->cdev->gadget)) {
- 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;
-
- /* copy descriptors, and track endpoint copies */
- f->ss_descriptors = usb_copy_descriptors(ecm_ss_function);
- if (!f->ss_descriptors)
- goto fail;
- }
+ 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
@@ -777,11 +815,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
- if (f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
-
if (ecm->notify_req) {
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
@@ -790,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_ep->desc)
+ if (ecm->port.out_ep)
ecm->port.out_ep->driver_data = NULL;
- if (ecm->port.in_ep->desc)
+ if (ecm->port.in_ep)
ecm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -800,88 +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_superspeed(c->cdev->gadget))
- usb_free_descriptors(f->ss_descriptors);
- 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
-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;
@@ -889,11 +963,11 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
ecm->port.func.get_alt = ecm_get_alt;
ecm->port.func.setup = ecm_setup;
ecm->port.func.disable = ecm_disable;
+ ecm->port.func.free_func = ecm_free;
- status = usb_add_function(c, &ecm->port.func);
- if (status) {
- 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
index 1a7b2dd7d40..d61c11d765d 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -12,12 +12,15 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_eem.h"
#define EEM_HLEN 2
@@ -40,7 +43,7 @@ static inline struct f_eem *func_to_eem(struct usb_function *f)
/* interface descriptor: */
-static struct usb_interface_descriptor eem_intf __initdata = {
+static struct usb_interface_descriptor eem_intf = {
.bLength = sizeof eem_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -54,7 +57,7 @@ static struct usb_interface_descriptor eem_intf __initdata = {
/* full speed support: */
-static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor eem_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -62,7 +65,7 @@ static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor eem_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -70,7 +73,7 @@ static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *eem_fs_function[] __initdata = {
+static struct usb_descriptor_header *eem_fs_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_fs_in_desc,
@@ -80,7 +83,7 @@ static struct usb_descriptor_header *eem_fs_function[] __initdata = {
/* high speed support: */
-static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = {
+static struct usb_endpoint_descriptor eem_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -89,7 +92,7 @@ static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = {
+static struct usb_endpoint_descriptor eem_hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -98,7 +101,7 @@ static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *eem_hs_function[] __initdata = {
+static struct usb_descriptor_header *eem_hs_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_hs_in_desc,
@@ -108,7 +111,7 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = {
/* super speed support: */
-static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
+static struct usb_endpoint_descriptor eem_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -117,7 +120,7 @@ static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
+static struct usb_endpoint_descriptor eem_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -126,7 +129,7 @@ static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
+static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
.bLength = sizeof eem_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -135,7 +138,7 @@ static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
/* .bmAttributes = 0, */
};
-static struct usb_descriptor_header *eem_ss_function[] __initdata = {
+static struct usb_descriptor_header *eem_ss_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_ss_in_desc,
@@ -242,14 +245,40 @@ static void eem_disable(struct usb_function *f)
/* EEM function driver setup/binding */
-static int __init
-eem_bind(struct usb_configuration *c, struct usb_function *f)
+static int eem_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_eem *eem = func_to_eem(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep;
+ struct f_eem_opts *eem_opts;
+
+ eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to eem_opts->bound access
+ */
+ if (!eem_opts->bound) {
+ mutex_lock(&eem_opts->lock);
+ gether_set_gadget(eem_opts->net, cdev->gadget);
+ status = gether_register_netdev(eem_opts->net);
+ mutex_unlock(&eem_opts->lock);
+ if (status)
+ return status;
+ eem_opts->bound = true;
+ }
+
+ us = usb_gstrings_attach(cdev, eem_strings,
+ ARRAY_SIZE(eem_string_defs));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ eem_intf.iInterface = us[0].id;
+
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
@@ -274,38 +303,20 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
status = -ENOMEM;
- /* copy descriptors, and track endpoint copies */
- f->descriptors = usb_copy_descriptors(eem_fs_function);
- if (!f->descriptors)
- goto fail;
-
/* 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)) {
- eem_hs_in_desc.bEndpointAddress =
- eem_fs_in_desc.bEndpointAddress;
- eem_hs_out_desc.bEndpointAddress =
- eem_fs_out_desc.bEndpointAddress;
-
- /* copy descriptors, and track endpoint copies */
- f->hs_descriptors = usb_copy_descriptors(eem_hs_function);
- if (!f->hs_descriptors)
- goto fail;
- }
+ eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
+ eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
- if (gadget_is_superspeed(c->cdev->gadget)) {
- eem_ss_in_desc.bEndpointAddress =
- eem_fs_in_desc.bEndpointAddress;
- eem_ss_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;
- /* copy descriptors, and track endpoint copies */
- f->ss_descriptors = usb_copy_descriptors(eem_ss_function);
- if (!f->ss_descriptors)
- goto fail;
- }
+ 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" :
@@ -314,15 +325,10 @@ eem_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
- if (f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
-
- /* we might as well release our claims on endpoints */
- if (eem->port.out_ep->desc)
+ usb_free_all_descriptors(f);
+ if (eem->port.out_ep)
eem->port.out_ep->driver_data = NULL;
- if (eem->port.in_ep->desc)
+ if (eem->port.in_ep)
eem->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -330,21 +336,6 @@ fail:
return status;
}
-static void
-eem_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_eem *eem = func_to_eem(f);
-
- DBG(c->cdev, "eem unbind\n");
-
- if (gadget_is_superspeed(c->cdev->gadget))
- usb_free_descriptors(f->ss_descriptors);
- if (gadget_is_dualspeed(c->cdev->gadget))
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->descriptors);
- kfree(eem);
-}
-
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
struct sk_buff *skb = (struct sk_buff *)req->context;
@@ -545,54 +536,127 @@ error:
return status;
}
-/**
- * eem_bind_config - add CDC Ethernet (EEM) network link to a configuration
- * @c: the configuration to support the network link
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gether_setup(). Caller is also responsible
- * for calling @gether_cleanup() before module unload.
- */
-int __init eem_bind_config(struct usb_configuration *c)
+static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
{
- struct f_eem *eem;
- int status;
+ return container_of(to_config_group(item), struct f_eem_opts,
+ func_inst.group);
+}
- /* maybe allocate device-global string IDs */
- if (eem_string_defs[0].id == 0) {
+/* f_eem_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(eem);
- /* control interface label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- eem_string_defs[0].id = status;
- eem_intf.iInterface = status;
+/* f_eem_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
+
+/* f_eem_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
+
+/* f_eem_opts_qmult */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
+
+/* f_eem_opts_ifname */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
+
+static struct configfs_attribute *eem_attrs[] = {
+ &f_eem_opts_dev_addr.attr,
+ &f_eem_opts_host_addr.attr,
+ &f_eem_opts_qmult.attr,
+ &f_eem_opts_ifname.attr,
+ NULL,
+};
+
+static struct config_item_type eem_func_type = {
+ .ct_item_ops = &eem_item_ops,
+ .ct_attrs = eem_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void eem_free_inst(struct usb_function_instance *f)
+{
+ struct f_eem_opts *opts;
+
+ opts = container_of(f, struct f_eem_opts, func_inst);
+ if (opts->bound)
+ gether_cleanup(netdev_priv(opts->net));
+ else
+ free_netdev(opts->net);
+ kfree(opts);
+}
+
+static struct usb_function_instance *eem_alloc_inst(void)
+{
+ struct f_eem_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = eem_free_inst;
+ opts->net = gether_setup_default();
+ if (IS_ERR(opts->net)) {
+ struct net_device *net = opts->net;
+ kfree(opts);
+ return ERR_CAST(net);
}
+ config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
+
+ return &opts->func_inst;
+}
+
+static void eem_free(struct usb_function *f)
+{
+ struct f_eem *eem;
+ struct f_eem_opts *opts;
+
+ eem = func_to_eem(f);
+ opts = container_of(f->fi, struct f_eem_opts, func_inst);
+ kfree(eem);
+ mutex_lock(&opts->lock);
+ opts->refcnt--;
+ mutex_unlock(&opts->lock);
+}
+
+static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ DBG(c->cdev, "eem unbind\n");
+
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *eem_alloc(struct usb_function_instance *fi)
+{
+ struct f_eem *eem;
+ struct f_eem_opts *opts;
+
/* allocate and initialize one new instance */
- eem = kzalloc(sizeof *eem, GFP_KERNEL);
+ eem = kzalloc(sizeof(*eem), GFP_KERNEL);
if (!eem)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+ opts = container_of(fi, struct f_eem_opts, func_inst);
+ mutex_lock(&opts->lock);
+ opts->refcnt++;
+
+ eem->port.ioport = netdev_priv(opts->net);
+ mutex_unlock(&opts->lock);
eem->port.cdc_filter = DEFAULT_FILTER;
eem->port.func.name = "cdc_eem";
- eem->port.func.strings = eem_strings;
/* descriptors are per-instance copies */
eem->port.func.bind = eem_bind;
eem->port.func.unbind = eem_unbind;
eem->port.func.set_alt = eem_set_alt;
eem->port.func.setup = eem_setup;
eem->port.func.disable = eem_disable;
+ eem->port.func.free_func = eem_free;
eem->port.wrap = eem_wrap;
eem->port.unwrap = eem_unwrap;
eem->port.header_len = EEM_HLEN;
- status = usb_add_function(c, &eem->port.func);
- if (status)
- kfree(eem);
- return status;
+ return &eem->port.func;
}
+DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index f63dc6c150d..8598c27c7d4 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -2,7 +2,7 @@
* f_fs.c -- user mode file system API for USB composite function controllers
*
* Copyright (C) 2010 Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * Author: Michal Nazarewicz <mina86@mina86.com>
*
* Based on inode.c (GadgetFS) which was:
* Copyright (C) 2003-2004 David Brownell
@@ -21,214 +21,22 @@
#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>
-#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
-
-
-/* Debugging ****************************************************************/
-
-#ifdef VERBOSE_DEBUG
-# define pr_vdebug pr_debug
-# define ffs_dump_mem(prefix, ptr, len) \
- print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
-#else
-# define pr_vdebug(...) do { } while (0)
-# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define ENTER() pr_vdebug("%s()\n", __func__)
-
-
-/* The data structure and setup file ****************************************/
-
-enum ffs_state {
- /*
- * Waiting for descriptors and strings.
- *
- * In this state no open(2), read(2) or write(2) on epfiles
- * may succeed (which should not be the problem as there
- * should be no such files opened in the first place).
- */
- FFS_READ_DESCRIPTORS,
- FFS_READ_STRINGS,
-
- /*
- * We've got descriptors and strings. We are or have called
- * functionfs_ready_callback(). functionfs_bind() may have
- * been called but we don't know.
- *
- * This is the only state in which operations on epfiles may
- * succeed.
- */
- FFS_ACTIVE,
-
- /*
- * All endpoints have been closed. This state is also set if
- * we encounter an unrecoverable error. The only
- * unrecoverable error is situation when after reading strings
- * from user space we fail to initialise epfiles or
- * functionfs_ready_callback() returns with error (<0).
- *
- * In this state no open(2), read(2) or write(2) (both on ep0
- * as well as epfile) may succeed (at this point epfiles are
- * unlinked and all closed so this is not a problem; ep0 is
- * also closed but ep0 file exists and so open(2) on ep0 must
- * fail).
- */
- FFS_CLOSING
-};
-
-
-enum ffs_setup_state {
- /* There is no setup request pending. */
- FFS_NO_SETUP,
- /*
- * User has read events and there was a setup request event
- * there. The next read/write on ep0 will handle the
- * request.
- */
- FFS_SETUP_PENDING,
- /*
- * There was event pending but before user space handled it
- * some other event was introduced which canceled existing
- * setup. If this state is set read/write on ep0 return
- * -EIDRM. This state is only set when adding event.
- */
- FFS_SETUP_CANCELED
-};
-
-
+#include "u_fs.h"
+#include "u_f.h"
+#include "configfs.h"
-struct ffs_epfile;
-struct ffs_function;
-
-struct ffs_data {
- struct usb_gadget *gadget;
-
- /*
- * Protect access read/write operations, only one read/write
- * at a time. As a consequence protects ep0req and company.
- * While setup request is being processed (queued) this is
- * held.
- */
- struct mutex mutex;
-
- /*
- * Protect access to endpoint related structures (basically
- * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
- * endpoint zero.
- */
- spinlock_t eps_lock;
-
- /*
- * XXX REVISIT do we need our own request? Since we are not
- * handling setup requests immediately user space may be so
- * slow that another setup will be sent to the gadget but this
- * time not to us but another function and then there could be
- * a race. Is that the case? Or maybe we can use cdev->req
- * after all, maybe we just need some spinlock for that?
- */
- struct usb_request *ep0req; /* P: mutex */
- struct completion ep0req_completion; /* P: mutex */
- int ep0req_status; /* P: mutex */
-
- /* reference counter */
- atomic_t ref;
- /* how many files are opened (EP0 and others) */
- atomic_t opened;
-
- /* EP0 state */
- enum ffs_state state;
-
- /*
- * Possible transitions:
- * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
- * happens only in ep0 read which is P: mutex
- * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
- * happens only in ep0 i/o which is P: mutex
- * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
- * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
- */
- enum ffs_setup_state setup_state;
-
-#define FFS_SETUP_STATE(ffs) \
- ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
- FFS_SETUP_CANCELED, FFS_NO_SETUP))
-
- /* Events & such. */
- struct {
- u8 types[4];
- unsigned short count;
- /* XXX REVISIT need to update it in some places, or do we? */
- unsigned short can_stall;
- struct usb_ctrlrequest setup;
-
- wait_queue_head_t waitq;
- } ev; /* the whole structure, P: ev.waitq.lock */
-
- /* Flags */
- unsigned long flags;
-#define FFS_FL_CALL_CLOSED_CALLBACK 0
-#define FFS_FL_BOUND 1
-
- /* Active function */
- struct ffs_function *func;
-
- /*
- * Device name, write once when file system is mounted.
- * Intended for user to read if she wants.
- */
- const char *dev_name;
- /* Private data for our user (ie. gadget). Managed by user. */
- void *private_data;
-
- /* filled by __ffs_data_got_descs() */
- /*
- * Real descriptors are 16 bytes after raw_descs (so you need
- * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
- * first full speed descriptor). raw_descs_length and
- * raw_fs_descs_length do not have those 16 bytes added.
- */
- const void *raw_descs;
- unsigned raw_descs_length;
- unsigned raw_fs_descs_length;
- unsigned fs_descs_count;
- unsigned hs_descs_count;
-
- unsigned short strings_count;
- unsigned short interfaces_count;
- unsigned short eps_count;
- unsigned short _pad1;
-
- /* filled by __ffs_data_got_strings() */
- /* ids in stringtabs are set in functionfs_bind() */
- const void *raw_strings;
- struct usb_gadget_strings **stringtabs;
-
- /*
- * File system's super block, write once when file system is
- * mounted.
- */
- struct super_block *sb;
-
- /* File permissions, written once when fs is mounted */
- struct ffs_file_perms {
- umode_t mode;
- uid_t uid;
- gid_t gid;
- } file_perms;
-
- /*
- * The endpoint files, filled by ffs_epfiles_create(),
- * destroyed by ffs_epfiles_destroy().
- */
- struct ffs_epfile *epfiles;
-};
+#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
/* Reference counter handling */
static void ffs_data_get(struct ffs_data *ffs);
@@ -269,15 +77,20 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
return container_of(f, struct ffs_function, function);
}
-static void ffs_func_free(struct ffs_function *func);
+
+static inline enum ffs_setup_state
+ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
+{
+ return (enum ffs_setup_state)
+ cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
+}
+
static void ffs_func_eps_disable(struct ffs_function *func);
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
static int ffs_func_bind(struct usb_configuration *,
struct usb_function *);
-static void ffs_func_unbind(struct usb_configuration *,
- struct usb_function *);
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
@@ -296,8 +109,8 @@ struct ffs_ep {
struct usb_ep *ep; /* P: ffs->eps_lock */
struct usb_request *req; /* P: epfile->mutex */
- /* [0]: full speed, [1]: high speed */
- struct usb_endpoint_descriptor *descs[2];
+ /* [0]: full speed, [1]: high speed, [2]: super speed */
+ struct usb_endpoint_descriptor *descs[3];
u8 num;
@@ -322,6 +135,25 @@ struct ffs_epfile {
unsigned char _pad;
};
+/* ffs_io_data structure ***************************************************/
+
+struct ffs_io_data {
+ bool aio;
+ bool read;
+
+ struct kiocb *kiocb;
+ const struct iovec *iovec;
+ unsigned long nr_segs;
+ char __user *buf;
+ size_t len;
+
+ struct mm_struct *mm;
+ struct work_struct work;
+
+ struct usb_ep *ep;
+ struct usb_request *req;
+};
+
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
@@ -330,12 +162,25 @@ 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)
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
__attribute__((warn_unused_result, nonnull));
@@ -368,7 +213,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
if (req->buf == NULL)
req->buf = (void *)0xDEADBABE;
- INIT_COMPLETION(ffs->ep0req_completion);
+ reinit_completion(&ffs->ep0req_completion);
ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
if (unlikely(ret < 0))
@@ -381,7 +226,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
}
ffs->setup_state = FFS_NO_SETUP;
- return ffs->ep0req_status;
+ return req->status ? req->status : req->actual;
}
static int __ffs_ep0_stall(struct ffs_data *ffs)
@@ -407,7 +252,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ENTER();
/* Fast check if setup was canceled */
- if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
return -EIDRM;
/* Acquire mutex */
@@ -455,7 +300,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ffs->state = FFS_ACTIVE;
mutex_unlock(&ffs->mutex);
- ret = functionfs_ready_callback(ffs);
+ ret = ffs_ready(ffs);
if (unlikely(ret < 0)) {
ffs->state = FFS_CLOSING;
return ret;
@@ -473,8 +318,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
* rather then _irqsave
*/
spin_lock_irq(&ffs->ev.waitq.lock);
- switch (FFS_SETUP_STATE(ffs)) {
- case FFS_SETUP_CANCELED:
+ switch (ffs_setup_state_clear_cancelled(ffs)) {
+ case FFS_SETUP_CANCELLED:
ret = -EIDRM;
goto done_spin;
@@ -509,7 +354,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
/*
* We are guaranteed to be still in FFS_ACTIVE state
* but the state of setup could have changed from
- * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+ * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
* to check for that. If that happened we copied data
* from user space in vain but it's unlikely.
*
@@ -518,7 +363,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
* transition can be performed and it's protected by
* mutex.
*/
- if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ if (ffs_setup_state_clear_cancelled(ffs) ==
+ FFS_SETUP_CANCELLED) {
ret = -EIDRM;
done_spin:
spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -584,7 +430,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
ENTER();
/* Fast check if setup was canceled */
- if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
return -EIDRM;
/* Acquire mutex */
@@ -604,8 +450,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
*/
spin_lock_irq(&ffs->ev.waitq.lock);
- switch (FFS_SETUP_STATE(ffs)) {
- case FFS_SETUP_CANCELED:
+ switch (ffs_setup_state_clear_cancelled(ffs)) {
+ case FFS_SETUP_CANCELLED:
ret = -EIDRM;
break;
@@ -652,7 +498,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
spin_lock_irq(&ffs->ev.waitq.lock);
/* See ffs_ep0_write() */
- if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ if (ffs_setup_state_clear_cancelled(ffs) ==
+ FFS_SETUP_CANCELLED) {
ret = -EIDRM;
break;
}
@@ -712,7 +559,7 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
if (code == FUNCTIONFS_INTERFACE_REVMAP) {
struct ffs_function *func = ffs->func;
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
- } else if (gadget->ops->ioctl) {
+ } else if (gadget && gadget->ops->ioctl) {
ret = gadget->ops->ioctl(gadget, code, value);
} else {
ret = -ENOTTY;
@@ -721,8 +568,46 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
return ret;
}
+static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
+{
+ struct ffs_data *ffs = file->private_data;
+ unsigned int mask = POLLWRNORM;
+ int ret;
+
+ poll_wait(file, &ffs->ev.waitq, wait);
+
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return mask;
+
+ switch (ffs->state) {
+ case FFS_READ_DESCRIPTORS:
+ case FFS_READ_STRINGS:
+ mask |= POLLOUT;
+ break;
+
+ case FFS_ACTIVE:
+ switch (ffs->setup_state) {
+ case FFS_NO_SETUP:
+ if (ffs->ev.count)
+ mask |= POLLIN;
+ break;
+
+ case FFS_SETUP_PENDING:
+ case FFS_SETUP_CANCELLED:
+ mask |= (POLLIN | POLLOUT);
+ break;
+ }
+ case FFS_CLOSING:
+ break;
+ }
+
+ mutex_unlock(&ffs->mutex);
+
+ return mask;
+}
+
static const struct file_operations ffs_ep0_operations = {
- .owner = THIS_MODULE,
.llseek = no_llseek,
.open = ffs_ep0_open,
@@ -730,6 +615,7 @@ static const struct file_operations ffs_ep0_operations = {
.read = ffs_ep0_read,
.release = ffs_ep0_release,
.unlocked_ioctl = ffs_ep0_ioctl,
+ .poll = ffs_ep0_poll,
};
@@ -745,114 +631,226 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
}
}
-static ssize_t ffs_epfile_io(struct file *file,
- char __user *buf, size_t len, int read)
+static void ffs_user_copy_worker(struct work_struct *work)
+{
+ struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
+ work);
+ int ret = io_data->req->status ? io_data->req->status :
+ io_data->req->actual;
+
+ if (io_data->read && ret > 0) {
+ int i;
+ size_t pos = 0;
+ use_mm(io_data->mm);
+ for (i = 0; i < io_data->nr_segs; i++) {
+ if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
+ &io_data->buf[pos],
+ io_data->iovec[i].iov_len))) {
+ ret = -EFAULT;
+ break;
+ }
+ pos += io_data->iovec[i].iov_len;
+ }
+ unuse_mm(io_data->mm);
+ }
+
+ aio_complete(io_data->kiocb, ret, ret);
+
+ usb_ep_free_request(io_data->ep, io_data->req);
+
+ io_data->kiocb->private = NULL;
+ if (io_data->read)
+ kfree(io_data->iovec);
+ kfree(io_data->buf);
+ kfree(io_data);
+}
+
+static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
+ struct usb_request *req)
+{
+ struct ffs_io_data *io_data = req->context;
+
+ ENTER();
+
+ INIT_WORK(&io_data->work, ffs_user_copy_worker);
+ schedule_work(&io_data->work);
+}
+
+static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
{
struct ffs_epfile *epfile = file->private_data;
struct ffs_ep *ep;
char *data = NULL;
- ssize_t ret;
+ ssize_t ret, data_len;
int halt;
- goto first_try;
- do {
- spin_unlock_irq(&epfile->ffs->eps_lock);
- mutex_unlock(&epfile->mutex);
+ /* Are we still active? */
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+ ret = -ENODEV;
+ goto error;
+ }
-first_try:
- /* Are we still active? */
- if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
- ret = -ENODEV;
+ /* Wait for endpoint to be enabled */
+ ep = epfile->ep;
+ if (!ep) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
goto error;
}
- /* Wait for endpoint to be enabled */
- ep = epfile->ep;
- if (!ep) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- goto error;
- }
-
- if (wait_event_interruptible(epfile->wait,
- (ep = epfile->ep))) {
- ret = -EINTR;
- goto error;
- }
- }
-
- /* Do we halt? */
- halt = !read == !epfile->in;
- if (halt && epfile->isoc) {
- ret = -EINVAL;
+ ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
+ if (ret) {
+ ret = -EINTR;
goto error;
}
+ }
+
+ /* 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;
- /* Allocate & copy */
- if (!halt && !data) {
- data = kzalloc(len, GFP_KERNEL);
- if (unlikely(!data))
- return -ENOMEM;
+ 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);
- if (!read &&
- unlikely(__copy_from_user(data, buf, len))) {
+ data = kmalloc(data_len, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+ if (io_data->aio && !io_data->read) {
+ int i;
+ size_t pos = 0;
+ for (i = 0; i < io_data->nr_segs; i++) {
+ if (unlikely(copy_from_user(&data[pos],
+ io_data->iovec[i].iov_base,
+ io_data->iovec[i].iov_len))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ pos += io_data->iovec[i].iov_len;
+ }
+ } else {
+ if (!io_data->read &&
+ unlikely(__copy_from_user(data, io_data->buf,
+ io_data->len))) {
ret = -EFAULT;
goto error;
}
}
+ }
- /* We will be using request */
- ret = ffs_mutex_lock(&epfile->mutex,
- file->f_flags & O_NONBLOCK);
- if (unlikely(ret))
- goto error;
-
- /*
- * We're called from user space, we can use _irq rather then
- * _irqsave
- */
- spin_lock_irq(&epfile->ffs->eps_lock);
+ /* We will be using request */
+ ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret))
+ goto error;
- /*
- * While we were acquiring mutex endpoint got disabled
- * or changed?
- */
- } while (unlikely(epfile->ep != ep));
+ spin_lock_irq(&epfile->ffs->eps_lock);
- /* Halt */
- if (unlikely(halt)) {
+ if (epfile->ep != ep) {
+ /* In the meantime, endpoint got disabled or changed. */
+ ret = -ESHUTDOWN;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ } else if (halt) {
+ /* Halt */
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
usb_ep_set_halt(ep->ep);
spin_unlock_irq(&epfile->ffs->eps_lock);
ret = -EBADMSG;
} else {
/* Fire the request */
- DECLARE_COMPLETION_ONSTACK(done);
+ struct usb_request *req;
- struct usb_request *req = ep->req;
- req->context = &done;
- req->complete = ffs_epfile_io_complete;
- req->buf = data;
- req->length = len;
+ if (io_data->aio) {
+ req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
+ if (unlikely(!req))
+ goto error_lock;
- ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+ req->buf = data;
+ req->length = io_data->len;
- spin_unlock_irq(&epfile->ffs->eps_lock);
+ io_data->buf = data;
+ io_data->ep = ep->ep;
+ io_data->req = req;
- if (unlikely(ret < 0)) {
- /* nop */
- } else if (unlikely(wait_for_completion_interruptible(&done))) {
- ret = -EINTR;
- usb_ep_dequeue(ep->ep, req);
+ req->context = io_data;
+ req->complete = ffs_epfile_async_io_complete;
+
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+ if (unlikely(ret)) {
+ usb_ep_free_request(ep->ep, req);
+ goto error_lock;
+ }
+ ret = -EIOCBQUEUED;
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
} else {
- ret = ep->status;
- if (read && ret > 0 &&
- unlikely(copy_to_user(buf, data, ret)))
- ret = -EFAULT;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ req = ep->req;
+ req->buf = data;
+ req->length = io_data->len;
+
+ req->context = &done;
+ req->complete = ffs_epfile_io_complete;
+
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ if (unlikely(ret < 0)) {
+ /* nop */
+ } else if (unlikely(
+ wait_for_completion_interruptible(&done))) {
+ ret = -EINTR;
+ usb_ep_dequeue(ep->ep, req);
+ } else {
+ /*
+ * XXX We may end up silently droping data
+ * here. Since data_len (i.e. req->length) may
+ * be bigger than len (after being rounded up
+ * to maxpacketsize), we may end up with more
+ * data then user space has space for.
+ */
+ ret = ep->status;
+ if (io_data->read && ret > 0) {
+ ret = min_t(size_t, ret, io_data->len);
+
+ if (unlikely(copy_to_user(io_data->buf,
+ data, ret)))
+ ret = -EFAULT;
+ }
+ }
+ kfree(data);
}
}
mutex_unlock(&epfile->mutex);
+ return ret;
+
+error_lock:
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ mutex_unlock(&epfile->mutex);
error:
kfree(data);
return ret;
@@ -862,17 +860,31 @@ static ssize_t
ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
loff_t *ptr)
{
+ struct ffs_io_data io_data;
+
ENTER();
- return ffs_epfile_io(file, (char __user *)buf, len, 0);
+ io_data.aio = false;
+ io_data.read = false;
+ io_data.buf = (char * __user)buf;
+ io_data.len = len;
+
+ return ffs_epfile_io(file, &io_data);
}
static ssize_t
ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
{
+ struct ffs_io_data io_data;
+
ENTER();
- return ffs_epfile_io(file, buf, len, 1);
+ io_data.aio = false;
+ io_data.read = true;
+ io_data.buf = buf;
+ io_data.len = len;
+
+ return ffs_epfile_io(file, &io_data);
}
static int
@@ -891,6 +903,89 @@ ffs_epfile_open(struct inode *inode, struct file *file)
return 0;
}
+static int ffs_aio_cancel(struct kiocb *kiocb)
+{
+ struct ffs_io_data *io_data = kiocb->private;
+ struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
+ int value;
+
+ ENTER();
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+
+ if (likely(io_data && io_data->ep && io_data->req))
+ value = usb_ep_dequeue(io_data->ep, io_data->req);
+ else
+ value = -EINVAL;
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ return value;
+}
+
+static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
+ const struct iovec *iovec,
+ unsigned long nr_segs, loff_t loff)
+{
+ struct ffs_io_data *io_data;
+
+ ENTER();
+
+ io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+ if (unlikely(!io_data))
+ return -ENOMEM;
+
+ io_data->aio = true;
+ io_data->read = false;
+ io_data->kiocb = kiocb;
+ io_data->iovec = iovec;
+ io_data->nr_segs = nr_segs;
+ io_data->len = kiocb->ki_nbytes;
+ io_data->mm = current->mm;
+
+ kiocb->private = io_data;
+
+ kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+ return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
+static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
+ const struct iovec *iovec,
+ unsigned long nr_segs, loff_t loff)
+{
+ struct ffs_io_data *io_data;
+ struct iovec *iovec_copy;
+
+ ENTER();
+
+ iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
+ if (unlikely(!iovec_copy))
+ return -ENOMEM;
+
+ memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
+
+ io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+ if (unlikely(!io_data)) {
+ kfree(iovec_copy);
+ return -ENOMEM;
+ }
+
+ io_data->aio = true;
+ io_data->read = true;
+ io_data->kiocb = kiocb;
+ io_data->iovec = iovec_copy;
+ io_data->nr_segs = nr_segs;
+ io_data->len = kiocb->ki_nbytes;
+ io_data->mm = current->mm;
+
+ kiocb->private = io_data;
+
+ kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+ return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
static int
ffs_epfile_release(struct inode *inode, struct file *file)
{
@@ -942,12 +1037,13 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
}
static const struct file_operations ffs_epfile_operations = {
- .owner = THIS_MODULE,
.llseek = no_llseek,
.open = ffs_epfile_open,
.write = ffs_epfile_write,
.read = ffs_epfile_read,
+ .aio_write = ffs_epfile_aio_write,
+ .aio_read = ffs_epfile_aio_read,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
};
@@ -1031,25 +1127,19 @@ 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;
+ struct ffs_data *ffs = data->ffs_data;
ENTER();
- /* Initialise data */
- ffs = ffs_data_new();
- if (unlikely(!ffs))
- goto Enomem;
-
ffs->sb = sb;
- ffs->dev_name = data->dev_name;
- ffs->file_perms = data->perms;
-
+ data->ffs_data = NULL;
sb->s_fs_info = ffs;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -1063,23 +1153,16 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
&simple_dir_operations,
&simple_dir_inode_operations,
&data->perms);
- if (unlikely(!inode))
- goto Enomem;
- sb->s_root = d_alloc_root(inode);
- if (unlikely(!sb->s_root)) {
- iput(inode);
- goto Enomem;
- }
+ 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)))
- goto Enomem;
+ return -ENOMEM;
return 0;
-
-Enomem:
- return -ENOMEM;
}
static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
@@ -1090,8 +1173,8 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
return 0;
for (;;) {
- char *end, *eq, *comma;
unsigned long value;
+ char *eq, *comma;
/* Option limit */
comma = strchr(opts, ',');
@@ -1107,8 +1190,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
*eq = 0;
/* Parse value */
- value = simple_strtoul(eq + 1, &end, 0);
- if (unlikely(*end != ',' && *end != 0)) {
+ if (kstrtoul(eq + 1, 0, &value)) {
pr_err("%s: invalid value: %s\n", opts, eq + 1);
return -EINVAL;
}
@@ -1134,12 +1216,21 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
break;
case 3:
- if (!memcmp(opts, "uid", 3))
- data->perms.uid = value;
- else if (!memcmp(opts, "gid", 3))
- data->perms.gid = value;
- else
+ 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:
@@ -1166,25 +1257,47 @@ ffs_fs_mount(struct file_system_type *t, int flags,
struct ffs_sb_fill_data data = {
.perms = {
.mode = S_IFREG | 0600,
- .uid = 0,
- .gid = 0
+ .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 = functionfs_check_dev_callback(dev_name);
- if (unlikely(ret < 0))
- return ERR_PTR(ret);
-
ret = ffs_fs_parse_opts(&data, opts);
if (unlikely(ret < 0))
return ERR_PTR(ret);
- data.dev_name = dev_name;
- return mount_single(t, flags, &data, ffs_sb_fill);
+ 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
@@ -1193,8 +1306,10 @@ ffs_fs_kill_sb(struct super_block *sb)
ENTER();
kill_litter_super(sb);
- if (sb->s_fs_info)
+ 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 = {
@@ -1203,6 +1318,7 @@ static struct file_system_type ffs_fs_type = {
.mount = ffs_fs_mount,
.kill_sb = ffs_fs_kill_sb,
};
+MODULE_ALIAS_FS("functionfs");
/* Driver's main init/cleanup functions *************************************/
@@ -1258,10 +1374,9 @@ static void ffs_data_put(struct ffs_data *ffs)
if (unlikely(atomic_dec_and_test(&ffs->ref))) {
pr_info("%s(): freeing\n", __func__);
ffs_data_clear(ffs);
- BUG_ON(mutex_is_locked(&ffs->mutex) ||
- spin_is_locked(&ffs->ev.waitq.lock) ||
- waitqueue_active(&ffs->ev.waitq) ||
+ BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
waitqueue_active(&ffs->ep0req_completion.wait));
+ kfree(ffs->dev_name);
kfree(ffs);
}
}
@@ -1282,7 +1397,7 @@ static struct ffs_data *ffs_data_new(void)
{
struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
if (unlikely(!ffs))
- return 0;
+ return NULL;
ENTER();
@@ -1305,14 +1420,14 @@ static void ffs_data_clear(struct ffs_data *ffs)
ENTER();
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
- functionfs_closed_callback(ffs);
+ ffs_closed(ffs);
BUG_ON(ffs->gadget);
if (ffs->epfiles)
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
- kfree(ffs->raw_descs);
+ kfree(ffs->raw_descs_data);
kfree(ffs->raw_strings);
kfree(ffs->stringtabs);
}
@@ -1324,14 +1439,15 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs_data_clear(ffs);
ffs->epfiles = NULL;
+ ffs->raw_descs_data = NULL;
ffs->raw_descs = NULL;
ffs->raw_strings = NULL;
ffs->stringtabs = NULL;
ffs->raw_descs_length = 0;
- ffs->raw_fs_descs_length = 0;
ffs->fs_descs_count = 0;
ffs->hs_descs_count = 0;
+ ffs->ss_descs_count = 0;
ffs->strings_count = 0;
ffs->interfaces_count = 0;
@@ -1367,11 +1483,13 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
ffs->ep0req->context = ffs;
lang = ffs->stringtabs;
- for (lang = ffs->stringtabs; *lang; ++lang) {
- struct usb_string *str = (*lang)->strings;
- int id = first_id;
- for (; str->s; ++id, ++str)
- str->id = id;
+ if (lang) {
+ for (; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
+ }
}
ffs->gadget = cdev->gadget;
@@ -1387,6 +1505,7 @@ static void functionfs_unbind(struct ffs_data *ffs)
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);
}
}
@@ -1440,57 +1559,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
kfree(epfiles);
}
-static int functionfs_bind_config(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct ffs_data *ffs)
-{
- struct ffs_function *func;
- int ret;
-
- ENTER();
-
- func = kzalloc(sizeof *func, GFP_KERNEL);
- if (unlikely(!func))
- return -ENOMEM;
-
- func->function.name = "Function FS Gadget";
- func->function.strings = ffs->stringtabs;
-
- func->function.bind = ffs_func_bind;
- func->function.unbind = ffs_func_unbind;
- func->function.set_alt = ffs_func_set_alt;
- func->function.disable = ffs_func_disable;
- func->function.setup = ffs_func_setup;
- func->function.suspend = ffs_func_suspend;
- func->function.resume = ffs_func_resume;
-
- func->conf = c;
- func->gadget = cdev->gadget;
- func->ffs = ffs;
- ffs_data_get(ffs);
-
- ret = usb_add_function(c, &func->function);
- if (unlikely(ret))
- ffs_func_free(func);
-
- return ret;
-}
-
-static void ffs_func_free(struct ffs_function *func)
-{
- ENTER();
-
- ffs_data_put(func->ffs);
-
- kfree(func->eps);
- /*
- * eps and interfaces_nums are allocated in the same chunk so
- * only one free is required. Descriptors are also allocated
- * in the same chunk.
- */
-
- kfree(func);
-}
static void ffs_func_eps_disable(struct ffs_function *func)
{
@@ -1524,7 +1592,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
spin_lock_irqsave(&func->ffs->eps_lock, flags);
do {
struct usb_endpoint_descriptor *ds;
- ds = ep->descs[ep->descs[1] ? 1 : 0];
+ int desc_idx;
+
+ if (ffs->gadget->speed == USB_SPEED_SUPER)
+ desc_idx = 2;
+ else if (ffs->gadget->speed == USB_SPEED_HIGH)
+ desc_idx = 1;
+ else
+ desc_idx = 0;
+
+ /* fall-back to lower speed if desc missing for current speed */
+ do {
+ ds = ep->descs[desc_idx];
+ } while (!ds && --desc_idx >= 0);
+
+ if (!ds) {
+ ret = -EINVAL;
+ break;
+ }
ep->ep->driver_data = ep;
ep->ep->desc = ds;
@@ -1638,6 +1723,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
}
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;
@@ -1653,6 +1744,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
}
break;
+ case USB_DT_SS_ENDPOINT_COMP:
+ pr_vdebug("EP SS companion descriptor\n");
+ if (length != sizeof(struct usb_ss_ep_comp_descriptor))
+ goto inv_length;
+ break;
+
case USB_DT_OTHER_SPEED_CONFIG:
case USB_DT_INTERFACE_POWER:
case USB_DT_DEBUG:
@@ -1763,60 +1860,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len)
{
- unsigned fs_count, hs_count;
- int fs_len, ret = -EINVAL;
- char *data = _data;
+ char *data = _data, *raw_descs;
+ unsigned counts[3], flags;
+ int ret = -EINVAL, i;
ENTER();
- if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
- get_unaligned_le32(data + 4) != len))
+ if (get_unaligned_le32(data + 4) != len)
goto error;
- fs_count = get_unaligned_le32(data + 8);
- hs_count = get_unaligned_le32(data + 12);
- if (!fs_count && !hs_count)
- goto einval;
-
- data += 16;
- len -= 16;
-
- if (likely(fs_count)) {
- fs_len = ffs_do_descs(fs_count, data, len,
- __ffs_data_do_entity, ffs);
- if (unlikely(fs_len < 0)) {
- ret = fs_len;
+ switch (get_unaligned_le32(data)) {
+ case FUNCTIONFS_DESCRIPTORS_MAGIC:
+ flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+ data += 8;
+ len -= 8;
+ break;
+ case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+ flags = get_unaligned_le32(data + 8);
+ if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC)) {
+ ret = -ENOSYS;
goto error;
}
+ data += 12;
+ len -= 12;
+ break;
+ default:
+ goto error;
+ }
- data += fs_len;
- len -= fs_len;
- } else {
- fs_len = 0;
+ /* Read fs_count, hs_count and ss_count (if present) */
+ for (i = 0; i < 3; ++i) {
+ if (!(flags & (1 << i))) {
+ counts[i] = 0;
+ } else if (len < 4) {
+ goto error;
+ } else {
+ counts[i] = get_unaligned_le32(data);
+ data += 4;
+ len -= 4;
+ }
}
- if (likely(hs_count)) {
- ret = ffs_do_descs(hs_count, data, len,
+ /* Read descriptors */
+ raw_descs = data;
+ for (i = 0; i < 3; ++i) {
+ if (!counts[i])
+ continue;
+ ret = ffs_do_descs(counts[i], data, len,
__ffs_data_do_entity, ffs);
- if (unlikely(ret < 0))
+ if (ret < 0)
goto error;
- } else {
- ret = 0;
+ data += ret;
+ len -= ret;
}
- if (unlikely(len != ret))
- goto einval;
+ if (raw_descs == data || len) {
+ ret = -EINVAL;
+ goto error;
+ }
- ffs->raw_fs_descs_length = fs_len;
- ffs->raw_descs_length = fs_len + ret;
- ffs->raw_descs = _data;
- ffs->fs_descs_count = fs_count;
- ffs->hs_descs_count = hs_count;
+ ffs->raw_descs_data = _data;
+ ffs->raw_descs = raw_descs;
+ ffs->raw_descs_length = data - raw_descs;
+ ffs->fs_descs_count = counts[0];
+ ffs->hs_descs_count = counts[1];
+ ffs->ss_descs_count = counts[2];
return 0;
-einval:
- ret = -EINVAL;
error:
kfree(_data);
return ret;
@@ -1858,30 +1971,34 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
/* Allocate everything in one chunk so there's less maintenance. */
{
- struct {
- struct usb_gadget_strings *stringtabs[lang_count + 1];
- struct usb_gadget_strings stringtab[lang_count];
- struct usb_string strings[lang_count*(needed_count+1)];
- } *d;
unsigned i = 0;
+ vla_group(d);
+ vla_item(d, struct usb_gadget_strings *, stringtabs,
+ lang_count + 1);
+ vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
+ vla_item(d, struct usb_string, strings,
+ lang_count*(needed_count+1));
+
+ char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
- d = kmalloc(sizeof *d, GFP_KERNEL);
- if (unlikely(!d)) {
+ if (unlikely(!vlabuf)) {
kfree(_data);
return -ENOMEM;
}
- stringtabs = d->stringtabs;
- t = d->stringtab;
+ /* Initialize the VLA pointers */
+ stringtabs = vla_ptr(vlabuf, d, stringtabs);
+ t = vla_ptr(vlabuf, d, stringtab);
i = lang_count;
do {
*stringtabs++ = t++;
} while (--i);
*stringtabs = NULL;
- stringtabs = d->stringtabs;
- t = d->stringtab;
- s = d->strings;
+ /* stringtabs = vlabuf = d_stringtabs for later kfree */
+ stringtabs = vla_ptr(vlabuf, d, stringtabs);
+ t = vla_ptr(vlabuf, d, stringtab);
+ s = vla_ptr(vlabuf, d, strings);
strings = s;
}
@@ -1969,7 +2086,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
* the source does nothing.
*/
if (ffs->setup_state == FFS_SETUP_PENDING)
- ffs->setup_state = FFS_SETUP_CANCELED;
+ ffs->setup_state = FFS_SETUP_CANCELLED;
switch (type) {
case FUNCTIONFS_RESUME:
@@ -2030,21 +2147,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_endpoint_descriptor *ds = (void *)desc;
struct ffs_function *func = priv;
struct ffs_ep *ffs_ep;
-
- /*
- * If hs_descriptors is not NULL then we are reading hs
- * descriptors now
- */
- const int isHS = func->function.hs_descriptors != NULL;
- unsigned idx;
+ unsigned ep_desc_id, idx;
+ static const char *speed_names[] = { "full", "high", "super" };
if (type != FFS_DESCRIPTOR)
return 0;
- if (isHS)
+ /*
+ * If ss_descriptors is not NULL, we are reading super speed
+ * descriptors; if hs_descriptors is not NULL, we are reading high
+ * speed descriptors; otherwise, we are reading full speed
+ * descriptors.
+ */
+ if (func->function.ss_descriptors) {
+ ep_desc_id = 2;
+ func->function.ss_descriptors[(long)valuep] = desc;
+ } else if (func->function.hs_descriptors) {
+ ep_desc_id = 1;
func->function.hs_descriptors[(long)valuep] = desc;
- else
- func->function.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;
@@ -2052,13 +2176,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
ffs_ep = func->eps + idx;
- if (unlikely(ffs_ep->descs[isHS])) {
- pr_vdebug("two %sspeed descriptors for EP %d\n",
- isHS ? "high" : "full",
+ if (unlikely(ffs_ep->descs[ep_desc_id])) {
+ pr_err("two %sspeed descriptors for EP %d\n",
+ speed_names[ep_desc_id],
ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
return -EINVAL;
}
- ffs_ep->descs[isHS] = ds;
+ ffs_ep->descs[ep_desc_id] = ds;
ffs_dump_mem(": Original ep desc", ds, ds->bLength);
if (ffs_ep->ep) {
@@ -2144,8 +2268,57 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
return 0;
}
-static int ffs_func_bind(struct usb_configuration *c,
- struct usb_function *f)
+static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
+ struct usb_configuration *c)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct f_fs_opts *ffs_opts =
+ container_of(f->fi, struct f_fs_opts, func_inst);
+ int ret;
+
+ ENTER();
+
+ /*
+ * Legacy gadget triggers binding in functionfs_ready_callback,
+ * which already uses locking; taking the same lock here would
+ * cause a deadlock.
+ *
+ * Configfs-enabled gadgets however do need ffs_dev_lock.
+ */
+ if (!ffs_opts->no_configfs)
+ ffs_dev_lock();
+ ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
+ func->ffs = ffs_opts->dev->ffs_data;
+ if (!ffs_opts->no_configfs)
+ ffs_dev_unlock();
+ if (ret)
+ return ERR_PTR(ret);
+
+ func->conf = c;
+ func->gadget = c->cdev->gadget;
+
+ ffs_data_get(func->ffs);
+
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to ffs_opts->bound access
+ */
+ if (!ffs_opts->refcnt) {
+ ret = functionfs_bind(func->ffs, c->cdev);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+ ffs_opts->refcnt++;
+ func->function.strings = func->ffs->stringtabs;
+
+ return ffs_opts;
+}
+
+static int _ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
@@ -2153,42 +2326,54 @@ static int ffs_func_bind(struct usb_configuration *c,
const int full = !!func->ffs->fs_descs_count;
const int high = gadget_is_dualspeed(func->gadget) &&
func->ffs->hs_descs_count;
+ const int super = gadget_is_superspeed(func->gadget) &&
+ func->ffs->ss_descs_count;
- int ret;
+ int fs_len, hs_len, ret;
/* Make it a single chunk, less management later on */
- struct {
- struct ffs_ep eps[ffs->eps_count];
- struct usb_descriptor_header
- *fs_descs[full ? ffs->fs_descs_count + 1 : 0];
- struct usb_descriptor_header
- *hs_descs[high ? ffs->hs_descs_count + 1 : 0];
- short inums[ffs->interfaces_count];
- char raw_descs[high ? ffs->raw_descs_length
- : ffs->raw_fs_descs_length];
- } *data;
+ vla_group(d);
+ vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
+ vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
+ full ? ffs->fs_descs_count + 1 : 0);
+ vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
+ high ? ffs->hs_descs_count + 1 : 0);
+ vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
+ super ? ffs->ss_descs_count + 1 : 0);
+ vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+ vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
+ char *vlabuf;
ENTER();
- /* Only high speed but not supported by gadget? */
- if (unlikely(!(full | high)))
+ /* Has descriptors only for speeds gadget does not support */
+ if (unlikely(!(full | high | super)))
return -ENOTSUPP;
- /* Allocate */
- data = kmalloc(sizeof *data, GFP_KERNEL);
- if (unlikely(!data))
+ /* Allocate a single chunk, less management later on */
+ vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+ if (unlikely(!vlabuf))
return -ENOMEM;
/* Zero */
- memset(data->eps, 0, sizeof data->eps);
- memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
- memset(data->inums, 0xff, sizeof data->inums);
- for (ret = ffs->eps_count; ret; --ret)
- data->eps[ret].num = -1;
+ memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+ /* Copy descriptors */
+ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
+ ffs->raw_descs_length);
- /* Save pointers */
- func->eps = data->eps;
- func->interfaces_nums = data->inums;
+ memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
+ for (ret = ffs->eps_count; ret; --ret) {
+ struct ffs_ep *ptr;
+
+ ptr = vla_ptr(vlabuf, d, eps);
+ ptr[ret].num = -1;
+ }
+
+ /* Save pointers
+ * d_eps == vlabuf, func->eps used to kfree vlabuf later
+ */
+ func->eps = vla_ptr(vlabuf, d, eps);
+ func->interfaces_nums = vla_ptr(vlabuf, d, inums);
/*
* Go through all the endpoint descriptors and allocate
@@ -2196,23 +2381,41 @@ static int ffs_func_bind(struct usb_configuration *c,
* numbers without worrying that it may be described later on.
*/
if (likely(full)) {
- func->function.descriptors = data->fs_descs;
- ret = ffs_do_descs(ffs->fs_descs_count,
- data->raw_descs,
- sizeof data->raw_descs,
- __ffs_func_bind_do_descs, func);
- if (unlikely(ret < 0))
+ func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
+ fs_len = ffs_do_descs(ffs->fs_descs_count,
+ vla_ptr(vlabuf, d, raw_descs),
+ d_raw_descs__sz,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(fs_len < 0)) {
+ ret = fs_len;
goto error;
+ }
} else {
- ret = 0;
+ fs_len = 0;
}
if (likely(high)) {
- func->function.hs_descriptors = data->hs_descs;
- ret = ffs_do_descs(ffs->hs_descs_count,
- data->raw_descs + ret,
- (sizeof data->raw_descs) - ret,
- __ffs_func_bind_do_descs, func);
+ func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
+ hs_len = ffs_do_descs(ffs->hs_descs_count,
+ vla_ptr(vlabuf, d, raw_descs) + fs_len,
+ d_raw_descs__sz - fs_len,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(hs_len < 0)) {
+ ret = hs_len;
+ goto error;
+ }
+ } else {
+ hs_len = 0;
+ }
+
+ if (likely(super)) {
+ func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
+ ret = ffs_do_descs(ffs->ss_descs_count,
+ vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
+ d_raw_descs__sz - fs_len - hs_len,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(ret < 0))
+ goto error;
}
/*
@@ -2221,8 +2424,9 @@ static int ffs_func_bind(struct usb_configuration *c,
* now.
*/
ret = ffs_do_descs(ffs->fs_descs_count +
- (high ? ffs->hs_descs_count : 0),
- data->raw_descs, sizeof data->raw_descs,
+ (high ? ffs->hs_descs_count : 0) +
+ (super ? ffs->ss_descs_count : 0),
+ vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
__ffs_func_bind_do_nums, func);
if (unlikely(ret < 0))
goto error;
@@ -2236,26 +2440,19 @@ error:
return ret;
}
-
-/* Other USB function hooks *************************************************/
-
-static void ffs_func_unbind(struct usb_configuration *c,
- struct usb_function *f)
+static int ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
{
- struct ffs_function *func = ffs_func_from_usb(f);
- struct ffs_data *ffs = func->ffs;
+ struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
- ENTER();
+ if (IS_ERR(ffs_opts))
+ return PTR_ERR(ffs_opts);
- if (ffs->func == func) {
- ffs_func_eps_disable(func);
- ffs->func = NULL;
- }
+ return _ffs_func_bind(c, f);
+}
- ffs_event_add(ffs, FUNCTIONFS_UNBIND);
- ffs_func_free(func);
-}
+/* Other USB function hooks *************************************************/
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
@@ -2383,6 +2580,413 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
}
+/* Devices management *******************************************************/
+
+static LIST_HEAD(ffs_devices);
+
+static struct ffs_dev *_ffs_do_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ list_for_each_entry(dev, &ffs_devices, entry) {
+ if (!dev->name || !name)
+ continue;
+ if (strcmp(dev->name, name) == 0)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_get_single_dev(void)
+{
+ struct ffs_dev *dev;
+
+ if (list_is_singular(&ffs_devices)) {
+ dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
+ if (dev->single)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ dev = _ffs_get_single_dev();
+ if (dev)
+ return dev;
+
+ return _ffs_do_find_dev(name);
+}
+
+/* Configfs support *********************************************************/
+
+static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_fs_opts,
+ func_inst.group);
+}
+
+static void ffs_attr_release(struct config_item *item)
+{
+ struct f_fs_opts *opts = to_ffs_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ffs_item_ops = {
+ .release = ffs_attr_release,
+};
+
+static struct config_item_type ffs_func_type = {
+ .ct_item_ops = &ffs_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+
+/* Function registration interface ******************************************/
+
+static void ffs_free_inst(struct usb_function_instance *f)
+{
+ struct f_fs_opts *opts;
+
+ opts = to_f_fs_opts(f);
+ ffs_dev_lock();
+ _ffs_free_dev(opts->dev);
+ ffs_dev_unlock();
+ kfree(opts);
+}
+
+#define MAX_INST_NAME_LEN 40
+
+static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
+{
+ struct f_fs_opts *opts;
+ char *ptr;
+ const char *tmp;
+ int name_len, ret;
+
+ name_len = strlen(name) + 1;
+ if (name_len > MAX_INST_NAME_LEN)
+ return -ENAMETOOLONG;
+
+ ptr = kstrndup(name, name_len, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ opts = to_f_fs_opts(fi);
+ tmp = NULL;
+
+ ffs_dev_lock();
+
+ tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
+ ret = _ffs_name_dev(opts->dev, ptr);
+ if (ret) {
+ kfree(ptr);
+ ffs_dev_unlock();
+ return ret;
+ }
+ opts->dev->name_allocated = true;
+
+ ffs_dev_unlock();
+
+ kfree(tmp);
+
+ return 0;
+}
+
+static struct usb_function_instance *ffs_alloc_inst(void)
+{
+ struct f_fs_opts *opts;
+ struct ffs_dev *dev;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ opts->func_inst.set_inst_name = ffs_set_inst_name;
+ opts->func_inst.free_func_inst = ffs_free_inst;
+ ffs_dev_lock();
+ dev = _ffs_alloc_dev();
+ ffs_dev_unlock();
+ if (IS_ERR(dev)) {
+ kfree(opts);
+ return ERR_CAST(dev);
+ }
+ opts->dev = dev;
+ dev->opts = opts;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &ffs_func_type);
+ return &opts->func_inst;
+}
+
+static void ffs_free(struct usb_function *f)
+{
+ kfree(ffs_func_from_usb(f));
+}
+
+static void ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ struct f_fs_opts *opts =
+ container_of(f->fi, struct f_fs_opts, func_inst);
+ struct ffs_ep *ep = func->eps;
+ unsigned count = ffs->eps_count;
+ unsigned long flags;
+
+ ENTER();
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ if (!--opts->refcnt)
+ functionfs_unbind(ffs);
+
+ /* cleanup after autoconfig */
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ if (ep->ep && ep->req)
+ usb_ep_free_request(ep->ep, ep->req);
+ ep->req = NULL;
+ ++ep;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+ kfree(func->eps);
+ func->eps = NULL;
+ /*
+ * eps, descriptors and interfaces_nums are allocated in the
+ * same chunk so only one free is required.
+ */
+ func->function.fs_descriptors = NULL;
+ func->function.hs_descriptors = NULL;
+ func->function.ss_descriptors = NULL;
+ func->interfaces_nums = NULL;
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+}
+
+static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
+{
+ struct ffs_function *func;
+
+ ENTER();
+
+ func = kzalloc(sizeof(*func), GFP_KERNEL);
+ if (unlikely(!func))
+ return ERR_PTR(-ENOMEM);
+
+ func->function.name = "Function FS Gadget";
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+ func->function.free_func = ffs_free;
+
+ return &func->function;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *_ffs_alloc_dev(void)
+{
+ struct ffs_dev *dev;
+ int ret;
+
+ if (_ffs_get_single_dev())
+ return ERR_PTR(-EBUSY);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (list_empty(&ffs_devices)) {
+ ret = functionfs_init();
+ if (ret) {
+ kfree(dev);
+ return ERR_PTR(ret);
+ }
+ }
+
+ list_add(&dev->entry, &ffs_devices);
+
+ return dev;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ struct ffs_dev *existing;
+
+ existing = _ffs_do_find_dev(name);
+ if (existing)
+ return -EBUSY;
+
+ dev->name = name;
+
+ return 0;
+}
+
+/*
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+int ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ int ret;
+
+ ffs_dev_lock();
+ ret = _ffs_name_dev(dev, name);
+ ffs_dev_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_name_dev);
+
+int ffs_single_dev(struct ffs_dev *dev)
+{
+ int ret;
+
+ ret = 0;
+ ffs_dev_lock();
+
+ if (!list_is_singular(&ffs_devices))
+ ret = -EBUSY;
+ else
+ dev->single = true;
+
+ ffs_dev_unlock();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ffs_single_dev);
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static void _ffs_free_dev(struct ffs_dev *dev)
+{
+ list_del(&dev->entry);
+ if (dev->name_allocated)
+ kfree(dev->name);
+ kfree(dev);
+ if (list_empty(&ffs_devices))
+ functionfs_cleanup();
+}
+
+static void *ffs_acquire_dev(const char *dev_name)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = _ffs_find_dev(dev_name);
+ if (!ffs_dev)
+ ffs_dev = ERR_PTR(-ENODEV);
+ else if (ffs_dev->mounted)
+ ffs_dev = ERR_PTR(-EBUSY);
+ else if (ffs_dev->ffs_acquire_dev_callback &&
+ ffs_dev->ffs_acquire_dev_callback(ffs_dev))
+ ffs_dev = ERR_PTR(-ENODEV);
+ else
+ ffs_dev->mounted = true;
+
+ ffs_dev_unlock();
+ return ffs_dev;
+}
+
+static void ffs_release_dev(struct ffs_data *ffs_data)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = ffs_data->private_data;
+ if (ffs_dev) {
+ ffs_dev->mounted = false;
+
+ if (ffs_dev->ffs_release_dev_callback)
+ ffs_dev->ffs_release_dev_callback(ffs_dev);
+ }
+
+ ffs_dev_unlock();
+}
+
+static int ffs_ready(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+ int ret = 0;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (WARN_ON(ffs_obj->desc_ready)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ffs_obj->desc_ready = true;
+ ffs_obj->ffs_data = ffs;
+
+ if (ffs_obj->ffs_ready_callback)
+ ret = ffs_obj->ffs_ready_callback(ffs);
+
+done:
+ ffs_dev_unlock();
+ return ret;
+}
+
+static void ffs_closed(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj)
+ goto done;
+
+ ffs_obj->desc_ready = false;
+
+ if (ffs_obj->ffs_closed_callback)
+ ffs_obj->ffs_closed_callback(ffs);
+
+ if (!ffs_obj->opts || ffs_obj->opts->no_configfs
+ || !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
+ goto done;
+
+ unregister_gadget_item(ffs_obj->opts->
+ func_inst.group.cg_item.ci_parent->ci_parent);
+done:
+ ffs_dev_unlock();
+}
+
/* Misc helper functions ****************************************************/
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
@@ -2392,7 +2996,7 @@ static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
: mutex_lock_interruptible(mutex);
}
-static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+static char *ffs_prepare_buffer(const char __user *buf, size_t len)
{
char *data;
@@ -2413,3 +3017,7 @@ static char *ffs_prepare_buffer(const char * __user buf, size_t len)
return data;
}
+
+DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index b2113420b80..a95290a1289 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/cdev.h>
@@ -18,14 +17,23 @@
#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;
@@ -35,10 +43,10 @@ struct f_hidg {
unsigned short report_length;
/* recv report */
- char *set_report_buff;
- unsigned short set_report_length;
+ struct list_head completed_out_req;
spinlock_t spinlock;
wait_queue_head_t read_queue;
+ unsigned int qlen;
/* send report */
struct mutex lock;
@@ -49,7 +57,9 @@ struct f_hidg {
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)
@@ -65,7 +75,7 @@ static struct usb_interface_descriptor hidg_interface_desc = {
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bAlternateSetting = 0,
- .bNumEndpoints = 1,
+ .bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
/* .bInterfaceSubClass = DYNAMIC */
/* .bInterfaceProtocol = DYNAMIC */
@@ -96,10 +106,23 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
*/
};
+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,
};
@@ -117,10 +140,23 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
*/
};
+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,
};
@@ -130,9 +166,11 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
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;
- char *tmp_buff = NULL;
- unsigned long flags;
+ 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;
@@ -142,8 +180,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->spinlock, flags);
-#define READ_COND (hidg->set_report_buff != NULL)
+#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)
@@ -155,19 +194,34 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->spinlock, flags);
}
-
- count = min_t(unsigned, count, hidg->set_report_length);
- tmp_buff = hidg->set_report_buff;
- hidg->set_report_buff = NULL;
-
+ /* 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);
- if (tmp_buff != NULL) {
- /* copy to user outside spinlock */
- count -= copy_to_user(buffer, tmp_buff, count);
- kfree(tmp_buff);
- } else
- count = -ENOMEM;
+ /* 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;
}
@@ -282,28 +336,27 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
/*-------------------------------------------------------------------------*/
/* 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 *hidg = (struct f_hidg *) req->context;
+ struct f_hidg_req_list *req_list;
+ unsigned long flags;
- if (req->status != 0 || req->buf == NULL || req->actual == 0) {
- ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__);
+ req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+ if (!req_list)
return;
- }
-
- spin_lock(&hidg->spinlock);
- hidg->set_report_buff = krealloc(hidg->set_report_buff,
- req->actual, GFP_ATOMIC);
+ req_list->req = req;
- if (hidg->set_report_buff == NULL) {
- spin_unlock(&hidg->spinlock);
- return;
- }
- hidg->set_report_length = req->actual;
- memcpy(hidg->set_report_buff, req->buf, req->actual);
-
- spin_unlock(&hidg->spinlock);
+ 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);
}
@@ -344,9 +397,7 @@ static int hidg_setup(struct usb_function *f,
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_REPORT):
VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
- req->context = hidg;
- req->complete = hidg_set_report_complete;
- goto respond;
+ goto stall;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
@@ -374,7 +425,7 @@ static int hidg_setup(struct usb_function *f,
break;
default:
- VDBG(cdev, "Unknown decriptor request 0x%x\n",
+ VDBG(cdev, "Unknown descriptor request 0x%x\n",
value >> 8);
goto stall;
break;
@@ -403,16 +454,25 @@ respond:
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 status = 0;
+ int i, status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
@@ -429,11 +489,55 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
status = usb_ep_enable(hidg->in_ep);
if (status < 0) {
- ERROR(cdev, "Enable endpoint FAILED!\n");
+ 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;
}
@@ -461,7 +565,6 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
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);
@@ -470,13 +573,18 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
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;
@@ -486,29 +594,27 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
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->set_report_buff = NULL;
+ hidg_hs_in_ep_desc.bEndpointAddress =
+ hidg_fs_in_ep_desc.bEndpointAddress;
+ hidg_hs_out_ep_desc.bEndpointAddress =
+ hidg_fs_out_ep_desc.bEndpointAddress;
- /* copy descriptors */
- f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
- if (!f->descriptors)
+ status = usb_assign_descriptors(f, hidg_fs_descriptors,
+ hidg_hs_descriptors, NULL);
+ if (status)
goto fail;
- if (gadget_is_dualspeed(c->cdev->gadget)) {
- hidg_hs_in_ep_desc.bEndpointAddress =
- hidg_fs_in_ep_desc.bEndpointAddress;
- f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
- if (!f->hs_descriptors)
- 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);
@@ -529,9 +635,7 @@ fail:
usb_ep_free_request(hidg->in_ep, hidg->req);
}
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->descriptors);
-
+ usb_free_all_descriptors(f);
return status;
}
@@ -548,12 +652,9 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
- /* free descriptors copies */
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->descriptors);
+ usb_free_all_descriptors(f);
kfree(hidg->report_desc);
- kfree(hidg->set_report_buff);
kfree(hidg);
}
@@ -624,6 +725,9 @@ int __init hidg_bind_config(struct usb_configuration *c,
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);
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index 2c0cd824c66..4557cd03f0b 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -15,10 +15,12 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/usb/composite.h>
#include "g_zero.h"
-#include "gadget_chips.h"
-
+#include "u_f.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
@@ -44,9 +46,8 @@ static inline struct f_loopback *func_to_loop(struct usb_function *f)
return container_of(f, struct f_loopback, function);
}
-static unsigned qlen = 32;
-module_param(qlen, uint, 0);
-MODULE_PARM_DESC(qlenn, "depth of loopback queue");
+static unsigned qlen;
+static unsigned buflen;
/*-------------------------------------------------------------------------*/
@@ -119,7 +120,7 @@ static struct usb_endpoint_descriptor ss_loop_source_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
@@ -135,7 +136,7 @@ static struct usb_endpoint_descriptor ss_loop_sink_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
@@ -171,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);
@@ -184,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);
@@ -201,22 +208,19 @@ 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 */
- if (gadget_is_superspeed(c->cdev->gadget)) {
- ss_loop_source_desc.bEndpointAddress =
- fs_loop_source_desc.bEndpointAddress;
- ss_loop_sink_desc.bEndpointAddress =
- fs_loop_sink_desc.bEndpointAddress;
- f->ss_descriptors = ss_loopback_descs;
- }
+ 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_superspeed(c->cdev->gadget) ? "super" :
@@ -225,9 +229,17 @@ autoconf_fail:
return 0;
}
-static void
-loopback_unbind(struct usb_configuration *c, struct usb_function *f)
+static void lb_free_func(struct usb_function *f)
{
+ struct f_lb_opts *opts;
+
+ opts = container_of(f->fi, struct f_lb_opts, func_inst);
+
+ mutex_lock(&opts->lock);
+ opts->refcnt--;
+ mutex_unlock(&opts->lock);
+
+ usb_free_all_descriptors(f);
kfree(func_to_loop(f));
}
@@ -286,10 +298,15 @@ 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)
{
@@ -329,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);
@@ -367,64 +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;
+}
+
+static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_lb_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_lb_opts);
+CONFIGFS_ATTR_OPS(f_lb_opts);
+
+static void lb_attr_release(struct config_item *item)
+{
+ struct f_lb_opts *lb_opts = to_f_lb_opts(item);
- status = usb_add_function(c, &loop->function);
- if (status)
- kfree(loop);
- return status;
+ usb_put_function_instance(&lb_opts->func_inst);
}
-static struct usb_configuration loopback_driver = {
- .label = "loopback",
- .strings = loopback_strings,
- .bConfigurationValue = 2,
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
- /* .iConfiguration = DYNAMIC */
+static struct configfs_item_operations lb_item_ops = {
+ .release = lb_attr_release,
+ .show_attribute = f_lb_opts_attr_show,
+ .store_attribute = f_lb_opts_attr_store,
};
-/**
- * loopback_add - add a loopback testing configuration to a device
- * @cdev: the device to support the loopback configuration
- */
-int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
+static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
{
- int id;
+ int result;
- /* allocate string ID(s) */
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_loopback[0].id = id;
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->qlen);
+ mutex_unlock(&opts->lock);
- loopback_intf.iInterface = id;
- loopback_driver.iConfiguration = id;
+ return result;
+}
- /* support autoresume for remote wakeup testing */
- if (autoresume)
- loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 num;
- /* support OTG systems */
- if (gadget_is_otg(cdev->gadget)) {
- loopback_driver.descriptors = otg_desc;
- loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
}
- return usb_add_config(cdev, &loopback_driver, loopback_bind_config);
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->qlen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_qlen =
+ __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
+ f_lb_opts_qlen_show,
+ f_lb_opts_qlen_store);
+
+static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->bulk_buflen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->bulk_buflen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
+ __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+ f_lb_opts_bulk_buflen_show,
+ f_lb_opts_bulk_buflen_store);
+
+static struct configfs_attribute *lb_attrs[] = {
+ &f_lb_opts_qlen.attr,
+ &f_lb_opts_bulk_buflen.attr,
+ NULL,
+};
+
+static struct config_item_type lb_func_type = {
+ .ct_item_ops = &lb_item_ops,
+ .ct_attrs = lb_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void lb_free_instance(struct usb_function_instance *fi)
+{
+ struct f_lb_opts *lb_opts;
+
+ lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+ kfree(lb_opts);
}
+
+static struct usb_function_instance *loopback_alloc_instance(void)
+{
+ struct f_lb_opts *lb_opts;
+
+ lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
+ if (!lb_opts)
+ return ERR_PTR(-ENOMEM);
+ mutex_init(&lb_opts->lock);
+ lb_opts->func_inst.free_func_inst = lb_free_instance;
+ lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+ lb_opts->qlen = GZERO_QLEN;
+
+ config_group_init_type_name(&lb_opts->func_inst.group, "",
+ &lb_func_type);
+
+ return &lb_opts->func_inst;
+}
+DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
+
+int __init lb_modinit(void)
+{
+ int ret;
+
+ ret = usb_function_register(&Loopbackusb_func);
+ if (ret)
+ return ret;
+ return ret;
+}
+void __exit lb_modexit(void)
+{
+ usb_function_unregister(&Loopbackusb_func);
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index ee8ceec0156..b9639390886 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2003-2008 Alan Stern
* Copyright (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * Author: Michal Nazarewicz <mina86@mina86.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,12 +44,12 @@
* function for a USB device, it also illustrates a technique of
* double-buffering for increased throughput.
*
- * Function supports multiple logical units (LUNs). Backing storage
- * for each LUN is provided by a regular file or a block device.
- * Access for each LUN can be limited to read-only. Moreover, the
- * function can indicate that LUN is removable and/or CD-ROM. (The
- * later implies read-only access.)
- *
+ * 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:
*
@@ -75,25 +75,6 @@
* ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12)
* commands for this LUN shall be ignored.
*
- * lun_name_format A printf-like format for names of the LUN
- * devices. This determines how the
- * directory in sysfs will be named.
- * Unless you are using several MSFs in
- * a single gadget (as opposed to single
- * MSF in many configurations) you may
- * leave it as NULL (in which case
- * "lun%d" will be used). In the format
- * you can use "%d" to index LUNs for
- * MSF's with more than one LUN. (Beware
- * that there is only one integer given
- * as an argument for the format and
- * specifying invalid format may cause
- * unspecified behaviour.)
- * thread_name Name of the kernel thread process used by the
- * MSF. You can safely set it to NULL
- * (in which case default "file-storage"
- * will be used).
- *
* vendor_name
* product_name
* release Information used as a reply to INQUIRY
@@ -114,62 +95,6 @@
* data track and no audio tracks; hence there need be only one
* backing file per LUN.
*
- *
- * MSF includes support for module parameters. If gadget using it
- * decides to use it, the following module parameters will be
- * available:
- *
- * file=filename[,filename...]
- * Names of the files or block devices used for
- * backing storage.
- * ro=b[,b...] Default false, boolean for read-only access.
- * removable=b[,b...]
- * Default true, boolean for removable media.
- * cdrom=b[,b...] Default false, boolean for whether to emulate
- * a CD-ROM drive.
- * nofua=b[,b...] Default false, booleans for ignore FUA flag
- * in SCSI WRITE(10,12) commands
- * 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.
- *
- * The module parameters may be prefixed with some string. You need
- * to consult gadget's documentation or source to verify whether it is
- * using those module parameters and if it does what are the prefixes
- * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is
- * the prefix).
- *
- *
- * Requirements are modest; only a bulk-in and a bulk-out endpoint are
- * needed. 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.
- *
- *
- * The pathnames of the backing files and the ro settings are
- * available in the attribute files "file" and "ro" in the lun<n> (or
- * to be more precise in a directory which name comes from
- * "lun_name_format" option!) 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.
- *
- * When a LUN receive an "eject" SCSI request (Start/Stop Unit),
- * if the LUN is removable, the backing file is released to simulate
- * ejection.
- *
- *
* 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
@@ -211,7 +136,7 @@
* 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. At of this moment, MSF
+ * 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.
*
@@ -288,13 +213,14 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/freezer.h>
-#include <linux/utsname.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"
/*------------------------------------------------------------------------*/
@@ -304,45 +230,30 @@
static const char fsg_string_interface[] = "Mass Storage";
-#define FSG_NO_INTR_EP 1
-#define FSG_NO_DEVICE_STRINGS 1
-#define FSG_NO_OTG 1
-#define FSG_NO_INTR_EP 1
+#include "storage_common.h"
+#include "f_mass_storage.h"
-#include "storage_common.c"
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
+static struct usb_gadget_strings *fsg_strings_array[] = {
+ &fsg_stringtab,
+ NULL,
+};
/*-------------------------------------------------------------------------*/
struct fsg_dev;
struct fsg_common;
-/* FSF callback functions */
-struct fsg_operations {
- /*
- * Callback function to call when thread exits. If no
- * callback is set or it returns value lower then zero MSF
- * will force eject all LUNs it operates on (including those
- * marked as non-removable or with prevent_medium_removal flag
- * set).
- */
- int (*thread_exits)(struct fsg_common *common);
-
- /*
- * Called prior to ejection. Negative return means error,
- * zero means to continue with ejection, positive means not to
- * eject.
- */
- int (*pre_eject)(struct fsg_common *common,
- struct fsg_lun *lun, int num);
- /*
- * Called after ejection. Negative return means error, zero
- * or positive is just a success.
- */
- int (*post_eject)(struct fsg_common *common,
- struct fsg_lun *lun, int num);
-};
-
/* Data shared by all the FSG instances. */
struct fsg_common {
struct usb_gadget *gadget;
@@ -363,13 +274,14 @@ struct fsg_common {
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
struct fsg_buffhd *buffhds;
+ unsigned int fsg_num_buffers;
int cmnd_size;
u8 cmnd[MAX_COMMAND_SIZE];
unsigned int nluns;
unsigned int lun;
- struct fsg_lun *luns;
+ struct fsg_lun **luns;
struct fsg_lun *curlun;
unsigned int bulk_out_maxpacket;
@@ -389,6 +301,7 @@ struct fsg_common {
unsigned int short_packet_received:1;
unsigned int bad_lun_okay:1;
unsigned int running:1;
+ unsigned int sysfs:1;
int thread_wakeup_needed;
struct completion thread_notifier;
@@ -408,31 +321,6 @@ struct fsg_common {
struct kref ref;
};
-struct fsg_config {
- unsigned nluns;
- struct fsg_lun_config {
- const char *filename;
- char ro;
- char removable;
- char cdrom;
- char nofua;
- } luns[FSG_MAX_LUNS];
-
- const char *lun_name_format;
- const char *thread_name;
-
- /* 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 */
- u16 release;
-
- char can_stall;
-};
-
struct fsg_dev {
struct usb_function function;
struct usb_gadget *gadget; /* Copy of cdev->gadget */
@@ -512,6 +400,7 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
/* Caller must hold fsg->lock */
static void wakeup_thread(struct fsg_common *common)
{
+ smp_wmb(); /* ensure the write of bh->state is complete */
/* Tell the main thread that something has happened */
common->thread_wakeup_needed = 1;
if (common->thread_task)
@@ -620,7 +509,7 @@ static int fsg_setup(struct usb_function *f,
switch (ctrl->bRequest) {
- case USB_BULK_RESET_REQUEST:
+ case US_BULK_RESET_REQUEST:
if (ctrl->bRequestType !=
(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
break;
@@ -634,9 +523,9 @@ static int fsg_setup(struct usb_function *f,
*/
DBG(fsg, "bulk reset request\n");
raise_exception(fsg->common, FSG_STATE_RESET);
- return DELAYED_STATUS;
+ return USB_GADGET_DELAYED_STATUS;
- case USB_BULK_GET_MAX_LUN_REQUEST:
+ case US_BULK_GET_MAX_LUN:
if (ctrl->bRequestType !=
(USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
break;
@@ -713,13 +602,14 @@ static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
return true;
}
-static int sleep_thread(struct fsg_common *common)
+static int sleep_thread(struct fsg_common *common, bool can_freeze)
{
int rc = 0;
/* Wait until a signal arrives or we are woken up */
for (;;) {
- try_to_freeze();
+ if (can_freeze)
+ try_to_freeze();
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
rc = -EINTR;
@@ -731,6 +621,7 @@ static int sleep_thread(struct fsg_common *common)
}
__set_current_state(TASK_RUNNING);
common->thread_wakeup_needed = 0;
+ smp_rmb(); /* ensure the latest bh->state is visible */
return rc;
}
@@ -792,7 +683,7 @@ static int do_read(struct fsg_common *common)
/* Wait for the next buffer to become available */
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- rc = sleep_thread(common);
+ rc = sleep_thread(common, false);
if (rc)
return rc;
}
@@ -1047,7 +938,7 @@ static int do_write(struct fsg_common *common)
}
/* Wait for something to happen */
- rc = sleep_thread(common);
+ rc = sleep_thread(common, false);
if (rc)
return rc;
}
@@ -1077,7 +968,7 @@ static int do_synchronize_cache(struct fsg_common *common)
static void invalidate_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
- struct inode *inode = filp->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(filp);
unsigned long rc;
rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
@@ -1459,26 +1350,13 @@ static int do_start_stop(struct fsg_common *common)
if (!loej)
return 0;
- /* Simulate an unload/eject */
- if (common->ops && common->ops->pre_eject) {
- int r = common->ops->pre_eject(common, curlun,
- curlun - common->luns);
- if (unlikely(r < 0))
- return r;
- else if (r)
- return 0;
- }
-
up_read(&common->filesem);
down_write(&common->filesem);
fsg_lun_close(curlun);
up_write(&common->filesem);
down_read(&common->filesem);
- return common->ops && common->ops->post_eject
- ? min(0, common->ops->post_eject(common, curlun,
- curlun - common->luns))
- : 0;
+ return 0;
}
static int do_prevent_allow(struct fsg_common *common)
@@ -1627,7 +1505,7 @@ static int throw_away_data(struct fsg_common *common)
}
/* Otherwise wait for something to happen */
- rc = sleep_thread(common);
+ rc = sleep_thread(common, true);
if (rc)
return rc;
}
@@ -1742,13 +1620,13 @@ static int send_status(struct fsg_common *common)
struct fsg_buffhd *bh;
struct bulk_cs_wrap *csw;
int rc;
- u8 status = USB_STATUS_PASS;
+ 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);
+ rc = sleep_thread(common, true);
if (rc)
return rc;
}
@@ -1763,11 +1641,11 @@ static int send_status(struct fsg_common *common)
if (common->phase_error) {
DBG(common, "sending phase-error status\n");
- status = USB_STATUS_PHASE_ERROR;
+ status = US_BULK_STAT_PHASE;
sd = SS_INVALID_COMMAND;
} else if (sd != SS_NO_SENSE) {
DBG(common, "sending command-failure status\n");
- status = USB_STATUS_FAIL;
+ 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);
@@ -1776,12 +1654,12 @@ static int send_status(struct fsg_common *common)
/* Store and send the Bulk-only CSW */
csw = (void *)bh->buf;
- csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
+ 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 = USB_BULK_CS_WRAP_LEN;
+ 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 */
@@ -1803,7 +1681,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
int needs_medium, const char *name)
{
int i;
- int lun = common->cmnd[1] >> 5;
+ unsigned int lun = common->cmnd[1] >> 5;
static const char dirletter[4] = {'u', 'o', 'i', 'n'};
char hdlen[20];
struct fsg_lun *curlun;
@@ -1869,7 +1747,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
/* Check that the LUN values are consistent */
if (common->lun != lun)
- DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
+ DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n",
common->lun, lun);
/* Check the LUN */
@@ -1889,7 +1767,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
*/
if (common->cmnd[0] != INQUIRY &&
common->cmnd[0] != REQUEST_SENSE) {
- DBG(common, "unsupported LUN %d\n", common->lun);
+ DBG(common, "unsupported LUN %u\n", common->lun);
return -EINVAL;
}
}
@@ -1951,7 +1829,7 @@ static int do_scsi_command(struct fsg_common *common)
bh = common->next_buffhd_to_fill;
common->next_buffhd_to_drain = bh;
while (bh->state != BUF_STATE_EMPTY) {
- rc = sleep_thread(common);
+ rc = sleep_thread(common, true);
if (rc)
return rc;
}
@@ -2190,7 +2068,7 @@ 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, 0xff, 0, unknown);
+ DATA_DIR_UNKNOWN, ~0, 0, unknown);
if (reply == 0) {
common->curlun->sense_data = SS_INVALID_COMMAND;
reply = -EINVAL;
@@ -2221,7 +2099,7 @@ unknown_cmnd:
static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct usb_request *req = bh->outreq;
- struct fsg_bulk_cb_wrap *cbw = req->buf;
+ struct bulk_cb_wrap *cbw = req->buf;
struct fsg_common *common = fsg->common;
/* Was this a real packet? Should it be ignored? */
@@ -2229,9 +2107,9 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
return -EINVAL;
/* Is the CBW valid? */
- if (req->actual != USB_BULK_CB_WRAP_LEN ||
+ if (req->actual != US_BULK_CB_WRAP_LEN ||
cbw->Signature != cpu_to_le32(
- USB_BULK_CB_SIG)) {
+ US_BULK_CB_SIGN)) {
DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
req->actual,
le32_to_cpu(cbw->Signature));
@@ -2253,7 +2131,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
}
/* Is the CBW meaningful? */
- if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
+ 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",
@@ -2273,7 +2151,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
/* Save the command for later */
common->cmnd_size = cbw->Length;
memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
- if (cbw->Flags & USB_BULK_IN_FLAG)
+ if (cbw->Flags & US_BULK_FLAG_IN)
common->data_dir = DATA_DIR_TO_HOST;
else
common->data_dir = DATA_DIR_FROM_HOST;
@@ -2281,8 +2159,8 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
if (common->data_size == 0)
common->data_dir = DATA_DIR_NONE;
common->lun = cbw->Lun;
- if (common->lun >= 0 && common->lun < common->nluns)
- common->curlun = &common->luns[common->lun];
+ if (common->lun < common->nluns)
+ common->curlun = common->luns[common->lun];
else
common->curlun = NULL;
common->tag = cbw->Tag;
@@ -2297,13 +2175,13 @@ static int get_next_command(struct fsg_common *common)
/* Wait for the next buffer to become available */
bh = common->next_buffhd_to_fill;
while (bh->state != BUF_STATE_EMPTY) {
- rc = sleep_thread(common);
+ rc = sleep_thread(common, true);
if (rc)
return rc;
}
/* Queue a request to read a Bulk-only CBW */
- set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
+ 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;
@@ -2316,7 +2194,7 @@ static int get_next_command(struct fsg_common *common)
/* Wait for the CBW to arrive */
while (bh->state != BUF_STATE_FULL) {
- rc = sleep_thread(common);
+ rc = sleep_thread(common, true);
if (rc)
return rc;
}
@@ -2354,7 +2232,7 @@ reset:
if (common->fsg) {
fsg = common->fsg;
- for (i = 0; i < fsg_num_buffers; ++i) {
+ for (i = 0; i < common->fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
if (bh->inreq) {
@@ -2370,10 +2248,12 @@ reset:
/* Disable the endpoints */
if (fsg->bulk_in_enabled) {
usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in->driver_data = NULL;
fsg->bulk_in_enabled = 0;
}
if (fsg->bulk_out_enabled) {
usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out->driver_data = NULL;
fsg->bulk_out_enabled = 0;
}
@@ -2411,7 +2291,7 @@ reset:
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
- for (i = 0; i < fsg_num_buffers; ++i) {
+ for (i = 0; i < common->fsg_num_buffers; ++i) {
struct fsg_buffhd *bh = &common->buffhds[i];
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
@@ -2428,7 +2308,9 @@ reset:
common->running = 1;
for (i = 0; i < common->nluns; ++i)
- common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ if (common->luns[i])
+ common->luns[i]->unit_attention_data =
+ SS_RESET_OCCURRED;
return rc;
}
@@ -2480,7 +2362,7 @@ static void handle_exception(struct fsg_common *common)
/* Cancel all the pending transfers */
if (likely(common->fsg)) {
- for (i = 0; i < fsg_num_buffers; ++i) {
+ for (i = 0; i < common->fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
if (bh->inreq_busy)
usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
@@ -2492,13 +2374,13 @@ static void handle_exception(struct fsg_common *common)
/* Wait until everything is idle */
for (;;) {
int num_active = 0;
- for (i = 0; i < fsg_num_buffers; ++i) {
+ for (i = 0; i < common->fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
num_active += bh->inreq_busy + bh->outreq_busy;
}
if (num_active == 0)
break;
- if (sleep_thread(common))
+ if (sleep_thread(common, true))
return;
}
@@ -2515,7 +2397,7 @@ static void handle_exception(struct fsg_common *common)
*/
spin_lock_irq(&common->lock);
- for (i = 0; i < fsg_num_buffers; ++i) {
+ for (i = 0; i < common->fsg_num_buffers; ++i) {
bh = &common->buffhds[i];
bh->state = BUF_STATE_EMPTY;
}
@@ -2528,7 +2410,9 @@ static void handle_exception(struct fsg_common *common)
common->state = FSG_STATE_STATUS_PHASE;
else {
for (i = 0; i < common->nluns; ++i) {
- curlun = &common->luns[i];
+ curlun = common->luns[i];
+ if (!curlun)
+ continue;
curlun->prevent_medium_removal = 0;
curlun->sense_data = SS_NO_SENSE;
curlun->unit_attention_data = SS_NO_SENSE;
@@ -2570,8 +2454,9 @@ static void handle_exception(struct fsg_common *common)
* CONFIG_CHANGE cases.
*/
/* for (i = 0; i < common->nluns; ++i) */
- /* common->luns[i].unit_attention_data = */
- /* SS_RESET_OCCURRED; */
+ /* if (common->luns[i]) */
+ /* common->luns[i]->unit_attention_data = */
+ /* SS_RESET_OCCURRED; */
break;
case FSG_STATE_CONFIG_CHANGE:
@@ -2632,7 +2517,7 @@ static int fsg_main_thread(void *common_)
}
if (!common->running) {
- sleep_thread(common);
+ sleep_thread(common, true);
continue;
}
@@ -2667,12 +2552,13 @@ static int fsg_main_thread(void *common_)
if (!common->ops || !common->ops->thread_exits
|| common->ops->thread_exits(common) < 0) {
- struct fsg_lun *curlun = common->luns;
+ struct fsg_lun **curlun_it = common->luns;
unsigned i = common->nluns;
down_write(&common->filesem);
- for (; i--; ++curlun) {
- if (!fsg_lun_is_open(curlun))
+ for (; i--; ++curlun_it) {
+ struct fsg_lun *curlun = *curlun_it;
+ if (!curlun || !fsg_lun_is_open(curlun))
continue;
fsg_lun_close(curlun);
@@ -2688,10 +2574,62 @@ static int fsg_main_thread(void *common_)
/*************************** DEVICE ATTRIBUTES ***************************/
-/* Write permission is checked per LUN in store_*() functions. */
-static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
-static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
-static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
+static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return fsg_show_ro(curlun, buf);
+}
+
+static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return fsg_show_nofua(curlun, buf);
+}
+
+static 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 ******************************/
@@ -2703,231 +2641,422 @@ static void fsg_lun_release(struct device *dev)
/* Nothing needs to be done */
}
-static inline void fsg_common_get(struct fsg_common *common)
+void fsg_common_get(struct fsg_common *common)
{
kref_get(&common->ref);
}
+EXPORT_SYMBOL_GPL(fsg_common_get);
-static inline void fsg_common_put(struct fsg_common *common)
+void fsg_common_put(struct fsg_common *common)
{
kref_put(&common->ref, fsg_common_release);
}
+EXPORT_SYMBOL_GPL(fsg_common_put);
-static struct fsg_common *fsg_common_init(struct fsg_common *common,
- struct usb_composite_dev *cdev,
- struct fsg_config *cfg)
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers)
{
- struct usb_gadget *gadget = cdev->gadget;
- struct fsg_buffhd *bh;
- struct fsg_lun *curlun;
- struct fsg_lun_config *lcfg;
- int nluns, i, rc;
- char *pathbuf;
-
- rc = fsg_num_buffers_validate();
- if (rc != 0)
- return ERR_PTR(rc);
-
- /* Find out how many LUNs there should be */
- nluns = cfg->nluns;
- if (nluns < 1 || nluns > FSG_MAX_LUNS) {
- dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
- return ERR_PTR(-EINVAL);
- }
+ if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ return 0;
+ pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+ fsg_num_buffers, 2, 4);
+ return -EINVAL;
+}
- /* Allocate? */
+static struct fsg_common *fsg_common_setup(struct fsg_common *common)
+{
if (!common) {
- common = kzalloc(sizeof *common, GFP_KERNEL);
+ common = kzalloc(sizeof(*common), GFP_KERNEL);
if (!common)
return ERR_PTR(-ENOMEM);
common->free_storage_on_release = 1;
} else {
- memset(common, 0, sizeof *common);
common->free_storage_on_release = 0;
}
+ init_rwsem(&common->filesem);
+ spin_lock_init(&common->lock);
+ kref_init(&common->ref);
+ init_completion(&common->thread_notifier);
+ init_waitqueue_head(&common->fsg_wait);
+ common->state = FSG_STATE_TERMINATED;
- common->buffhds = kcalloc(fsg_num_buffers,
- sizeof *(common->buffhds), GFP_KERNEL);
- if (!common->buffhds) {
- if (common->free_storage_on_release)
- kfree(common);
- return ERR_PTR(-ENOMEM);
- }
-
- common->ops = cfg->ops;
- common->private_data = cfg->private_data;
-
- common->gadget = gadget;
- common->ep0 = gadget->ep0;
- common->ep0req = cdev->req;
- common->cdev = cdev;
+ return common;
+}
- /* Maybe allocate device-global string IDs, and patch descriptors */
- if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
- rc = usb_string_id(cdev);
- if (unlikely(rc < 0))
- goto error_release;
- fsg_strings[FSG_STRING_INTERFACE].id = rc;
- fsg_intf_desc.iInterface = rc;
- }
+void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs)
+{
+ common->sysfs = sysfs;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_sysfs);
- /*
- * Create the LUNs, open their backing files, and register the
- * LUN devices in sysfs.
- */
- curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
- if (unlikely(!curlun)) {
- rc = -ENOMEM;
- goto error_release;
+static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n)
+{
+ if (buffhds) {
+ struct fsg_buffhd *bh = buffhds;
+ while (n--) {
+ kfree(bh->buf);
+ ++bh;
+ }
+ kfree(buffhds);
}
- common->luns = curlun;
-
- init_rwsem(&common->filesem);
+}
- for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
- curlun->cdrom = !!lcfg->cdrom;
- curlun->ro = lcfg->cdrom || lcfg->ro;
- curlun->initially_ro = curlun->ro;
- curlun->removable = lcfg->removable;
- curlun->dev.release = fsg_lun_release;
- curlun->dev.parent = &gadget->dev;
- /* curlun->dev.driver = &fsg_driver.driver; XXX */
- dev_set_drvdata(&curlun->dev, &common->filesem);
- dev_set_name(&curlun->dev,
- cfg->lun_name_format
- ? cfg->lun_name_format
- : "lun%d",
- i);
-
- rc = device_register(&curlun->dev);
- if (rc) {
- INFO(common, "failed to register LUN%d: %d\n", i, rc);
- common->nluns = i;
- put_device(&curlun->dev);
- goto error_release;
- }
+int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
+{
+ struct fsg_buffhd *bh, *buffhds;
+ int i, rc;
- rc = device_create_file(&curlun->dev, &dev_attr_ro);
- if (rc)
- goto error_luns;
- rc = device_create_file(&curlun->dev, &dev_attr_file);
- if (rc)
- goto error_luns;
- rc = device_create_file(&curlun->dev, &dev_attr_nofua);
- if (rc)
- goto error_luns;
+ rc = fsg_num_buffers_validate(n);
+ if (rc != 0)
+ return rc;
- if (lcfg->filename) {
- rc = fsg_lun_open(curlun, lcfg->filename);
- if (rc)
- goto error_luns;
- } else if (!curlun->removable) {
- ERROR(common, "no file given for LUN%d\n", i);
- rc = -EINVAL;
- goto error_luns;
- }
- }
- common->nluns = nluns;
+ buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
+ if (!buffhds)
+ return -ENOMEM;
/* Data buffers cyclic list */
- bh = common->buffhds;
- i = fsg_num_buffers;
+ 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)) {
- rc = -ENOMEM;
+ if (unlikely(!bh->buf))
goto error_release;
- }
} while (--i);
- bh->next = common->buffhds;
+ bh->next = buffhds;
- /* Prepare inquiryString */
- if (cfg->release != 0xffff) {
- i = cfg->release;
- } else {
- i = usb_gadget_controller_number(gadget);
- if (i >= 0) {
- i = 0x0300 + i;
- } else {
- WARNING(common, "controller '%s' not recognized\n",
- gadget->name);
- i = 0x0399;
+ _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;
}
- snprintf(common->inquiry_string, sizeof common->inquiry_string,
- "%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
- /* Assume product name dependent on the first LUN */
- cfg->product_name ?: (common->luns->cdrom
- ? "File-Stor Gadget"
- : "File-CD Gadget"),
- i);
+
+ 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 = cfg->can_stall &&
- !(gadget_is_at91(common->gadget));
+ common->can_stall = can_stall && !(gadget_is_at91(common->gadget));
- spin_lock_init(&common->lock);
- kref_init(&common->ref);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
- /* Tell the thread to start working */
- common->thread_task =
- kthread_create(fsg_main_thread, common,
- cfg->thread_name ?: "file-storage");
- if (IS_ERR(common->thread_task)) {
- rc = PTR_ERR(common->thread_task);
- goto error_release;
+static inline int fsg_common_add_sysfs(struct fsg_common *common,
+ struct fsg_lun *lun)
+{
+ int rc;
+
+ rc = device_register(&lun->dev);
+ if (rc) {
+ put_device(&lun->dev);
+ return rc;
}
- init_completion(&common->thread_notifier);
- init_waitqueue_head(&common->fsg_wait);
- /* Information */
- INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
- INFO(common, "Number of LUNs=%d\n", common->nluns);
+ 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);
- for (i = 0, nluns = common->nluns, curlun = common->luns;
- i < nluns;
- ++curlun, ++i) {
- char *p = "(no medium)";
- if (fsg_lun_is_open(curlun)) {
- p = "(error)";
- if (pathbuf) {
- p = d_path(&curlun->filp->f_path,
- pathbuf, PATH_MAX);
- if (IS_ERR(p))
- p = "(error)";
- }
+ p = "(no medium)";
+ if (fsg_lun_is_open(lun)) {
+ p = "(error)";
+ if (pathbuf) {
+ p = d_path(&lun->filp->f_path, pathbuf, PATH_MAX);
+ if (IS_ERR(p))
+ p = "(error)";
}
- LINFO(curlun, "LUN: %s%s%sfile: %s\n",
- curlun->removable ? "removable " : "",
- curlun->ro ? "read only " : "",
- curlun->cdrom ? "CD-ROM " : "",
- p);
}
+ pr_info("LUN: %s%s%sfile: %s\n",
+ lun->removable ? "removable " : "",
+ lun->ro ? "read only " : "",
+ lun->cdrom ? "CD-ROM " : "",
+ p);
kfree(pathbuf);
+ return 0;
+
+error_lun:
+ if (common->sysfs) {
+ fsg_common_remove_sysfs(lun);
+ device_unregister(&lun->dev);
+ }
+ fsg_lun_close(lun);
+ common->luns[id] = NULL;
+error_sysfs:
+ kfree(lun);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_lun);
+
+int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg)
+{
+ char buf[8]; /* enough for 100000000 different numbers, decimal */
+ int i, rc;
+
+ for (i = 0; i < common->nluns; ++i) {
+ snprintf(buf, sizeof(buf), "lun%d", i);
+ rc = fsg_common_create_lun(common, &cfg->luns[i], i, buf, NULL);
+ if (rc)
+ goto fail;
+ }
+
+ pr_info("Number of LUNs=%d\n", common->nluns);
+
+ return 0;
+
+fail:
+ _fsg_common_remove_luns(common, i);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fsg_common_create_luns);
+
+void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
+ const char *pn)
+{
+ int i;
+
+ /* Prepare inquiryString */
+ i = get_default_bcdDevice();
+ snprintf(common->inquiry_string, sizeof(common->inquiry_string),
+ "%-8s%-16s%04x", vn ?: "Linux",
+ /* Assume product name dependent on the first LUN */
+ pn ?: ((*common->luns)->cdrom
+ ? "File-CD Gadget"
+ : "File-Stor Gadget"),
+ i);
+}
+EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
+
+int fsg_common_run_thread(struct fsg_common *common)
+{
+ common->state = FSG_STATE_IDLE;
+ /* Tell the thread to start working */
+ common->thread_task =
+ kthread_create(fsg_main_thread, common, "file-storage");
+ if (IS_ERR(common->thread_task)) {
+ common->state = FSG_STATE_TERMINATED;
+ return PTR_ERR(common->thread_task);
+ }
+
DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
wake_up_process(common->thread_task);
- return common;
-
-error_luns:
- common->nluns = i + 1;
-error_release:
- common->state = FSG_STATE_TERMINATED; /* The thread is dead */
- /* Call fsg_common_release() directly, ref might be not initialised. */
- fsg_common_release(&common->ref);
- return ERR_PTR(rc);
+ return 0;
}
+EXPORT_SYMBOL_GPL(fsg_common_run_thread);
static void fsg_common_release(struct kref *ref)
{
@@ -2940,30 +3069,26 @@ static void fsg_common_release(struct kref *ref)
}
if (likely(common->luns)) {
- struct fsg_lun *lun = common->luns;
+ struct fsg_lun **lun_it = common->luns;
unsigned i = common->nluns;
/* In error recovery common->nluns may be zero. */
- for (; i; --i, ++lun) {
- device_remove_file(&lun->dev, &dev_attr_nofua);
- device_remove_file(&lun->dev, &dev_attr_ro);
- device_remove_file(&lun->dev, &dev_attr_file);
+ for (; i; --i, ++lun_it) {
+ struct fsg_lun *lun = *lun_it;
+ if (!lun)
+ continue;
+ if (common->sysfs)
+ fsg_common_remove_sysfs(lun);
fsg_lun_close(lun);
- device_unregister(&lun->dev);
+ if (common->sysfs)
+ device_unregister(&lun->dev);
+ kfree(lun);
}
kfree(common->luns);
}
- {
- struct fsg_buffhd *bh = common->buffhds;
- unsigned i = fsg_num_buffers;
- do {
- kfree(bh->buf);
- } while (++bh, --i);
- }
-
- kfree(common->buffhds);
+ _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
if (common->free_storage_on_release)
kfree(common);
}
@@ -2971,32 +3096,27 @@ static void fsg_common_release(struct kref *ref)
/*-------------------------------------------------------------------------*/
-static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct fsg_dev *fsg = fsg_from_func(f);
- struct fsg_common *common = fsg->common;
-
- DBG(fsg, "unbind\n");
- if (fsg->common->fsg == fsg) {
- fsg->common->new_fsg = NULL;
- raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
- /* FIXME: make interruptible or killable somehow? */
- wait_event(common->fsg_wait, common->fsg != fsg);
- }
-
- fsg_common_put(common);
- usb_free_descriptors(fsg->function.descriptors);
- usb_free_descriptors(fsg->function.hs_descriptors);
- usb_free_descriptors(fsg->function.ss_descriptors);
- kfree(fsg);
-}
-
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;
@@ -3020,45 +3140,27 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
ep->driver_data = fsg->common; /* claim the endpoint */
fsg->bulk_out = ep;
- /* Copy descriptors */
- f->descriptors = usb_copy_descriptors(fsg_fs_function);
- if (unlikely(!f->descriptors))
- return -ENOMEM;
-
- if (gadget_is_dualspeed(gadget)) {
- /* 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;
- f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
- if (unlikely(!f->hs_descriptors)) {
- usb_free_descriptors(f->descriptors);
- return -ENOMEM;
- }
- }
-
- if (gadget_is_superspeed(gadget)) {
- unsigned max_burst;
+ /* 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);
+ /* 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_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;
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
- f->ss_descriptors = usb_copy_descriptors(fsg_ss_function);
- if (unlikely(!f->ss_descriptors)) {
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->descriptors);
- return -ENOMEM;
- }
- }
+ ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
+ fsg_ss_function);
+ if (ret)
+ goto autoconf_fail;
return 0;
@@ -3067,103 +3169,472 @@ autoconf_fail:
return -ENOTSUPP;
}
+/****************************** ALLOCATE FUNCTION *************************/
-/****************************** ADD 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;
-static struct usb_gadget_strings *fsg_strings_array[] = {
- &fsg_stringtab,
+ DBG(fsg, "unbind\n");
+ if (fsg->common->fsg == fsg) {
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ /* FIXME: make interruptible or killable somehow? */
+ wait_event(common->fsg_wait, common->fsg != fsg);
+ }
+
+ usb_free_all_descriptors(&fsg->function);
+}
+
+static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct fsg_lun_opts, group);
+}
+
+static inline struct fsg_opts *to_fsg_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct fsg_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_lun_opts);
+CONFIGFS_ATTR_OPS(fsg_lun_opts);
+
+static void fsg_lun_attr_release(struct config_item *item)
+{
+ struct fsg_lun_opts *lun_opts;
+
+ lun_opts = to_fsg_lun_opts(item);
+ kfree(lun_opts);
+}
+
+static struct configfs_item_operations fsg_lun_item_ops = {
+ .release = fsg_lun_attr_release,
+ .show_attribute = fsg_lun_opts_attr_show,
+ .store_attribute = fsg_lun_opts_attr_store,
+};
+
+static ssize_t fsg_lun_opts_file_show(struct fsg_lun_opts *opts, char *page)
+{
+ struct fsg_opts *fsg_opts;
+
+ fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+ return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page);
+}
+
+static ssize_t fsg_lun_opts_file_store(struct fsg_lun_opts *opts,
+ const char *page, size_t len)
+{
+ struct fsg_opts *fsg_opts;
+
+ fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+ return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_file =
+ __CONFIGFS_ATTR(file, S_IRUGO | S_IWUSR, fsg_lun_opts_file_show,
+ fsg_lun_opts_file_store);
+
+static ssize_t fsg_lun_opts_ro_show(struct fsg_lun_opts *opts, char *page)
+{
+ return fsg_show_ro(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_ro_store(struct fsg_lun_opts *opts,
+ const char *page, size_t len)
+{
+ struct fsg_opts *fsg_opts;
+
+ fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+ return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_ro =
+ __CONFIGFS_ATTR(ro, S_IRUGO | S_IWUSR, fsg_lun_opts_ro_show,
+ fsg_lun_opts_ro_store);
+
+static ssize_t fsg_lun_opts_removable_show(struct fsg_lun_opts *opts,
+ char *page)
+{
+ return fsg_show_removable(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_removable_store(struct fsg_lun_opts *opts,
+ const char *page, size_t len)
+{
+ return fsg_store_removable(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_removable =
+ __CONFIGFS_ATTR(removable, S_IRUGO | S_IWUSR,
+ fsg_lun_opts_removable_show,
+ fsg_lun_opts_removable_store);
+
+static ssize_t fsg_lun_opts_cdrom_show(struct fsg_lun_opts *opts, char *page)
+{
+ return fsg_show_cdrom(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_cdrom_store(struct fsg_lun_opts *opts,
+ const char *page, size_t len)
+{
+ struct fsg_opts *fsg_opts;
+
+ fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+ return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page,
+ len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_cdrom =
+ __CONFIGFS_ATTR(cdrom, S_IRUGO | S_IWUSR, fsg_lun_opts_cdrom_show,
+ fsg_lun_opts_cdrom_store);
+
+static ssize_t fsg_lun_opts_nofua_show(struct fsg_lun_opts *opts, char *page)
+{
+ return fsg_show_nofua(opts->lun, page);
+}
+
+static ssize_t fsg_lun_opts_nofua_store(struct fsg_lun_opts *opts,
+ const char *page, size_t len)
+{
+ return fsg_store_nofua(opts->lun, page, len);
+}
+
+static struct fsg_lun_opts_attribute fsg_lun_opts_nofua =
+ __CONFIGFS_ATTR(nofua, S_IRUGO | S_IWUSR, fsg_lun_opts_nofua_show,
+ fsg_lun_opts_nofua_store);
+
+static struct configfs_attribute *fsg_lun_attrs[] = {
+ &fsg_lun_opts_file.attr,
+ &fsg_lun_opts_ro.attr,
+ &fsg_lun_opts_removable.attr,
+ &fsg_lun_opts_cdrom.attr,
+ &fsg_lun_opts_nofua.attr,
NULL,
};
-static int fsg_bind_config(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct fsg_common *common)
+static struct config_item_type fsg_lun_type = {
+ .ct_item_ops = &fsg_lun_item_ops,
+ .ct_attrs = fsg_lun_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *fsg_lun_make(struct config_group *group,
+ const char *name)
{
- struct fsg_dev *fsg;
+ struct fsg_lun_opts *opts;
+ struct fsg_opts *fsg_opts;
+ struct fsg_lun_config config;
+ char *num_str;
+ u8 num;
+ int ret;
+
+ num_str = strchr(name, '.');
+ if (!num_str) {
+ pr_err("Unable to locate . in LUN.NUMBER\n");
+ return ERR_PTR(-EINVAL);
+ }
+ num_str++;
+
+ ret = kstrtou8(num_str, 0, &num);
+ if (ret)
+ return ERR_PTR(ret);
+
+ fsg_opts = to_fsg_opts(&group->cg_item);
+ if (num >= FSG_MAX_LUNS)
+ return ERR_PTR(-ERANGE);
+
+ mutex_lock(&fsg_opts->lock);
+ if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.removable = true;
+
+ ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
+ (const char **)&group->cg_item.ci_name);
+ if (ret) {
+ kfree(opts);
+ goto out;
+ }
+ opts->lun = fsg_opts->common->luns[num];
+ opts->lun_id = num;
+ mutex_unlock(&fsg_opts->lock);
+
+ config_group_init_type_name(&opts->group, name, &fsg_lun_type);
+
+ return &opts->group;
+out:
+ mutex_unlock(&fsg_opts->lock);
+ return ERR_PTR(ret);
+}
+
+static void fsg_lun_drop(struct config_group *group, struct config_item *item)
+{
+ struct fsg_lun_opts *lun_opts;
+ struct fsg_opts *fsg_opts;
+
+ lun_opts = to_fsg_lun_opts(item);
+ fsg_opts = to_fsg_opts(&group->cg_item);
+
+ mutex_lock(&fsg_opts->lock);
+ if (fsg_opts->refcnt) {
+ struct config_item *gadget;
+
+ gadget = group->cg_item.ci_parent->ci_parent;
+ unregister_gadget_item(gadget);
+ }
+
+ fsg_common_remove_lun(lun_opts->lun, fsg_opts->common->sysfs);
+ fsg_opts->common->luns[lun_opts->lun_id] = NULL;
+ lun_opts->lun_id = 0;
+ mutex_unlock(&fsg_opts->lock);
+
+ config_item_put(item);
+}
+
+CONFIGFS_ATTR_STRUCT(fsg_opts);
+CONFIGFS_ATTR_OPS(fsg_opts);
+
+static void fsg_attr_release(struct config_item *item)
+{
+ struct fsg_opts *opts = to_fsg_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations fsg_item_ops = {
+ .release = fsg_attr_release,
+ .show_attribute = fsg_opts_attr_show,
+ .store_attribute = fsg_opts_attr_store,
+};
+
+static ssize_t fsg_opts_stall_show(struct fsg_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->common->can_stall);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t fsg_opts_stall_store(struct fsg_opts *opts, const char *page,
+ size_t len)
+{
+ int ret;
+ bool stall;
+
+ mutex_lock(&opts->lock);
+
+ if (opts->refcnt) {
+ mutex_unlock(&opts->lock);
+ return -EBUSY;
+ }
+
+ ret = strtobool(page, &stall);
+ if (!ret) {
+ opts->common->can_stall = stall;
+ ret = len;
+ }
+
+ mutex_unlock(&opts->lock);
+
+ return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_stall =
+ __CONFIGFS_ATTR(stall, S_IRUGO | S_IWUSR, fsg_opts_stall_show,
+ fsg_opts_stall_store);
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+static ssize_t fsg_opts_num_buffers_show(struct fsg_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->common->fsg_num_buffers);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t fsg_opts_num_buffers_store(struct fsg_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ ret = fsg_num_buffers_validate(num);
+ if (ret)
+ goto end;
+
+ fsg_common_set_num_buffers(opts->common, num);
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct fsg_opts_attribute fsg_opts_num_buffers =
+ __CONFIGFS_ATTR(num_buffers, S_IRUGO | S_IWUSR,
+ fsg_opts_num_buffers_show,
+ fsg_opts_num_buffers_store);
+
+#endif
+
+static struct configfs_attribute *fsg_attrs[] = {
+ &fsg_opts_stall.attr,
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ &fsg_opts_num_buffers.attr,
+#endif
+ NULL,
+};
+
+static struct configfs_group_operations fsg_group_ops = {
+ .make_group = fsg_lun_make,
+ .drop_item = fsg_lun_drop,
+};
+
+static struct config_item_type fsg_func_type = {
+ .ct_item_ops = &fsg_item_ops,
+ .ct_group_ops = &fsg_group_ops,
+ .ct_attrs = fsg_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void fsg_free_inst(struct usb_function_instance *fi)
+{
+ struct fsg_opts *opts;
+
+ opts = fsg_opts_from_func_inst(fi);
+ fsg_common_put(opts->common);
+ kfree(opts);
+}
+
+static struct usb_function_instance *fsg_alloc_inst(void)
+{
+ struct fsg_opts *opts;
+ struct fsg_lun_config config;
int rc;
- fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
- if (unlikely(!fsg))
- return -ENOMEM;
+ 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;
- fsg->function.name = FSG_DRIVER_DESC;
- fsg->function.strings = fsg_strings_array;
- fsg->function.bind = fsg_bind;
- fsg->function.unbind = fsg_unbind;
- fsg->function.setup = fsg_setup;
- fsg->function.set_alt = fsg_set_alt;
- fsg->function.disable = fsg_disable;
+ rc = fsg_common_set_num_buffers(opts->common,
+ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
+ if (rc)
+ goto release_luns;
- fsg->common = common;
- /*
- * Our caller holds a reference to common structure so we
- * don't have to be worry about it being freed until we return
- * from this function. So instead of incrementing counter now
- * and decrement in error recovery we increment it only when
- * call to usb_add_function() was successful.
- */
+ pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
- rc = usb_add_function(c, &fsg->function);
- if (unlikely(rc))
- kfree(fsg);
- else
- fsg_common_get(fsg->common);
- return rc;
+ 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 inline int __deprecated __maybe_unused
-fsg_add(struct usb_composite_dev *cdev, struct usb_configuration *c,
- struct fsg_common *common)
+static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
{
- return fsg_bind_config(cdev, c, common);
+ 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 *************************/
-struct fsg_module_parameters {
- char *file[FSG_MAX_LUNS];
- bool ro[FSG_MAX_LUNS];
- bool removable[FSG_MAX_LUNS];
- bool cdrom[FSG_MAX_LUNS];
- bool nofua[FSG_MAX_LUNS];
-
- unsigned int file_count, ro_count, removable_count, cdrom_count;
- unsigned int nofua_count;
- unsigned int luns; /* nluns */
- bool stall; /* can_stall */
-};
-#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
- module_param_array_named(prefix ## name, params.name, type, \
- &prefix ## params.name ## _count, \
- S_IRUGO); \
- MODULE_PARM_DESC(prefix ## name, desc)
-
-#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
- module_param_named(prefix ## name, params.name, type, \
- S_IRUGO); \
- MODULE_PARM_DESC(prefix ## name, desc)
-
-#define FSG_MODULE_PARAMETERS(prefix, params) \
- _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
- "names of backing files or devices"); \
- _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
- "true to force read-only"); \
- _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
- "true to simulate removable media"); \
- _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
- "true to simulate CD-ROM instead of disk"); \
- _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \
- "true to ignore SCSI WRITE(10,12) FUA bit"); \
- _FSG_MODULE_PARAM(prefix, params, luns, uint, \
- "number of LUNs"); \
- _FSG_MODULE_PARAM(prefix, params, stall, bool, \
- "false to prevent bulk stalls")
-
-static void
-fsg_config_from_params(struct fsg_config *cfg,
- const struct fsg_module_parameters *params)
+void fsg_config_from_params(struct fsg_config *cfg,
+ const struct fsg_module_parameters *params,
+ unsigned int fsg_num_buffers)
{
struct fsg_lun_config *lun;
unsigned i;
@@ -3175,40 +3646,23 @@ fsg_config_from_params(struct fsg_config *cfg,
for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
lun->ro = !!params->ro[i];
lun->cdrom = !!params->cdrom[i];
- lun->removable = /* Removable by default */
- params->removable_count <= i || params->removable[i];
+ lun->removable = !!params->removable[i];
lun->filename =
params->file_count > i && params->file[i][0]
? params->file[i]
- : 0;
+ : NULL;
}
/* Let MSF use defaults */
- cfg->lun_name_format = 0;
- cfg->thread_name = 0;
- cfg->vendor_name = 0;
- cfg->product_name = 0;
- cfg->release = 0xffff;
+ 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;
}
-
-static inline struct fsg_common *
-fsg_common_from_params(struct fsg_common *common,
- struct usb_composite_dev *cdev,
- const struct fsg_module_parameters *params)
- __attribute__((unused));
-static inline struct fsg_common *
-fsg_common_from_params(struct fsg_common *common,
- struct usb_composite_dev *cdev,
- const struct fsg_module_parameters *params)
-{
- struct fsg_config cfg;
- fsg_config_from_params(&cfg, params);
- return fsg_common_init(common, cdev, &cfg);
-}
+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
index 3797b3d6c62..807b31c0edc 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/f_midi.c
@@ -21,7 +21,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -33,6 +32,8 @@
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
+#include "u_f.h"
+
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
@@ -192,20 +193,10 @@ static struct usb_gadget_strings *midi_strings[] = {
NULL,
};
-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
+ unsigned length)
{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (req) {
- req->length = length;
- req->buf = kmalloc(length, GFP_ATOMIC);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- req = NULL;
- }
- }
- return req;
+ return alloc_ep_req(ep, length, length);
}
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
@@ -366,7 +357,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req =
- alloc_ep_req(midi->out_ep, midi->buflen);
+ midi_alloc_ep_req(midi->out_ep, midi->buflen);
if (req == NULL)
return -ENOMEM;
@@ -415,7 +406,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(midi->id);
midi->id = NULL;
- usb_free_descriptors(f->descriptors);
+ usb_free_all_descriptors(f);
kfree(midi);
}
@@ -547,7 +538,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
return;
if (!req)
- req = alloc_ep_req(ep, midi->buflen);
+ req = midi_alloc_ep_req(ep, midi->buflen);
if (!req) {
ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
@@ -673,9 +664,10 @@ static int f_midi_register_card(struct f_midi *midi)
.dev_free = f_midi_snd_free,
};
- err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&midi->gadget->dev, midi->index, midi->id,
+ THIS_MODULE, 0, &card);
if (err < 0) {
- ERROR(midi, "snd_card_create() failed\n");
+ ERROR(midi, "snd_card_new() failed\n");
goto fail;
}
midi->card = card;
@@ -712,8 +704,6 @@ static int f_midi_register_card(struct f_midi *midi)
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
- snd_card_set_dev(card, &midi->gadget->dev);
-
/* register it - we're ready to go */
err = snd_card_register(card);
if (err < 0) {
@@ -780,7 +770,7 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
midi->out_ep->driver_data = cdev; /* claim */
/* allocate temporary function list */
- midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(midi_function),
+ midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function),
GFP_KERNEL);
if (!midi_function) {
status = -ENOMEM;
@@ -882,19 +872,25 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
* 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)) {
- c->highspeed = true;
bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
f->hs_descriptors = usb_copy_descriptors(midi_function);
- } else {
- f->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)
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index aab8eded045..a9499fd3079 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -16,6 +16,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
@@ -23,6 +24,8 @@
#include <linux/usb/cdc.h>
#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_ncm.h"
/*
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
@@ -56,8 +59,9 @@ struct f_ncm {
u8 notify_state;
bool is_open;
- struct ndp_parser_opts *parser_opts;
+ const struct ndp_parser_opts *parser_opts;
bool is_crc;
+ u32 ndp_sign;
/*
* for notification, it is accessed from both
@@ -102,7 +106,7 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g)
USB_CDC_NCM_NTB32_SUPPORTED)
static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
- .wLength = sizeof 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),
@@ -121,10 +125,10 @@ static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
* waste less bandwidth.
*/
-#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define NCM_STATUS_INTERVAL_MS 32
#define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
-static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
+static struct usb_interface_assoc_descriptor ncm_iad_desc = {
.bLength = sizeof ncm_iad_desc,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
@@ -138,7 +142,7 @@ static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
/* interface descriptor: */
-static struct usb_interface_descriptor ncm_control_intf __initdata = {
+static struct usb_interface_descriptor ncm_control_intf = {
.bLength = sizeof ncm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -150,7 +154,7 @@ static struct usb_interface_descriptor ncm_control_intf __initdata = {
/* .iInterface = DYNAMIC */
};
-static struct usb_cdc_header_desc ncm_header_desc __initdata = {
+static struct usb_cdc_header_desc ncm_header_desc = {
.bLength = sizeof ncm_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
@@ -158,7 +162,7 @@ static struct usb_cdc_header_desc ncm_header_desc __initdata = {
.bcdCDC = cpu_to_le16(0x0110),
};
-static struct usb_cdc_union_desc ncm_union_desc __initdata = {
+static struct usb_cdc_union_desc ncm_union_desc = {
.bLength = sizeof(ncm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -166,7 +170,7 @@ static struct usb_cdc_union_desc ncm_union_desc __initdata = {
/* .bSlaveInterface0 = DYNAMIC */
};
-static struct usb_cdc_ether_desc ecm_desc __initdata = {
+static struct usb_cdc_ether_desc ecm_desc = {
.bLength = sizeof ecm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
@@ -181,7 +185,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
#define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
-static struct usb_cdc_ncm_desc ncm_desc __initdata = {
+static struct usb_cdc_ncm_desc ncm_desc = {
.bLength = sizeof ncm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_NCM_TYPE,
@@ -193,7 +197,7 @@ static struct usb_cdc_ncm_desc ncm_desc __initdata = {
/* the default data interface has no endpoints ... */
-static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
+static struct usb_interface_descriptor ncm_data_nop_intf = {
.bLength = sizeof ncm_data_nop_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -208,7 +212,7 @@ static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
/* ... but the "real" data interface has two bulk endpoints */
-static struct usb_interface_descriptor ncm_data_intf __initdata = {
+static struct usb_interface_descriptor ncm_data_intf = {
.bLength = sizeof ncm_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
@@ -223,17 +227,17 @@ static struct usb_interface_descriptor ncm_data_intf __initdata = {
/* full speed support: */
-static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
- .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+ .bInterval = NCM_STATUS_INTERVAL_MS,
};
-static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -241,7 +245,7 @@ static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
+static struct usb_endpoint_descriptor fs_ncm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -249,7 +253,7 @@ static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
+static struct usb_descriptor_header *ncm_fs_function[] = {
(struct usb_descriptor_header *) &ncm_iad_desc,
/* CDC NCM control descriptors */
(struct usb_descriptor_header *) &ncm_control_intf,
@@ -268,16 +272,16 @@ static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
/* high speed support: */
-static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
- .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+ .bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
};
-static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -286,7 +290,7 @@ static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
+static struct usb_endpoint_descriptor hs_ncm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -295,7 +299,7 @@ static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *ncm_hs_function[] __initdata = {
+static struct usb_descriptor_header *ncm_hs_function[] = {
(struct usb_descriptor_header *) &ncm_iad_desc,
/* CDC NCM control descriptors */
(struct usb_descriptor_header *) &ncm_control_intf,
@@ -321,7 +325,7 @@ static struct usb_descriptor_header *ncm_hs_function[] __initdata = {
static struct usb_string ncm_string_defs[] = {
[STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
- [STRING_MAC_IDX].s = NULL /* DYNAMIC */,
+ [STRING_MAC_IDX].s = "",
[STRING_DATA_IDX].s = "CDC Network Data",
[STRING_IAD_IDX].s = "CDC NCM",
{ } /* end of list */
@@ -390,8 +394,8 @@ struct ndp_parser_opts {
.next_fp_index = 2, \
}
-static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
-static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static const struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
{
@@ -732,8 +736,7 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
default:
goto invalid;
}
- ncm->parser_opts->ndp_sign &= ~NCM_NDP_HDR_CRC_MASK;
- ncm->parser_opts->ndp_sign |= ndp_hdr_crc;
+ ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
value = 0;
break;
}
@@ -869,15 +872,19 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
struct sk_buff *skb2;
int ncb_len = 0;
__le16 *tmp;
- int div = ntb_parameters.wNdpInDivisor;
- int rem = ntb_parameters.wNdpInPayloadRemainder;
+ int div;
+ int rem;
int pad;
- int ndp_align = ntb_parameters.wNdpInAlignment;
+ int ndp_align;
int ndp_pad;
unsigned max_size = ncm->port.fixed_in_len;
- struct ndp_parser_opts *opts = ncm->parser_opts;
+ const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
+ div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+ 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;
@@ -917,7 +924,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
tmp = (void *)tmp + ndp_pad;
/* NDP */
- put_unaligned_le32(opts->ndp_sign, tmp); /* dwSignature */
+ put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
tmp += 2;
/* wLength */
put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
@@ -961,7 +968,7 @@ static int ncm_unwrap_ntb(struct gether *port,
struct sk_buff *skb2;
int ret = -EINVAL;
unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
- struct ndp_parser_opts *opts = ncm->parser_opts;
+ const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter;
@@ -998,7 +1005,7 @@ static int ncm_unwrap_ntb(struct gether *port,
/* walk through NDP */
tmp = ((void *)skb->data) + index;
- if (get_unaligned_le32(tmp) != opts->ndp_sign) {
+ if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
goto err;
}
@@ -1148,13 +1155,44 @@ static void ncm_close(struct gether *geth)
/* ethernet function driver setup/binding */
-static int __init
-ncm_bind(struct usb_configuration *c, struct usb_function *f)
+static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep;
+ struct f_ncm_opts *ncm_opts;
+
+ if (!can_support_ecm(cdev->gadget))
+ return -EINVAL;
+
+ ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to ncm_opts->bound access
+ */
+ if (!ncm_opts->bound) {
+ mutex_lock(&ncm_opts->lock);
+ gether_set_gadget(ncm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ncm_opts->net);
+ mutex_unlock(&ncm_opts->lock);
+ if (status)
+ return status;
+ ncm_opts->bound = true;
+ }
+ us = usb_gstrings_attach(cdev, ncm_strings,
+ ARRAY_SIZE(ncm_string_defs));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
+ ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
+ ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
+ ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
+ ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
@@ -1208,30 +1246,18 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->notify_req->context = ncm;
ncm->notify_req->complete = ncm_notify_complete;
- /* copy descriptors, and track endpoint copies */
- f->descriptors = usb_copy_descriptors(ncm_fs_function);
- if (!f->descriptors)
- goto fail;
-
/*
* 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_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;
-
- /* copy descriptors, and track endpoint copies */
- f->hs_descriptors = usb_copy_descriptors(ncm_hs_function);
- if (!f->hs_descriptors)
- goto fail;
- }
+ 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
@@ -1248,9 +1274,7 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
-
+ usb_free_all_descriptors(f);
if (ncm->notify_req) {
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
@@ -1259,9 +1283,9 @@ fail:
/* we might as well release our claims on endpoints */
if (ncm->notify)
ncm->notify->driver_data = NULL;
- if (ncm->port.out_ep->desc)
+ if (ncm->port.out_ep)
ncm->port.out_ep->driver_data = NULL;
- if (ncm->port.in_ep->desc)
+ if (ncm->port.in_ep)
ncm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -1269,95 +1293,131 @@ fail:
return status;
}
-static void
-ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
{
- struct f_ncm *ncm = func_to_ncm(f);
+ return container_of(to_config_group(item), struct f_ncm_opts,
+ func_inst.group);
+}
- DBG(c->cdev, "ncm unbind\n");
+/* f_ncm_item_ops */
+USB_ETHERNET_CONFIGFS_ITEM(ncm);
- if (gadget_is_dualspeed(c->cdev->gadget))
- usb_free_descriptors(f->hs_descriptors);
- usb_free_descriptors(f->descriptors);
+/* f_ncm_opts_dev_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
- kfree(ncm->notify_req->buf);
- usb_ep_free_request(ncm->notify, ncm->notify_req);
+/* f_ncm_opts_host_addr */
+USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
- ncm_string_defs[1].s = NULL;
- kfree(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);
}
-/**
- * ncm_bind_config - add CDC Network link to a configuration
- * @c: the configuration to support the network link
- * @ethaddr: a buffer in which the ethernet address of the host side
- * side of the link was recorded
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gether_setup(). Caller is also responsible
- * for calling @gether_cleanup() before module unload.
- */
-int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+static struct usb_function_instance *ncm_alloc_inst(void)
{
- struct f_ncm *ncm;
- int status;
+ 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);
+ }
- if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
- return -EINVAL;
+ config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
- /* maybe allocate device-global string IDs */
- if (ncm_string_defs[0].id == 0) {
+ return &opts->func_inst;
+}
- /* control interface label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- ncm_string_defs[STRING_CTRL_IDX].id = status;
- ncm_control_intf.iInterface = status;
+static void ncm_free(struct usb_function *f)
+{
+ struct f_ncm *ncm;
+ struct f_ncm_opts *opts;
- /* data interface label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- ncm_string_defs[STRING_DATA_IDX].id = status;
- ncm_data_nop_intf.iInterface = status;
- ncm_data_intf.iInterface = status;
+ 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);
+}
- /* MAC address */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- ncm_string_defs[STRING_MAC_IDX].id = status;
- ecm_desc.iMACAddress = status;
+static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
- /* IAD */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- ncm_string_defs[STRING_IAD_IDX].id = status;
- ncm_iad_desc.iFunction = status;
- }
+ 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);
+ ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
if (!ncm)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ opts = container_of(fi, struct f_ncm_opts, func_inst);
+ mutex_lock(&opts->lock);
+ opts->refcnt++;
/* export host's Ethernet address in CDC format */
- snprintf(ncm->ethaddr, sizeof ncm->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
- ncm_string_defs[1].s = ncm->ethaddr;
+ status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12) { /* strlen("01234567890a") */
+ kfree(ncm);
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-EINVAL);
+ }
+ ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
+ ncm->port.ioport = netdev_priv(opts->net);
+ mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.func.name = "cdc_network";
- ncm->port.func.strings = ncm_strings;
/* descriptors are per-instance copies */
ncm->port.func.bind = ncm_bind;
ncm->port.func.unbind = ncm_unbind;
@@ -1365,14 +1425,14 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
ncm->port.func.get_alt = ncm_get_alt;
ncm->port.func.setup = ncm_setup;
ncm->port.func.disable = ncm_disable;
+ ncm->port.func.free_func = ncm_free;
ncm->port.wrap = ncm_wrap_ntb;
ncm->port.unwrap = ncm_unwrap_ntb;
- status = usb_add_function(c, &ncm->port.func);
- if (status) {
- ncm_string_defs[1].s = NULL;
- kfree(ncm);
- }
- return status;
+ return &ncm->port.func;
}
+
+DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
index 5f400f66aa9..aebae1853bc 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/f_obex.c
@@ -72,7 +72,7 @@ static struct usb_gadget_strings *obex_strings[] = {
/*-------------------------------------------------------------------------*/
-static struct usb_interface_descriptor obex_control_intf __initdata = {
+static struct usb_interface_descriptor obex_control_intf = {
.bLength = sizeof(obex_control_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
@@ -83,7 +83,7 @@ static struct usb_interface_descriptor obex_control_intf __initdata = {
.bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX,
};
-static struct usb_interface_descriptor obex_data_nop_intf __initdata = {
+static struct usb_interface_descriptor obex_data_nop_intf = {
.bLength = sizeof(obex_data_nop_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
@@ -93,7 +93,7 @@ static struct usb_interface_descriptor obex_data_nop_intf __initdata = {
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
-static struct usb_interface_descriptor obex_data_intf __initdata = {
+static struct usb_interface_descriptor obex_data_intf = {
.bLength = sizeof(obex_data_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 2,
@@ -103,14 +103,14 @@ static struct usb_interface_descriptor obex_data_intf __initdata = {
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
-static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = {
+static struct usb_cdc_header_desc obex_cdc_header_desc = {
.bLength = sizeof(obex_cdc_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0120),
};
-static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = {
+static struct usb_cdc_union_desc obex_cdc_union_desc = {
.bLength = sizeof(obex_cdc_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
@@ -118,7 +118,7 @@ static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = {
.bSlaveInterface0 = 2,
};
-static struct usb_cdc_obex_desc obex_desc __initdata = {
+static struct usb_cdc_obex_desc obex_desc = {
.bLength = sizeof(obex_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_OBEX_TYPE,
@@ -127,7 +127,7 @@ static struct usb_cdc_obex_desc obex_desc __initdata = {
/* High-Speed Support */
-static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = {
+static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -136,7 +136,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = {
+static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -145,7 +145,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(512),
};
-static struct usb_descriptor_header *hs_function[] __initdata = {
+static struct usb_descriptor_header *hs_function[] = {
(struct usb_descriptor_header *) &obex_control_intf,
(struct usb_descriptor_header *) &obex_cdc_header_desc,
(struct usb_descriptor_header *) &obex_desc,
@@ -160,7 +160,7 @@ static struct usb_descriptor_header *hs_function[] __initdata = {
/* Full-Speed Support */
-static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = {
+static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -168,7 +168,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = {
+static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -176,7 +176,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *fs_function[] __initdata = {
+static struct usb_descriptor_header *fs_function[] = {
(struct usb_descriptor_header *) &obex_control_intf,
(struct usb_descriptor_header *) &obex_cdc_header_desc,
(struct usb_descriptor_header *) &obex_desc,
@@ -290,14 +290,40 @@ static void obex_disconnect(struct gserial *g)
/*-------------------------------------------------------------------------*/
-static int __init
-obex_bind(struct usb_configuration *c, struct usb_function *f)
+/* Some controllers can't support CDC OBEX ... */
+static inline bool can_support_obex(struct usb_configuration *c)
+{
+ /* Since the first interface is a NOP, we can ignore the
+ * issue of multi-interface support on most controllers.
+ *
+ * Altsettings are mandatory, however...
+ */
+ if (!gadget_supports_altsettings(c->cdev->gadget))
+ return false;
+
+ /* everything else is *probably* fine ... */
+ return true;
+}
+
+static int obex_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_obex *obex = func_to_obex(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep;
+ if (!can_support_obex(c))
+ return -EINVAL;
+
+ us = usb_gstrings_attach(cdev, obex_strings,
+ ARRAY_SIZE(obex_string_defs));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
+ obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
+ obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
+
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -319,6 +345,7 @@ obex_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific endpoints */
+ status = -ENODEV;
ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
if (!ep)
goto fail;
@@ -331,23 +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);
-
/* 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;
+ 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);
- }
+ 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.
@@ -368,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;
@@ -379,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);
+}
+
+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);
+}
- /* 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 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;
+
+ opts = container_of(f, struct f_serial_opts, func_inst);
+ gserial_free_line(opts->port_num);
+ kfree(opts);
+}
- obex_data_nop_intf.iInterface =
- obex_data_intf.iInterface = status;
+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 7cdcb63b21f..f2b781773ee 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/netdevice.h>
@@ -25,6 +26,7 @@
#include <linux/usb/composite.h>
#include "u_phonet.h"
+#include "u_ether.h"
#define PN_MEDIA_USB 0x1B
#define MAXPACKET 512
@@ -301,7 +303,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
struct page *page;
int err;
- page = alloc_page(gfp_flags);
+ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
@@ -345,7 +347,7 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
}
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
- skb->len <= 1, req->actual);
+ skb->len <= 1, req->actual, PAGE_SIZE);
page = NULL;
if (req->actual < req->length) { /* Last fragment */
@@ -478,8 +480,7 @@ static void pn_disconnect(struct usb_function *f)
/*-------------------------------------------------------------------------*/
-static __init
-int pn_bind(struct usb_configuration *c, struct usb_function *f)
+static int pn_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct usb_gadget *gadget = cdev->gadget;
@@ -487,6 +488,25 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
int status, i;
+ struct f_phonet_opts *phonet_opts;
+
+ phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
+
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to phonet_opts->bound access
+ */
+ if (!phonet_opts->bound) {
+ gphonet_set_gadget(phonet_opts->net, gadget);
+ status = gphonet_register_netdev(phonet_opts->net);
+ if (status)
+ return status;
+ phonet_opts->bound = true;
+ }
+
/* Reserve interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
@@ -515,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;
@@ -531,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;
@@ -540,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)
@@ -556,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;
@@ -569,61 +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, size;
+ struct f_phonet_opts *opts;
+ int size;
size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
fp = kzalloc(size, GFP_KERNEL);
if (!fp)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ opts = container_of(fi, struct f_phonet_opts, func_inst);
- fp->dev = dev;
+ fp->dev = opts->net;
fp->function.name = "phonet";
fp->function.bind = pn_bind;
fp->function.unbind = pn_unbind;
fp->function.set_alt = pn_set_alt;
fp->function.get_alt = pn_get_alt;
fp->function.disable = pn_disconnect;
+ fp->function.free_func = phonet_free;
spin_lock_init(&fp->rx.lock);
- err = usb_add_function(c, &fp->function);
- if (err)
- kfree(fp);
- return err;
+ return &fp->function;
}
-int __init gphonet_setup(struct usb_gadget *gadget)
+struct net_device *gphonet_setup_default(void)
{
+ struct net_device *dev;
struct phonet_port *port;
- int err;
/* Create net device */
- BUG_ON(dev);
dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup);
if (!dev)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
port = netdev_priv(dev);
spin_lock_init(&port->lock);
netif_carrier_off(dev);
- SET_NETDEV_DEV(dev, &gadget->dev);
- err = register_netdev(dev);
- if (err)
- free_netdev(dev);
- return err;
+ return dev;
+}
+
+void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
+{
+ SET_NETDEV_DEV(net, &g->dev);
}
-void gphonet_cleanup(void)
+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 704d1d94f72..9c41e9515b8 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -5,7 +5,7 @@
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ * 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
@@ -17,14 +17,17 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/atomic.h>
#include "u_ether.h"
+#include "u_ether_configfs.h"
+#include "u_rndis.h"
#include "rndis.h"
-
+#include "configfs.h"
/*
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
@@ -71,6 +74,8 @@ struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
+ u32 vendorID;
+ const char *manufacturer;
int config;
struct usb_ep *notify;
@@ -99,7 +104,7 @@ 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 */
@@ -188,7 +193,7 @@ static struct usb_endpoint_descriptor fs_notify_desc = {
.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 = {
@@ -234,7 +239,7 @@ static struct usb_endpoint_descriptor hs_notify_desc = {
.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 = {
@@ -282,7 +287,7 @@ static struct usb_endpoint_descriptor ss_notify_desc = {
.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_ss_ep_comp_descriptor ss_intr_comp_desc = {
@@ -373,7 +378,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
if (skb2)
rndis_add_hdr(skb2);
- dev_kfree_skb_any(skb);
+ dev_kfree_skb(skb);
return skb2;
}
@@ -445,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);
}
@@ -500,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;
}
@@ -636,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);
}
@@ -647,12 +652,19 @@ 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
@@ -660,9 +672,51 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_string *us;
int status;
struct usb_ep *ep;
+ struct f_rndis_opts *rndis_opts;
+
+ if (!can_support_rndis(c))
+ return -EINVAL;
+
+ rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+
+ if (cdev->use_os_string) {
+ f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
+ GFP_KERNEL);
+ if (!f->os_desc_table)
+ return -ENOMEM;
+ f->os_desc_n = 1;
+ f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
+ }
+
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to rndis_opts->bound access
+ */
+ if (!rndis_opts->bound) {
+ gether_set_gadget(rndis_opts->net, cdev->gadget);
+ status = gether_register_netdev(rndis_opts->net);
+ if (status)
+ goto fail;
+ rndis_opts->bound = true;
+ }
+
+ us = usb_gstrings_attach(cdev, rndis_strings,
+ ARRAY_SIZE(rndis_string_defs));
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto fail;
+ }
+ rndis_control_intf.iInterface = us[0].id;
+ rndis_data_intf.iInterface = us[1].id;
+ rndis_iad_descriptor.iFunction = us[2].id;
+
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
@@ -719,60 +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;
-
/* 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;
- if (gadget_is_superspeed(c->cdev->gadget)) {
- ss_in_desc.bEndpointAddress =
- fs_in_desc.bEndpointAddress;
- ss_out_desc.bEndpointAddress =
- fs_out_desc.bEndpointAddress;
- ss_notify_desc.bEndpointAddress =
- fs_notify_desc.bEndpointAddress;
-
- /* copy descriptors, and track endpoint copies */
- f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
- if (!f->ss_descriptors)
- goto fail;
- }
+ 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
@@ -787,12 +814,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
- usb_free_descriptors(f->ss_descriptors);
- 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);
@@ -802,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_ep->desc)
+ if (rndis->port.out_ep)
rndis->port.out_ep->driver_data = NULL;
- if (rndis->port.in_ep->desc)
+ if (rndis->port.in_ep)
rndis->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -812,92 +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_superspeed(c->cdev->gadget))
- usb_free_descriptors(f->ss_descriptors);
- 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)
{
- /* everything else is *presumably* fine */
- return true;
+ struct f_rndis_opts *opts;
+ struct usb_os_desc *descs[1];
+ char *names[1];
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = rndis_free_inst;
+ opts->net = gether_setup_default();
+ if (IS_ERR(opts->net)) {
+ struct net_device *net = opts->net;
+ kfree(opts);
+ return ERR_CAST(net);
+ }
+ INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
+
+ descs[0] = &opts->rndis_os_desc;
+ names[0] = "rndis";
+ usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+ names, THIS_MODULE);
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &rndis_func_type);
+
+ return &opts->func_inst;
}
-/**
- * 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
-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;
-
- /* IAD iFunction label */
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- rndis_string_defs[2].id = status;
- rndis_iad_descriptor.iFunction = 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;
@@ -907,19 +988,44 @@ 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 cf33a8d0fd5..9ecbcbf36a4 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include "u_serial.h"
@@ -42,7 +43,7 @@ static inline struct f_gser *func_to_gser(struct usb_function *f)
/* interface descriptor: */
-static struct usb_interface_descriptor gser_interface_desc __initdata = {
+static struct usb_interface_descriptor gser_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -55,21 +56,21 @@ static struct usb_interface_descriptor gser_interface_desc __initdata = {
/* full speed support: */
-static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static struct usb_descriptor_header *gser_fs_function[] __initdata = {
+static struct usb_descriptor_header *gser_fs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_fs_in_desc,
(struct usb_descriptor_header *) &gser_fs_out_desc,
@@ -78,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[] = {
@@ -155,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)
@@ -185,25 +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);
-
/* 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_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;
@@ -220,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;
@@ -271,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 7aa7ac82c02..d3cd52db78f 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -16,10 +16,12 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include <linux/err.h>
#include "g_zero.h"
#include "gadget_chips.h"
-
+#include "u_f.h"
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
@@ -51,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)
@@ -59,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: */
@@ -91,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,
};
@@ -116,10 +161,34 @@ 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,
};
@@ -133,9 +202,10 @@ static struct usb_endpoint_descriptor ss_source_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
.bMaxBurst = 0,
.bmAttributes = 0,
.wBytesPerInterval = 0,
@@ -149,20 +219,67 @@ static struct usb_endpoint_descriptor ss_sink_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
.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,
+ (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,
};
@@ -185,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:
@@ -213,34 +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;
}
+ 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 */
- if (gadget_is_superspeed(c->cdev->gadget)) {
- ss_source_desc.bEndpointAddress =
- fs_source_desc.bEndpointAddress;
- ss_sink_desc.bEndpointAddress =
- fs_sink_desc.bEndpointAddress;
- f->ss_descriptors = ss_source_sink_descs;
- }
+ ss_source_desc.bEndpointAddress =
+ fs_source_desc.bEndpointAddress;
+ ss_sink_desc.bEndpointAddress =
+ fs_sink_desc.bEndpointAddress;
- DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ /*
+ * 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);
+ 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));
}
@@ -251,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) {
@@ -265,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))
@@ -292,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 */
@@ -344,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;
@@ -380,17 +661,20 @@ 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;
+ int speed = cdev->gadget->speed;
struct usb_ep *ep;
- /* one endpoint writes (sources) zeroes IN (to the host) */
+ /* one bulk endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
if (result)
@@ -400,7 +684,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
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;
@@ -409,7 +693,7 @@ 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 = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
if (result)
@@ -419,27 +703,82 @@ fail:
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)
@@ -451,38 +790,17 @@ 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_BUFSIZ;
+ req->length = USB_COMP_EP0_BUFSIZ;
/* composite driver infrastructure handles everything except
* the two control test requests.
@@ -536,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);
}
@@ -544,41 +862,386 @@ unknown:
return value;
}
-static struct usb_configuration sourcesink_driver = {
- .label = "source/sink",
- .strings = sourcesink_strings,
- .setup = sourcesink_setup,
- .bConfigurationValue = 3,
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
- /* .iConfiguration = DYNAMIC */
+static struct usb_function *source_sink_alloc_func(
+ struct usb_function_instance *fi)
+{
+ struct f_sourcesink *ss;
+ struct f_ss_opts *ss_opts;
+
+ ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+ if (!ss)
+ return NULL;
+
+ ss_opts = container_of(fi, struct f_ss_opts, func_inst);
+
+ mutex_lock(&ss_opts->lock);
+ ss_opts->refcnt++;
+ mutex_unlock(&ss_opts->lock);
+
+ pattern = ss_opts->pattern;
+ isoc_interval = ss_opts->isoc_interval;
+ isoc_maxpacket = ss_opts->isoc_maxpacket;
+ isoc_mult = ss_opts->isoc_mult;
+ isoc_maxburst = ss_opts->isoc_maxburst;
+ buflen = ss_opts->bulk_buflen;
+
+ ss->function.name = "source/sink";
+ ss->function.bind = sourcesink_bind;
+ ss->function.set_alt = sourcesink_set_alt;
+ ss->function.get_alt = sourcesink_get_alt;
+ ss->function.disable = sourcesink_disable;
+ ss->function.setup = sourcesink_setup;
+ ss->function.strings = sourcesink_strings;
+
+ ss->function.free_func = sourcesink_free_func;
+
+ return &ss->function;
+}
+
+static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_ss_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_ss_opts);
+CONFIGFS_ATTR_OPS(f_ss_opts);
+
+static void ss_attr_release(struct config_item *item)
+{
+ struct f_ss_opts *ss_opts = to_f_ss_opts(item);
+
+ usb_put_function_instance(&ss_opts->func_inst);
+}
+
+static struct configfs_item_operations ss_item_ops = {
+ .release = ss_attr_release,
+ .show_attribute = f_ss_opts_attr_show,
+ .store_attribute = f_ss_opts_attr_store,
};
-/**
- * sourcesink_add - add a source/sink testing configuration to a device
- * @cdev: the device to support the configuration
- */
-int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
+static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
{
- int id;
+ int result;
- /* allocate string ID(s) */
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_sourcesink[0].id = id;
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->pattern);
+ mutex_unlock(&opts->lock);
+
+ 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;
+}
+
+static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u16 num;
- source_sink_intf.iInterface = id;
- sourcesink_driver.iConfiguration = id;
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou16(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 1024) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_maxpacket = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket =
+ __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_maxpacket_show,
+ f_ss_opts_isoc_maxpacket_store);
+
+static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_mult);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 2) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_mult = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_mult =
+ __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_mult_show,
+ f_ss_opts_isoc_mult_store);
+
+static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_maxburst);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
- /* support autoresume for remote wakeup testing */
- if (autoresume)
- sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ if (num > 15) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_maxburst = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
- /* support OTG systems */
- if (gadget_is_otg(cdev->gadget)) {
- sourcesink_driver.descriptors = otg_desc;
- sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+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;
}
- return usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config);
+ 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 c1540648125..1ea8baf3333 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -12,11 +12,13 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
-
+#include "u_ether_configfs.h"
+#include "u_gether.h"
/*
* This function packages a simple "CDC Subset" Ethernet port with no real
@@ -74,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,
@@ -87,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,
@@ -95,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,
@@ -111,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,
@@ -121,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,
@@ -136,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,
@@ -144,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,
@@ -152,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,
@@ -165,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,
@@ -173,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,
@@ -181,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,
@@ -194,7 +196,7 @@ static struct usb_descriptor_header *hs_eth_function[] __initdata = {
/* super speed support: */
-static struct usb_endpoint_descriptor ss_subset_in_desc __initdata = {
+static struct usb_endpoint_descriptor ss_subset_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -202,7 +204,7 @@ static struct usb_endpoint_descriptor ss_subset_in_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor ss_subset_out_desc __initdata = {
+static struct usb_endpoint_descriptor ss_subset_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -210,7 +212,7 @@ static struct usb_endpoint_descriptor ss_subset_out_desc __initdata = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc __initdata = {
+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,
@@ -219,7 +221,7 @@ static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc __initdata = {
/* .bmAttributes = 0, */
};
-static struct usb_descriptor_header *ss_eth_function[] __initdata = {
+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,
@@ -236,7 +238,7 @@ static struct usb_descriptor_header *ss_eth_function[] __initdata = {
static struct usb_string geth_string_defs[] = {
[0].s = "CDC Ethernet Subset/SAFE",
- [1].s = NULL /* DYNAMIC */,
+ [1].s = "",
{ } /* end of list */
};
@@ -274,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
net = gether_connect(&geth->port);
- return IS_ERR(net) ? PTR_ERR(net) : 0;
+ return PTR_ERR_OR_ZERO(net);
}
static void geth_disable(struct usb_function *f)
@@ -290,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)
@@ -319,38 +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);
- if (!f->descriptors)
- goto fail;
-
/* 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);
- if (!f->hs_descriptors)
- goto fail;
- }
+ hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
+ hs_subset_out_desc.bEndpointAddress =
+ fs_subset_out_desc.bEndpointAddress;
- if (gadget_is_superspeed(c->cdev->gadget)) {
- ss_subset_in_desc.bEndpointAddress =
- fs_subset_in_desc.bEndpointAddress;
- ss_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;
- /* copy descriptors, and track endpoint copies */
- f->ss_descriptors = usb_copy_descriptors(ss_eth_function);
- if (!f->ss_descriptors)
- goto fail;
- }
+ 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
@@ -364,15 +380,11 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
- if (f->descriptors)
- usb_free_descriptors(f->descriptors);
- if (f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
-
+ usb_free_all_descriptors(f);
/* we might as well release our claims on endpoints */
- if (geth->port.out_ep->desc)
+ if (geth->port.out_ep)
geth->port.out_ep->driver_data = NULL;
- if (geth->port.in_ep->desc)
+ if (geth->port.in_ep)
geth->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
@@ -380,81 +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_superspeed(c->cdev->gadget))
- usb_free_descriptors(f->ss_descriptors);
- 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_audio.c b/drivers/usb/gadget/f_uac1.c
index ec7ffcd0d0c..2b4c82d84bf 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_uac1.c
@@ -14,7 +14,7 @@
#include <linux/device.h>
#include <linux/atomic.h>
-#include "u_audio.h"
+#include "u_uac1.h"
#define OUT_EP_MAX_PACKET_SIZE 200
static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
@@ -216,29 +216,6 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = {
NULL,
};
-/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
-static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
- { } /* end of list */
-};
-
-static struct usb_gadget_strings stringtab_dev = {
- .language = 0x0409, /* en-us */
- .strings = strings_dev,
-};
-
-static struct usb_gadget_strings *audio_strings[] = {
- &stringtab_dev,
- NULL,
-};
-
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
@@ -441,6 +418,7 @@ static int audio_get_intf_req(struct usb_function *f,
req->context = audio;
req->complete = f_audio_complete;
+ len = min_t(size_t, sizeof(value), len);
memcpy(req->buf, &value, len);
return len;
@@ -653,7 +631,7 @@ 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;
+ struct usb_ep *ep = NULL;
f_audio_build_desc(audio);
@@ -682,22 +660,14 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
status = -ENOMEM;
/* copy descriptors, and track endpoint copies */
- f->descriptors = usb_copy_descriptors(f_audio_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)) {
- c->highspeed = true;
- f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
- }
-
+ 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;
}
@@ -706,8 +676,7 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_audio *audio = func_to_audio(f);
- usb_free_descriptors(f->descriptors);
- usb_free_descriptors(f->hs_descriptors);
+ usb_free_all_descriptors(f);
kfree(audio);
}
@@ -726,7 +695,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
}
/* Todo: add more control selecotor dynamically */
-int __init control_selector_init(struct f_audio *audio)
+static int __init control_selector_init(struct f_audio *audio)
{
INIT_LIST_HEAD(&audio->cs);
list_add(&feature_unit.list, &audio->cs);
@@ -750,7 +719,7 @@ int __init control_selector_init(struct f_audio *audio)
*
* Returns zero on success, else negative errno.
*/
-int __init audio_bind_config(struct usb_configuration *c)
+static int __init audio_bind_config(struct usb_configuration *c)
{
struct f_audio *audio;
int status;
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c
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
index 2022fe49214..e2a1f50bd93 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/video.h>
@@ -29,19 +30,32 @@
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_ASSOCIATION_IDX 0
-#define UVC_STRING_CONTROL_IDX 1
-#define UVC_STRING_STREAMING_IDX 2
+#define UVC_STRING_CONTROL_IDX 0
+#define UVC_STRING_STREAMING_IDX 1
static struct usb_string uvc_en_us_strings[] = {
- [UVC_STRING_ASSOCIATION_IDX].s = "UVC Camera",
- [UVC_STRING_CONTROL_IDX].s = "Video Control",
+ [UVC_STRING_CONTROL_IDX].s = "UVC Camera",
[UVC_STRING_STREAMING_IDX].s = "Video Streaming",
{ }
};
@@ -59,6 +73,8 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
#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,
@@ -87,15 +103,24 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(16),
+ .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(16),
+ .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
@@ -122,24 +147,64 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
.iInterface = 0,
};
-static struct usb_endpoint_descriptor uvc_streaming_ep = {
+static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
- .wMaxPacketSize = cpu_to_le16(512),
- .bInterval = 1,
+ .bmAttributes = USB_ENDPOINT_SYNC_ASYNC
+ | USB_ENDPOINT_XFER_ISOC,
+ /* The wMaxPacketSize and bInterval values will be initialized from
+ * module parameters.
+ */
+};
+
+static struct usb_endpoint_descriptor uvc_hs_streaming_ep __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_streaming_ep,
+ (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_streaming_ep,
+ (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,
};
@@ -194,6 +259,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
return 0;
}
+void uvc_function_setup_continue(struct uvc_device *uvc)
+{
+ struct usb_composite_dev *cdev = uvc->func.config->cdev;
+
+ usb_composite_setup_continue(cdev);
+}
+
static int
uvc_function_get_alt(struct usb_function *f, unsigned interface)
{
@@ -215,6 +287,7 @@ 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);
@@ -255,29 +328,28 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_CONNECTED;
- break;
+ return 0;
case 1:
if (uvc->state != UVC_STATE_CONNECTED)
return 0;
if (uvc->video.ep) {
- uvc->video.ep->desc = &uvc_streaming_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);
-
- uvc->state = UVC_STATE_STREAMING;
- break;
+ return USB_GADGET_DELAYED_STATUS;
default:
return -EINVAL;
}
-
- return 0;
}
static void
@@ -334,11 +406,10 @@ uvc_register_video(struct uvc_device *uvc)
if (video == NULL)
return -ENOMEM;
- video->parent = &cdev->gadget->dev;
- video->minor = -1;
+ video->v4l2_dev = &uvc->v4l2_dev;
video->fops = &uvc_v4l2_fops;
video->release = video_device_release;
- strncpy(video->name, cdev->gadget->name, sizeof(video->name));
+ strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
uvc->vdev = video;
video_set_drvdata(video, uvc);
@@ -368,6 +439,7 @@ 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;
@@ -379,10 +451,26 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
unsigned int bytes;
void *mem;
- uvc_streaming_cls = (speed == USB_SPEED_FULL)
- ? uvc->desc.fs_streaming : uvc->desc.hs_streaming;
- uvc_streaming_std = (speed == USB_SPEED_FULL)
- ? uvc_fs_streaming : uvc_hs_streaming;
+ 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
*
@@ -391,6 +479,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
* Class-specific UVC control descriptors
* uvc_control_ep
* uvc_control_cs_ep
+ * uvc_ss_control_comp (for SS only)
* uvc_streaming_intf_alt0
* Class-specific UVC streaming descriptors
* uvc_{fs|hs}_streaming
@@ -402,14 +491,22 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
bytes = uvc_iad.bLength + uvc_control_intf.bLength
+ uvc_control_ep.bLength + uvc_control_cs_ep.bLength
+ uvc_streaming_intf_alt0.bLength;
- n_desc = 5;
- for (src = (const struct usb_descriptor_header**)uvc->desc.control; *src; ++src) {
+ 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) {
+ for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
+ *src; ++src) {
streaming_size += (*src)->bLength;
bytes += (*src)->bLength;
n_desc++;
@@ -433,12 +530,15 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
uvc_control_header = mem;
UVC_COPY_DESCRIPTORS(mem, dst,
- (const struct usb_descriptor_header**)uvc->desc.control);
+ (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);
@@ -446,7 +546,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
UVC_COPY_DESCRIPTORS(mem, dst,
(const struct usb_descriptor_header**)uvc_streaming_cls);
uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
- uvc_streaming_header->bEndpointAddress = uvc_streaming_ep.bEndpointAddress;
+ uvc_streaming_header->bEndpointAddress = uvc->video.ep->address;
UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
@@ -462,26 +562,16 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
INFO(cdev, "uvc_function_unbind\n");
- if (uvc->vdev) {
- if (uvc->vdev->minor == -1)
- video_device_release(uvc->vdev);
- else
- video_unregister_device(uvc->vdev);
- uvc->vdev = NULL;
- }
-
- if (uvc->control_ep)
- uvc->control_ep->driver_data = NULL;
- if (uvc->video.ep)
- uvc->video.ep->driver_data = NULL;
+ video_unregister_device(uvc->vdev);
+ v4l2_device_unregister(&uvc->v4l2_dev);
+ uvc->control_ep->driver_data = NULL;
+ uvc->video.ep->driver_data = NULL;
- if (uvc->control_req) {
- usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
- kfree(uvc->control_buf);
- }
+ uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
+ usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+ kfree(uvc->control_buf);
- kfree(f->descriptors);
- kfree(f->hs_descriptors);
+ usb_free_all_descriptors(f);
kfree(uvc);
}
@@ -491,11 +581,50 @@ 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) {
@@ -505,7 +634,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc->control_ep = ep;
ep->driver_data = uvc;
- ep = usb_ep_autoconfig(cdev->gadget, &uvc_streaming_ep);
+ if (gadget_is_superspeed(c->cdev->gadget))
+ ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
+ &uvc_ss_streaming_comp);
+ else if (gadget_is_dualspeed(cdev->gadget))
+ ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
+ else
+ ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+
if (!ep) {
INFO(cdev, "Unable to allocate streaming EP\n");
goto error;
@@ -513,6 +649,10 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc->video.ep = ep;
ep->driver_data = uvc;
+ uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+ uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+ uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+
/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
@@ -526,9 +666,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
uvc->streaming_intf = ret;
- /* Copy descriptors. */
- f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
- f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+ /* 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);
@@ -548,6 +691,11 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
if ((ret = usb_function_deactivate(f)) < 0)
goto error;
+ if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
+ printk(KERN_INFO "v4l2_device_register failed\n");
+ goto error;
+ }
+
/* Initialise video. */
ret = uvc_video_init(&uvc->video);
if (ret < 0)
@@ -563,7 +711,21 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
error:
- uvc_function_unbind(c, f);
+ 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;
}
@@ -583,9 +745,11 @@ error:
*/
int __init
uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *control,
+ 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 *hs_streaming,
+ const struct uvc_descriptor_header * const *ss_streaming)
{
struct uvc_device *uvc;
int ret = 0;
@@ -603,8 +767,12 @@ uvc_bind_config(struct usb_configuration *c,
uvc->state = UVC_STATE_DISCONNECTED;
/* Validate the descriptors. */
- if (control == NULL || control[0] == NULL ||
- control[0]->bDescriptorSubType != UVC_VC_HEADER)
+ 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 ||
@@ -615,26 +783,32 @@ uvc_bind_config(struct usb_configuration *c,
hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
goto error;
- uvc->desc.control = control;
- uvc->desc.fs_streaming = fs_streaming;
- uvc->desc.hs_streaming = hs_streaming;
-
- /* Allocate string descriptor numbers. */
- if ((ret = usb_string_id(c->cdev)) < 0)
+ if (ss_streaming == NULL || ss_streaming[0] == NULL ||
+ ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
goto error;
- uvc_en_us_strings[UVC_STRING_ASSOCIATION_IDX].id = ret;
- uvc_iad.iFunction = ret;
- if ((ret = usb_string_id(c->cdev)) < 0)
- goto error;
- uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = ret;
- uvc_control_intf.iInterface = ret;
+ 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;
- if ((ret = usb_string_id(c->cdev)) < 0)
- goto error;
- uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id = ret;
- uvc_streaming_intf_alt0.iInterface = ret;
- uvc_streaming_intf_alt1.iInterface = ret;
+ /* 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";
diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h
index abf83293513..ec52752f732 100644
--- a/drivers/usb/gadget/f_uvc.h
+++ b/drivers/usb/gadget/f_uvc.h
@@ -16,10 +16,12 @@
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
-extern int uvc_bind_config(struct usb_configuration *c,
- const struct uvc_descriptor_header * const *control,
- const struct uvc_descriptor_header * const *fs_streaming,
- const struct uvc_descriptor_header * const *hs_streaming);
+int uvc_bind_config(struct usb_configuration *c,
+ const struct uvc_descriptor_header * const *fs_control,
+ const struct uvc_descriptor_header * const *hs_control,
+ const struct uvc_descriptor_header * const *fs_streaming,
+ const struct uvc_descriptor_header * const *hs_streaming,
+ const struct uvc_descriptor_header * const *ss_streaming);
#endif /* _F_UVC_H_ */
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
deleted file mode 100644
index 47766f0e7ca..00000000000
--- a/drivers/usb/gadget/file_storage.c
+++ /dev/null
@@ -1,3676 +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, release number and serial 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.
- *
- * 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
- * serial=HHHH... Required serial number (string of hex chars)
- * 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
- * nofua=b[,b...] Default false, booleans for ignore FUA flag
- * in SCSI WRITE(10,12) commands
- * 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", "serial", "ro",
- * "removable", "luns", "nofua", "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", "nofua", 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/module.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 "1 September 2010"
-
-static char fsg_string_manufacturer[64];
-static const char fsg_string_product[] = DRIVER_DESC;
-static const char fsg_string_config[] = "Self-powered";
-static const char fsg_string_interface[] = "Mass Storage";
-
-
-#include "storage_common.c"
-
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Alan Stern");
-MODULE_LICENSE("Dual BSD/GPL");
-
-/*
- * 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.
- */
-
-
-/*-------------------------------------------------------------------------*/
-
-
-/* Encapsulate the module parameter settings */
-
-static struct {
- char *file[FSG_MAX_LUNS];
- char *serial;
- bool ro[FSG_MAX_LUNS];
- bool nofua[FSG_MAX_LUNS];
- unsigned int num_filenames;
- unsigned int num_ros;
- unsigned int num_nofuas;
- unsigned int nluns;
-
- bool removable;
- bool can_stall;
- bool 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 = FSG_VENDOR_ID,
- .product = FSG_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_named(serial, mod_data.serial, charp, S_IRUGO);
-MODULE_PARM_DESC(serial, "USB serial number");
-
-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_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
- S_IRUGO);
-MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
-
-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 */
-
-
-/*
- * 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 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;
-
- 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 fsg_lun *luns;
- struct fsg_lun *curlun;
- /* Must be the last entry */
- struct fsg_buffhd buffhds[];
-};
-
-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 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);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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().
- */
-
-/* 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(FSG_VENDOR_ID),
- .idProduct = cpu_to_le16(FSG_PRODUCT_ID),
- .bcdDevice = cpu_to_le16(0xffff),
-
- .iManufacturer = FSG_STRING_MANUFACTURER,
- .iProduct = FSG_STRING_PRODUCT,
- .iSerialNumber = FSG_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 = FSG_STRING_CONFIG,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
-};
-
-
-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 int populate_bos(struct fsg_dev *fsg, u8 *buf)
-{
- memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE);
- buf += USB_DT_BOS_SIZE;
-
- memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE);
- buf += USB_DT_USB_EXT_CAP_SIZE;
-
- memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE);
-
- return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE
- + USB_DT_USB_EXT_CAP_SIZE;
-}
-
-/*
- * 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;
- function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
- ? (const struct usb_descriptor_header **)fsg_hs_function
- : (const struct usb_descriptor_header **)fsg_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] = {
- 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 || w_length != 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 || w_length != 1) {
- 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");
- device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
- 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) ||
- fsg->gadget->speed == USB_SPEED_SUPER)
- break;
- /*
- * Assume ep0 uses the same maxpacket value for both
- * speeds
- */
- dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
- 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) ||
- fsg->gadget->speed == USB_SPEED_SUPER)
- 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(&fsg_stringtab,
- w_value & 0xff, req->buf);
- break;
-
- case USB_DT_BOS:
- VDBG(fsg, "get bos descriptor\n");
-
- if (gadget_is_superspeed(fsg->gadget))
- value = populate_bos(fsg, 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 fsg_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;
- ssize_t nread;
-
- /* Get the starting Logical Block Address and check that it's
- * not too big */
- if (fsg->cmnd[0] == READ_6)
- lba = get_unaligned_be24(&fsg->cmnd[1]);
- else {
- lba = get_unaligned_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) << curlun->blkbits;
-
- /* 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.
- */
- amount = min((unsigned int) amount_left, mod_data.buflen);
- amount = min((loff_t) amount,
- curlun->file_length - file_offset);
-
- /* 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 >> 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;
- fsg->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;
- 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 fsg_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;
- 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] == WRITE_6)
- lba = get_unaligned_be24(&fsg->cmnd[1]);
- else {
- lba = get_unaligned_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;
- }
- /* FUA */
- if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
- spin_lock(&curlun->filp->f_lock);
- curlun->filp->f_flags |= O_DSYNC;
- 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 = 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 not more than the buffer size.
- */
- amount = min(amount_left_to_req, mod_data.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;
- fsg->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(fsg, bh, amount);
- 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 >> 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;
- 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 >> 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) {
- 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
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static int do_synchronize_cache(struct fsg_dev *fsg)
-{
- struct fsg_lun *curlun = fsg->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 = filp->f_path.dentry->d_inode;
- 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_dev *fsg)
-{
- struct fsg_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_unaligned_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_unaligned_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 << 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((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 >> 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_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_ROM : 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 fsg_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_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_dev *fsg, struct fsg_buffhd *bh)
-{
- struct fsg_lun *curlun = fsg->curlun;
- u32 lba = get_unaligned_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_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_dev *fsg, struct fsg_buffhd *bh)
-{
- struct fsg_lun *curlun = fsg->curlun;
- int msf = fsg->cmnd[1] & 0x02;
- u32 lba = get_unaligned_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 fsg_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 fsg_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 == 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 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_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_dev *fsg)
-{
- struct fsg_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);
- fsg_lun_close(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 (!fsg_lun_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 fsg_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)
- fsg_lun_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 fsg_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_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_dev *fsg, struct fsg_buffhd *bh)
-{
- struct fsg_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 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->bulk_out_intended_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);
-
- /* 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(fsg, bh, amount);
- 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, 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;
- start_transfer(fsg, fsg->bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state);
- fsg->next_buffhd_to_fill = bh->next;
- if (mod_data.can_stall)
- rc = halt_bulk_in_endpoint(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 fsg_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 fsg_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);
- }
-
- /* Check the LUN */
- curlun = fsg->curlun;
- if (curlun) {
- if (fsg->cmnd[0] != REQUEST_SENSE) {
- curlun->sense_data = SS_NO_SENSE;
- curlun->sense_data_info = 0;
- curlun->info_valid = 0;
- }
- } else {
- 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] != INQUIRY &&
- fsg->cmnd[0] != 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] != INQUIRY &&
- fsg->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 */
- 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 && !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_dev *fsg, int cmnd_size,
- enum data_direction data_dir, unsigned int mask,
- int needs_medium, const char *name)
-{
- if (fsg->curlun)
- fsg->data_size_from_cmnd <<= fsg->curlun->blkbits;
- return check_command(fsg, cmnd_size, data_dir,
- mask, needs_medium, name);
-}
-
-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 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 MODE_SELECT:
- 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 MODE_SELECT_10:
- fsg->data_size_from_cmnd = get_unaligned_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 MODE_SENSE:
- 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 MODE_SENSE_10:
- fsg->data_size_from_cmnd = get_unaligned_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 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 READ_6:
- i = fsg->cmnd[4];
- fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
- if ((reply = check_command_size_in_blocks(fsg, 6,
- DATA_DIR_TO_HOST,
- (7<<1) | (1<<4), 1,
- "READ(6)")) == 0)
- reply = do_read(fsg);
- break;
-
- case READ_10:
- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- if ((reply = check_command_size_in_blocks(fsg, 10,
- DATA_DIR_TO_HOST,
- (1<<1) | (0xf<<2) | (3<<7), 1,
- "READ(10)")) == 0)
- reply = do_read(fsg);
- break;
-
- case READ_12:
- fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
- if ((reply = check_command_size_in_blocks(fsg, 12,
- DATA_DIR_TO_HOST,
- (1<<1) | (0xf<<2) | (0xf<<6), 1,
- "READ(12)")) == 0)
- reply = do_read(fsg);
- break;
-
- case 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 READ_HEADER:
- if (!mod_data.cdrom)
- goto unknown_cmnd;
- fsg->data_size_from_cmnd = get_unaligned_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 READ_TOC:
- if (!mod_data.cdrom)
- goto unknown_cmnd;
- fsg->data_size_from_cmnd = get_unaligned_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 READ_FORMAT_CAPACITIES:
- fsg->data_size_from_cmnd = get_unaligned_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 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 START_STOP:
- 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 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 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 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 WRITE_6:
- i = fsg->cmnd[4];
- fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
- if ((reply = check_command_size_in_blocks(fsg, 6,
- DATA_DIR_FROM_HOST,
- (7<<1) | (1<<4), 1,
- "WRITE(6)")) == 0)
- reply = do_write(fsg);
- break;
-
- case WRITE_10:
- fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- if ((reply = check_command_size_in_blocks(fsg, 10,
- DATA_DIR_FROM_HOST,
- (1<<1) | (0xf<<2) | (3<<7), 1,
- "WRITE(10)")) == 0)
- reply = do_write(fsg);
- break;
-
- case WRITE_12:
- fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
- if ((reply = check_command_size_in_blocks(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 FORMAT_UNIT:
- case RELEASE:
- case RESERVE:
- case 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 fsg_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 >= FSG_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);
- 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);
-
- /* Use LUN from the command */
- fsg->lun = fsg->cmnd[1] >> 5;
- }
-
- /* Update current lun */
- if (fsg->lun >= 0 && fsg->lun < fsg->nluns)
- fsg->curlun = &fsg->luns[fsg->lun];
- else
- fsg->curlun = NULL;
-
- 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;
- ep->desc = d;
- rc = usb_ep_enable(ep);
- 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 < fsg_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 = fsg_ep_desc(fsg->gadget,
- &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc,
- &fsg_ss_bulk_in_desc);
- if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
- goto reset;
- fsg->bulk_in_enabled = 1;
-
- d = fsg_ep_desc(fsg->gadget,
- &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc,
- &fsg_ss_bulk_out_desc);
- if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
- goto reset;
- fsg->bulk_out_enabled = 1;
- fsg->bulk_out_maxpacket = usb_endpoint_maxp(d);
- clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
-
- if (transport_is_cbi()) {
- d = fsg_ep_desc(fsg->gadget,
- &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc,
- &fsg_ss_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 < fsg_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
- INFO(fsg, "%s config #%d\n",
- usb_speed_string(fsg->gadget->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 fsg_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 < fsg_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 < fsg_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 < fsg_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:
- for (i = 0; i < fsg->nluns; ++i)
- fsg_lun_fsync_sub(fsg->luns + i);
- 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);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-
-/* The write permissions and store_xxx pointers are set in fsg_bind() */
-static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
-static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
-static DEVICE_ATTR(file, 0444, fsg_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 rw_semaphore *filesem = dev_get_drvdata(dev);
- struct fsg_dev *fsg =
- container_of(filesem, struct fsg_dev, filesem);
-
- 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 fsg_lun *curlun;
- struct usb_request *req = fsg->ep0req;
-
- DBG(fsg, "unbind\n");
- clear_bit(REGISTERED, &fsg->atomic_bitflags);
-
- /* 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);
- }
-
- /* 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_nofua);
- device_remove_file(&curlun->dev, &dev_attr_ro);
- device_remove_file(&curlun->dev, &dev_attr_file);
- fsg_lun_close(curlun);
- device_unregister(&curlun->dev);
- curlun->registered = 0;
- }
- }
-
- /* Free the data buffers */
- for (i = 0; i < fsg_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_at91(fsg->gadget))
- mod_data.can_stall = 0;
-
- if (mod_data.release == 0xffff) { // Parameter wasn't set
- 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 */
-
- /* Serial string handling.
- * On a real device, the serial string would be loaded
- * from permanent storage. */
- if (mod_data.serial) {
- const char *ch;
- unsigned len = 0;
-
- /* Sanity check :
- * The CB[I] specification limits the serial string to
- * 12 uppercase hexadecimal characters.
- * BBB need at least 12 uppercase hexadecimal characters,
- * with a maximum of 126. */
- for (ch = mod_data.serial; *ch; ++ch) {
- ++len;
- if ((*ch < '0' || *ch > '9') &&
- (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
- WARNING(fsg,
- "Invalid serial string character: %c\n",
- *ch);
- goto no_serial;
- }
- }
- if (len > 126 ||
- (mod_data.transport_type == USB_PR_BULK && len < 12) ||
- (mod_data.transport_type != USB_PR_BULK && len > 12)) {
- WARNING(fsg, "Invalid serial string length!\n");
- goto no_serial;
- }
- fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
- } else {
- WARNING(fsg, "No serial-number string provided!\n");
- no_serial:
- device_desc.iSerialNumber = 0;
- }
-
- return 0;
-}
-
-
-static int __init fsg_bind(struct usb_gadget *gadget)
-{
- struct fsg_dev *fsg = the_fsg;
- int rc;
- int i;
- struct fsg_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 = fsg_store_file;
- if (!mod_data.cdrom) {
- dev_attr_ro.attr.mode = 0644;
- dev_attr_ro.store = fsg_store_ro;
- }
- }
-
- /* Only for removable media? */
- dev_attr_nofua.attr.mode = 0644;
- dev_attr_nofua.store = fsg_store_nofua;
-
- /* Find out how many LUNs there should be */
- i = mod_data.nluns;
- if (i == 0)
- i = max(mod_data.num_filenames, 1u);
- if (i > FSG_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 fsg_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->cdrom = !!mod_data.cdrom;
- curlun->ro = mod_data.cdrom || mod_data.ro[i];
- curlun->initially_ro = curlun->ro;
- curlun->removable = mod_data.removable;
- curlun->nofua = mod_data.nofua[i];
- curlun->dev.release = lun_release;
- curlun->dev.parent = &gadget->dev;
- curlun->dev.driver = &fsg_driver.driver;
- dev_set_drvdata(&curlun->dev, &fsg->filesem);
- dev_set_name(&curlun->dev,"%s-lun%d",
- dev_name(&gadget->dev), i);
-
- kref_get(&fsg->ref);
- rc = device_register(&curlun->dev);
- if (rc) {
- INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
- put_device(&curlun->dev);
- goto out;
- }
- curlun->registered = 1;
-
- rc = device_create_file(&curlun->dev, &dev_attr_ro);
- if (rc)
- goto out;
- rc = device_create_file(&curlun->dev, &dev_attr_nofua);
- if (rc)
- goto out;
- rc = device_create_file(&curlun->dev, &dev_attr_file);
- if (rc)
- goto out;
-
- if (mod_data.file[i] && *mod_data.file[i]) {
- rc = fsg_lun_open(curlun, mod_data.file[i]);
- if (rc)
- 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, &fsg_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, &fsg_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, &fsg_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.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
- fsg_intf_desc.bNumEndpoints = i;
- fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type;
- fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type;
- fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
-
- if (gadget_is_dualspeed(gadget)) {
- fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
-
- /* 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;
- fsg_hs_intr_in_desc.bEndpointAddress =
- fsg_fs_intr_in_desc.bEndpointAddress;
- }
-
- if (gadget_is_superspeed(gadget)) {
- unsigned max_burst;
-
- fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL;
-
- /* Calculate bMaxBurst, we know packet size is 1024 */
- max_burst = min_t(unsigned, mod_data.buflen / 1024, 15);
-
- /* Assume endpoint addresses are the same for both speeds */
- 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;
- }
-
- if (gadget_is_otg(gadget))
- fsg_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 < fsg_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[fsg_num_buffers - 1].next = &fsg->buffhds[0];
-
- /* This should reflect the actual gadget power source */
- usb_gadget_set_selfpowered(gadget);
-
- snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer,
- "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
- 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, "NOTE: This driver is deprecated. "
- "Consider using g_mass_storage instead.\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 (fsg_lun_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, nofua=%d, file: %s\n",
- curlun->ro, curlun->nofua, (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 = {
- .max_speed = USB_SPEED_SUPER,
- .function = (char *) fsg_string_product,
- .unbind = fsg_unbind,
- .disconnect = fsg_disconnect,
- .setup = fsg_setup,
- .suspend = fsg_suspend,
- .resume = fsg_resume,
-
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- // .release = ...
- // .suspend = ...
- // .resume = ...
- },
-};
-
-
-static int __init fsg_alloc(void)
-{
- struct fsg_dev *fsg;
-
- fsg = kzalloc(sizeof *fsg +
- fsg_num_buffers * sizeof *(fsg->buffhds), 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;
-
- rc = fsg_num_buffers_validate();
- if (rc != 0)
- return rc;
-
- if ((rc = fsg_alloc()) != 0)
- return rc;
- fsg = the_fsg;
- if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 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
index dcbc0a2e48d..9b140fc4d3b 100644
--- a/drivers/usb/gadget/fsl_mxc_udc.c
+++ b/drivers/usb/gadget/fsl_mxc_udc.c
@@ -18,13 +18,13 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <mach/hardware.h>
-
static struct clk *mxc_ahb_clk;
-static struct clk *mxc_usb_clk;
+static struct clk *mxc_per_clk;
+static struct clk *mxc_ipg_clk;
/* workaround ENGcm09152 for i.MX35 */
-#define USBPHYCTRL_OTGBASE_OFFSET 0x608
+#define MX35_USBPHYCTRL_OFFSET 0x600
+#define USBPHYCTRL_OTGBASE_OFFSET 0x8
#define USBPHYCTRL_EVDO (1 << 23)
int fsl_udc_clk_init(struct platform_device *pdev)
@@ -33,30 +33,33 @@ int fsl_udc_clk_init(struct platform_device *pdev)
unsigned long freq;
int ret;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
- if (!cpu_is_mx35() && !cpu_is_mx25()) {
- mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
- if (IS_ERR(mxc_ahb_clk))
- return PTR_ERR(mxc_ahb_clk);
+ 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);
+ }
- ret = clk_enable(mxc_ahb_clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
- goto eenahb;
- }
+ 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);
}
- /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
- mxc_usb_clk = clk_get(&pdev->dev, "usb");
- if (IS_ERR(mxc_usb_clk)) {
- dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
- ret = PTR_ERR(mxc_usb_clk);
- goto egusb;
+ 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);
}
- if (!cpu_is_mx51()) {
- freq = clk_get_rate(mxc_usb_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);
@@ -65,59 +68,56 @@ int fsl_udc_clk_init(struct platform_device *pdev)
}
}
- ret = clk_enable(mxc_usb_clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
- goto eenusb;
- }
-
return 0;
-eenusb:
eclkrate:
- clk_put(mxc_usb_clk);
- mxc_usb_clk = NULL;
-egusb:
- if (!cpu_is_mx35())
- clk_disable(mxc_ahb_clk);
-eenahb:
- if (!cpu_is_mx35())
- clk_put(mxc_ahb_clk);
+ clk_disable_unprepare(mxc_ipg_clk);
+ clk_disable_unprepare(mxc_ahb_clk);
+ clk_disable_unprepare(mxc_per_clk);
+ mxc_per_clk = NULL;
return ret;
}
-void fsl_udc_clk_finalize(struct platform_device *pdev)
+int fsl_udc_clk_finalize(struct platform_device *pdev)
{
- struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
- if (cpu_is_mx35()) {
- unsigned int v;
+ 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) {
- v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
- USBPHYCTRL_OTGBASE_OFFSET));
- writel(v | USBPHYCTRL_EVDO,
- MX35_IO_ADDRESS(MX35_USB_BASE_ADDR +
- USBPHYCTRL_OTGBASE_OFFSET));
+ /* 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(mxc_usb_clk);
- clk_put(mxc_usb_clk);
- mxc_usb_clk = NULL;
+ clk_disable_unprepare(mxc_per_clk);
+ mxc_per_clk = NULL;
}
+
+ return ret;
}
void fsl_udc_clk_release(void)
{
- if (mxc_usb_clk) {
- clk_disable(mxc_usb_clk);
- clk_put(mxc_usb_clk);
- }
- if (!cpu_is_mx35()) {
- clk_disable(mxc_ahb_clk);
- clk_put(mxc_ahb_clk);
- }
+ 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 b95697c03d0..ad548333516 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -22,7 +22,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -33,6 +32,7 @@
#include <linux/io.h>
#include <linux/moduleparam.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
@@ -71,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
********************************************************************/
@@ -188,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;
}
@@ -450,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;
@@ -489,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;
@@ -644,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;
@@ -698,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);
}
/*----------------------------------------------------------------------------*
@@ -1599,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;
@@ -1629,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;
}
@@ -1637,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);
@@ -1655,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);
}
@@ -1714,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;
}
@@ -1825,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;
}
@@ -1879,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
@@ -1890,57 +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_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int fsl_qe_stop(struct usb_gadget_driver *driver);
+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,
- .start = fsl_qe_start,
- .stop = fsl_qe_stop,
+ .udc_start = fsl_qe_start,
+ .udc_stop = fsl_qe_stop,
};
/*-------------------------------------------------------------------------
@@ -2014,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]);
@@ -2189,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++) {
@@ -2201,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;
}
@@ -2326,98 +2283,69 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc)
/*-------------------------------------------------------------------------
Gadget driver probe and unregister.
--------------------------------------------------------------------------*/
-static int fsl_qe_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+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->max_speed < USB_SPEED_FULL
- || !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 = driver->max_speed;
- spin_unlock_irqrestore(&udc_controller->lock, flags);
-
- retval = 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;
}
-static int fsl_qe_stop(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;
}
/* udc structure's alloc and setup, include ep-param alloc */
-static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev)
+static struct qe_udc *qe_udc_config(struct platform_device *ofdev)
{
struct qe_udc *udc;
struct device_node *np = ofdev->dev.of_node;
@@ -2472,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;
@@ -2490,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];
@@ -2500,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;
@@ -2530,21 +2458,22 @@ 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 const struct of_device_id qe_udc_match[];
-static int __devinit qe_udc_probe(struct platform_device *ofdev)
+static int qe_udc_probe(struct platform_device *ofdev)
{
+ struct qe_udc *udc;
const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct qe_ep *ep;
@@ -2561,44 +2490,38 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev)
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++) {
@@ -2607,104 +2530,99 @@ static int __devinit qe_udc_probe(struct platform_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;
- ret = usb_add_gadget_udc(&ofdev->dev, &udc_controller->gadget);
- if (ret)
- goto err7;
-
- 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;
-err7:
- device_unregister(&udc_controller->gadget.dev);
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;
}
@@ -2720,46 +2638,43 @@ static int qe_udc_resume(struct platform_device *dev)
}
#endif
-static int __devexit qe_udc_remove(struct platform_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);
- usb_del_gadget_udc(&udc_controller->gadget);
+ udc->done = &done;
+ tasklet_disable(&udc->rx_tasklet);
- udc_controller->done = &done;
- tasklet_disable(&udc_controller->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);
}
@@ -2767,14 +2682,13 @@ static int __devexit qe_udc_remove(struct platform_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);
@@ -2782,7 +2696,7 @@ static int __devexit qe_udc_remove(struct platform_device *ofdev)
}
/*-------------------------------------------------------------------------*/
-static const struct of_device_id qe_udc_match[] __devinitconst = {
+static const struct of_device_id qe_udc_match[] = {
{
.compatible = "fsl,mpc8323-qe-usb",
.data = (void *)PORT_QE,
@@ -2802,12 +2716,12 @@ MODULE_DEVICE_TABLE(of, qe_udc_match);
static struct platform_driver udc_driver = {
.driver = {
- .name = (char *)driver_name,
+ .name = driver_name,
.owner = THIS_MODULE,
.of_match_table = qe_udc_match,
},
.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,
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index 1da5fb03d21..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 */
@@ -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;
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index b04712f19f1..28e4fc95702 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2007,2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc.
* All rights reserved.
*
* Author: Li Yang <leoli@freescale.com>
@@ -24,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>
@@ -40,10 +41,10 @@
#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>
@@ -59,9 +60,8 @@ static const char driver_name[] = "fsl-usb2-udc";
static const char driver_desc[] = DRIVER_DESC;
static struct usb_dr_device *dr_regs;
-#ifndef CONFIG_ARCH_MXC
+
static struct usb_sys_interface *usb_sys_regs;
-#endif
/* it is initialized in probe() */
static struct fsl_udc *udc_controller = NULL;
@@ -185,20 +185,7 @@ static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
}
- if (req->mapped) {
- dma_unmap_single(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ep_is_in(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
- req->req.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else
- dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ep_is_in(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
+ usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
if (status && (status != -ESHUTDOWN))
VDBG("complete %s req %p stat %d len %u/%u",
@@ -245,10 +232,9 @@ static int dr_controller_setup(struct fsl_udc *udc)
{
unsigned int tmp, portctrl, ep_num;
unsigned int max_no_of_ep;
-#ifndef CONFIG_ARCH_MXC
unsigned int ctrl;
-#endif
unsigned long timeout;
+
#define FSL_UDC_RESET_TIMEOUT 1000
/* Config PHY interface */
@@ -256,12 +242,32 @@ static int dr_controller_setup(struct fsl_udc *udc)
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:
@@ -550,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;
@@ -591,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 */
@@ -615,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:
@@ -635,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;
}
@@ -658,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);
@@ -730,12 +736,14 @@ 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_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)
return;
@@ -768,7 +776,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
* @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;
@@ -777,7 +785,7 @@ 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;
@@ -827,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;
@@ -836,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;
@@ -867,6 +875,7 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct fsl_req *req = container_of(_req, struct fsl_req, req);
struct fsl_udc *udc;
unsigned long flags;
+ int ret;
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
@@ -874,11 +883,11 @@ 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 (usb_endpoint_xfer_isoc(ep->desc)) {
+ if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
if (req->req.length > ep->ep.maxpacket)
return -EMSGSIZE;
}
@@ -889,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);
@@ -1023,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 (usb_endpoint_xfer_isoc(ep->desc)) {
+ if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
status = -EOPNOTSUPP;
goto out;
}
@@ -1067,7 +1057,7 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
struct ep_queue_head *qh;
ep = container_of(_ep, struct fsl_ep, ep);
- if (!_ep || (!ep->desc && ep_index(ep) != 0))
+ if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
return -ENODEV;
udc = (struct fsl_udc *)ep->udc;
@@ -1100,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);
@@ -1216,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;
}
@@ -1229,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),
@@ -1240,19 +1234,20 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int fsl_stop(struct usb_gadget_driver *driver);
+static int fsl_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int fsl_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
/* defined in gadget.h */
-static struct usb_gadget_ops fsl_gadget_ops = {
+static const struct usb_gadget_ops fsl_gadget_ops = {
.get_frame = fsl_get_frame,
.wakeup = fsl_wakeup,
/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
.vbus_session = fsl_vbus_session,
.vbus_draw = fsl_vbus_draw,
.pullup = fsl_pullup,
- .start = fsl_start,
- .stop = fsl_stop,
+ .udc_start = fsl_udc_start,
+ .udc_stop = fsl_udc_stop,
};
/* Set protocol stall on ep0, protocol stall will automatically be cleared
@@ -1274,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;
@@ -1281,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;
@@ -1290,12 +1287,11 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)
req->req.complete = NULL;
req->dtd_count = 0;
- req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
- req->req.buf, req->req.length,
- ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = 1;
+ ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+ if (ret)
+ return ret;
- if (fsl_req_to_dtd(req) == 0)
+ if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0)
fsl_queue_td(ep, req);
else
return -ENOMEM;
@@ -1336,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];
@@ -1354,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;
@@ -1373,19 +1370,21 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
req->req.complete = NULL;
req->dtd_count = 0;
- req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
- req->req.buf, req->req.length,
- ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = 1;
+ ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+ if (ret)
+ goto stall;
/* prime the data phase */
- if ((fsl_req_to_dtd(req) == 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);
@@ -1494,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;
@@ -1522,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 */
@@ -1925,53 +1931,33 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
* Hook to gadget drivers
* Called by initialization code of gadget drivers
*----------------------------------------------------------------*/
-static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int fsl_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- int retval = -ENODEV;
+ int retval = 0;
unsigned long flags = 0;
- if (!udc_controller)
- return -ENODEV;
-
- if (!driver || driver->max_speed < USB_SPEED_FULL
- || !bind || !driver->disconnect || !driver->setup)
- return -EINVAL;
-
- if (udc_controller->driver)
- return -EBUSY;
-
/* lock is needed but whether should use this lock or another */
spin_lock_irqsave(&udc_controller->lock, flags);
driver->driver.bus = NULL;
/* hook up the driver */
udc_controller->driver = driver;
- udc_controller->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
- /* bind udc driver to gadget driver */
- retval = bind(&udc_controller->gadget);
- if (retval) {
- VDBG("bind to %s --> %d", driver->driver.name, retval);
- udc_controller->gadget.dev.driver = NULL;
- udc_controller->driver = NULL;
- goto out;
- }
-
- if (udc_controller->transceiver) {
+ 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 (udc_controller->transceiver) {
- retval = otg_set_peripheral(udc_controller->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->gadget.dev.driver = 0;
udc_controller->driver = 0;
return retval;
}
@@ -1983,30 +1969,19 @@ static int fsl_start(struct usb_gadget_driver *driver,
udc_controller->ep0_state = WAIT_FOR_SETUP;
udc_controller->ep0_dir = 0;
}
- printk(KERN_INFO "%s: bind to driver %s\n",
- udc_controller->gadget.name, driver->driver.name);
-out:
- if (retval)
- printk(KERN_WARNING "gadget driver register failed %d\n",
- retval);
return retval;
}
/* Disconnect from gadget driver */
-static int fsl_stop(struct usb_gadget_driver *driver)
+static int fsl_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
struct fsl_ep *loop_ep;
unsigned long flags;
- if (!udc_controller)
- return -ENODEV;
-
- if (!driver || driver != udc_controller->driver || !driver->unbind)
- return -EINVAL;
-
- if (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);
@@ -2025,16 +2000,8 @@ static int fsl_stop(struct usb_gadget_driver *driver)
nuke(loop_ep, -ESHUTDOWN);
spin_unlock_irqrestore(&udc_controller->lock, flags);
- /* report disconnect; the controller is already quiesced */
- driver->disconnect(&udc_controller->gadget);
-
- /* unbind gadget and unhook driver. */
- driver->unbind(&udc_controller->gadget);
- udc_controller->gadget.dev.driver = NULL;
udc_controller->driver = NULL;
- printk(KERN_WARNING "unregistered gadget driver '%s'\n",
- driver->driver.name);
return 0;
}
@@ -2047,47 +2014,37 @@ static int fsl_stop(struct usb_gadget_driver *driver)
static const char proc_filename[] = "driver/fsl_usb2_udc";
-static int fsl_proc_read(char *page, char **start, off_t off, int count,
- int *eof, void *_dev)
+static int fsl_proc_read(struct seq_file *m, void *v)
{
- char *buf = page;
- char *next = buf;
- unsigned size = count;
unsigned long flags;
- int t, i;
+ int i;
u32 tmp_reg;
struct fsl_ep *ep = NULL;
struct fsl_req *req;
struct fsl_udc *udc = udc_controller;
- if (off != 0)
- return 0;
spin_lock_irqsave(&udc->lock, flags);
/* ------basic driver information ---- */
- t = scnprintf(next, size,
+ seq_printf(m,
DRIVER_DESC "\n"
"%s version: %s\n"
"Gadget driver: %s\n\n",
driver_name, DRIVER_VERSION,
udc->driver ? udc->driver->driver.name : "(none)");
- size -= t;
- next += t;
/* ------ DR Registers ----- */
tmp_reg = fsl_readl(&dr_regs->usbcmd);
- t = scnprintf(next, size,
+ seq_printf(m,
"USBCMD reg:\n"
"SetupTW: %d\n"
"Run/Stop: %s\n\n",
(tmp_reg & USB_CMD_SUTW) ? 1 : 0,
(tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
- size -= t;
- next += t;
tmp_reg = fsl_readl(&dr_regs->usbsts);
- t = scnprintf(next, size,
+ seq_printf(m,
"USB Status Reg:\n"
"Dr Suspend: %d Reset Received: %d System Error: %s "
"USB Error Interrupt: %s\n\n",
@@ -2095,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 "
@@ -2113,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 "
@@ -2148,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;
@@ -2174,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;
@@ -2193,103 +2138,87 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
}
s;
} ));
- size -= t;
- next += t;
tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
- t = scnprintf(next, size,
+ seq_printf(m,
"Endpoint Setup Status Reg: SETUP on ep 0x%x\n\n",
(tmp_reg & EP_SETUP_STATUS_MASK));
- size -= t;
- next += t;
for (i = 0; i < udc->max_ep / 2; i++) {
tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
- t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
- i, tmp_reg);
- size -= t;
- next += t;
+ seq_printf(m, "EP Ctrl Reg [0x%x]: = [0x%x]\n", i, tmp_reg);
}
tmp_reg = fsl_readl(&dr_regs->endpointprime);
- t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n\n", tmp_reg);
- size -= t;
- next += t;
+ seq_printf(m, "EP Prime Reg = [0x%x]\n\n", tmp_reg);
#ifndef CONFIG_ARCH_MXC
if (udc->pdata->have_sysif_regs) {
tmp_reg = usb_sys_regs->snoop1;
- t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
- size -= t;
- next += t;
+ seq_printf(m, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
tmp_reg = usb_sys_regs->control;
- t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
- tmp_reg);
- size -= t;
- next += t;
+ seq_printf(m, "General Control Reg : = [0x%x]\n\n", tmp_reg);
}
#endif
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
ep = &udc->eps[0];
- t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
+ seq_printf(m, "For %s Maxpkt is 0x%x index is 0x%x\n",
ep->ep.name, ep_maxpacket(ep), ep_index(ep));
- size -= t;
- next += t;
if (list_empty(&ep->queue)) {
- t = scnprintf(next, size, "its req queue is empty\n\n");
- size -= t;
- next += t;
+ seq_puts(m, "its req queue is empty\n\n");
} else {
list_for_each_entry(req, &ep->queue, queue) {
- t = scnprintf(next, size,
+ seq_printf(m,
"req %p actual 0x%x length 0x%x buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
- size -= t;
- next += t;
}
}
/* other gadget->eplist ep */
list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- if (ep->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 */
@@ -2323,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);
@@ -2386,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);
@@ -2412,26 +2339,19 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
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 = pdev->dev.platform_data;
+ 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 = otg_get_transceiver();
- if (!udc_controller->transceiver) {
+ 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;
@@ -2515,13 +2435,15 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_free_irq;
}
- if (!udc_controller->transceiver) {
+ 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);
}
- fsl_udc_clk_finalize(pdev);
+ ret = fsl_udc_clk_finalize(pdev);
+ if (ret)
+ goto err_free_irq;
/* Setup gadget structure */
udc_controller->gadget.ops = &fsl_gadget_ops;
@@ -2533,13 +2455,9 @@ 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 (udc_controller->transceiver)
+ if (!IS_ERR_OR_NULL(udc_controller->transceiver))
udc_controller->gadget.is_otg = 1;
/* setup QH and epctrl for ep0 */
@@ -2550,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 */
@@ -2570,10 +2489,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
if (udc_controller->td_pool == NULL) {
ret = -ENOMEM;
- goto err_unregister;
+ goto err_free_irq;
}
- ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget);
+ ret = usb_add_gadget_udc_release(&pdev->dev, &udc_controller->gadget,
+ fsl_udc_release);
if (ret)
goto err_del_udc;
@@ -2582,8 +2502,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
err_del_udc:
dma_pool_destroy(udc_controller->td_pool);
-err_unregister:
- device_unregister(&udc_controller->gadget.dev);
err_free_irq:
free_irq(udc_controller->irq, udc_controller);
err_iounmap:
@@ -2607,15 +2525,15 @@ err_kfree:
static int __exit fsl_udc_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
DECLARE_COMPLETION(done);
if (!udc_controller)
return -ENODEV;
- usb_del_gadget_udc(&udc_controller->gadget);
udc_controller->done = &done;
+ usb_del_gadget_udc(&udc_controller->gadget);
fsl_udc_clk_release();
@@ -2633,7 +2551,6 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
release_mem_region(res->start, resource_size(res));
- device_unregister(&udc_controller->gadget.dev);
/* free udc --wait for the release() finished */
wait_for_completion(&done);
@@ -2729,40 +2646,36 @@ static int fsl_udc_otg_resume(struct device *dev)
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,
- /* udc suspend/resume called from OTG driver */
- .suspend = fsl_udc_otg_suspend,
- .resume = fsl_udc_otg_resume,
+ .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 f781f5dec41..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
@@ -348,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
@@ -450,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];
@@ -471,7 +481,7 @@ 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;
@@ -558,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])
@@ -582,15 +592,16 @@ static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep)
struct platform_device;
#ifdef CONFIG_ARCH_MXC
int fsl_udc_clk_init(struct platform_device *pdev);
-void fsl_udc_clk_finalize(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 void fsl_udc_clk_finalize(struct platform_device *pdev)
+static inline int fsl_udc_clk_finalize(struct platform_device *pdev)
{
+ return 0;
}
static inline void fsl_udc_clk_release(void)
{
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
new file mode 100644
index 00000000000..b13f839e736
--- /dev/null
+++ b/drivers/usb/gadget/functions.c
@@ -0,0 +1,116 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <linux/usb/composite.h>
+
+static LIST_HEAD(func_list);
+static DEFINE_MUTEX(func_lock);
+
+static struct usb_function_instance *try_get_usb_function_instance(const char *name)
+{
+ struct usb_function_driver *fd;
+ struct usb_function_instance *fi;
+
+ fi = ERR_PTR(-ENOENT);
+ mutex_lock(&func_lock);
+ list_for_each_entry(fd, &func_list, list) {
+
+ if (strcmp(name, fd->name))
+ continue;
+
+ if (!try_module_get(fd->mod)) {
+ fi = ERR_PTR(-EBUSY);
+ break;
+ }
+ fi = fd->alloc_inst();
+ if (IS_ERR(fi))
+ module_put(fd->mod);
+ else
+ fi->fd = fd;
+ break;
+ }
+ mutex_unlock(&func_lock);
+ return fi;
+}
+
+struct usb_function_instance *usb_get_function_instance(const char *name)
+{
+ struct usb_function_instance *fi;
+ int ret;
+
+ fi = try_get_usb_function_instance(name);
+ if (!IS_ERR(fi))
+ return fi;
+ ret = PTR_ERR(fi);
+ if (ret != -ENOENT)
+ return fi;
+ ret = request_module("usbfunc:%s", name);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ return try_get_usb_function_instance(name);
+}
+EXPORT_SYMBOL_GPL(usb_get_function_instance);
+
+struct usb_function *usb_get_function(struct usb_function_instance *fi)
+{
+ struct usb_function *f;
+
+ f = fi->fd->alloc_func(fi);
+ if (IS_ERR(f))
+ return f;
+ f->fi = fi;
+ return f;
+}
+EXPORT_SYMBOL_GPL(usb_get_function);
+
+void usb_put_function_instance(struct usb_function_instance *fi)
+{
+ struct module *mod;
+
+ if (!fi)
+ return;
+
+ mod = fi->fd->mod;
+ fi->free_func_inst(fi);
+ module_put(mod);
+}
+EXPORT_SYMBOL_GPL(usb_put_function_instance);
+
+void usb_put_function(struct usb_function *f)
+{
+ if (!f)
+ return;
+
+ f->free_func(f);
+}
+EXPORT_SYMBOL_GPL(usb_put_function);
+
+int usb_function_register(struct usb_function_driver *newf)
+{
+ struct usb_function_driver *fd;
+ int ret;
+
+ ret = -EEXIST;
+
+ mutex_lock(&func_lock);
+ list_for_each_entry(fd, &func_list, list) {
+ if (!strcmp(fd->name, newf->name))
+ goto out;
+ }
+ ret = 0;
+ list_add_tail(&newf->list, &func_list);
+out:
+ mutex_unlock(&func_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_function_register);
+
+void usb_function_unregister(struct usb_function_driver *fd)
+{
+ mutex_lock(&func_lock);
+ list_del(&fd->list);
+ mutex_unlock(&func_lock);
+}
+EXPORT_SYMBOL_GPL(usb_function_unregister);
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index 5831cb4a0b3..3deb4e93807 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -22,7 +22,7 @@
MODULE_DESCRIPTION("FUSB300 USB gadget driver");
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yuan Hsin Chen <yhchen@faraday-tech.com>");
+MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
MODULE_ALIAS("platform:fusb300_udc");
#define DRIVER_VERSION "20 October 2010"
@@ -203,7 +203,7 @@ static int config_ep(struct fusb300_ep *ep,
struct fusb300 *fusb300 = ep->fusb300;
struct fusb300_ep_info info;
- ep->desc = desc;
+ ep->ep.desc = desc;
info.interval = 0;
info.addrofs = 0;
@@ -394,7 +394,7 @@ static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep)
if (reg & FUSB300_EPSET0_STL) {
printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep);
- reg &= ~FUSB300_EPSET0_STL;
+ reg |= FUSB300_EPSET0_STL_CLR;
iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
}
}
@@ -443,7 +443,7 @@ static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req,
req->req.actual = 0;
req->req.status = -EINPROGRESS;
- if (ep->desc == NULL) /* ep0 */
+ if (ep->ep.desc == NULL) /* ep0 */
ep0_queue(ep, req);
else if (request && !ep->stall)
enable_fifo_int(ep);
@@ -557,7 +557,7 @@ static void fusb300_set_cxdone(struct fusb300 *fusb300)
}
/* read data from cx fifo */
-void fusb300_rdcxf(struct fusb300 *fusb300,
+static void fusb300_rdcxf(struct fusb300 *fusb300,
u8 *buffer, u32 length)
{
int i = 0;
@@ -930,33 +930,33 @@ static void fusb300_wait_idma_finished(struct fusb300_ep *ep)
fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0,
FUSB300_IGR0_EPn_PRD_INT(ep->epnum));
+ return;
+
IDMA_RESET:
- fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGER0,
- FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
+ reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0);
+ reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum);
+ iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0);
}
-static void fusb300_set_idma(struct fusb300_ep *ep,
+static void fusb300_set_idma(struct fusb300_ep *ep,
struct fusb300_request *req)
{
- dma_addr_t d;
-
- d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE);
+ int ret;
- if (dma_mapping_error(NULL, d)) {
- printk(KERN_DEBUG "dma_mapping_error\n");
+ ret = usb_gadget_map_request(&ep->fusb300->gadget,
+ &req->req, DMA_TO_DEVICE);
+ if (ret)
return;
- }
-
- dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE);
fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0,
FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
- fusb300_fill_idma_prdtbl(ep, d, req->req.length);
+ fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length);
/* check idma is done */
fusb300_wait_idma_finished(ep);
- dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE);
+ usb_gadget_unmap_request(&ep->fusb300->gadget,
+ &req->req, DMA_TO_DEVICE);
}
static void in_ep_fifo_handler(struct fusb300_ep *ep)
@@ -1308,65 +1308,26 @@ static void init_controller(struct fusb300 *fusb300)
iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1);
}
/*------------------------------------------------------------------------*/
-static struct fusb300 *the_controller;
-
-static int fusb300_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int fusb300_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct fusb300 *fusb300 = the_controller;
- int retval;
-
- if (!driver
- || driver->max_speed < USB_SPEED_FULL
- || !bind
- || !driver->setup)
- return -EINVAL;
-
- if (!fusb300)
- return -ENODEV;
-
- if (fusb300->driver)
- return -EBUSY;
+ struct fusb300 *fusb300 = to_fusb300(g);
/* hook up the driver */
driver->driver.bus = NULL;
fusb300->driver = driver;
- fusb300->gadget.dev.driver = &driver->driver;
-
- retval = device_add(&fusb300->gadget.dev);
- if (retval) {
- pr_err("device_add error (%d)\n", retval);
- goto error;
- }
-
- retval = bind(&fusb300->gadget);
- if (retval) {
- pr_err("bind to driver error (%d)\n", retval);
- device_del(&fusb300->gadget.dev);
- goto error;
- }
return 0;
-
-error:
- fusb300->driver = NULL;
- fusb300->gadget.dev.driver = NULL;
-
- return retval;
}
-static int fusb300_udc_stop(struct usb_gadget_driver *driver)
+static int fusb300_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct fusb300 *fusb300 = the_controller;
-
- if (driver != fusb300->driver || !driver->unbind)
- return -EINVAL;
+ struct fusb300 *fusb300 = to_fusb300(g);
driver->unbind(&fusb300->gadget);
- fusb300->gadget.dev.driver = NULL;
init_controller(fusb300);
- device_del(&fusb300->gadget.dev);
fusb300->driver = NULL;
return 0;
@@ -1378,15 +1339,15 @@ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
return 0;
}
-static struct usb_gadget_ops fusb300_gadget_ops = {
+static const struct usb_gadget_ops fusb300_gadget_ops = {
.pullup = fusb300_udc_pullup,
- .start = fusb300_udc_start,
- .stop = fusb300_udc_stop,
+ .udc_start = fusb300_udc_start,
+ .udc_stop = fusb300_udc_stop,
};
static int __exit fusb300_remove(struct platform_device *pdev)
{
- struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev);
+ struct fusb300 *fusb300 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&fusb300->gadget);
iounmap(fusb300->reg);
@@ -1439,34 +1400,23 @@ static int __init fusb300_probe(struct platform_device *pdev)
/* initialize udc */
fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
- if (fusb300 == NULL) {
- pr_err("kzalloc error\n");
+ if (fusb300 == NULL)
goto clean_up;
- }
for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
_ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
- if (_ep[i] == NULL) {
- pr_err("_ep kzalloc error\n");
+ if (_ep[i] == NULL)
goto clean_up;
- }
fusb300->ep[i] = _ep[i];
}
spin_lock_init(&fusb300->lock);
- dev_set_drvdata(&pdev->dev, fusb300);
+ platform_set_drvdata(pdev, fusb300);
fusb300->gadget.ops = &fusb300_gadget_ops;
- device_initialize(&fusb300->gadget.dev);
-
- dev_set_name(&fusb300->gadget.dev, "gadget");
-
fusb300->gadget.max_speed = USB_SPEED_HIGH;
- fusb300->gadget.dev.parent = &pdev->dev;
- fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask;
- fusb300->gadget.dev.release = pdev->dev.release;
fusb300->gadget.name = udc_name;
fusb300->reg = reg;
@@ -1498,19 +1448,19 @@ static int __init fusb300_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = fusb300_ep_name[i];
ep->ep.ops = &fusb300_ep_ops;
- ep->ep.maxpacket = HS_BULK_MAX_PACKET_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE);
}
- fusb300->ep[0]->ep.maxpacket = HS_CTL_MAX_PACKET_SIZE;
+ usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE);
fusb300->ep[0]->epnum = 0;
fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
- the_controller = fusb300;
-
fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep,
GFP_KERNEL);
- if (fusb300->ep0_req == NULL)
+ if (fusb300->ep0_req == NULL) {
+ ret = -ENOMEM;
goto clean_up3;
+ }
init_controller(fusb300);
ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget);
@@ -1520,6 +1470,7 @@ static int __init fusb300_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
return 0;
+
err_add_udc:
fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
@@ -1547,15 +1498,4 @@ static struct platform_driver fusb300_driver = {
},
};
-static int __init fusb300_udc_init(void)
-{
- return platform_driver_probe(&fusb300_driver, fusb300_probe);
-}
-
-module_init(fusb300_udc_init);
-
-static void __exit fusb300_udc_cleanup(void)
-{
- platform_driver_unregister(&fusb300_driver);
-}
-module_exit(fusb300_udc_cleanup);
+module_platform_driver_probe(fusb300_driver, fusb300_probe);
diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h
index 92745bd0306..ae811d8d38b 100644
--- a/drivers/usb/gadget/fusb300_udc.h
+++ b/drivers/usb/gadget/fusb300_udc.h
@@ -111,8 +111,8 @@
/*
* * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 )
* */
+#define FUSB300_EPSET0_STL_CLR (1 << 3)
#define FUSB300_EPSET0_CLRSEQNUM (1 << 2)
-#define FUSB300_EPSET0_EPn_TX0BYTE (1 << 1)
#define FUSB300_EPSET0_STL (1 << 0)
/*
@@ -650,7 +650,6 @@ struct fusb300_ep {
unsigned char epnum;
unsigned char type;
- const struct usb_endpoint_descriptor *desc;
};
struct fusb300 {
@@ -674,4 +673,6 @@ struct fusb300 {
u8 reenum; /* if re-enumeration */
};
+#define to_fusb300(g) (container_of((g), struct fusb300, gadget))
+
#endif
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 0519d77915e..fe12e6a2744 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -2,7 +2,7 @@
* g_ffs.c -- user mode file system API for USB composite function controllers
*
* Copyright (C) 2010 Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * 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
@@ -13,22 +13,10 @@
#define pr_fmt(fmt) "g_ffs: " fmt
#include <linux/module.h>
-#include <linux/utsname.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"
#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
@@ -36,25 +24,31 @@
# define USB_ETH_RNDIS y
# endif
-# include "f_ecm.c"
-# include "f_subset.c"
+# include "u_ecm.h"
+# include "u_gether.h"
# ifdef USB_ETH_RNDIS
-# include "f_rndis.c"
-# include "rndis.c"
+# include "u_rndis.h"
+# include "rndis.h"
# endif
-# include "u_ether.c"
+# include "u_ether.h"
+
+USB_ETHERNET_MODULE_PARAMETERS();
-static u8 gfs_hostaddr[ETH_ALEN];
# ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+static int eth_bind_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+# endif
+# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+static int bind_rndis_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
# endif
-#else
-# define gether_cleanup() do { } while (0)
-# define gether_setup(gadget, hostaddr) ((int)0)
-# define gfs_hostaddr NULL
#endif
-#include "f_fs.c"
+#include "u_fs.h"
#define DRIVER_NAME "g_ffs"
#define DRIVER_DESC "USB Function Filesystem"
@@ -67,6 +61,10 @@ 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,
@@ -78,12 +76,17 @@ static struct usb_device_descriptor gfs_dev_desc = {
.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 *)
@@ -103,6 +106,9 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
/* 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
@@ -125,11 +131,12 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
struct gfs_configuration {
struct usb_configuration c;
- int (*eth)(struct usb_configuration *c, u8 *ethaddr);
+ int (*eth)(struct usb_configuration *c);
+ int num;
} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
- .eth = rndis_bind_config,
+ .eth = bind_rndis_config,
},
#endif
@@ -145,143 +152,338 @@ struct gfs_configuration {
#endif
};
+static void *functionfs_acquire_dev(struct ffs_dev *dev);
+static void functionfs_release_dev(struct ffs_dev *dev);
+static int functionfs_ready_callback(struct ffs_data *ffs);
+static void functionfs_closed_callback(struct ffs_data *ffs);
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
-static struct usb_composite_driver gfs_driver = {
+
+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,
- .iProduct = DRIVER_DESC,
};
-static struct ffs_data *gfs_ffs_data;
-static unsigned long gfs_registered;
+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
-static int gfs_init(void)
+#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();
- return functionfs_init();
+ 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 gfs_exit(void)
+static void __exit gfs_exit(void)
{
+ int i;
+
ENTER();
- if (test_and_clear_bit(0, &gfs_registered))
+ if (gfs_registered)
usb_composite_unregister(&gfs_driver);
+ gfs_registered = false;
+
+ kfree(f_ffs[0]);
- functionfs_cleanup();
+ 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;
+ int ret = 0;
- ENTER();
+ if (--missing_funcs)
+ return 0;
- if (WARN_ON(test_and_set_bit(0, &gfs_registered)))
+ if (gfs_registered)
return -EBUSY;
- gfs_ffs_data = ffs;
- ret = usb_composite_probe(&gfs_driver, gfs_bind);
+ gfs_registered = true;
+
+ ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
- clear_bit(0, &gfs_registered);
+ gfs_registered = false;
+
return ret;
}
+/*
+ * The caller of this function takes ffs_lock
+ */
static void functionfs_closed_callback(struct ffs_data *ffs)
{
- ENTER();
+ missing_funcs++;
- if (test_and_clear_bit(0, &gfs_registered))
+ if (gfs_registered)
usb_composite_unregister(&gfs_driver);
+ gfs_registered = false;
}
-static int functionfs_check_dev_callback(const char *dev_name)
-{
- return 0;
-}
-
+/*
+ * 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 (WARN_ON(!gfs_ffs_data))
+ 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
- ret = gether_setup(cdev->gadget, gfs_hostaddr);
- if (unlikely(ret < 0))
- goto error_quick;
+#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
- ret = usb_string_ids_tab(cdev, gfs_strings);
- if (unlikely(ret < 0))
- goto error;
+#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
- ret = functionfs_bind(gfs_ffs_data, cdev);
+#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;
+ 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[i].s;
- c->c.iConfiguration = gfs_strings[i].id;
+ 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:
- functionfs_unbind(gfs_ffs_data);
+error_rndis:
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ usb_put_function_instance(fi_rndis);
error:
- gether_cleanup();
-error_quick:
- gfs_ffs_data = NULL;
+#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();
- /*
- * We may have been called in an error recovery from
- * composite_bind() after gfs_unbind() failure so we need to
- * check if gfs_ffs_data is not NULL since gfs_bind() handles
- * all error recovery itself. I'd rather we werent called
- * from composite on orror recovery, but what you're gonna
- * do...?
- */
- if (gfs_ffs_data) {
- gether_cleanup();
- functionfs_unbind(gfs_ffs_data);
- gfs_ffs_data = NULL;
+
+#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 (WARN_ON(!gfs_ffs_data))
+ if (missing_funcs)
return -ENODEV;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -290,14 +492,23 @@ static int gfs_do_config(struct usb_configuration *c)
}
if (gc->eth) {
- ret = gc->eth(c, gfs_hostaddr);
+ ret = gc->eth(c);
if (unlikely(ret < 0))
return ret;
}
- ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data);
- 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
@@ -313,15 +524,59 @@ static int gfs_do_config(struct usb_configuration *c)
c->interface[c->next_interface_id] = NULL;
return 0;
+error:
+ while (--i >= 0) {
+ if (!IS_ERR(f_ffs[gc->num][i]))
+ usb_remove_function(c, f_ffs[gc->num][i]);
+ usb_put_function(f_ffs[gc->num][i]);
+ }
+ return ret;
}
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+static int eth_bind_config(struct usb_configuration *c)
{
- return can_support_ecm(c->cdev->gadget)
- ? ecm_bind_config(c, ethaddr)
- : geth_bind_config(c, ethaddr);
+ int status = 0;
+
+ if (can_support_ecm(c->cdev->gadget)) {
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ status = usb_add_function(c, f_ecm);
+ if (status < 0)
+ usb_put_function(f_ecm);
+
+ } else {
+ f_geth = usb_get_function(fi_geth);
+ if (IS_ERR(f_geth))
+ return PTR_ERR(f_geth);
+
+ status = usb_add_function(c, f_geth);
+ if (status < 0)
+ usb_put_function(f_geth);
+ }
+ return status;
+}
+
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+
+static int bind_rndis_config(struct usb_configuration *c)
+{
+ int status = 0;
+
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ status = usb_add_function(c, f_rndis);
+ if (status < 0)
+ usb_put_function(f_rndis);
+
+ return status;
}
#endif
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
index 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 a8855d0b7f3..bcd04bc66b9 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,6 +15,8 @@
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
+#include <linux/usb/gadget.h>
+
/*
* NOTICE: the entries below are alphabetical and should be kept
* that way.
@@ -25,103 +27,12 @@
* If you have forgotten the alphabetical order let VIM/EMACS
* do that for you.
*/
-#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
-#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
-#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
-#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
-#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
-#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
-#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
-#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
-#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
-#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
-#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
-#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
-#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
-#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
-#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
-#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
-#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
-#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
-#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
-
-/**
- * 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.
- *
- * 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.
- *
- * 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.
- */
-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_goku(gadget))
- return 0x06;
- else if (gadget_is_omap(gadget))
- return 0x08;
- 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_musbhdrc(gadget))
- return 0x16;
- 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_pci(gadget))
- return 0x23;
- else if (gadget_is_langwell(gadget))
- return 0x24;
- else if (gadget_is_r8a66597(gadget))
- return 0x25;
- else if (gadget_is_s3c_hsotg(gadget))
- return 0x26;
- else if (gadget_is_pch(gadget))
- return 0x27;
- else if (gadget_is_ci13xxx_msm(gadget))
- return 0x28;
- else if (gadget_is_renesas_usbhs(gadget))
- return 0x29;
- else if (gadget_is_s3c_hsudc(gadget))
- return 0x30;
- else if (gadget_is_net2272(gadget))
- return 0x31;
- else if (gadget_is_dwc3(gadget))
- return 0x32;
-
- return -ENOENT;
-}
-
/**
* gadget_supports_altsettings - return true if altsettings work
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 681bd038b1d..e879e2c9f46 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -22,7 +22,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -31,16 +30,13 @@
#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"
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "f_midi.c"
/*-------------------------------------------------------------------------*/
@@ -51,6 +47,8 @@ MODULE_LICENSE("GPL v2");
static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
+USB_GADGET_COMPOSITE_OPTIONS();
+
static int index = SNDRV_DEFAULT_IDX1;
module_param(index, int, S_IRUGO);
MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
@@ -85,9 +83,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
@@ -102,8 +98,9 @@ static struct usb_device_descriptor device_desc = {
};
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = "Grey Innovation",
- [STRING_PRODUCT_IDX].s = "MIDI Gadget",
+ [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 */
};
@@ -128,7 +125,7 @@ static struct usb_configuration midi_config = {
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE,
- .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
+ .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
static int __init midi_bind_config(struct usb_configuration *c)
@@ -140,61 +137,35 @@ static int __init midi_bind_config(struct usb_configuration *c)
static int __init midi_bind(struct usb_composite_dev *cdev)
{
- struct usb_gadget *gadget = cdev->gadget;
- int gcnum, status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
+ int status;
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
- midi_config.iConfiguration = status;
-
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum < 0) {
- /* 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",
- __func__, gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x9999);
- } else {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- }
+ 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;
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;
}
-static struct usb_composite_driver midi_driver = {
+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 midi_init(void)
{
- return usb_composite_probe(&midi_driver, midi_bind);
+ return usb_composite_probe(&midi_driver);
}
module_init(midi_init);
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 5af70fcce13..6c85839e15a 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -30,11 +30,11 @@
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -43,7 +43,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
@@ -52,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;
@@ -103,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;
@@ -177,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,
@@ -233,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;
@@ -247,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)
@@ -276,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;
}
@@ -310,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)
@@ -725,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))
@@ -736,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
@@ -777,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))
@@ -817,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)
@@ -898,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;
}
@@ -957,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;
}
@@ -996,21 +990,22 @@ static int goku_get_frame(struct usb_gadget *_gadget)
return -EOPNOTSUPP;
}
-static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int goku_stop(struct usb_gadget_driver *driver);
+static int goku_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int goku_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops goku_ops = {
.get_frame = goku_get_frame,
- .start = goku_start,
- .stop = goku_stop,
+ .udc_start = goku_udc_start,
+ .udc_stop = goku_udc_stop,
// no remote wakeup
// not selfpowered
};
/*-------------------------------------------------------------------------*/
-static inline char *dmastr(void)
+static inline const char *dmastr(void)
{
if (use_dma == 0)
return "(dma disabled)";
@@ -1027,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" : "",
@@ -1060,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"
@@ -1098,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;
@@ -1110,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+" : "",
@@ -1145,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,
@@ -1186,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) {
@@ -1216,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 */
/*-------------------------------------------------------------------------*/
@@ -1267,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);
}
@@ -1342,96 +1325,56 @@ static void udc_enable(struct goku_udc *dev)
* - one function driver, initted second
*/
-static struct goku_udc *the_controller;
-
/* when a driver is successfully registered, it will receive
* control requests including set_configuration(), which enables
* non-control requests. then usb traffic follows until a
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int goku_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct goku_udc *dev = the_controller;
- int retval;
-
- if (!driver
- || driver->max_speed < USB_SPEED_FULL
- || !bind
- || !driver->disconnect
- || !driver->setup)
- return -EINVAL;
- if (!dev)
- return -ENODEV;
- if (dev->driver)
- return -EBUSY;
+ struct goku_udc *dev = to_goku_udc(g);
/* hook up the driver */
driver->driver.bus = NULL;
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
- if (retval) {
- DBG(dev, "bind to driver %s --> error %d\n",
- driver->driver.name, retval);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
- /* then enable host detection and ep0; and we're ready
+ /*
+ * then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
udc_enable(dev);
- DBG(dev, "registered gadget driver '%s'\n", driver->driver.name);
return 0;
}
-static void
-stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
+static void stop_activity(struct goku_udc *dev)
{
unsigned i;
DBG (dev, "%s\n", __func__);
- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
-
/* disconnect gadget driver after quiesceing hw and the driver */
udc_reset (dev);
for (i = 0; i < 4; i++)
nuke(&dev->ep [i], -ESHUTDOWN);
- if (driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
if (dev->driver)
udc_enable(dev);
}
-static int goku_stop(struct usb_gadget_driver *driver)
+static int goku_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct goku_udc *dev = the_controller;
+ struct goku_udc *dev = to_goku_udc(g);
unsigned long flags;
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver || !driver->unbind)
- return -EINVAL;
-
spin_lock_irqsave(&dev->lock, flags);
dev->driver = NULL;
- stop_activity(dev, driver);
+ stop_activity(dev);
spin_unlock_irqrestore(&dev->lock, flags);
- driver->unbind(&dev->gadget);
- dev->gadget.dev.driver = NULL;
-
- DBG(dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
@@ -1475,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)) {
@@ -1572,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
@@ -1587,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);
@@ -1751,12 +1695,8 @@ static void goku_remove(struct pci_dev *pdev)
pci_resource_len (pdev, 0));
if (dev->enabled)
pci_disable_device(pdev);
- if (dev->registered)
- device_unregister(&dev->gadget.dev);
- pci_set_drvdata(pdev, NULL);
dev->regs = NULL;
- the_controller = NULL;
INFO(dev, "unbind\n");
}
@@ -1772,13 +1712,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
void __iomem *base = NULL;
int retval;
- /* if you want to support more than one controller in a system,
- * usb_gadget_driver_{register,unregister}() must change.
- */
- if (the_controller) {
- pr_warning("ignoring %s\n", pci_name(pdev));
- return -EBUSY;
- }
if (!pdev->irq) {
printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev));
retval = -ENODEV;
@@ -1799,10 +1732,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.max_speed = USB_SPEED_FULL;
/* the "gadget" abstracts/virtualizes the controller */
- dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.dev.parent = &pdev->dev;
- dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
- dev->gadget.dev.release = gadget_release;
dev->gadget.name = driver_name;
/* now all the pci goodies ... */
@@ -1838,7 +1767,7 @@ 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;
@@ -1850,17 +1779,11 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);
+ proc_create_data(proc_node_name, 0, NULL, &udc_proc_fops, dev);
#endif
- the_controller = dev;
- retval = device_register(&dev->gadget.dev);
- if (retval) {
- put_device(&dev->gadget.dev);
- goto err;
- }
- dev->registered = 1;
- retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+ gadget_release);
if (retval)
goto err;
@@ -1897,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 e7e0c69d3b1..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;
@@ -251,8 +250,7 @@ struct goku_udc {
got_region:1,
req_config:1,
configured:1,
- enabled:1,
- registered:1;
+ enabled:1;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
@@ -262,6 +260,7 @@ struct goku_udc {
/* statistics... */
unsigned long irqs;
};
+#define to_goku_udc(g) (container_of((g), struct goku_udc, gadget))
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c
new file mode 100644
index 00000000000..c7004ee89c9
--- /dev/null
+++ b/drivers/usb/gadget/gr_udc.c
@@ -0,0 +1,2236 @@
+/*
+ * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports GRUSBDC USB Device Controller cores available in the
+ * GRLIB VHDL IP core library.
+ *
+ * Full documentation of the GRUSBDC core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Contributors:
+ * - Andreas Larsson <andreas@gaisler.com>
+ * - Marko Isomaki
+ */
+
+/*
+ * A GRUSBDC core can have up to 16 IN endpoints and 16 OUT endpoints each
+ * individually configurable to any of the four USB transfer types. This driver
+ * only supports cores in DMA mode.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include <asm/byteorder.h>
+
+#include "gr_udc.h"
+
+#define DRIVER_NAME "gr_udc"
+#define DRIVER_DESC "Aeroflex Gaisler GRUSBDC USB Peripheral Controller"
+
+static const char driver_name[] = DRIVER_NAME;
+static const char driver_desc[] = DRIVER_DESC;
+
+#define gr_read32(x) (ioread32be((x)))
+#define gr_write32(x, v) (iowrite32be((v), (x)))
+
+/* USB speed and corresponding string calculated from status register value */
+#define GR_SPEED(status) \
+ ((status & GR_STATUS_SP) ? USB_SPEED_FULL : USB_SPEED_HIGH)
+#define GR_SPEED_STR(status) usb_speed_string(GR_SPEED(status))
+
+/* Size of hardware buffer calculated from epctrl register value */
+#define GR_BUFFER_SIZE(epctrl) \
+ ((((epctrl) & GR_EPCTRL_BUFSZ_MASK) >> GR_EPCTRL_BUFSZ_POS) * \
+ GR_EPCTRL_BUFSZ_SCALER)
+
+/* ---------------------------------------------------------------------- */
+/* Debug printout functionality */
+
+static const char * const gr_modestring[] = {"control", "iso", "bulk", "int"};
+
+static const char *gr_ep0state_string(enum gr_ep0state state)
+{
+ static const char *const names[] = {
+ [GR_EP0_DISCONNECT] = "disconnect",
+ [GR_EP0_SETUP] = "setup",
+ [GR_EP0_IDATA] = "idata",
+ [GR_EP0_ODATA] = "odata",
+ [GR_EP0_ISTATUS] = "istatus",
+ [GR_EP0_OSTATUS] = "ostatus",
+ [GR_EP0_STALL] = "stall",
+ [GR_EP0_SUSPEND] = "suspend",
+ };
+
+ if (state < 0 || state >= ARRAY_SIZE(names))
+ return "UNKNOWN";
+
+ return names[state];
+}
+
+#ifdef VERBOSE_DEBUG
+
+static void gr_dbgprint_request(const char *str, struct gr_ep *ep,
+ struct gr_request *req)
+{
+ int buflen = ep->is_in ? req->req.length : req->req.actual;
+ int rowlen = 32;
+ int plen = min(rowlen, buflen);
+
+ dev_dbg(ep->dev->dev, "%s: 0x%p, %d bytes data%s:\n", str, req, buflen,
+ (buflen > plen ? " (truncated)" : ""));
+ print_hex_dump_debug(" ", DUMP_PREFIX_NONE,
+ rowlen, 4, req->req.buf, plen, false);
+}
+
+static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request,
+ u16 value, u16 index, u16 length)
+{
+ dev_vdbg(dev->dev, "REQ: %02x.%02x v%04x i%04x l%04x\n",
+ type, request, value, index, length);
+}
+#else /* !VERBOSE_DEBUG */
+
+static void gr_dbgprint_request(const char *str, struct gr_ep *ep,
+ struct gr_request *req) {}
+
+static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request,
+ u16 value, u16 index, u16 length) {}
+
+#endif /* VERBOSE_DEBUG */
+
+/* ---------------------------------------------------------------------- */
+/* Debugfs functionality */
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FS
+
+static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep)
+{
+ u32 epctrl = gr_read32(&ep->regs->epctrl);
+ u32 epstat = gr_read32(&ep->regs->epstat);
+ int mode = (epctrl & GR_EPCTRL_TT_MASK) >> GR_EPCTRL_TT_POS;
+ struct gr_request *req;
+
+ seq_printf(seq, "%s:\n", ep->ep.name);
+ seq_printf(seq, " mode = %s\n", gr_modestring[mode]);
+ seq_printf(seq, " halted: %d\n", !!(epctrl & GR_EPCTRL_EH));
+ seq_printf(seq, " disabled: %d\n", !!(epctrl & GR_EPCTRL_ED));
+ seq_printf(seq, " valid: %d\n", !!(epctrl & GR_EPCTRL_EV));
+ seq_printf(seq, " dma_start = %d\n", ep->dma_start);
+ seq_printf(seq, " stopped = %d\n", ep->stopped);
+ seq_printf(seq, " wedged = %d\n", ep->wedged);
+ seq_printf(seq, " callback = %d\n", ep->callback);
+ seq_printf(seq, " maxpacket = %d\n", ep->ep.maxpacket);
+ seq_printf(seq, " maxpacket_limit = %d\n", ep->ep.maxpacket_limit);
+ seq_printf(seq, " bytes_per_buffer = %d\n", ep->bytes_per_buffer);
+ if (mode == 1 || mode == 3)
+ seq_printf(seq, " nt = %d\n",
+ (epctrl & GR_EPCTRL_NT_MASK) >> GR_EPCTRL_NT_POS);
+
+ seq_printf(seq, " Buffer 0: %s %s%d\n",
+ epstat & GR_EPSTAT_B0 ? "valid" : "invalid",
+ epstat & GR_EPSTAT_BS ? " " : "selected ",
+ (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS);
+ seq_printf(seq, " Buffer 1: %s %s%d\n",
+ epstat & GR_EPSTAT_B1 ? "valid" : "invalid",
+ epstat & GR_EPSTAT_BS ? "selected " : " ",
+ (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS);
+
+ if (list_empty(&ep->queue)) {
+ seq_puts(seq, " Queue: empty\n\n");
+ return;
+ }
+
+ seq_puts(seq, " Queue:\n");
+ list_for_each_entry(req, &ep->queue, queue) {
+ struct gr_dma_desc *desc;
+ struct gr_dma_desc *next;
+
+ seq_printf(seq, " 0x%p: 0x%p %d %d\n", req,
+ &req->req.buf, req->req.actual, req->req.length);
+
+ next = req->first_desc;
+ do {
+ desc = next;
+ next = desc->next_desc;
+ seq_printf(seq, " %c 0x%p (0x%08x): 0x%05x 0x%08x\n",
+ desc == req->curr_desc ? 'c' : ' ',
+ desc, desc->paddr, desc->ctrl, desc->data);
+ } while (desc != req->last_desc);
+ }
+ seq_puts(seq, "\n");
+}
+
+
+static int gr_seq_show(struct seq_file *seq, void *v)
+{
+ struct gr_udc *dev = seq->private;
+ u32 control = gr_read32(&dev->regs->control);
+ u32 status = gr_read32(&dev->regs->status);
+ struct gr_ep *ep;
+
+ seq_printf(seq, "usb state = %s\n",
+ usb_state_string(dev->gadget.state));
+ seq_printf(seq, "address = %d\n",
+ (control & GR_CONTROL_UA_MASK) >> GR_CONTROL_UA_POS);
+ seq_printf(seq, "speed = %s\n", GR_SPEED_STR(status));
+ seq_printf(seq, "ep0state = %s\n", gr_ep0state_string(dev->ep0state));
+ seq_printf(seq, "irq_enabled = %d\n", dev->irq_enabled);
+ seq_printf(seq, "remote_wakeup = %d\n", dev->remote_wakeup);
+ seq_printf(seq, "test_mode = %d\n", dev->test_mode);
+ seq_puts(seq, "\n");
+
+ list_for_each_entry(ep, &dev->ep_list, ep_list)
+ gr_seq_ep_show(seq, ep);
+
+ return 0;
+}
+
+static int gr_dfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, gr_seq_show, inode->i_private);
+}
+
+static const struct file_operations gr_dfs_fops = {
+ .owner = THIS_MODULE,
+ .open = gr_dfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void gr_dfs_create(struct gr_udc *dev)
+{
+ const char *name = "gr_udc_state";
+
+ dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
+ dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
+ &gr_dfs_fops);
+}
+
+static void gr_dfs_delete(struct gr_udc *dev)
+{
+ /* Handles NULL and ERR pointers internally */
+ debugfs_remove(dev->dfs_state);
+ debugfs_remove(dev->dfs_root);
+}
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FS */
+
+static void gr_dfs_create(struct gr_udc *dev) {}
+static void gr_dfs_delete(struct gr_udc *dev) {}
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FS */
+
+/* ---------------------------------------------------------------------- */
+/* DMA and request handling */
+
+/* Allocates a new struct gr_dma_desc, sets paddr and zeroes the rest */
+static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags)
+{
+ dma_addr_t paddr;
+ struct gr_dma_desc *dma_desc;
+
+ dma_desc = dma_pool_alloc(ep->dev->desc_pool, gfp_flags, &paddr);
+ if (!dma_desc) {
+ dev_err(ep->dev->dev, "Could not allocate from DMA pool\n");
+ return NULL;
+ }
+
+ memset(dma_desc, 0, sizeof(*dma_desc));
+ dma_desc->paddr = paddr;
+
+ return dma_desc;
+}
+
+static inline void gr_free_dma_desc(struct gr_udc *dev,
+ struct gr_dma_desc *desc)
+{
+ dma_pool_free(dev->desc_pool, desc, (dma_addr_t)desc->paddr);
+}
+
+/* Frees the chain of struct gr_dma_desc for the given request */
+static void gr_free_dma_desc_chain(struct gr_udc *dev, struct gr_request *req)
+{
+ struct gr_dma_desc *desc;
+ struct gr_dma_desc *next;
+
+ next = req->first_desc;
+ if (!next)
+ return;
+
+ do {
+ desc = next;
+ next = desc->next_desc;
+ gr_free_dma_desc(dev, desc);
+ } while (desc != req->last_desc);
+
+ req->first_desc = NULL;
+ req->curr_desc = NULL;
+ req->last_desc = NULL;
+}
+
+static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req);
+
+/*
+ * Frees allocated resources and calls the appropriate completion function/setup
+ * package handler for a finished request.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
+ int status)
+ __releases(&dev->lock)
+ __acquires(&dev->lock)
+{
+ struct gr_udc *dev;
+
+ list_del_init(&req->queue);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ dev = ep->dev;
+ usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+ gr_free_dma_desc_chain(dev, req);
+
+ if (ep->is_in) /* For OUT, actual gets updated bit by bit */
+ req->req.actual = req->req.length;
+
+ if (!status) {
+ if (ep->is_in)
+ gr_dbgprint_request("SENT", ep, req);
+ else
+ gr_dbgprint_request("RECV", ep, req);
+ }
+
+ /* Prevent changes to ep->queue during callback */
+ ep->callback = 1;
+ if (req == dev->ep0reqo && !status) {
+ if (req->setup)
+ gr_ep0_setup(dev, req);
+ else
+ dev_err(dev->dev,
+ "Unexpected non setup packet on ep0in\n");
+ } else if (req->req.complete) {
+ spin_unlock(&dev->lock);
+
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock(&dev->lock);
+ }
+ ep->callback = 0;
+}
+
+static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct gr_request *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+/*
+ * Starts DMA for endpoint ep if there are requests in the queue.
+ *
+ * Must be called with dev->lock held and with !ep->stopped.
+ */
+static void gr_start_dma(struct gr_ep *ep)
+{
+ struct gr_request *req;
+ u32 dmactrl;
+
+ if (list_empty(&ep->queue)) {
+ ep->dma_start = 0;
+ return;
+ }
+
+ req = list_first_entry(&ep->queue, struct gr_request, queue);
+
+ /* A descriptor should already have been allocated */
+ BUG_ON(!req->curr_desc);
+
+ wmb(); /* Make sure all is settled before handing it over to DMA */
+
+ /* Set the descriptor pointer in the hardware */
+ gr_write32(&ep->regs->dmaaddr, req->curr_desc->paddr);
+
+ /* Announce available descriptors */
+ dmactrl = gr_read32(&ep->regs->dmactrl);
+ gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_DA);
+
+ ep->dma_start = 1;
+}
+
+/*
+ * Finishes the first request in the ep's queue and, if available, starts the
+ * next request in queue.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static void gr_dma_advance(struct gr_ep *ep, int status)
+{
+ struct gr_request *req;
+
+ req = list_first_entry(&ep->queue, struct gr_request, queue);
+ gr_finish_request(ep, req, status);
+ gr_start_dma(ep); /* Regardless of ep->dma_start */
+}
+
+/*
+ * Abort DMA for an endpoint. Sets the abort DMA bit which causes an ongoing DMA
+ * transfer to be canceled and clears GR_DMACTRL_DA.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_abort_dma(struct gr_ep *ep)
+{
+ u32 dmactrl;
+
+ dmactrl = gr_read32(&ep->regs->dmactrl);
+ gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_AD);
+}
+
+/*
+ * Allocates and sets up a struct gr_dma_desc and putting it on the descriptor
+ * chain.
+ *
+ * Size is not used for OUT endpoints. Hardware can not be instructed to handle
+ * smaller buffer than MAXPL in the OUT direction.
+ */
+static int gr_add_dma_desc(struct gr_ep *ep, struct gr_request *req,
+ dma_addr_t data, unsigned size, gfp_t gfp_flags)
+{
+ struct gr_dma_desc *desc;
+
+ desc = gr_alloc_dma_desc(ep, gfp_flags);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->data = data;
+ if (ep->is_in)
+ desc->ctrl =
+ (GR_DESC_IN_CTRL_LEN_MASK & size) | GR_DESC_IN_CTRL_EN;
+ else
+ desc->ctrl = GR_DESC_OUT_CTRL_IE;
+
+ if (!req->first_desc) {
+ req->first_desc = desc;
+ req->curr_desc = desc;
+ } else {
+ req->last_desc->next_desc = desc;
+ req->last_desc->next = desc->paddr;
+ req->last_desc->ctrl |= GR_DESC_OUT_CTRL_NX;
+ }
+ req->last_desc = desc;
+
+ return 0;
+}
+
+/*
+ * Sets up a chain of struct gr_dma_descriptors pointing to buffers that
+ * together covers req->req.length bytes of the buffer at DMA address
+ * req->req.dma for the OUT direction.
+ *
+ * The first descriptor in the chain is enabled, the rest disabled. The
+ * interrupt handler will later enable them one by one when needed so we can
+ * find out when the transfer is finished. For OUT endpoints, all descriptors
+ * therefore generate interrutps.
+ */
+static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req,
+ gfp_t gfp_flags)
+{
+ u16 bytes_left; /* Bytes left to provide descriptors for */
+ u16 bytes_used; /* Bytes accommodated for */
+ int ret = 0;
+
+ req->first_desc = NULL; /* Signals that no allocation is done yet */
+ bytes_left = req->req.length;
+ bytes_used = 0;
+ while (bytes_left > 0) {
+ dma_addr_t start = req->req.dma + bytes_used;
+ u16 size = min(bytes_left, ep->bytes_per_buffer);
+
+ /* Should not happen however - gr_queue stops such lengths */
+ if (size < ep->bytes_per_buffer)
+ dev_warn(ep->dev->dev,
+ "Buffer overrun risk: %u < %u bytes/buffer\n",
+ size, ep->bytes_per_buffer);
+
+ ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
+ if (ret)
+ goto alloc_err;
+
+ bytes_left -= size;
+ bytes_used += size;
+ }
+
+ req->first_desc->ctrl |= GR_DESC_OUT_CTRL_EN;
+
+ return 0;
+
+alloc_err:
+ gr_free_dma_desc_chain(ep->dev, req);
+
+ return ret;
+}
+
+/*
+ * Sets up a chain of struct gr_dma_descriptors pointing to buffers that
+ * together covers req->req.length bytes of the buffer at DMA address
+ * req->req.dma for the IN direction.
+ *
+ * When more data is provided than the maximum payload size, the hardware splits
+ * this up into several payloads automatically. Moreover, ep->bytes_per_buffer
+ * is always set to a multiple of the maximum payload (restricted to the valid
+ * number of maximum payloads during high bandwidth isochronous or interrupt
+ * transfers)
+ *
+ * All descriptors are enabled from the beginning and we only generate an
+ * interrupt for the last one indicating that the entire request has been pushed
+ * to hardware.
+ */
+static int gr_setup_in_desc_list(struct gr_ep *ep, struct gr_request *req,
+ gfp_t gfp_flags)
+{
+ u16 bytes_left; /* Bytes left in req to provide descriptors for */
+ u16 bytes_used; /* Bytes in req accommodated for */
+ int ret = 0;
+
+ req->first_desc = NULL; /* Signals that no allocation is done yet */
+ bytes_left = req->req.length;
+ bytes_used = 0;
+ do { /* Allow for zero length packets */
+ dma_addr_t start = req->req.dma + bytes_used;
+ u16 size = min(bytes_left, ep->bytes_per_buffer);
+
+ ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
+ if (ret)
+ goto alloc_err;
+
+ bytes_left -= size;
+ bytes_used += size;
+ } while (bytes_left > 0);
+
+ /*
+ * Send an extra zero length packet to indicate that no more data is
+ * available when req->req.zero is set and the data length is even
+ * multiples of ep->ep.maxpacket.
+ */
+ if (req->req.zero && (req->req.length % ep->ep.maxpacket == 0)) {
+ ret = gr_add_dma_desc(ep, req, 0, 0, gfp_flags);
+ if (ret)
+ goto alloc_err;
+ }
+
+ /*
+ * For IN packets we only want to know when the last packet has been
+ * transmitted (not just put into internal buffers).
+ */
+ req->last_desc->ctrl |= GR_DESC_IN_CTRL_PI;
+
+ return 0;
+
+alloc_err:
+ gr_free_dma_desc_chain(ep->dev, req);
+
+ return ret;
+}
+
+/* Must be called with dev->lock held */
+static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags)
+{
+ struct gr_udc *dev = ep->dev;
+ int ret;
+
+ if (unlikely(!ep->ep.desc && ep->num != 0)) {
+ dev_err(dev->dev, "No ep descriptor for %s\n", ep->ep.name);
+ return -EINVAL;
+ }
+
+ if (unlikely(!req->req.buf || !list_empty(&req->queue))) {
+ dev_err(dev->dev,
+ "Invalid request for %s: buf=%p list_empty=%d\n",
+ ep->ep.name, req->req.buf, list_empty(&req->queue));
+ return -EINVAL;
+ }
+
+ /*
+ * The DMA controller can not handle smaller OUT buffers than
+ * maxpacket. It could lead to buffer overruns if unexpectedly long
+ * packet are received.
+ */
+ if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
+ dev_err(dev->dev,
+ "OUT request length %d is not multiple of maxpacket\n",
+ req->req.length);
+ return -EMSGSIZE;
+ }
+
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ dev_err(dev->dev, "-ESHUTDOWN");
+ return -ESHUTDOWN;
+ }
+
+ /* Can't touch registers when suspended */
+ if (dev->ep0state == GR_EP0_SUSPEND) {
+ dev_err(dev->dev, "-EBUSY");
+ return -EBUSY;
+ }
+
+ /* Set up DMA mapping in case the caller didn't */
+ ret = usb_gadget_map_request(&dev->gadget, &req->req, ep->is_in);
+ if (ret) {
+ dev_err(dev->dev, "usb_gadget_map_request");
+ return ret;
+ }
+
+ if (ep->is_in)
+ ret = gr_setup_in_desc_list(ep, req, gfp_flags);
+ else
+ ret = gr_setup_out_desc_list(ep, req, gfp_flags);
+ if (ret)
+ return ret;
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ list_add_tail(&req->queue, &ep->queue);
+
+ /* Start DMA if not started, otherwise interrupt handler handles it */
+ if (!ep->dma_start && likely(!ep->stopped))
+ gr_start_dma(ep);
+
+ return 0;
+}
+
+/*
+ * Queue a request from within the driver.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_queue_int(struct gr_ep *ep, struct gr_request *req,
+ gfp_t gfp_flags)
+{
+ if (ep->is_in)
+ gr_dbgprint_request("RESP", ep, req);
+
+ return gr_queue(ep, req, gfp_flags);
+}
+
+/* ---------------------------------------------------------------------- */
+/* General helper functions */
+
+/*
+ * Dequeue ALL requests.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_ep_nuke(struct gr_ep *ep)
+{
+ struct gr_request *req;
+
+ ep->stopped = 1;
+ ep->dma_start = 0;
+ gr_abort_dma(ep);
+
+ while (!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct gr_request, queue);
+ gr_finish_request(ep, req, -ESHUTDOWN);
+ }
+}
+
+/*
+ * Reset the hardware state of this endpoint.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_ep_reset(struct gr_ep *ep)
+{
+ gr_write32(&ep->regs->epctrl, 0);
+ gr_write32(&ep->regs->dmactrl, 0);
+
+ ep->ep.maxpacket = MAX_CTRL_PL_SIZE;
+ ep->ep.desc = NULL;
+ ep->stopped = 1;
+ ep->dma_start = 0;
+}
+
+/*
+ * Generate STALL on ep0in/out.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_control_stall(struct gr_udc *dev)
+{
+ u32 epctrl;
+
+ epctrl = gr_read32(&dev->epo[0].regs->epctrl);
+ gr_write32(&dev->epo[0].regs->epctrl, epctrl | GR_EPCTRL_CS);
+ epctrl = gr_read32(&dev->epi[0].regs->epctrl);
+ gr_write32(&dev->epi[0].regs->epctrl, epctrl | GR_EPCTRL_CS);
+
+ dev->ep0state = GR_EP0_STALL;
+}
+
+/*
+ * Halts, halts and wedges, or clears halt for an endpoint.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_ep_halt_wedge(struct gr_ep *ep, int halt, int wedge, int fromhost)
+{
+ u32 epctrl;
+ int retval = 0;
+
+ if (ep->num && !ep->ep.desc)
+ return -EINVAL;
+
+ if (ep->num && ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+ return -EOPNOTSUPP;
+
+ /* Never actually halt ep0, and therefore never clear halt for ep0 */
+ if (!ep->num) {
+ if (halt && !fromhost) {
+ /* ep0 halt from gadget - generate protocol stall */
+ gr_control_stall(ep->dev);
+ dev_dbg(ep->dev->dev, "EP: stall ep0\n");
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ dev_dbg(ep->dev->dev, "EP: %s halt %s\n",
+ (halt ? (wedge ? "wedge" : "set") : "clear"), ep->ep.name);
+
+ epctrl = gr_read32(&ep->regs->epctrl);
+ if (halt) {
+ /* Set HALT */
+ gr_write32(&ep->regs->epctrl, epctrl | GR_EPCTRL_EH);
+ ep->stopped = 1;
+ if (wedge)
+ ep->wedged = 1;
+ } else {
+ gr_write32(&ep->regs->epctrl, epctrl & ~GR_EPCTRL_EH);
+ ep->stopped = 0;
+ ep->wedged = 0;
+
+ /* Things might have been queued up in the meantime */
+ if (!ep->dma_start)
+ gr_start_dma(ep);
+ }
+
+ return retval;
+}
+
+/* Must be called with dev->lock held */
+static inline void gr_set_ep0state(struct gr_udc *dev, enum gr_ep0state value)
+{
+ if (dev->ep0state != value)
+ dev_vdbg(dev->dev, "STATE: ep0state=%s\n",
+ gr_ep0state_string(value));
+ dev->ep0state = value;
+}
+
+/*
+ * Should only be called when endpoints can not generate interrupts.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_disable_interrupts_and_pullup(struct gr_udc *dev)
+{
+ gr_write32(&dev->regs->control, 0);
+ wmb(); /* Make sure that we do not deny one of our interrupts */
+ dev->irq_enabled = 0;
+}
+
+/*
+ * Stop all device activity and disable data line pullup.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static void gr_stop_activity(struct gr_udc *dev)
+{
+ struct gr_ep *ep;
+
+ list_for_each_entry(ep, &dev->ep_list, ep_list)
+ gr_ep_nuke(ep);
+
+ gr_disable_interrupts_and_pullup(dev);
+
+ gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+ usb_gadget_set_state(&dev->gadget, USB_STATE_NOTATTACHED);
+}
+
+/* ---------------------------------------------------------------------- */
+/* ep0 setup packet handling */
+
+static void gr_ep0_testmode_complete(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct gr_ep *ep;
+ struct gr_udc *dev;
+ u32 control;
+
+ ep = container_of(_ep, struct gr_ep, ep);
+ dev = ep->dev;
+
+ spin_lock(&dev->lock);
+
+ control = gr_read32(&dev->regs->control);
+ control |= GR_CONTROL_TM | (dev->test_mode << GR_CONTROL_TS_POS);
+ gr_write32(&dev->regs->control, control);
+
+ spin_unlock(&dev->lock);
+}
+
+static void gr_ep0_dummy_complete(struct usb_ep *_ep, struct usb_request *_req)
+{
+ /* Nothing needs to be done here */
+}
+
+/*
+ * Queue a response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_ep0_respond(struct gr_udc *dev, u8 *buf, int length,
+ void (*complete)(struct usb_ep *ep,
+ struct usb_request *req))
+{
+ u8 *reqbuf = dev->ep0reqi->req.buf;
+ int status;
+ int i;
+
+ for (i = 0; i < length; i++)
+ reqbuf[i] = buf[i];
+ dev->ep0reqi->req.length = length;
+ dev->ep0reqi->req.complete = complete;
+
+ status = gr_queue_int(&dev->epi[0], dev->ep0reqi, GFP_ATOMIC);
+ if (status < 0)
+ dev_err(dev->dev,
+ "Could not queue ep0in setup response: %d\n", status);
+
+ return status;
+}
+
+/*
+ * Queue a 2 byte response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_ep0_respond_u16(struct gr_udc *dev, u16 response)
+{
+ __le16 le_response = cpu_to_le16(response);
+
+ return gr_ep0_respond(dev, (u8 *)&le_response, 2,
+ gr_ep0_dummy_complete);
+}
+
+/*
+ * Queue a ZLP response on ep0in.
+ *
+ * Must be called with dev->lock held.
+ */
+static inline int gr_ep0_respond_empty(struct gr_udc *dev)
+{
+ return gr_ep0_respond(dev, NULL, 0, gr_ep0_dummy_complete);
+}
+
+/*
+ * This is run when a SET_ADDRESS request is received. First writes
+ * the new address to the control register which is updated internally
+ * when the next IN packet is ACKED.
+ *
+ * Must be called with dev->lock held.
+ */
+static void gr_set_address(struct gr_udc *dev, u8 address)
+{
+ u32 control;
+
+ control = gr_read32(&dev->regs->control) & ~GR_CONTROL_UA_MASK;
+ control |= (address << GR_CONTROL_UA_POS) & GR_CONTROL_UA_MASK;
+ control |= GR_CONTROL_SU;
+ gr_write32(&dev->regs->control, control);
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_device_request(struct gr_udc *dev, u8 type, u8 request,
+ u16 value, u16 index)
+{
+ u16 response;
+ u8 test;
+
+ switch (request) {
+ case USB_REQ_SET_ADDRESS:
+ dev_dbg(dev->dev, "STATUS: address %d\n", value & 0xff);
+ gr_set_address(dev, value & 0xff);
+ if (value)
+ usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS);
+ else
+ usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT);
+ return gr_ep0_respond_empty(dev);
+
+ case USB_REQ_GET_STATUS:
+ /* Self powered | remote wakeup */
+ response = 0x0001 | (dev->remote_wakeup ? 0x0002 : 0);
+ return gr_ep0_respond_u16(dev, response);
+
+ case USB_REQ_SET_FEATURE:
+ switch (value) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ /* Allow remote wakeup */
+ dev->remote_wakeup = 1;
+ return gr_ep0_respond_empty(dev);
+
+ case USB_DEVICE_TEST_MODE:
+ /* The hardware does not support TEST_FORCE_EN */
+ test = index >> 8;
+ if (test >= TEST_J && test <= TEST_PACKET) {
+ dev->test_mode = test;
+ return gr_ep0_respond(dev, NULL, 0,
+ gr_ep0_testmode_complete);
+ }
+ }
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ switch (value) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ /* Disallow remote wakeup */
+ dev->remote_wakeup = 0;
+ return gr_ep0_respond_empty(dev);
+ }
+ break;
+ }
+
+ return 1; /* Delegate the rest */
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_interface_request(struct gr_udc *dev, u8 type, u8 request,
+ u16 value, u16 index)
+{
+ if (dev->gadget.state != USB_STATE_CONFIGURED)
+ return -1;
+
+ /*
+ * Should return STALL for invalid interfaces, but udc driver does not
+ * know anything about that. However, many gadget drivers do not handle
+ * GET_STATUS so we need to take care of that.
+ */
+
+ switch (request) {
+ case USB_REQ_GET_STATUS:
+ return gr_ep0_respond_u16(dev, 0x0000);
+
+ case USB_REQ_SET_FEATURE:
+ case USB_REQ_CLEAR_FEATURE:
+ /*
+ * No possible valid standard requests. Still let gadget drivers
+ * have a go at it.
+ */
+ break;
+ }
+
+ return 1; /* Delegate the rest */
+}
+
+/*
+ * Returns negative for STALL, 0 for successful handling and positive for
+ * delegation.
+ *
+ * Must be called with dev->lock held.
+ */
+static int gr_endpoint_request(struct gr_udc *dev, u8 type, u8 request,
+ u16 value, u16 index)
+{
+ struct gr_ep *ep;
+ int status;
+ int halted;
+ u8 epnum = index & USB_ENDPOINT_NUMBER_MASK;
+ u8 is_in = index & USB_ENDPOINT_DIR_MASK;
+
+ if ((is_in && epnum >= dev->nepi) || (!is_in && epnum >= dev->nepo))
+ return -1;
+
+ if (dev->gadget.state != USB_STATE_CONFIGURED && epnum != 0)
+ return -1;
+
+ ep = (is_in ? &dev->epi[epnum] : &dev->epo[epnum]);
+
+ switch (request) {
+ case USB_REQ_GET_STATUS:
+ halted = gr_read32(&ep->regs->epctrl) & GR_EPCTRL_EH;
+ return gr_ep0_respond_u16(dev, halted ? 0x0001 : 0);
+
+ case USB_REQ_SET_FEATURE:
+ switch (value) {
+ case USB_ENDPOINT_HALT:
+ status = gr_ep_halt_wedge(ep, 1, 0, 1);
+ if (status >= 0)
+ status = gr_ep0_respond_empty(dev);
+ return status;
+ }
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ switch (value) {
+ case USB_ENDPOINT_HALT:
+ if (ep->wedged)
+ return -1;
+ status = gr_ep_halt_wedge(ep, 0, 0, 1);
+ if (status >= 0)
+ status = gr_ep0_respond_empty(dev);
+ return status;
+ }
+ break;
+ }
+
+ return 1; /* Delegate the rest */
+}
+
+/* Must be called with dev->lock held */
+static void gr_ep0out_requeue(struct gr_udc *dev)
+{
+ int ret = gr_queue_int(&dev->epo[0], dev->ep0reqo, GFP_ATOMIC);
+
+ if (ret)
+ dev_err(dev->dev, "Could not queue ep0out setup request: %d\n",
+ ret);
+}
+
+/*
+ * The main function dealing with setup requests on ep0.
+ *
+ * Must be called with dev->lock held and irqs disabled
+ */
+static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req)
+ __releases(&dev->lock)
+ __acquires(&dev->lock)
+{
+ union {
+ struct usb_ctrlrequest ctrl;
+ u8 raw[8];
+ u32 word[2];
+ } u;
+ u8 type;
+ u8 request;
+ u16 value;
+ u16 index;
+ u16 length;
+ int i;
+ int status;
+
+ /* Restore from ep0 halt */
+ if (dev->ep0state == GR_EP0_STALL) {
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ if (!req->req.actual)
+ goto out;
+ }
+
+ if (dev->ep0state == GR_EP0_ISTATUS) {
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ if (req->req.actual > 0)
+ dev_dbg(dev->dev,
+ "Unexpected setup packet at state %s\n",
+ gr_ep0state_string(GR_EP0_ISTATUS));
+ else
+ goto out; /* Got expected ZLP */
+ } else if (dev->ep0state != GR_EP0_SETUP) {
+ dev_info(dev->dev,
+ "Unexpected ep0out request at state %s - stalling\n",
+ gr_ep0state_string(dev->ep0state));
+ gr_control_stall(dev);
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ goto out;
+ } else if (!req->req.actual) {
+ dev_dbg(dev->dev, "Unexpected ZLP at state %s\n",
+ gr_ep0state_string(dev->ep0state));
+ goto out;
+ }
+
+ /* Handle SETUP packet */
+ for (i = 0; i < req->req.actual; i++)
+ u.raw[i] = ((u8 *)req->req.buf)[i];
+
+ type = u.ctrl.bRequestType;
+ request = u.ctrl.bRequest;
+ value = le16_to_cpu(u.ctrl.wValue);
+ index = le16_to_cpu(u.ctrl.wIndex);
+ length = le16_to_cpu(u.ctrl.wLength);
+
+ gr_dbgprint_devreq(dev, type, request, value, index, length);
+
+ /* Check for data stage */
+ if (length) {
+ if (type & USB_DIR_IN)
+ gr_set_ep0state(dev, GR_EP0_IDATA);
+ else
+ gr_set_ep0state(dev, GR_EP0_ODATA);
+ }
+
+ status = 1; /* Positive status flags delegation */
+ if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (type & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ status = gr_device_request(dev, type, request,
+ value, index);
+ break;
+ case USB_RECIP_ENDPOINT:
+ status = gr_endpoint_request(dev, type, request,
+ value, index);
+ break;
+ case USB_RECIP_INTERFACE:
+ status = gr_interface_request(dev, type, request,
+ value, index);
+ break;
+ }
+ }
+
+ if (status > 0) {
+ spin_unlock(&dev->lock);
+
+ dev_vdbg(dev->dev, "DELEGATE\n");
+ status = dev->driver->setup(&dev->gadget, &u.ctrl);
+
+ spin_lock(&dev->lock);
+ }
+
+ /* Generate STALL on both ep0out and ep0in if requested */
+ if (unlikely(status < 0)) {
+ dev_vdbg(dev->dev, "STALL\n");
+ gr_control_stall(dev);
+ }
+
+ if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
+ request == USB_REQ_SET_CONFIGURATION) {
+ if (!value) {
+ dev_dbg(dev->dev, "STATUS: deconfigured\n");
+ usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS);
+ } else if (status >= 0) {
+ /* Not configured unless gadget OK:s it */
+ dev_dbg(dev->dev, "STATUS: configured: %d\n", value);
+ usb_gadget_set_state(&dev->gadget,
+ USB_STATE_CONFIGURED);
+ }
+ }
+
+ /* Get ready for next stage */
+ if (dev->ep0state == GR_EP0_ODATA)
+ gr_set_ep0state(dev, GR_EP0_OSTATUS);
+ else if (dev->ep0state == GR_EP0_IDATA)
+ gr_set_ep0state(dev, GR_EP0_ISTATUS);
+ else
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+
+out:
+ gr_ep0out_requeue(dev);
+}
+
+/* ---------------------------------------------------------------------- */
+/* VBUS and USB reset handling */
+
+/* Must be called with dev->lock held and irqs disabled */
+static void gr_vbus_connected(struct gr_udc *dev, u32 status)
+{
+ u32 control;
+
+ dev->gadget.speed = GR_SPEED(status);
+ usb_gadget_set_state(&dev->gadget, USB_STATE_POWERED);
+
+ /* Turn on full interrupts and pullup */
+ control = (GR_CONTROL_SI | GR_CONTROL_UI | GR_CONTROL_VI |
+ GR_CONTROL_SP | GR_CONTROL_EP);
+ gr_write32(&dev->regs->control, control);
+}
+
+/* Must be called with dev->lock held */
+static void gr_enable_vbus_detect(struct gr_udc *dev)
+{
+ u32 status;
+
+ dev->irq_enabled = 1;
+ wmb(); /* Make sure we do not ignore an interrupt */
+ gr_write32(&dev->regs->control, GR_CONTROL_VI);
+
+ /* Take care of the case we are already plugged in at this point */
+ status = gr_read32(&dev->regs->status);
+ if (status & GR_STATUS_VB)
+ gr_vbus_connected(dev, status);
+}
+
+/* Must be called with dev->lock held and irqs disabled */
+static void gr_vbus_disconnected(struct gr_udc *dev)
+{
+ gr_stop_activity(dev);
+
+ /* Report disconnect */
+ if (dev->driver && dev->driver->disconnect) {
+ spin_unlock(&dev->lock);
+
+ dev->driver->disconnect(&dev->gadget);
+
+ spin_lock(&dev->lock);
+ }
+
+ gr_enable_vbus_detect(dev);
+}
+
+/* Must be called with dev->lock held and irqs disabled */
+static void gr_udc_usbreset(struct gr_udc *dev, u32 status)
+{
+ gr_set_address(dev, 0);
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT);
+ dev->gadget.speed = GR_SPEED(status);
+
+ gr_ep_nuke(&dev->epo[0]);
+ gr_ep_nuke(&dev->epi[0]);
+ dev->epo[0].stopped = 0;
+ dev->epi[0].stopped = 0;
+ gr_ep0out_requeue(dev);
+}
+
+/* ---------------------------------------------------------------------- */
+/* Irq handling */
+
+/*
+ * Handles interrupts from in endpoints. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static int gr_handle_in_ep(struct gr_ep *ep)
+{
+ struct gr_request *req;
+
+ req = list_first_entry(&ep->queue, struct gr_request, queue);
+ if (!req->last_desc)
+ return 0;
+
+ if (ACCESS_ONCE(req->last_desc->ctrl) & GR_DESC_IN_CTRL_EN)
+ return 0; /* Not put in hardware buffers yet */
+
+ if (gr_read32(&ep->regs->epstat) & (GR_EPSTAT_B1 | GR_EPSTAT_B0))
+ return 0; /* Not transmitted yet, still in hardware buffers */
+
+ /* Write complete */
+ gr_dma_advance(ep, 0);
+
+ return 1;
+}
+
+/*
+ * Handles interrupts from out endpoints. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held, irqs disabled and with !ep->stopped.
+ */
+static int gr_handle_out_ep(struct gr_ep *ep)
+{
+ u32 ep_dmactrl;
+ u32 ctrl;
+ u16 len;
+ struct gr_request *req;
+ struct gr_udc *dev = ep->dev;
+
+ req = list_first_entry(&ep->queue, struct gr_request, queue);
+ if (!req->curr_desc)
+ return 0;
+
+ ctrl = ACCESS_ONCE(req->curr_desc->ctrl);
+ if (ctrl & GR_DESC_OUT_CTRL_EN)
+ return 0; /* Not received yet */
+
+ /* Read complete */
+ len = ctrl & GR_DESC_OUT_CTRL_LEN_MASK;
+ req->req.actual += len;
+ if (ctrl & GR_DESC_OUT_CTRL_SE)
+ req->setup = 1;
+
+ if (len < ep->ep.maxpacket || req->req.actual == req->req.length) {
+ /* Short packet or the expected size - we are done */
+
+ if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) {
+ /*
+ * Send a status stage ZLP to ack the DATA stage in the
+ * OUT direction. This needs to be done before
+ * gr_dma_advance as that can lead to a call to
+ * ep0_setup that can change dev->ep0state.
+ */
+ gr_ep0_respond_empty(dev);
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ }
+
+ gr_dma_advance(ep, 0);
+ } else {
+ /* Not done yet. Enable the next descriptor to receive more. */
+ req->curr_desc = req->curr_desc->next_desc;
+ req->curr_desc->ctrl |= GR_DESC_OUT_CTRL_EN;
+
+ ep_dmactrl = gr_read32(&ep->regs->dmactrl);
+ gr_write32(&ep->regs->dmactrl, ep_dmactrl | GR_DMACTRL_DA);
+ }
+
+ return 1;
+}
+
+/*
+ * Handle state changes. Returns whether something was handled.
+ *
+ * Must be called with dev->lock held and irqs disabled.
+ */
+static int gr_handle_state_changes(struct gr_udc *dev)
+{
+ u32 status = gr_read32(&dev->regs->status);
+ int handled = 0;
+ int powstate = !(dev->gadget.state == USB_STATE_NOTATTACHED ||
+ dev->gadget.state == USB_STATE_ATTACHED);
+
+ /* VBUS valid detected */
+ if (!powstate && (status & GR_STATUS_VB)) {
+ dev_dbg(dev->dev, "STATUS: vbus valid detected\n");
+ gr_vbus_connected(dev, status);
+ handled = 1;
+ }
+
+ /* Disconnect */
+ if (powstate && !(status & GR_STATUS_VB)) {
+ dev_dbg(dev->dev, "STATUS: vbus invalid detected\n");
+ gr_vbus_disconnected(dev);
+ handled = 1;
+ }
+
+ /* USB reset detected */
+ if (status & GR_STATUS_UR) {
+ dev_dbg(dev->dev, "STATUS: USB reset - speed is %s\n",
+ GR_SPEED_STR(status));
+ gr_write32(&dev->regs->status, GR_STATUS_UR);
+ gr_udc_usbreset(dev, status);
+ handled = 1;
+ }
+
+ /* Speed change */
+ if (dev->gadget.speed != GR_SPEED(status)) {
+ dev_dbg(dev->dev, "STATUS: USB Speed change to %s\n",
+ GR_SPEED_STR(status));
+ dev->gadget.speed = GR_SPEED(status);
+ handled = 1;
+ }
+
+ /* Going into suspend */
+ if ((dev->ep0state != GR_EP0_SUSPEND) && !(status & GR_STATUS_SU)) {
+ dev_dbg(dev->dev, "STATUS: USB suspend\n");
+ gr_set_ep0state(dev, GR_EP0_SUSPEND);
+ dev->suspended_from = dev->gadget.state;
+ usb_gadget_set_state(&dev->gadget, USB_STATE_SUSPENDED);
+
+ if ((dev->gadget.speed != USB_SPEED_UNKNOWN) &&
+ dev->driver && dev->driver->suspend) {
+ spin_unlock(&dev->lock);
+
+ dev->driver->suspend(&dev->gadget);
+
+ spin_lock(&dev->lock);
+ }
+ handled = 1;
+ }
+
+ /* Coming out of suspend */
+ if ((dev->ep0state == GR_EP0_SUSPEND) && (status & GR_STATUS_SU)) {
+ dev_dbg(dev->dev, "STATUS: USB resume\n");
+ if (dev->suspended_from == USB_STATE_POWERED)
+ gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+ else
+ gr_set_ep0state(dev, GR_EP0_SETUP);
+ usb_gadget_set_state(&dev->gadget, dev->suspended_from);
+
+ if ((dev->gadget.speed != USB_SPEED_UNKNOWN) &&
+ dev->driver && dev->driver->resume) {
+ spin_unlock(&dev->lock);
+
+ dev->driver->resume(&dev->gadget);
+
+ spin_lock(&dev->lock);
+ }
+ handled = 1;
+ }
+
+ return handled;
+}
+
+/* Non-interrupt context irq handler */
+static irqreturn_t gr_irq_handler(int irq, void *_dev)
+{
+ struct gr_udc *dev = _dev;
+ struct gr_ep *ep;
+ int handled = 0;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (!dev->irq_enabled)
+ goto out;
+
+ /*
+ * Check IN ep interrupts. We check these before the OUT eps because
+ * some gadgets reuse the request that might already be currently
+ * outstanding and needs to be completed (mainly setup requests).
+ */
+ for (i = 0; i < dev->nepi; i++) {
+ ep = &dev->epi[i];
+ if (!ep->stopped && !ep->callback && !list_empty(&ep->queue))
+ handled = gr_handle_in_ep(ep) || handled;
+ }
+
+ /* Check OUT ep interrupts */
+ for (i = 0; i < dev->nepo; i++) {
+ ep = &dev->epo[i];
+ if (!ep->stopped && !ep->callback && !list_empty(&ep->queue))
+ handled = gr_handle_out_ep(ep) || handled;
+ }
+
+ /* Check status interrupts */
+ handled = gr_handle_state_changes(dev) || handled;
+
+ /*
+ * Check AMBA DMA errors. Only check if we didn't find anything else to
+ * handle because this shouldn't happen if we did everything right.
+ */
+ if (!handled) {
+ list_for_each_entry(ep, &dev->ep_list, ep_list) {
+ if (gr_read32(&ep->regs->dmactrl) & GR_DMACTRL_AE) {
+ dev_err(dev->dev,
+ "AMBA Error occurred for %s\n",
+ ep->ep.name);
+ handled = 1;
+ }
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* Interrupt context irq handler */
+static irqreturn_t gr_irq(int irq, void *_dev)
+{
+ struct gr_udc *dev = _dev;
+
+ if (!dev->irq_enabled)
+ return IRQ_NONE;
+
+ return IRQ_WAKE_THREAD;
+}
+
+/* ---------------------------------------------------------------------- */
+/* USB ep ops */
+
+/* Enable endpoint. Not for ep0in and ep0out that are handled separately. */
+static int gr_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct gr_udc *dev;
+ struct gr_ep *ep;
+ u8 mode;
+ u8 nt;
+ u16 max;
+ u16 buffer_size = 0;
+ u32 epctrl;
+
+ ep = container_of(_ep, struct gr_ep, ep);
+ if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+
+ dev = ep->dev;
+
+ /* 'ep0' IN and OUT are reserved */
+ if (ep == &dev->epo[0] || ep == &dev->epi[0])
+ return -EINVAL;
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* Make sure we are clear for enabling */
+ epctrl = gr_read32(&ep->regs->epctrl);
+ if (epctrl & GR_EPCTRL_EV)
+ return -EBUSY;
+
+ /* Check that directions match */
+ if (!ep->is_in != !usb_endpoint_dir_in(desc))
+ return -EINVAL;
+
+ /* Check ep num */
+ if ((!ep->is_in && ep->num >= dev->nepo) ||
+ (ep->is_in && ep->num >= dev->nepi))
+ return -EINVAL;
+
+ if (usb_endpoint_xfer_control(desc)) {
+ mode = 0;
+ } else if (usb_endpoint_xfer_isoc(desc)) {
+ mode = 1;
+ } else if (usb_endpoint_xfer_bulk(desc)) {
+ mode = 2;
+ } else if (usb_endpoint_xfer_int(desc)) {
+ mode = 3;
+ } else {
+ dev_err(dev->dev, "Unknown transfer type for %s\n",
+ ep->ep.name);
+ return -EINVAL;
+ }
+
+ /*
+ * Bits 10-0 set the max payload. 12-11 set the number of
+ * additional transactions.
+ */
+ max = 0x7ff & usb_endpoint_maxp(desc);
+ nt = 0x3 & (usb_endpoint_maxp(desc) >> 11);
+ buffer_size = GR_BUFFER_SIZE(epctrl);
+ if (nt && (mode == 0 || mode == 2)) {
+ dev_err(dev->dev,
+ "%s mode: multiple trans./microframe not valid\n",
+ (mode == 2 ? "Bulk" : "Control"));
+ return -EINVAL;
+ } else if (nt == 0x3) {
+ dev_err(dev->dev,
+ "Invalid value 0x3 for additional trans./microframe\n");
+ return -EINVAL;
+ } else if ((nt + 1) * max > buffer_size) {
+ dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n",
+ buffer_size, (nt + 1), max);
+ return -EINVAL;
+ } else if (max == 0) {
+ dev_err(dev->dev, "Max payload cannot be set to 0\n");
+ return -EINVAL;
+ } else if (max > ep->ep.maxpacket_limit) {
+ dev_err(dev->dev, "Requested max payload %d > limit %d\n",
+ max, ep->ep.maxpacket_limit);
+ return -EINVAL;
+ }
+
+ spin_lock(&ep->dev->lock);
+
+ if (!ep->stopped) {
+ spin_unlock(&ep->dev->lock);
+ return -EBUSY;
+ }
+
+ ep->stopped = 0;
+ ep->wedged = 0;
+ ep->ep.desc = desc;
+ ep->ep.maxpacket = max;
+ ep->dma_start = 0;
+
+
+ if (nt) {
+ /*
+ * Maximum possible size of all payloads in one microframe
+ * regardless of direction when using high-bandwidth mode.
+ */
+ ep->bytes_per_buffer = (nt + 1) * max;
+ } else if (ep->is_in) {
+ /*
+ * The biggest multiple of maximum packet size that fits into
+ * the buffer. The hardware will split up into many packets in
+ * the IN direction.
+ */
+ ep->bytes_per_buffer = (buffer_size / max) * max;
+ } else {
+ /*
+ * Only single packets will be placed the buffers in the OUT
+ * direction.
+ */
+ ep->bytes_per_buffer = max;
+ }
+
+ epctrl = (max << GR_EPCTRL_MAXPL_POS)
+ | (nt << GR_EPCTRL_NT_POS)
+ | (mode << GR_EPCTRL_TT_POS)
+ | GR_EPCTRL_EV;
+ if (ep->is_in)
+ epctrl |= GR_EPCTRL_PI;
+ gr_write32(&ep->regs->epctrl, epctrl);
+
+ gr_write32(&ep->regs->dmactrl, GR_DMACTRL_IE | GR_DMACTRL_AI);
+
+ spin_unlock(&ep->dev->lock);
+
+ dev_dbg(ep->dev->dev, "EP: %s enabled - %s with %d bytes/buffer\n",
+ ep->ep.name, gr_modestring[mode], ep->bytes_per_buffer);
+ return 0;
+}
+
+/* Disable endpoint. Not for ep0in and ep0out that are handled separately. */
+static int gr_ep_disable(struct usb_ep *_ep)
+{
+ struct gr_ep *ep;
+ struct gr_udc *dev;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct gr_ep, ep);
+ if (!_ep || !ep->ep.desc)
+ return -ENODEV;
+
+ dev = ep->dev;
+
+ /* 'ep0' IN and OUT are reserved */
+ if (ep == &dev->epo[0] || ep == &dev->epi[0])
+ return -EINVAL;
+
+ if (dev->ep0state == GR_EP0_SUSPEND)
+ return -EBUSY;
+
+ dev_dbg(ep->dev->dev, "EP: disable %s\n", ep->ep.name);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ gr_ep_nuke(ep);
+ gr_ep_reset(ep);
+ ep->ep.desc = NULL;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/*
+ * Frees a request, but not any DMA buffers associated with it
+ * (gr_finish_request should already have taken care of that).
+ */
+static void gr_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct gr_request *req;
+
+ if (!_ep || !_req)
+ return;
+ req = container_of(_req, struct gr_request, req);
+
+ /* Leads to memory leak */
+ WARN(!list_empty(&req->queue),
+ "request not dequeued properly before freeing\n");
+
+ kfree(req);
+}
+
+/* Queue a request from the gadget */
+static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct gr_ep *ep;
+ struct gr_request *req;
+ struct gr_udc *dev;
+ int ret;
+
+ if (unlikely(!_ep || !_req))
+ return -EINVAL;
+
+ ep = container_of(_ep, struct gr_ep, ep);
+ req = container_of(_req, struct gr_request, req);
+ dev = ep->dev;
+
+ spin_lock(&ep->dev->lock);
+
+ /*
+ * The ep0 pointer in the gadget struct is used both for ep0in and
+ * ep0out. In a data stage in the out direction ep0out needs to be used
+ * instead of the default ep0in. Completion functions might use
+ * driver_data, so that needs to be copied as well.
+ */
+ if ((ep == &dev->epi[0]) && (dev->ep0state == GR_EP0_ODATA)) {
+ ep = &dev->epo[0];
+ ep->ep.driver_data = dev->epi[0].ep.driver_data;
+ }
+
+ if (ep->is_in)
+ gr_dbgprint_request("EXTERN", ep, req);
+
+ ret = gr_queue(ep, req, GFP_ATOMIC);
+
+ spin_unlock(&ep->dev->lock);
+
+ return ret;
+}
+
+/* Dequeue JUST ONE request */
+static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct gr_request *req;
+ struct gr_ep *ep;
+ struct gr_udc *dev;
+ int ret = 0;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct gr_ep, ep);
+ if (!_ep || !_req || (!ep->ep.desc && ep->num != 0))
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver)
+ return -ESHUTDOWN;
+
+ /* We can't touch (DMA) registers when suspended */
+ if (dev->ep0state == GR_EP0_SUSPEND)
+ return -EBUSY;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* Make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (list_first_entry(&ep->queue, struct gr_request, queue) == req) {
+ /* This request is currently being processed */
+ gr_abort_dma(ep);
+ if (ep->stopped)
+ gr_finish_request(ep, req, -ECONNRESET);
+ else
+ gr_dma_advance(ep, -ECONNRESET);
+ } else if (!list_empty(&req->queue)) {
+ /* Not being processed - gr_finish_request dequeues it */
+ gr_finish_request(ep, req, -ECONNRESET);
+ } else {
+ ret = -EOPNOTSUPP;
+ }
+
+out:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+}
+
+/* Helper for gr_set_halt and gr_set_wedge */
+static int gr_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+ int ret;
+ struct gr_ep *ep;
+
+ if (!_ep)
+ return -ENODEV;
+ ep = container_of(_ep, struct gr_ep, ep);
+
+ spin_lock(&ep->dev->lock);
+
+ /* Halting an IN endpoint should fail if queue is not empty */
+ if (halt && ep->is_in && !list_empty(&ep->queue)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = gr_ep_halt_wedge(ep, halt, wedge, 0);
+
+out:
+ spin_unlock(&ep->dev->lock);
+
+ return ret;
+}
+
+/* Halt endpoint */
+static int gr_set_halt(struct usb_ep *_ep, int halt)
+{
+ return gr_set_halt_wedge(_ep, halt, 0);
+}
+
+/* Halt and wedge endpoint */
+static int gr_set_wedge(struct usb_ep *_ep)
+{
+ return gr_set_halt_wedge(_ep, 1, 1);
+}
+
+/*
+ * Return the total number of bytes currently stored in the internal buffers of
+ * the endpoint.
+ */
+static int gr_fifo_status(struct usb_ep *_ep)
+{
+ struct gr_ep *ep;
+ u32 epstat;
+ u32 bytes = 0;
+
+ if (!_ep)
+ return -ENODEV;
+ ep = container_of(_ep, struct gr_ep, ep);
+
+ epstat = gr_read32(&ep->regs->epstat);
+
+ if (epstat & GR_EPSTAT_B0)
+ bytes += (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS;
+ if (epstat & GR_EPSTAT_B1)
+ bytes += (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS;
+
+ return bytes;
+}
+
+
+/* Empty data from internal buffers of an endpoint. */
+static void gr_fifo_flush(struct usb_ep *_ep)
+{
+ struct gr_ep *ep;
+ u32 epctrl;
+
+ if (!_ep)
+ return;
+ ep = container_of(_ep, struct gr_ep, ep);
+ dev_vdbg(ep->dev->dev, "EP: flush fifo %s\n", ep->ep.name);
+
+ spin_lock(&ep->dev->lock);
+
+ epctrl = gr_read32(&ep->regs->epctrl);
+ epctrl |= GR_EPCTRL_CB;
+ gr_write32(&ep->regs->epctrl, epctrl);
+
+ spin_unlock(&ep->dev->lock);
+}
+
+static struct usb_ep_ops gr_ep_ops = {
+ .enable = gr_ep_enable,
+ .disable = gr_ep_disable,
+
+ .alloc_request = gr_alloc_request,
+ .free_request = gr_free_request,
+
+ .queue = gr_queue_ext,
+ .dequeue = gr_dequeue,
+
+ .set_halt = gr_set_halt,
+ .set_wedge = gr_set_wedge,
+ .fifo_status = gr_fifo_status,
+ .fifo_flush = gr_fifo_flush,
+};
+
+/* ---------------------------------------------------------------------- */
+/* USB Gadget ops */
+
+static int gr_get_frame(struct usb_gadget *_gadget)
+{
+ struct gr_udc *dev;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct gr_udc, gadget);
+ return gr_read32(&dev->regs->status) & GR_STATUS_FN_MASK;
+}
+
+static int gr_wakeup(struct usb_gadget *_gadget)
+{
+ struct gr_udc *dev;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct gr_udc, gadget);
+
+ /* Remote wakeup feature not enabled by host*/
+ if (!dev->remote_wakeup)
+ return -EINVAL;
+
+ spin_lock(&dev->lock);
+
+ gr_write32(&dev->regs->control,
+ gr_read32(&dev->regs->control) | GR_CONTROL_RW);
+
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int gr_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct gr_udc *dev;
+ u32 control;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct gr_udc, gadget);
+
+ spin_lock(&dev->lock);
+
+ control = gr_read32(&dev->regs->control);
+ if (is_on)
+ control |= GR_CONTROL_EP;
+ else
+ control &= ~GR_CONTROL_EP;
+ gr_write32(&dev->regs->control, control);
+
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int gr_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct gr_udc *dev = to_gr_udc(gadget);
+
+ spin_lock(&dev->lock);
+
+ /* Hook up the driver */
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+
+ /* Get ready for host detection */
+ gr_enable_vbus_detect(dev);
+
+ spin_unlock(&dev->lock);
+
+ dev_info(dev->dev, "Started with gadget driver '%s'\n",
+ driver->driver.name);
+
+ return 0;
+}
+
+static int gr_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct gr_udc *dev = to_gr_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ dev->driver = NULL;
+ gr_stop_activity(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev_info(dev->dev, "Stopped\n");
+
+ return 0;
+}
+
+static const struct usb_gadget_ops gr_ops = {
+ .get_frame = gr_get_frame,
+ .wakeup = gr_wakeup,
+ .pullup = gr_pullup,
+ .udc_start = gr_udc_start,
+ .udc_stop = gr_udc_stop,
+ /* Other operations not supported */
+};
+
+/* ---------------------------------------------------------------------- */
+/* Module probe, removal and of-matching */
+
+static const char * const onames[] = {
+ "ep0out", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out",
+ "ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out",
+ "ep12out", "ep13out", "ep14out", "ep15out"
+};
+
+static const char * const inames[] = {
+ "ep0in", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in",
+ "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in",
+ "ep12in", "ep13in", "ep14in", "ep15in"
+};
+
+/* Must be called with dev->lock held */
+static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
+{
+ struct gr_ep *ep;
+ struct gr_request *req;
+ struct usb_request *_req;
+ void *buf;
+
+ if (is_in) {
+ ep = &dev->epi[num];
+ ep->ep.name = inames[num];
+ ep->regs = &dev->regs->epi[num];
+ } else {
+ ep = &dev->epo[num];
+ ep->ep.name = onames[num];
+ ep->regs = &dev->regs->epo[num];
+ }
+
+ gr_ep_reset(ep);
+ ep->num = num;
+ ep->is_in = is_in;
+ ep->dev = dev;
+ ep->ep.ops = &gr_ep_ops;
+ INIT_LIST_HEAD(&ep->queue);
+
+ if (num == 0) {
+ _req = gr_alloc_request(&ep->ep, GFP_ATOMIC);
+ buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_ATOMIC);
+ if (!_req || !buf) {
+ /* possible _req freed by gr_probe via gr_remove */
+ return -ENOMEM;
+ }
+
+ req = container_of(_req, struct gr_request, req);
+ req->req.buf = buf;
+ req->req.length = MAX_CTRL_PL_SIZE;
+
+ if (is_in)
+ dev->ep0reqi = req; /* Complete gets set as used */
+ else
+ dev->ep0reqo = req; /* Completion treated separately */
+
+ usb_ep_set_maxpacket_limit(&ep->ep, MAX_CTRL_PL_SIZE);
+ ep->bytes_per_buffer = MAX_CTRL_PL_SIZE;
+ } else {
+ usb_ep_set_maxpacket_limit(&ep->ep, (u16)maxplimit);
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+ }
+ list_add_tail(&ep->ep_list, &dev->ep_list);
+
+ return 0;
+}
+
+/* Must be called with dev->lock held */
+static int gr_udc_init(struct gr_udc *dev)
+{
+ struct device_node *np = dev->dev->of_node;
+ u32 epctrl_val;
+ u32 dmactrl_val;
+ int i;
+ int ret = 0;
+ u32 bufsize;
+
+ gr_set_address(dev, 0);
+
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ dev->gadget.ep0 = &dev->epi[0].ep;
+
+ INIT_LIST_HEAD(&dev->ep_list);
+ gr_set_ep0state(dev, GR_EP0_DISCONNECT);
+
+ for (i = 0; i < dev->nepo; i++) {
+ if (of_property_read_u32_index(np, "epobufsizes", i, &bufsize))
+ bufsize = 1024;
+ ret = gr_ep_init(dev, i, 0, bufsize);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < dev->nepi; i++) {
+ if (of_property_read_u32_index(np, "epibufsizes", i, &bufsize))
+ bufsize = 1024;
+ ret = gr_ep_init(dev, i, 1, bufsize);
+ if (ret)
+ return ret;
+ }
+
+ /* Must be disabled by default */
+ dev->remote_wakeup = 0;
+
+ /* Enable ep0out and ep0in */
+ epctrl_val = (MAX_CTRL_PL_SIZE << GR_EPCTRL_MAXPL_POS) | GR_EPCTRL_EV;
+ dmactrl_val = GR_DMACTRL_IE | GR_DMACTRL_AI;
+ gr_write32(&dev->epo[0].regs->epctrl, epctrl_val);
+ gr_write32(&dev->epi[0].regs->epctrl, epctrl_val | GR_EPCTRL_PI);
+ gr_write32(&dev->epo[0].regs->dmactrl, dmactrl_val);
+ gr_write32(&dev->epi[0].regs->dmactrl, dmactrl_val);
+
+ return 0;
+}
+
+static int gr_remove(struct platform_device *pdev)
+{
+ struct gr_udc *dev = platform_get_drvdata(pdev);
+
+ if (dev->added)
+ usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
+ if (dev->driver)
+ return -EBUSY;
+
+ gr_dfs_delete(dev);
+ if (dev->desc_pool)
+ dma_pool_destroy(dev->desc_pool);
+ platform_set_drvdata(pdev, NULL);
+
+ gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
+ gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
+
+ return 0;
+}
+static int gr_request_irq(struct gr_udc *dev, int irq)
+{
+ return devm_request_threaded_irq(dev->dev, irq, gr_irq, gr_irq_handler,
+ IRQF_SHARED, driver_name, dev);
+}
+
+static int gr_probe(struct platform_device *pdev)
+{
+ struct gr_udc *dev;
+ struct resource *res;
+ struct gr_regs __iomem *regs;
+ int retval;
+ u32 status;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq <= 0) {
+ dev_err(dev->dev, "No irq found\n");
+ return -ENODEV;
+ }
+
+ /* Some core configurations has separate irqs for IN and OUT events */
+ dev->irqi = platform_get_irq(pdev, 1);
+ if (dev->irqi > 0) {
+ dev->irqo = platform_get_irq(pdev, 2);
+ if (dev->irqo <= 0) {
+ dev_err(dev->dev, "Found irqi but not irqo\n");
+ return -ENODEV;
+ }
+ } else {
+ dev->irqi = 0;
+ }
+
+ dev->gadget.name = driver_name;
+ dev->gadget.max_speed = USB_SPEED_HIGH;
+ dev->gadget.ops = &gr_ops;
+ dev->gadget.quirk_ep_out_aligned_size = true;
+
+ spin_lock_init(&dev->lock);
+ dev->regs = regs;
+
+ platform_set_drvdata(pdev, dev);
+
+ /* Determine number of endpoints and data interface mode */
+ status = gr_read32(&dev->regs->status);
+ dev->nepi = ((status & GR_STATUS_NEPI_MASK) >> GR_STATUS_NEPI_POS) + 1;
+ dev->nepo = ((status & GR_STATUS_NEPO_MASK) >> GR_STATUS_NEPO_POS) + 1;
+
+ if (!(status & GR_STATUS_DM)) {
+ dev_err(dev->dev, "Slave mode cores are not supported\n");
+ return -ENODEV;
+ }
+
+ /* --- Effects of the following calls might need explicit cleanup --- */
+
+ /* Create DMA pool for descriptors */
+ dev->desc_pool = dma_pool_create("desc_pool", dev->dev,
+ sizeof(struct gr_dma_desc), 4, 0);
+ if (!dev->desc_pool) {
+ dev_err(dev->dev, "Could not allocate DMA pool");
+ return -ENOMEM;
+ }
+
+ spin_lock(&dev->lock);
+
+ /* Inside lock so that no gadget can use this udc until probe is done */
+ retval = usb_add_gadget_udc(dev->dev, &dev->gadget);
+ if (retval) {
+ dev_err(dev->dev, "Could not add gadget udc");
+ goto out;
+ }
+ dev->added = 1;
+
+ retval = gr_udc_init(dev);
+ if (retval)
+ goto out;
+
+ gr_dfs_create(dev);
+
+ /* Clear all interrupt enables that might be left on since last boot */
+ gr_disable_interrupts_and_pullup(dev);
+
+ retval = gr_request_irq(dev, dev->irq);
+ if (retval) {
+ dev_err(dev->dev, "Failed to request irq %d\n", dev->irq);
+ goto out;
+ }
+
+ if (dev->irqi) {
+ retval = gr_request_irq(dev, dev->irqi);
+ if (retval) {
+ dev_err(dev->dev, "Failed to request irqi %d\n",
+ dev->irqi);
+ goto out;
+ }
+ retval = gr_request_irq(dev, dev->irqo);
+ if (retval) {
+ dev_err(dev->dev, "Failed to request irqo %d\n",
+ dev->irqo);
+ goto out;
+ }
+ }
+
+ if (dev->irqi)
+ dev_info(dev->dev, "regs: %p, irqs %d, %d, %d\n", dev->regs,
+ dev->irq, dev->irqi, dev->irqo);
+ else
+ dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq);
+
+out:
+ spin_unlock(&dev->lock);
+
+ if (retval)
+ gr_remove(pdev);
+
+ return retval;
+}
+
+static struct of_device_id gr_match[] = {
+ {.name = "GAISLER_USBDC"},
+ {.name = "01_021"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, gr_match);
+
+static struct platform_driver gr_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = gr_match,
+ },
+ .probe = gr_probe,
+ .remove = gr_remove,
+};
+module_platform_driver(gr_driver);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB.");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/gr_udc.h
new file mode 100644
index 00000000000..8388897d9ec
--- /dev/null
+++ b/drivers/usb/gadget/gr_udc.h
@@ -0,0 +1,220 @@
+/*
+ * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC.
+ *
+ * 2013 (c) Aeroflex Gaisler AB
+ *
+ * This driver supports GRUSBDC USB Device Controller cores available in the
+ * GRLIB VHDL IP core library.
+ *
+ * Full documentation of the GRUSBDC core can be found here:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Contributors:
+ * - Andreas Larsson <andreas@gaisler.com>
+ * - Marko Isomaki
+ */
+
+/* Control registers on the AMBA bus */
+
+#define GR_MAXEP 16 /* Max # endpoints for *each* direction */
+
+struct gr_epregs {
+ u32 epctrl;
+ union {
+ struct { /* Slave mode*/
+ u32 slvctrl;
+ u32 slvdata;
+ };
+ struct { /* DMA mode*/
+ u32 dmactrl;
+ u32 dmaaddr;
+ };
+ };
+ u32 epstat;
+};
+
+struct gr_regs {
+ struct gr_epregs epo[GR_MAXEP]; /* 0x000 - 0x0fc */
+ struct gr_epregs epi[GR_MAXEP]; /* 0x100 - 0x1fc */
+ u32 control; /* 0x200 */
+ u32 status; /* 0x204 */
+};
+
+#define GR_EPCTRL_BUFSZ_SCALER 8
+#define GR_EPCTRL_BUFSZ_MASK 0xffe00000
+#define GR_EPCTRL_BUFSZ_POS 21
+#define GR_EPCTRL_PI BIT(20)
+#define GR_EPCTRL_CB BIT(19)
+#define GR_EPCTRL_CS BIT(18)
+#define GR_EPCTRL_MAXPL_MASK 0x0003ff80
+#define GR_EPCTRL_MAXPL_POS 7
+#define GR_EPCTRL_NT_MASK 0x00000060
+#define GR_EPCTRL_NT_POS 5
+#define GR_EPCTRL_TT_MASK 0x00000018
+#define GR_EPCTRL_TT_POS 3
+#define GR_EPCTRL_EH BIT(2)
+#define GR_EPCTRL_ED BIT(1)
+#define GR_EPCTRL_EV BIT(0)
+
+#define GR_DMACTRL_AE BIT(10)
+#define GR_DMACTRL_AD BIT(3)
+#define GR_DMACTRL_AI BIT(2)
+#define GR_DMACTRL_IE BIT(1)
+#define GR_DMACTRL_DA BIT(0)
+
+#define GR_EPSTAT_PT BIT(29)
+#define GR_EPSTAT_PR BIT(29)
+#define GR_EPSTAT_B1CNT_MASK 0x1fff0000
+#define GR_EPSTAT_B1CNT_POS 16
+#define GR_EPSTAT_B0CNT_MASK 0x0000fff8
+#define GR_EPSTAT_B0CNT_POS 3
+#define GR_EPSTAT_B1 BIT(2)
+#define GR_EPSTAT_B0 BIT(1)
+#define GR_EPSTAT_BS BIT(0)
+
+#define GR_CONTROL_SI BIT(31)
+#define GR_CONTROL_UI BIT(30)
+#define GR_CONTROL_VI BIT(29)
+#define GR_CONTROL_SP BIT(28)
+#define GR_CONTROL_FI BIT(27)
+#define GR_CONTROL_EP BIT(14)
+#define GR_CONTROL_DH BIT(13)
+#define GR_CONTROL_RW BIT(12)
+#define GR_CONTROL_TS_MASK 0x00000e00
+#define GR_CONTROL_TS_POS 9
+#define GR_CONTROL_TM BIT(8)
+#define GR_CONTROL_UA_MASK 0x000000fe
+#define GR_CONTROL_UA_POS 1
+#define GR_CONTROL_SU BIT(0)
+
+#define GR_STATUS_NEPI_MASK 0xf0000000
+#define GR_STATUS_NEPI_POS 28
+#define GR_STATUS_NEPO_MASK 0x0f000000
+#define GR_STATUS_NEPO_POS 24
+#define GR_STATUS_DM BIT(23)
+#define GR_STATUS_SU BIT(17)
+#define GR_STATUS_UR BIT(16)
+#define GR_STATUS_VB BIT(15)
+#define GR_STATUS_SP BIT(14)
+#define GR_STATUS_AF_MASK 0x00003800
+#define GR_STATUS_AF_POS 11
+#define GR_STATUS_FN_MASK 0x000007ff
+#define GR_STATUS_FN_POS 0
+
+
+#define MAX_CTRL_PL_SIZE 64 /* As per USB standard for full and high speed */
+
+/*-------------------------------------------------------------------------*/
+
+/* Driver data structures and utilities */
+
+struct gr_dma_desc {
+ u32 ctrl;
+ u32 data;
+ u32 next;
+
+ /* These must be last because hw uses the previous three */
+ u32 paddr;
+ struct gr_dma_desc *next_desc;
+};
+
+#define GR_DESC_OUT_CTRL_SE BIT(17)
+#define GR_DESC_OUT_CTRL_IE BIT(15)
+#define GR_DESC_OUT_CTRL_NX BIT(14)
+#define GR_DESC_OUT_CTRL_EN BIT(13)
+#define GR_DESC_OUT_CTRL_LEN_MASK 0x00001fff
+
+#define GR_DESC_IN_CTRL_MO BIT(18)
+#define GR_DESC_IN_CTRL_PI BIT(17)
+#define GR_DESC_IN_CTRL_ML BIT(16)
+#define GR_DESC_IN_CTRL_IE BIT(15)
+#define GR_DESC_IN_CTRL_NX BIT(14)
+#define GR_DESC_IN_CTRL_EN BIT(13)
+#define GR_DESC_IN_CTRL_LEN_MASK 0x00001fff
+
+#define GR_DESC_DMAADDR_MASK 0xfffffffc
+
+struct gr_ep {
+ struct usb_ep ep;
+ struct gr_udc *dev;
+ u16 bytes_per_buffer;
+ unsigned int dma_start;
+ struct gr_epregs __iomem *regs;
+
+ unsigned num:8;
+ unsigned is_in:1;
+ unsigned stopped:1;
+ unsigned wedged:1;
+ unsigned callback:1;
+
+ /* analogous to a host-side qh */
+ struct list_head queue;
+
+ struct list_head ep_list;
+};
+
+struct gr_request {
+ struct usb_request req;
+ struct list_head queue;
+
+ /* Chain of dma descriptors */
+ struct gr_dma_desc *first_desc; /* First in the chain */
+ struct gr_dma_desc *curr_desc; /* Current descriptor */
+ struct gr_dma_desc *last_desc; /* Last in the chain */
+
+ u8 setup; /* Setup packet */
+};
+
+enum gr_ep0state {
+ GR_EP0_DISCONNECT = 0, /* No host */
+ GR_EP0_SETUP, /* Between STATUS ack and SETUP report */
+ GR_EP0_IDATA, /* IN data stage */
+ GR_EP0_ODATA, /* OUT data stage */
+ GR_EP0_ISTATUS, /* Status stage after IN data stage */
+ GR_EP0_OSTATUS, /* Status stage after OUT data stage */
+ GR_EP0_STALL, /* Data or status stages */
+ GR_EP0_SUSPEND, /* USB suspend */
+};
+
+struct gr_udc {
+ struct usb_gadget gadget;
+ struct gr_ep epi[GR_MAXEP];
+ struct gr_ep epo[GR_MAXEP];
+ struct usb_gadget_driver *driver;
+ struct dma_pool *desc_pool;
+ struct device *dev;
+
+ enum gr_ep0state ep0state;
+ struct gr_request *ep0reqo;
+ struct gr_request *ep0reqi;
+
+ struct gr_regs __iomem *regs;
+ int irq;
+ int irqi;
+ int irqo;
+
+ unsigned added:1;
+ unsigned irq_enabled:1;
+ unsigned remote_wakeup:1;
+
+ u8 test_mode;
+
+ enum usb_device_state suspended_from;
+
+ unsigned int nepi;
+ unsigned int nepo;
+
+ struct list_head ep_list;
+
+ spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */
+
+ struct dentry *dfs_root;
+ struct dentry *dfs_state;
+};
+
+#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget))
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index f888c3ede86..778613eb37a 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -15,7 +15,10 @@
#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"
@@ -33,12 +36,6 @@
* 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_hid.c"
@@ -50,6 +47,7 @@ struct hidg_func_node {
static LIST_HEAD(hidg_func_list);
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -60,9 +58,9 @@ static struct usb_device_descriptor device_desc = {
/* .bDeviceClass = USB_CLASS_COMM, */
/* .bDeviceSubClass = 0, */
/* .bDeviceProtocol = 0, */
- .bDeviceClass = 0xEF,
- .bDeviceSubClass = 2,
- .bDeviceProtocol = 1,
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
@@ -92,15 +90,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 */
};
@@ -150,7 +143,7 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
- int status, gcnum, funcs = 0;
+ int status, funcs = 0;
list_for_each(tmp, &hidg_func_list)
funcs++;
@@ -163,38 +156,22 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
if (status < 0)
return status;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- 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)
return status;
- 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, &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;
@@ -208,7 +185,7 @@ static int __exit hid_unbind(struct usb_composite_dev *cdev)
static int __init hidg_plat_driver_probe(struct platform_device *pdev)
{
- struct hidg_func_descriptor *func = pdev->dev.platform_data;
+ struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev);
struct hidg_func_node *entry;
if (!func) {
@@ -226,7 +203,7 @@ static int __init hidg_plat_driver_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit hidg_plat_driver_remove(struct platform_device *pdev)
+static int hidg_plat_driver_remove(struct platform_device *pdev)
{
struct hidg_func_node *e, *n;
@@ -242,16 +219,17 @@ static int __devexit hidg_plat_driver_remove(struct platform_device *pdev)
/****************************** Some noise ******************************/
-static struct usb_composite_driver hidg_driver = {
+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 = __devexit_p(hidg_plat_driver_remove),
+ .remove = hidg_plat_driver_remove,
.driver = {
.owner = THIS_MODULE,
.name = "hidg",
@@ -272,7 +250,7 @@ static int __init hidg_init(void)
if (status < 0)
return status;
- status = usb_composite_probe(&hidg_driver, hid_bind);
+ status = usb_composite_probe(&hidg_driver);
if (status < 0)
platform_driver_unregister(&hidg_plat_driver);
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
deleted file mode 100644
index 8d1c75abd73..00000000000
--- a/drivers/usb/gadget/imx_udc.c
+++ /dev/null
@@ -1,1595 +0,0 @@
-/*
- * driver/usb/gadget/imx_udc.c
- *
- * Copyright (C) 2005 Mike Lee <eemike@gmail.com>
- * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/slab.h>
-#include <linux/prefetch.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-#include <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;
-
- if (unlikely(__raw_readl(imx_ep->imx_usb->base +
- USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) {
- D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n",
- __func__, imx_ep->ep.name);
- return -1;
- }
-
- buf = req->req.buf + req->req.actual;
- prefetch(buf);
-
- length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize);
-
- if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) {
- D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n",
- __func__, imx_ep->ep.name);
- return -1;
- }
-
- req->req.actual += length;
- count = length;
-
- if (!count && req->req.zero) { /* zlp */
- temp = __raw_readl(imx_ep->imx_usb->base
- + USB_EP_STAT(EP_NO(imx_ep)));
- __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base
- + USB_EP_STAT(EP_NO(imx_ep)));
- D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__);
- return 0;
- }
-
- while (count--) {
- if (count == 0) { /* last byte */
- temp = __raw_readl(imx_ep->imx_usb->base
- + USB_EP_FCTRL(EP_NO(imx_ep)));
- __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base
- + USB_EP_FCTRL(EP_NO(imx_ep)));
- }
- __raw_writeb(*buf++,
- imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep)));
- }
-
- return length;
-}
-
-static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req)
-{
- int bytes = 0,
- count,
- completed = 0;
-
- while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)))
- & FSTAT_FR) {
- count = read_packet(imx_ep, req);
- bytes += count;
-
- completed = (count != imx_ep->fifosize);
- if (completed || req->req.actual == req->req.length) {
- completed = 1;
- break;
- }
- }
-
- if (completed || !req->req.length) {
- done(imx_ep, req, 0);
- D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n",
- __func__, imx_ep->ep.name, req,
- completed ? "completed" : "not completed");
- if (!EP_NO(imx_ep))
- ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE);
- }
-
- D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes);
-
- return completed;
-}
-
-static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req)
-{
- int bytes = 0,
- count,
- completed = 0;
-
- while (!completed) {
- count = write_packet(imx_ep, req);
- if (count < 0)
- break; /* busy */
- bytes += count;
-
- /* last packet "must be" short (or a zlp) */
- completed = (count != imx_ep->fifosize);
-
- if (unlikely(completed)) {
- done(imx_ep, req, 0);
- D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n",
- __func__, imx_ep->ep.name, req,
- completed ? "completed" : "not completed");
- if (!EP_NO(imx_ep))
- ep0_chg_stat(__func__,
- imx_ep->imx_usb, EP0_IDLE);
- }
- }
-
- D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes);
-
- return completed;
-}
-
-/*******************************************************************************
- * Endpoint handlers
- *******************************************************************************
- */
-static int handle_ep(struct imx_ep_struct *imx_ep)
-{
- struct imx_request *req;
- int completed = 0;
-
- do {
- if (!list_empty(&imx_ep->queue))
- req = list_entry(imx_ep->queue.next,
- struct imx_request, queue);
- else {
- D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n",
- __func__, imx_ep->ep.name);
- return 0;
- }
-
- if (EP_DIR(imx_ep)) /* to host */
- completed = write_fifo(imx_ep, req);
- else /* to device */
- completed = read_fifo(imx_ep, req);
-
- dump_ep_stat(__func__, imx_ep);
-
- } while (completed);
-
- return 0;
-}
-
-static int handle_ep0(struct imx_ep_struct *imx_ep)
-{
- struct imx_request *req = NULL;
- int ret = 0;
-
- if (!list_empty(&imx_ep->queue)) {
- req = list_entry(imx_ep->queue.next, struct imx_request, queue);
-
- switch (imx_ep->imx_usb->ep0state) {
-
- case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */
- write_fifo(imx_ep, req);
- break;
- case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */
- read_fifo(imx_ep, req);
- break;
- default:
- D_EP0(imx_ep->imx_usb->dev,
- "<%s> ep0 i/o, odd state %d\n",
- __func__, imx_ep->imx_usb->ep0state);
- ep_del_request(imx_ep, req);
- ret = -EL2HLT;
- break;
- }
- }
-
- else
- D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n",
- __func__, imx_ep->ep.name);
-
- return ret;
-}
-
-static void handle_ep0_devreq(struct imx_udc_struct *imx_usb)
-{
- struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0];
- union {
- struct usb_ctrlrequest r;
- u8 raw[8];
- u32 word[2];
- } u;
- int temp, i;
-
- nuke(imx_ep, -EPROTO);
-
- /* read SETUP packet */
- for (i = 0; i < 2; i++) {
- if (imx_ep_empty(imx_ep)) {
- D_ERR(imx_usb->dev,
- "<%s> no setup packet received\n", __func__);
- goto stall;
- }
- u.word[i] = __raw_readl(imx_usb->base
- + USB_EP_FDAT(EP_NO(imx_ep)));
- }
-
- temp = imx_ep_empty(imx_ep);
- while (!imx_ep_empty(imx_ep)) {
- i = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep)));
- D_ERR(imx_usb->dev,
- "<%s> wrong to have extra bytes for setup : 0x%08x\n",
- __func__, i);
- }
- if (!temp)
- goto stall;
-
- le16_to_cpus(&u.r.wValue);
- le16_to_cpus(&u.r.wIndex);
- le16_to_cpus(&u.r.wLength);
-
- D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n",
- __func__, u.r.bRequestType, u.r.bRequest,
- u.r.wValue, u.r.wIndex, u.r.wLength);
-
- if (imx_usb->set_config) {
- /* NACK the host by using CMDOVER */
- temp = __raw_readl(imx_usb->base + USB_CTRL);
- __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL);
-
- D_ERR(imx_usb->dev,
- "<%s> set config req is pending, NACK the host\n",
- __func__);
- return;
- }
-
- if (u.r.bRequestType & USB_DIR_IN)
- ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE);
- else
- ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE);
-
- i = imx_usb->driver->setup(&imx_usb->gadget, &u.r);
- if (i < 0) {
- D_ERR(imx_usb->dev, "<%s> device setup error %d\n",
- __func__, i);
- goto stall;
- }
-
- return;
-stall:
- D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__);
- imx_ep_stall(imx_ep);
- ep0_chg_stat(__func__, imx_usb, EP0_STALL);
- return;
-}
-
-/*******************************************************************************
- * USB gadget callback functions
- *******************************************************************************
- */
-
-static int imx_ep_enable(struct usb_ep *usb_ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct imx_ep_struct *imx_ep = container_of(usb_ep,
- struct imx_ep_struct, ep);
- struct imx_udc_struct *imx_usb = imx_ep->imx_usb;
- unsigned long flags;
-
- if (!usb_ep
- || !desc
- || !EP_NO(imx_ep)
- || desc->bDescriptorType != USB_DT_ENDPOINT
- || imx_ep->bEndpointAddress != desc->bEndpointAddress) {
- D_ERR(imx_usb->dev,
- "<%s> bad ep or descriptor\n", __func__);
- return -EINVAL;
- }
-
- if (imx_ep->bmAttributes != desc->bmAttributes) {
- D_ERR(imx_usb->dev,
- "<%s> %s type mismatch\n", __func__, usb_ep->name);
- return -EINVAL;
- }
-
- if (imx_ep->fifosize < usb_endpoint_maxp(desc)) {
- D_ERR(imx_usb->dev,
- "<%s> bad %s maxpacket\n", __func__, usb_ep->name);
- return -ERANGE;
- }
-
- if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) {
- D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__);
- return -ESHUTDOWN;
- }
-
- local_irq_save(flags);
-
- imx_ep->stopped = 0;
- imx_flush(imx_ep);
- imx_ep_irq_enable(imx_ep);
-
- local_irq_restore(flags);
-
- D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name);
- return 0;
-}
-
-static int imx_ep_disable(struct usb_ep *usb_ep)
-{
- struct imx_ep_struct *imx_ep = container_of(usb_ep,
- struct imx_ep_struct, ep);
- unsigned long flags;
-
- if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) {
- D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n",
- __func__, usb_ep ? imx_ep->ep.name : NULL);
- return -EINVAL;
- }
-
- local_irq_save(flags);
-
- imx_ep->stopped = 1;
- nuke(imx_ep, -ESHUTDOWN);
- imx_flush(imx_ep);
- imx_ep_irq_disable(imx_ep);
-
- local_irq_restore(flags);
-
- D_EPX(imx_ep->imx_usb->dev,
- "<%s> DISABLED %s\n", __func__, usb_ep->name);
- return 0;
-}
-
-static struct usb_request *imx_ep_alloc_request
- (struct usb_ep *usb_ep, gfp_t gfp_flags)
-{
- struct imx_request *req;
-
- if (!usb_ep)
- return NULL;
-
- req = kzalloc(sizeof *req, gfp_flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD(&req->queue);
- req->in_use = 0;
-
- return &req->req;
-}
-
-static void imx_ep_free_request
- (struct usb_ep *usb_ep, struct usb_request *usb_req)
-{
- struct imx_request *req;
-
- req = container_of(usb_req, struct imx_request, req);
- WARN_ON(!list_empty(&req->queue));
- kfree(req);
-}
-
-static int imx_ep_queue
- (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags)
-{
- struct imx_ep_struct *imx_ep;
- struct imx_udc_struct *imx_usb;
- struct imx_request *req;
- unsigned long flags;
- int ret = 0;
-
- imx_ep = container_of(usb_ep, struct imx_ep_struct, ep);
- imx_usb = imx_ep->imx_usb;
- req = container_of(usb_req, struct imx_request, req);
-
- /*
- Special care on IMX udc.
- Ignore enqueue when after set configuration from the
- host. This assume all gadget drivers reply set
- configuration with the next ep0 req enqueue.
- */
- if (imx_usb->set_config && !EP_NO(imx_ep)) {
- imx_usb->set_config = 0;
- D_ERR(imx_usb->dev,
- "<%s> gadget reply set config\n", __func__);
- return 0;
- }
-
- if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) {
- D_ERR(imx_usb->dev, "<%s> bad params\n", __func__);
- return -EINVAL;
- }
-
- if (unlikely(!usb_ep || !imx_ep)) {
- D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__);
- return -EINVAL;
- }
-
- if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) {
- D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__);
- return -ESHUTDOWN;
- }
-
- /* Debug */
- D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n",
- __func__, EP_NO(imx_ep),
- ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state
- == EP0_IN_DATA_PHASE)
- || (EP_NO(imx_ep) && EP_DIR(imx_ep)))
- ? "IN" : "OUT", usb_req->length);
- dump_req(__func__, imx_ep, usb_req);
-
- if (imx_ep->stopped) {
- usb_req->status = -ESHUTDOWN;
- return -ESHUTDOWN;
- }
-
- if (req->in_use) {
- D_ERR(imx_usb->dev,
- "<%s> refusing to queue req %p (already queued)\n",
- __func__, req);
- return 0;
- }
-
- local_irq_save(flags);
-
- usb_req->status = -EINPROGRESS;
- usb_req->actual = 0;
-
- ep_add_request(imx_ep, req);
-
- if (!EP_NO(imx_ep))
- ret = handle_ep0(imx_ep);
- else
- ret = handle_ep(imx_ep);
-
- local_irq_restore(flags);
- return ret;
-}
-
-static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
-{
-
- struct imx_ep_struct *imx_ep = container_of
- (usb_ep, struct imx_ep_struct, ep);
- struct imx_request *req;
- unsigned long flags;
-
- if (unlikely(!usb_ep || !EP_NO(imx_ep))) {
- D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
- return -EINVAL;
- }
-
- local_irq_save(flags);
-
- /* make sure it's actually queued on this endpoint */
- list_for_each_entry(req, &imx_ep->queue, queue) {
- if (&req->req == usb_req)
- break;
- }
- if (&req->req != usb_req) {
- local_irq_restore(flags);
- return -EINVAL;
- }
-
- done(imx_ep, req, -ECONNRESET);
-
- local_irq_restore(flags);
- return 0;
-}
-
-static int imx_ep_set_halt(struct usb_ep *usb_ep, int value)
-{
- struct imx_ep_struct *imx_ep = container_of
- (usb_ep, struct imx_ep_struct, ep);
- unsigned long flags;
-
- if (unlikely(!usb_ep || !EP_NO(imx_ep))) {
- D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
- return -EINVAL;
- }
-
- local_irq_save(flags);
-
- if ((imx_ep->bEndpointAddress & USB_DIR_IN)
- && !list_empty(&imx_ep->queue)) {
- local_irq_restore(flags);
- return -EAGAIN;
- }
-
- imx_ep_stall(imx_ep);
-
- local_irq_restore(flags);
-
- D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name);
- return 0;
-}
-
-static int imx_ep_fifo_status(struct usb_ep *usb_ep)
-{
- struct imx_ep_struct *imx_ep = container_of
- (usb_ep, struct imx_ep_struct, ep);
-
- if (!usb_ep) {
- D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
- return -ENODEV;
- }
-
- if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN)
- return 0;
- else
- return imx_fifo_bcount(imx_ep);
-}
-
-static void imx_ep_fifo_flush(struct usb_ep *usb_ep)
-{
- struct imx_ep_struct *imx_ep = container_of
- (usb_ep, struct imx_ep_struct, ep);
- unsigned long flags;
-
- local_irq_save(flags);
-
- if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) {
- D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__);
- local_irq_restore(flags);
- return;
- }
-
- /* toggle and halt bits stay unchanged */
- imx_flush(imx_ep);
-
- local_irq_restore(flags);
-}
-
-static struct usb_ep_ops imx_ep_ops = {
- .enable = imx_ep_enable,
- .disable = imx_ep_disable,
-
- .alloc_request = imx_ep_alloc_request,
- .free_request = imx_ep_free_request,
-
- .queue = imx_ep_queue,
- .dequeue = imx_ep_dequeue,
-
- .set_halt = imx_ep_set_halt,
- .fifo_status = imx_ep_fifo_status,
- .fifo_flush = imx_ep_fifo_flush,
-};
-
-/*******************************************************************************
- * USB endpoint control functions
- *******************************************************************************
- */
-
-void ep0_chg_stat(const char *label,
- struct imx_udc_struct *imx_usb, enum ep0_state stat)
-{
- D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n",
- label, state_name[imx_usb->ep0state], state_name[stat]);
-
- if (imx_usb->ep0state == stat)
- return;
-
- imx_usb->ep0state = stat;
-}
-
-static void usb_init_data(struct imx_udc_struct *imx_usb)
-{
- struct imx_ep_struct *imx_ep;
- u8 i;
-
- /* device/ep0 records init */
- INIT_LIST_HEAD(&imx_usb->gadget.ep_list);
- INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list);
- ep0_chg_stat(__func__, imx_usb, EP0_IDLE);
-
- /* basic endpoint records init */
- for (i = 0; i < IMX_USB_NB_EP; i++) {
- imx_ep = &imx_usb->imx_ep[i];
-
- if (i) {
- list_add_tail(&imx_ep->ep.ep_list,
- &imx_usb->gadget.ep_list);
- imx_ep->stopped = 1;
- } else
- imx_ep->stopped = 0;
-
- INIT_LIST_HEAD(&imx_ep->queue);
- }
-}
-
-static void udc_stop_activity(struct imx_udc_struct *imx_usb,
- struct usb_gadget_driver *driver)
-{
- struct imx_ep_struct *imx_ep;
- int i;
-
- if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
-
- /* prevent new request submissions, kill any outstanding requests */
- for (i = 1; i < IMX_USB_NB_EP; i++) {
- imx_ep = &imx_usb->imx_ep[i];
- imx_flush(imx_ep);
- imx_ep->stopped = 1;
- imx_ep_irq_disable(imx_ep);
- nuke(imx_ep, -ESHUTDOWN);
- }
-
- imx_usb->cfg = 0;
- imx_usb->intf = 0;
- imx_usb->alt = 0;
-
- if (driver)
- driver->disconnect(&imx_usb->gadget);
-}
-
-/*******************************************************************************
- * Interrupt handlers
- *******************************************************************************
- */
-
-/*
- * Called when timer expires.
- * Timer is started when CFG_CHG is received.
- */
-static void handle_config(unsigned long data)
-{
- struct imx_udc_struct *imx_usb = (void *)data;
- struct usb_ctrlrequest u;
- int temp, cfg, intf, alt;
-
- local_irq_disable();
-
- temp = __raw_readl(imx_usb->base + USB_STAT);
- cfg = (temp & STAT_CFG) >> 5;
- intf = (temp & STAT_INTF) >> 3;
- alt = temp & STAT_ALTSET;
-
- D_REQ(imx_usb->dev,
- "<%s> orig config C=%d, I=%d, A=%d / "
- "req config C=%d, I=%d, A=%d\n",
- __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt,
- cfg, intf, alt);
-
- if (cfg == 1 || cfg == 2) {
-
- if (imx_usb->cfg != cfg) {
- u.bRequest = USB_REQ_SET_CONFIGURATION;
- u.bRequestType = USB_DIR_OUT |
- USB_TYPE_STANDARD |
- USB_RECIP_DEVICE;
- u.wValue = cfg;
- u.wIndex = 0;
- u.wLength = 0;
- imx_usb->cfg = cfg;
- imx_usb->driver->setup(&imx_usb->gadget, &u);
-
- }
- if (imx_usb->intf != intf || imx_usb->alt != alt) {
- u.bRequest = USB_REQ_SET_INTERFACE;
- u.bRequestType = USB_DIR_OUT |
- USB_TYPE_STANDARD |
- USB_RECIP_INTERFACE;
- u.wValue = alt;
- u.wIndex = intf;
- u.wLength = 0;
- imx_usb->intf = intf;
- imx_usb->alt = alt;
- imx_usb->driver->setup(&imx_usb->gadget, &u);
- }
- }
-
- imx_usb->set_config = 0;
-
- local_irq_enable();
-}
-
-static irqreturn_t imx_udc_irq(int irq, void *dev)
-{
- struct imx_udc_struct *imx_usb = dev;
- int intr = __raw_readl(imx_usb->base + USB_INTR);
- int temp;
-
- if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START
- | INTR_RESET_STOP | INTR_CFG_CHG)) {
- dump_intr(__func__, intr, imx_usb->dev);
- dump_usb_stat(__func__, imx_usb);
- }
-
- if (!imx_usb->driver)
- goto end_irq;
-
- if (intr & INTR_SOF) {
- /* Copy from Freescale BSP.
- We must enable SOF intr and set CMDOVER.
- Datasheet don't specifiy this action, but it
- is done in Freescale BSP, so just copy it.
- */
- if (imx_usb->ep0state == EP0_IDLE) {
- temp = __raw_readl(imx_usb->base + USB_CTRL);
- __raw_writel(temp | CTRL_CMDOVER,
- imx_usb->base + USB_CTRL);
- }
- }
-
- if (intr & INTR_CFG_CHG) {
- /* A workaround of serious IMX UDC bug.
- Handling of CFG_CHG should be delayed for some time, because
- IMX does not NACK the host when CFG_CHG interrupt is pending.
- There is no time to handle current CFG_CHG
- if next CFG_CHG or SETUP packed is send immediately.
- We have to clear CFG_CHG, start the timer and
- NACK the host by setting CTRL_CMDOVER
- if it sends any SETUP packet.
- When timer expires, handler is called to handle configuration
- changes. While CFG_CHG is not handled (set_config=1),
- we must NACK the host to every SETUP packed.
- This delay prevents from going out of sync with host.
- */
- __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR);
- imx_usb->set_config = 1;
- mod_timer(&imx_usb->timer, jiffies + 5);
- goto end_irq;
- }
-
- if (intr & INTR_WAKEUP) {
- if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN
- && imx_usb->driver && imx_usb->driver->resume)
- imx_usb->driver->resume(&imx_usb->gadget);
- imx_usb->set_config = 0;
- del_timer(&imx_usb->timer);
- imx_usb->gadget.speed = USB_SPEED_FULL;
- }
-
- if (intr & INTR_SUSPEND) {
- if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN
- && imx_usb->driver && imx_usb->driver->suspend)
- imx_usb->driver->suspend(&imx_usb->gadget);
- imx_usb->set_config = 0;
- del_timer(&imx_usb->timer);
- imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
- }
-
- if (intr & INTR_RESET_START) {
- __raw_writel(intr, imx_usb->base + USB_INTR);
- udc_stop_activity(imx_usb, imx_usb->driver);
- imx_usb->set_config = 0;
- del_timer(&imx_usb->timer);
- imx_usb->gadget.speed = USB_SPEED_UNKNOWN;
- }
-
- if (intr & INTR_RESET_STOP)
- imx_usb->gadget.speed = USB_SPEED_FULL;
-
-end_irq:
- __raw_writel(intr, imx_usb->base + USB_INTR);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
-{
- struct imx_udc_struct *imx_usb = dev;
- struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0];
- int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0));
-
- dump_ep_intr(__func__, 0, intr, imx_usb->dev);
-
- if (!imx_usb->driver) {
- __raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
- return IRQ_HANDLED;
- }
-
- /* DEVREQ has highest priority */
- if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ))
- handle_ep0_devreq(imx_usb);
- /* Seem i.MX is missing EOF interrupt sometimes.
- * Therefore we don't monitor EOF.
- * We call handle_ep0() only if a request is queued for ep0.
- */
- else if (!list_empty(&imx_ep->queue))
- handle_ep0(imx_ep);
-
- __raw_writel(intr, imx_usb->base + USB_EP_INTR(0));
-
- return IRQ_HANDLED;
-}
-
-#ifndef MX1_INT_USBD0
-#define MX1_INT_USBD0 MX1_USBD_INT0
-#endif
-
-static irqreturn_t imx_udc_bulk_irq(int irq, void *dev)
-{
- struct imx_udc_struct *imx_usb = dev;
- struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0];
- int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
-
- dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev);
-
- if (!imx_usb->driver) {
- __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
- return IRQ_HANDLED;
- }
-
- handle_ep(imx_ep);
-
- __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
-
- return IRQ_HANDLED;
-}
-
-irq_handler_t intr_handler(int i)
-{
- switch (i) {
- case 0:
- return imx_udc_ctrl_irq;
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- return imx_udc_bulk_irq;
- default:
- return imx_udc_irq;
- }
-}
-
-/*******************************************************************************
- * Static defined IMX UDC structure
- *******************************************************************************
- */
-
-static int imx_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int imx_udc_stop(struct usb_gadget_driver *driver);
-static const struct usb_gadget_ops imx_udc_ops = {
- .get_frame = imx_udc_get_frame,
- .wakeup = imx_udc_wakeup,
- .start = imx_udc_start,
- .stop = imx_udc_stop,
-};
-
-static struct imx_udc_struct controller = {
- .gadget = {
- .ops = &imx_udc_ops,
- .ep0 = &controller.imx_ep[0].ep,
- .name = driver_name,
- .dev = {
- .init_name = "gadget",
- },
- },
-
- .imx_ep[0] = {
- .ep = {
- .name = ep0name,
- .ops = &imx_ep_ops,
- .maxpacket = 32,
- },
- .imx_usb = &controller,
- .fifosize = 32,
- .bEndpointAddress = 0,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
- },
- .imx_ep[1] = {
- .ep = {
- .name = "ep1in-bulk",
- .ops = &imx_ep_ops,
- .maxpacket = 64,
- },
- .imx_usb = &controller,
- .fifosize = 64,
- .bEndpointAddress = USB_DIR_IN | 1,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- },
- .imx_ep[2] = {
- .ep = {
- .name = "ep2out-bulk",
- .ops = &imx_ep_ops,
- .maxpacket = 64,
- },
- .imx_usb = &controller,
- .fifosize = 64,
- .bEndpointAddress = USB_DIR_OUT | 2,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- },
- .imx_ep[3] = {
- .ep = {
- .name = "ep3out-bulk",
- .ops = &imx_ep_ops,
- .maxpacket = 32,
- },
- .imx_usb = &controller,
- .fifosize = 32,
- .bEndpointAddress = USB_DIR_OUT | 3,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- },
- .imx_ep[4] = {
- .ep = {
- .name = "ep4in-int",
- .ops = &imx_ep_ops,
- .maxpacket = 32,
- },
- .imx_usb = &controller,
- .fifosize = 32,
- .bEndpointAddress = USB_DIR_IN | 4,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- },
- .imx_ep[5] = {
- .ep = {
- .name = "ep5out-int",
- .ops = &imx_ep_ops,
- .maxpacket = 32,
- },
- .imx_usb = &controller,
- .fifosize = 32,
- .bEndpointAddress = USB_DIR_OUT | 5,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- },
-};
-
-/*******************************************************************************
- * USB gadget driver functions
- *******************************************************************************
- */
-static int imx_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
-{
- struct imx_udc_struct *imx_usb = &controller;
- int retval;
-
- if (!driver
- || driver->max_speed < USB_SPEED_FULL
- || !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 = 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;
-}
-
-static int imx_udc_stop(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;
-}
-
-/*******************************************************************************
- * Module functions
- *******************************************************************************
- */
-
-static int __init imx_udc_probe(struct platform_device *pdev)
-{
- struct imx_udc_struct *imx_usb = &controller;
- struct resource *res;
- struct imxusb_platform_data *pdata;
- struct clk *clk;
- void __iomem *base;
- int ret = 0;
- int i;
- resource_size_t res_size;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "can't get device resources\n");
- return -ENODEV;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "driver needs platform data\n");
- return -ENODEV;
- }
-
- res_size = resource_size(res);
- if (!request_mem_region(res->start, res_size, res->name)) {
- dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
- res_size, res->start);
- return -ENOMEM;
- }
-
- if (pdata->init) {
- ret = pdata->init(&pdev->dev);
- if (ret)
- goto fail0;
- }
-
- base = ioremap(res->start, res_size);
- if (!base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -EIO;
- goto fail1;
- }
-
- clk = clk_get(NULL, "usbd_clk");
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- dev_err(&pdev->dev, "can't get USB clock\n");
- goto fail2;
- }
- clk_enable(clk);
-
- if (clk_get_rate(clk) != 48000000) {
- D_INI(&pdev->dev,
- "Bad USB clock (%d Hz), changing to 48000000 Hz\n",
- (int)clk_get_rate(clk));
- if (clk_set_rate(clk, 48000000)) {
- dev_err(&pdev->dev,
- "Unable to set correct USB clock (48MHz)\n");
- ret = -EIO;
- goto fail3;
- }
- }
-
- for (i = 0; i < IMX_USB_NB_EP + 1; i++) {
- imx_usb->usbd_int[i] = platform_get_irq(pdev, i);
- if (imx_usb->usbd_int[i] < 0) {
- dev_err(&pdev->dev, "can't get irq number\n");
- ret = -ENODEV;
- goto fail3;
- }
- }
-
- for (i = 0; i < IMX_USB_NB_EP + 1; i++) {
- ret = request_irq(imx_usb->usbd_int[i], intr_handler(i),
- 0, driver_name, imx_usb);
- if (ret) {
- dev_err(&pdev->dev, "can't get irq %i, err %d\n",
- imx_usb->usbd_int[i], ret);
- for (--i; i >= 0; i--)
- free_irq(imx_usb->usbd_int[i], imx_usb);
- goto fail3;
- }
- }
-
- imx_usb->res = res;
- imx_usb->base = base;
- imx_usb->clk = clk;
- imx_usb->dev = &pdev->dev;
-
- device_initialize(&imx_usb->gadget.dev);
-
- imx_usb->gadget.dev.parent = &pdev->dev;
- imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask;
-
- platform_set_drvdata(pdev, imx_usb);
-
- usb_init_data(imx_usb);
- imx_udc_init(imx_usb);
-
- init_timer(&imx_usb->timer);
- imx_usb->timer.function = handle_config;
- imx_usb->timer.data = (unsigned long)imx_usb;
-
- ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget);
- if (ret)
- goto fail4;
-
- return 0;
-fail4:
- for (i = 0; i < IMX_USB_NB_EP + 1; i++)
- free_irq(imx_usb->usbd_int[i], imx_usb);
-fail3:
- clk_put(clk);
- clk_disable(clk);
-fail2:
- iounmap(base);
-fail1:
- if (pdata->exit)
- pdata->exit(&pdev->dev);
-fail0:
- release_mem_region(res->start, res_size);
- return ret;
-}
-
-static int __exit imx_udc_remove(struct platform_device *pdev)
-{
- struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev);
- struct imxusb_platform_data *pdata = pdev->dev.platform_data;
- int i;
-
- usb_del_gadget_udc(&imx_usb->gadget);
- imx_udc_disable(imx_usb);
- del_timer(&imx_usb->timer);
-
- for (i = 0; i < IMX_USB_NB_EP + 1; i++)
- free_irq(imx_usb->usbd_int[i], imx_usb);
-
- clk_put(imx_usb->clk);
- clk_disable(imx_usb->clk);
- iounmap(imx_usb->base);
-
- release_mem_region(imx_usb->res->start, resource_size(imx_usb->res));
-
- if (pdata->exit)
- pdata->exit(&pdev->dev);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-
-#ifdef CONFIG_PM
-#define imx_udc_suspend NULL
-#define imx_udc_resume NULL
-#else
-#define imx_udc_suspend NULL
-#define imx_udc_resume NULL
-#endif
-
-/*----------------------------------------------------------------------------*/
-
-static struct platform_driver udc_driver = {
- .driver = {
- .name = driver_name,
- .owner = THIS_MODULE,
- },
- .remove = __exit_p(imx_udc_remove),
- .suspend = imx_udc_suspend,
- .resume = imx_udc_resume,
-};
-
-static int __init udc_init(void)
-{
- return platform_driver_probe(&udc_driver, imx_udc_probe);
-}
-module_init(udc_init);
-
-static void __exit udc_exit(void)
-{
- platform_driver_unregister(&udc_driver);
-}
-module_exit(udc_exit);
-
-MODULE_DESCRIPTION("IMX USB Device Controller driver");
-MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx_udc");
diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h
deleted file mode 100644
index d118fb77784..00000000000
--- a/drivers/usb/gadget/imx_udc.h
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2005 Mike Lee(eemike@gmail.com)
- *
- * This udc driver is now under testing and code is based on pxa2xx_udc.h
- * Please use it with your own risk!
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __LINUX_USB_GADGET_IMX_H
-#define __LINUX_USB_GADGET_IMX_H
-
-#include <linux/types.h>
-
-/* Helper macros */
-#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
-#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
-#define IMX_USB_NB_EP 6
-
-/* Driver structures */
-struct imx_request {
- struct usb_request req;
- struct list_head queue;
- unsigned int in_use;
-};
-
-enum ep0_state {
- EP0_IDLE,
- EP0_IN_DATA_PHASE,
- EP0_OUT_DATA_PHASE,
- EP0_CONFIG,
- EP0_STALL,
-};
-
-struct imx_ep_struct {
- struct usb_ep ep;
- struct imx_udc_struct *imx_usb;
- struct list_head queue;
- unsigned char stopped;
- unsigned char fifosize;
- unsigned char bEndpointAddress;
- unsigned char bmAttributes;
-};
-
-struct imx_udc_struct {
- struct usb_gadget gadget;
- struct usb_gadget_driver *driver;
- struct device *dev;
- struct imx_ep_struct imx_ep[IMX_USB_NB_EP];
- struct clk *clk;
- struct timer_list timer;
- enum ep0_state ep0state;
- struct resource *res;
- void __iomem *base;
- unsigned char set_config;
- int cfg,
- intf,
- alt,
- usbd_int[7];
-};
-
-/* USB registers */
-#define USB_FRAME (0x00) /* USB frame */
-#define USB_SPEC (0x04) /* USB Spec */
-#define USB_STAT (0x08) /* USB Status */
-#define USB_CTRL (0x0C) /* USB Control */
-#define USB_DADR (0x10) /* USB Desc RAM addr */
-#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */
-#define USB_INTR (0x18) /* USB interrupt */
-#define USB_MASK (0x1C) /* USB Mask */
-#define USB_ENAB (0x24) /* USB Enable */
-#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */
-#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */
-#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */
-#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */
-#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */
-#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */
-#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */
-#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */
-#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */
-#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */
-#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last rd f. pointer */
-#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last wr f. pointer */
-#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */
-#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */
-#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */
-/* USB Control Register Bit Fields.*/
-#define CTRL_CMDOVER (1<<6) /* UDC status */
-#define CTRL_CMDERROR (1<<5) /* UDC status */
-#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */
-#define CTRL_UDC_RST (1<<2) /* UDC reset */
-#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */
-#define CTRL_RESUME (1<<0) /* UDC resume */
-/* USB Status Register Bit Fields.*/
-#define STAT_RST (1<<8)
-#define STAT_SUSP (1<<7)
-#define STAT_CFG (3<<5)
-#define STAT_INTF (3<<3)
-#define STAT_ALTSET (7<<0)
-/* USB Interrupt Status/Mask Registers Bit fields */
-#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */
-#define INTR_MSOF (1<<7) /* Missed Start of Frame */
-#define INTR_SOF (1<<6) /* Start of Frame */
-#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */
-#define INTR_RESET_START (1<<4) /* Reset Signaling start */
-#define INTR_RESUME (1<<3) /* Suspend to resume */
-#define INTR_SUSPEND (1<<2) /* Active to suspend */
-#define INTR_FRAME_MATCH (1<<1) /* Frame matched */
-#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */
-/* USB Enable Register Bit Fields.*/
-#define ENAB_RST (1<<31) /* Reset USB modules */
-#define ENAB_ENAB (1<<30) /* Enable USB modules*/
-#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */
-#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */
-#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */
-/* USB Descriptor Ram Address Register bit fields */
-#define DADR_CFG (1<<31) /* Configuration */
-#define DADR_BSY (1<<30) /* Busy status */
-#define DADR_DADR (0x1FF) /* Descriptor Ram Address */
-/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */
-#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */
-/* USB Endpoint Status Register bit fields */
-#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */
-#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */
-#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */
-#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */
-#define EPSTAT_TYP (3<<3) /* Endpoint type */
-#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */
-#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */
-#define EPSTAT_STALL (1<<0) /* Force stall */
-/* USB Endpoint FIFO Status Register bit fields */
-#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */
-#define FSTAT_ERR (1<<22) /* FIFO error */
-#define FSTAT_UF (1<<21) /* FIFO underflow */
-#define FSTAT_OF (1<<20) /* FIFO overflow */
-#define FSTAT_FR (1<<19) /* FIFO frame ready */
-#define FSTAT_FULL (1<<18) /* FIFO full */
-#define FSTAT_ALRM (1<<17) /* FIFO alarm */
-#define FSTAT_EMPTY (1<<16) /* FIFO empty */
-/* USB Endpoint FIFO Control Register bit fields */
-#define FCTRL_WFR (1<<29) /* Write frame end */
-/* USB Endpoint Interrupt Status Regsiter bit fields */
-#define EPINTR_FIFO_FULL (1<<8) /* fifo full */
-#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */
-#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */
-#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */
-#define EPINTR_FIFO_LOW (1<<4) /* fifo low */
-#define EPINTR_MDEVREQ (1<<3) /* multi Device request */
-#define EPINTR_EOT (1<<2) /* fifo end of transfer */
-#define EPINTR_DEVREQ (1<<1) /* Device request */
-#define EPINTR_EOF (1<<0) /* fifo end of frame */
-
-/* Debug macros */
-#ifdef DEBUG
-
-/* #define DEBUG_REQ */
-/* #define DEBUG_TRX */
-/* #define DEBUG_INIT */
-/* #define DEBUG_EP0 */
-/* #define DEBUG_EPX */
-/* #define DEBUG_IRQ */
-/* #define DEBUG_EPIRQ */
-/* #define DEBUG_DUMP */
-/* #define DEBUG_ERR */
-
-#ifdef DEBUG_REQ
- #define D_REQ(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_REQ(dev, args...) do {} while (0)
-#endif /* DEBUG_REQ */
-
-#ifdef DEBUG_TRX
- #define D_TRX(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_TRX(dev, args...) do {} while (0)
-#endif /* DEBUG_TRX */
-
-#ifdef DEBUG_INIT
- #define D_INI(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_INI(dev, args...) do {} while (0)
-#endif /* DEBUG_INIT */
-
-#ifdef DEBUG_EP0
- static const char *state_name[] = {
- "EP0_IDLE",
- "EP0_IN_DATA_PHASE",
- "EP0_OUT_DATA_PHASE",
- "EP0_CONFIG",
- "EP0_STALL"
- };
- #define D_EP0(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_EP0(dev, args...) do {} while (0)
-#endif /* DEBUG_EP0 */
-
-#ifdef DEBUG_EPX
- #define D_EPX(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_EPX(dev, args...) do {} while (0)
-#endif /* DEBUG_EP0 */
-
-#ifdef DEBUG_IRQ
- static void dump_intr(const char *label, int irqreg, struct device *dev)
- {
- dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label,
- (irqreg & INTR_WAKEUP) ? " wake" : "",
- (irqreg & INTR_MSOF) ? " msof" : "",
- (irqreg & INTR_SOF) ? " sof" : "",
- (irqreg & INTR_RESUME) ? " resume" : "",
- (irqreg & INTR_SUSPEND) ? " suspend" : "",
- (irqreg & INTR_RESET_STOP) ? " noreset" : "",
- (irqreg & INTR_RESET_START) ? " reset" : "",
- (irqreg & INTR_FRAME_MATCH) ? " fmatch" : "",
- (irqreg & INTR_CFG_CHG) ? " config" : "");
- }
-#else
- #define dump_intr(x, y, z) do {} while (0)
-#endif /* DEBUG_IRQ */
-
-#ifdef DEBUG_EPIRQ
- static void dump_ep_intr(const char *label, int nr, int irqreg,
- struct device *dev)
- {
- dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr,
- (irqreg & EPINTR_FIFO_FULL) ? " full" : "",
- (irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "",
- (irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "",
- (irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "",
- (irqreg & EPINTR_FIFO_LOW) ? " flow" : "",
- (irqreg & EPINTR_MDEVREQ) ? " mreq" : "",
- (irqreg & EPINTR_EOF) ? " eof" : "",
- (irqreg & EPINTR_DEVREQ) ? " devreq" : "",
- (irqreg & EPINTR_EOT) ? " eot" : "");
- }
-#else
- #define dump_ep_intr(x, y, z, i) do {} while (0)
-#endif /* DEBUG_IRQ */
-
-#ifdef DEBUG_DUMP
- static void dump_usb_stat(const char *label,
- struct imx_udc_struct *imx_usb)
- {
- int temp = __raw_readl(imx_usb->base + USB_STAT);
-
- dev_dbg(imx_usb->dev,
- "<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label,
- (temp & STAT_RST) ? " reset" : "",
- (temp & STAT_SUSP) ? " suspend" : "",
- (temp & STAT_CFG) >> 5,
- (temp & STAT_INTF) >> 3,
- (temp & STAT_ALTSET));
- }
-
- static void dump_ep_stat(const char *label,
- struct imx_ep_struct *imx_ep)
- {
- int temp = __raw_readl(imx_ep->imx_usb->base
- + USB_EP_INTR(EP_NO(imx_ep)));
-
- dev_dbg(imx_ep->imx_usb->dev,
- "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n",
- label, EP_NO(imx_ep),
- (temp & EPINTR_FIFO_FULL) ? " full" : "",
- (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "",
- (temp & EPINTR_FIFO_ERROR) ? " ferr" : "",
- (temp & EPINTR_FIFO_HIGH) ? " fhigh" : "",
- (temp & EPINTR_FIFO_LOW) ? " flow" : "",
- (temp & EPINTR_MDEVREQ) ? " mreq" : "",
- (temp & EPINTR_EOF) ? " eof" : "",
- (temp & EPINTR_DEVREQ) ? " devreq" : "",
- (temp & EPINTR_EOT) ? " eot" : "");
-
- temp = __raw_readl(imx_ep->imx_usb->base
- + USB_EP_STAT(EP_NO(imx_ep)));
-
- dev_dbg(imx_ep->imx_usb->dev,
- "<%s> EP%d_STAT=[%s%s bcount=%d]\n",
- label, EP_NO(imx_ep),
- (temp & EPSTAT_SIP) ? " sip" : "",
- (temp & EPSTAT_STALL) ? " stall" : "",
- (temp & EPSTAT_BCOUNT) >> 16);
-
- temp = __raw_readl(imx_ep->imx_usb->base
- + USB_EP_FSTAT(EP_NO(imx_ep)));
-
- dev_dbg(imx_ep->imx_usb->dev,
- "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n",
- label, EP_NO(imx_ep),
- (temp & FSTAT_ERR) ? " ferr" : "",
- (temp & FSTAT_UF) ? " funder" : "",
- (temp & FSTAT_OF) ? " fover" : "",
- (temp & FSTAT_FR) ? " fready" : "",
- (temp & FSTAT_FULL) ? " ffull" : "",
- (temp & FSTAT_ALRM) ? " falarm" : "",
- (temp & FSTAT_EMPTY) ? " fempty" : "");
- }
-
- static void dump_req(const char *label, struct imx_ep_struct *imx_ep,
- struct usb_request *req)
- {
- int i;
-
- if (!req || !req->buf) {
- dev_dbg(imx_ep->imx_usb->dev,
- "<%s> req or req buf is free\n", label);
- return;
- }
-
- if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state
- == EP0_IN_DATA_PHASE)
- || (EP_NO(imx_ep) && EP_DIR(imx_ep))) {
-
- dev_dbg(imx_ep->imx_usb->dev,
- "<%s> request dump <", label);
- for (i = 0; i < req->length; i++)
- printk("%02x-", *((u8 *)req->buf + i));
- printk(">\n");
- }
- }
-
-#else
- #define dump_ep_stat(x, y) do {} while (0)
- #define dump_usb_stat(x, y) do {} while (0)
- #define dump_req(x, y, z) do {} while (0)
-#endif /* DEBUG_DUMP */
-
-#ifdef DEBUG_ERR
- #define D_ERR(dev, args...) dev_dbg(dev, ## args)
-#else
- #define D_ERR(dev, args...) do {} while (0)
-#endif
-
-#else
- #define D_REQ(dev, args...) do {} while (0)
- #define D_TRX(dev, args...) do {} while (0)
- #define D_INI(dev, args...) do {} while (0)
- #define D_EP0(dev, args...) do {} while (0)
- #define D_EPX(dev, args...) do {} while (0)
- #define dump_ep_intr(x, y, z, i) do {} while (0)
- #define dump_intr(x, y, z) do {} while (0)
- #define dump_ep_stat(x, y) do {} while (0)
- #define dump_usb_stat(x, y) do {} while (0)
- #define dump_req(x, y, z) do {} while (0)
- #define D_ERR(dev, args...) do {} while (0)
-#endif /* DEBUG */
-
-#endif /* __LINUX_USB_GADGET_IMX_H */
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index ae04266dba1..2e4ce770490 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -24,6 +24,8 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
+#include <linux/mmu_context.h>
+#include <linux/aio.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -76,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 {
@@ -438,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
value = -ENOMEM;
- kbuf = kmalloc (len, GFP_KERNEL);
- if (!kbuf)
- goto free1;
- if (copy_from_user (kbuf, buf, len)) {
- value = -EFAULT;
+ kbuf = memdup_user(buf, len);
+ if (!kbuf) {
+ value = PTR_ERR(kbuf);
goto free1;
}
@@ -451,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
data->name, len, (int) value);
free1:
mutex_unlock(&data->lock);
- kfree (kbuf);
return value;
}
@@ -514,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;
@@ -529,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
@@ -537,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;
@@ -569,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)
@@ -597,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);
@@ -634,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)) {
@@ -643,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.
@@ -675,7 +691,7 @@ fail:
kfree(priv);
put_ep(epdata);
} else
- value = (iv ? -EIOCBRETRY : -EIOCBQUEUED);
+ value = -EIOCBQUEUED;
return value;
}
@@ -689,12 +705,11 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
return -EINVAL;
- buf = kmalloc(iocb->ki_left, GFP_KERNEL);
+ buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
- iocb->ki_retry = ep_aio_read_retry;
- return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs);
+ return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs);
}
static ssize_t
@@ -709,7 +724,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
return -EINVAL;
- buf = kmalloc(iocb->ki_left, GFP_KERNEL);
+ buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
@@ -828,7 +843,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
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... */
ep->desc = &data->hs_desc;
@@ -836,7 +850,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#endif
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
@@ -890,7 +903,6 @@ ep_open (struct inode *inode, struct file *fd)
/* used before endpoint configuration */
static const struct file_operations ep_config_operations = {
- .owner = THIS_MODULE,
.llseek = no_llseek,
.open = ep_open,
@@ -920,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;
@@ -1043,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 */
}
@@ -1251,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;
}
@@ -1322,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;
@@ -1345,7 +1358,6 @@ static void make_qualifier (struct dev_data *dev)
memcpy (dev->rbuf, &qual, sizeof qual);
}
-#endif
static int
config_buf (struct dev_data *dev, u8 type, unsigned index)
@@ -1410,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) {
@@ -1425,7 +1436,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
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;
@@ -1435,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,
@@ -1490,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;
@@ -1497,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)
@@ -1569,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;
@@ -1605,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);
}
@@ -1687,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;
@@ -1765,12 +1771,8 @@ gadgetfs_suspend (struct usb_gadget *gadget)
}
static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .max_speed = USB_SPEED_HIGH,
-#else
- .max_speed = USB_SPEED_FULL,
-#endif
.function = (char *) driver_desc,
+ .bind = gadgetfs_bind,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
.disconnect = gadgetfs_disconnect,
@@ -1785,7 +1787,8 @@ 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;
@@ -1793,6 +1796,7 @@ static int gadgetfs_probe (struct usb_gadget *gadget)
static struct usb_gadget_driver probe_driver = {
.max_speed = USB_SPEED_HIGH,
+ .bind = gadgetfs_probe,
.unbind = gadgetfs_nop,
.setup = (void *)gadgetfs_nop,
.disconnect = gadgetfs_nop,
@@ -1902,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_probe_driver(&gadgetfs_driver, gadgetfs_bind);
+ 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;
@@ -1948,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,
@@ -1990,8 +1998,8 @@ gadgetfs_make_inode (struct super_block *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;
@@ -2041,7 +2049,8 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
return -ESRCH;
/* fake probe to determine $CHIP */
- (void) usb_gadget_probe_driver(&probe_driver, gadgetfs_probe);
+ CHIP = NULL;
+ usb_gadget_probe_driver(&probe_driver);
if (!CHIP)
return -ENODEV;
@@ -2059,10 +2068,8 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
if (!inode)
goto Enomem;
inode->i_op = &simple_dir_inode_operations;
- if (!(sb->s_root = d_alloc_root (inode))) {
- iput(inode);
+ 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.
@@ -2115,6 +2122,7 @@ static struct file_system_type gadgetfs_type = {
.mount = gadgetfs_mount,
.kill_sb = gadgetfs_kill_sb,
};
+MODULE_ALIAS_FS("gadgetfs");
/*----------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
deleted file mode 100644
index e2293c1588e..00000000000
--- a/drivers/usb/gadget/langwell_udc.c
+++ /dev/null
@@ -1,3465 +0,0 @@
-/*
- * Intel Langwell USB Device Controller driver
- * Copyright (C) 2008-2009, Intel Corporation.
- *
- * 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.
- */
-
-
-/* #undef DEBUG */
-/* #undef VERBOSE_DEBUG */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.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/init.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 <asm/system.h>
-#include <asm/unaligned.h>
-
-#include "langwell_udc.h"
-
-
-#define DRIVER_DESC "Intel Langwell USB Device Controller driver"
-#define DRIVER_VERSION "16 May 2009"
-
-static const char driver_name[] = "langwell_udc";
-static const char driver_desc[] = DRIVER_DESC;
-
-
-/* for endpoint 0 operations */
-static const struct usb_endpoint_descriptor
-langwell_ep0_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 0,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
- .wMaxPacketSize = EP0_MAX_PKT_SIZE,
-};
-
-
-/*-------------------------------------------------------------------------*/
-/* debugging */
-
-#ifdef VERBOSE_DEBUG
-static inline void print_all_registers(struct langwell_udc *dev)
-{
- int i;
-
- /* Capability Registers */
- dev_dbg(&dev->pdev->dev,
- "Capability Registers (offset: 0x%04x, length: 0x%08x)\n",
- CAP_REG_OFFSET, (u32)sizeof(struct langwell_cap_regs));
- dev_dbg(&dev->pdev->dev, "caplength=0x%02x\n",
- readb(&dev->cap_regs->caplength));
- dev_dbg(&dev->pdev->dev, "hciversion=0x%04x\n",
- readw(&dev->cap_regs->hciversion));
- dev_dbg(&dev->pdev->dev, "hcsparams=0x%08x\n",
- readl(&dev->cap_regs->hcsparams));
- dev_dbg(&dev->pdev->dev, "hccparams=0x%08x\n",
- readl(&dev->cap_regs->hccparams));
- dev_dbg(&dev->pdev->dev, "dciversion=0x%04x\n",
- readw(&dev->cap_regs->dciversion));
- dev_dbg(&dev->pdev->dev, "dccparams=0x%08x\n",
- readl(&dev->cap_regs->dccparams));
-
- /* Operational Registers */
- dev_dbg(&dev->pdev->dev,
- "Operational Registers (offset: 0x%04x, length: 0x%08x)\n",
- OP_REG_OFFSET, (u32)sizeof(struct langwell_op_regs));
- dev_dbg(&dev->pdev->dev, "extsts=0x%08x\n",
- readl(&dev->op_regs->extsts));
- dev_dbg(&dev->pdev->dev, "extintr=0x%08x\n",
- readl(&dev->op_regs->extintr));
- dev_dbg(&dev->pdev->dev, "usbcmd=0x%08x\n",
- readl(&dev->op_regs->usbcmd));
- dev_dbg(&dev->pdev->dev, "usbsts=0x%08x\n",
- readl(&dev->op_regs->usbsts));
- dev_dbg(&dev->pdev->dev, "usbintr=0x%08x\n",
- readl(&dev->op_regs->usbintr));
- dev_dbg(&dev->pdev->dev, "frindex=0x%08x\n",
- readl(&dev->op_regs->frindex));
- dev_dbg(&dev->pdev->dev, "ctrldssegment=0x%08x\n",
- readl(&dev->op_regs->ctrldssegment));
- dev_dbg(&dev->pdev->dev, "deviceaddr=0x%08x\n",
- readl(&dev->op_regs->deviceaddr));
- dev_dbg(&dev->pdev->dev, "endpointlistaddr=0x%08x\n",
- readl(&dev->op_regs->endpointlistaddr));
- dev_dbg(&dev->pdev->dev, "ttctrl=0x%08x\n",
- readl(&dev->op_regs->ttctrl));
- dev_dbg(&dev->pdev->dev, "burstsize=0x%08x\n",
- readl(&dev->op_regs->burstsize));
- dev_dbg(&dev->pdev->dev, "txfilltuning=0x%08x\n",
- readl(&dev->op_regs->txfilltuning));
- dev_dbg(&dev->pdev->dev, "txttfilltuning=0x%08x\n",
- readl(&dev->op_regs->txttfilltuning));
- dev_dbg(&dev->pdev->dev, "ic_usb=0x%08x\n",
- readl(&dev->op_regs->ic_usb));
- dev_dbg(&dev->pdev->dev, "ulpi_viewport=0x%08x\n",
- readl(&dev->op_regs->ulpi_viewport));
- dev_dbg(&dev->pdev->dev, "configflag=0x%08x\n",
- readl(&dev->op_regs->configflag));
- dev_dbg(&dev->pdev->dev, "portsc1=0x%08x\n",
- readl(&dev->op_regs->portsc1));
- dev_dbg(&dev->pdev->dev, "devlc=0x%08x\n",
- readl(&dev->op_regs->devlc));
- dev_dbg(&dev->pdev->dev, "otgsc=0x%08x\n",
- readl(&dev->op_regs->otgsc));
- dev_dbg(&dev->pdev->dev, "usbmode=0x%08x\n",
- readl(&dev->op_regs->usbmode));
- dev_dbg(&dev->pdev->dev, "endptnak=0x%08x\n",
- readl(&dev->op_regs->endptnak));
- dev_dbg(&dev->pdev->dev, "endptnaken=0x%08x\n",
- readl(&dev->op_regs->endptnaken));
- dev_dbg(&dev->pdev->dev, "endptsetupstat=0x%08x\n",
- readl(&dev->op_regs->endptsetupstat));
- dev_dbg(&dev->pdev->dev, "endptprime=0x%08x\n",
- readl(&dev->op_regs->endptprime));
- dev_dbg(&dev->pdev->dev, "endptflush=0x%08x\n",
- readl(&dev->op_regs->endptflush));
- dev_dbg(&dev->pdev->dev, "endptstat=0x%08x\n",
- readl(&dev->op_regs->endptstat));
- dev_dbg(&dev->pdev->dev, "endptcomplete=0x%08x\n",
- readl(&dev->op_regs->endptcomplete));
-
- for (i = 0; i < dev->ep_max / 2; i++) {
- dev_dbg(&dev->pdev->dev, "endptctrl[%d]=0x%08x\n",
- i, readl(&dev->op_regs->endptctrl[i]));
- }
-}
-#else
-
-#define print_all_registers(dev) do { } while (0)
-
-#endif /* VERBOSE_DEBUG */
-
-
-/*-------------------------------------------------------------------------*/
-
-#define is_in(ep) (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \
- USB_DIR_IN) : (usb_endpoint_dir_in((ep)->desc)))
-
-#define DIR_STRING(ep) (is_in(ep) ? "in" : "out")
-
-
-static char *type_string(const struct usb_endpoint_descriptor *desc)
-{
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_BULK:
- return "bulk";
- case USB_ENDPOINT_XFER_ISOC:
- return "iso";
- case USB_ENDPOINT_XFER_INT:
- return "int";
- };
-
- return "control";
-}
-
-
-/* configure endpoint control registers */
-static void ep_reset(struct langwell_ep *ep, unsigned char ep_num,
- unsigned char is_in, unsigned char ep_type)
-{
- struct langwell_udc *dev;
- u32 endptctrl;
-
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
- if (is_in) { /* TX */
- if (ep_num)
- endptctrl |= EPCTRL_TXR;
- endptctrl |= EPCTRL_TXE;
- endptctrl |= ep_type << EPCTRL_TXT_SHIFT;
- } else { /* RX */
- if (ep_num)
- endptctrl |= EPCTRL_RXR;
- endptctrl |= EPCTRL_RXE;
- endptctrl |= ep_type << EPCTRL_RXT_SHIFT;
- }
-
- writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* reset ep0 dQH and endptctrl */
-static void ep0_reset(struct langwell_udc *dev)
-{
- struct langwell_ep *ep;
- int i;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* ep0 in and out */
- for (i = 0; i < 2; i++) {
- ep = &dev->ep[i];
- ep->dev = dev;
-
- /* ep0 dQH */
- ep->dqh = &dev->ep_dqh[i];
-
- /* configure ep0 endpoint capabilities in dQH */
- ep->dqh->dqh_ios = 1;
- ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE;
-
- /* enable ep0-in HW zero length termination select */
- if (is_in(ep))
- ep->dqh->dqh_zlt = 0;
- ep->dqh->dqh_mult = 0;
-
- ep->dqh->dtd_next = DTD_TERM;
-
- /* configure ep0 control registers */
- ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL);
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* endpoints operations */
-
-/* configure endpoint, making it usable */
-static int langwell_ep_enable(struct usb_ep *_ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct langwell_udc *dev;
- struct langwell_ep *ep;
- u16 max = 0;
- unsigned long flags;
- int i, retval = 0;
- unsigned char zlt, ios = 0, mult = 0;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !desc || ep->desc
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- max = usb_endpoint_maxp(desc);
-
- /*
- * disable HW zero length termination select
- * driver handles zero length packet through req->req.zero
- */
- zlt = 1;
-
- /*
- * sanity check type, direction, address, and then
- * initialize the endpoint capabilities fields in dQH
- */
- switch (usb_endpoint_type(desc)) {
- case USB_ENDPOINT_XFER_CONTROL:
- ios = 1;
- break;
- case USB_ENDPOINT_XFER_BULK:
- if ((dev->gadget.speed == USB_SPEED_HIGH
- && max != 512)
- || (dev->gadget.speed == USB_SPEED_FULL
- && max > 64)) {
- goto done;
- }
- break;
- case USB_ENDPOINT_XFER_INT:
- if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
- goto done;
-
- switch (dev->gadget.speed) {
- case USB_SPEED_HIGH:
- if (max <= 1024)
- break;
- case USB_SPEED_FULL:
- if (max <= 64)
- break;
- default:
- if (max <= 8)
- break;
- goto done;
- }
- break;
- case USB_ENDPOINT_XFER_ISOC:
- if (strstr(ep->ep.name, "-bulk")
- || strstr(ep->ep.name, "-int"))
- goto done;
-
- switch (dev->gadget.speed) {
- case USB_SPEED_HIGH:
- if (max <= 1024)
- break;
- case USB_SPEED_FULL:
- if (max <= 1023)
- break;
- default:
- goto done;
- }
- /*
- * FIXME:
- * calculate transactions needed for high bandwidth iso
- */
- mult = (unsigned char)(1 + ((max >> 11) & 0x03));
- max = max & 0x8ff; /* bit 0~10 */
- /* 3 transactions at most */
- if (mult > 3)
- goto done;
- break;
- default:
- goto done;
- }
-
- spin_lock_irqsave(&dev->lock, flags);
-
- ep->ep.maxpacket = max;
- ep->desc = desc;
- ep->stopped = 0;
- ep->ep_num = usb_endpoint_num(desc);
-
- /* ep_type */
- ep->ep_type = usb_endpoint_type(desc);
-
- /* configure endpoint control registers */
- ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type);
-
- /* configure endpoint capabilities in dQH */
- i = ep->ep_num * 2 + is_in(ep);
- ep->dqh = &dev->ep_dqh[i];
- ep->dqh->dqh_ios = ios;
- ep->dqh->dqh_mpl = cpu_to_le16(max);
- ep->dqh->dqh_zlt = zlt;
- ep->dqh->dqh_mult = mult;
- ep->dqh->dtd_next = DTD_TERM;
-
- dev_dbg(&dev->pdev->dev, "enabled %s (ep%d%s-%s), max %04x\n",
- _ep->name,
- ep->ep_num,
- DIR_STRING(ep),
- type_string(desc),
- max);
-
- spin_unlock_irqrestore(&dev->lock, flags);
-done:
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return retval;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* retire a request */
-static void done(struct langwell_ep *ep, struct langwell_request *req,
- int status)
-{
- struct langwell_udc *dev = ep->dev;
- unsigned stopped = ep->stopped;
- struct langwell_dtd *curr_dtd, *next_dtd;
- int i;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* remove the req from ep->queue */
- list_del_init(&req->queue);
-
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- /* free dTD for the request */
- next_dtd = req->head;
- for (i = 0; i < req->dtd_count; i++) {
- curr_dtd = next_dtd;
- if (i != req->dtd_count - 1)
- next_dtd = curr_dtd->next_dtd_virt;
- dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma);
- }
-
- if (req->mapped) {
- dma_unmap_single(&dev->pdev->dev,
- req->req.dma, req->req.length,
- is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
- req->req.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else
- dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma,
- req->req.length,
- is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
-
- if (status != -ESHUTDOWN)
- dev_dbg(&dev->pdev->dev,
- "complete %s, req %p, stat %d, len %u/%u\n",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length);
-
- /* don't modify queue heads during completion callback */
- ep->stopped = 1;
-
- spin_unlock(&dev->lock);
- /* complete routine from gadget driver */
- if (req->req.complete)
- req->req.complete(&ep->ep, &req->req);
-
- spin_lock(&dev->lock);
- ep->stopped = stopped;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-static void langwell_ep_fifo_flush(struct usb_ep *_ep);
-
-/* delete all endpoint requests, called with spinlock held */
-static void nuke(struct langwell_ep *ep, int status)
-{
- /* called with spinlock held */
- ep->stopped = 1;
-
- /* endpoint fifo flush */
- if (&ep->ep && ep->desc)
- langwell_ep_fifo_flush(&ep->ep);
-
- while (!list_empty(&ep->queue)) {
- struct langwell_request *req = NULL;
- req = list_entry(ep->queue.next, struct langwell_request,
- queue);
- done(ep, req, status);
- }
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* endpoint is no longer usable */
-static int langwell_ep_disable(struct usb_ep *_ep)
-{
- struct langwell_ep *ep;
- unsigned long flags;
- struct langwell_udc *dev;
- int ep_num;
- u32 endptctrl;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !ep->desc)
- return -EINVAL;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* disable endpoint control register */
- ep_num = ep->ep_num;
- endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
- if (is_in(ep))
- endptctrl &= ~EPCTRL_TXE;
- else
- endptctrl &= ~EPCTRL_RXE;
- writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
-
- /* nuke all pending requests (does flush) */
- nuke(ep, -ESHUTDOWN);
-
- ep->desc = NULL;
- ep->stopped = 1;
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_dbg(&dev->pdev->dev, "disabled %s\n", _ep->name);
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-
- return 0;
-}
-
-
-/* allocate a request object to use with this endpoint */
-static struct usb_request *langwell_alloc_request(struct usb_ep *_ep,
- gfp_t gfp_flags)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- struct langwell_request *req = NULL;
-
- if (!_ep)
- return NULL;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- req = kzalloc(sizeof(*req), gfp_flags);
- if (!req)
- return NULL;
-
- req->req.dma = DMA_ADDR_INVALID;
- INIT_LIST_HEAD(&req->queue);
-
- dev_vdbg(&dev->pdev->dev, "alloc request for %s\n", _ep->name);
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return &req->req;
-}
-
-
-/* free a request object */
-static void langwell_free_request(struct usb_ep *_ep,
- struct usb_request *_req)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- struct langwell_request *req = NULL;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !_req)
- return;
-
- req = container_of(_req, struct langwell_request, req);
- WARN_ON(!list_empty(&req->queue));
-
- if (_req)
- kfree(req);
-
- dev_vdbg(&dev->pdev->dev, "free request for %s\n", _ep->name);
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* queue dTD and PRIME endpoint */
-static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req)
-{
- u32 bit_mask, usbcmd, endptstat, dtd_dma;
- u8 dtd_status;
- int i;
- struct langwell_dqh *dqh;
- struct langwell_udc *dev;
-
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- i = ep->ep_num * 2 + is_in(ep);
- dqh = &dev->ep_dqh[i];
-
- if (ep->ep_num)
- dev_vdbg(&dev->pdev->dev, "%s\n", ep->name);
- else
- /* ep0 */
- dev_vdbg(&dev->pdev->dev, "%s-%s\n", ep->name, DIR_STRING(ep));
-
- dev_vdbg(&dev->pdev->dev, "ep_dqh[%d] addr: 0x%p\n",
- i, &(dev->ep_dqh[i]));
-
- bit_mask = is_in(ep) ?
- (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num));
-
- dev_vdbg(&dev->pdev->dev, "bit_mask = 0x%08x\n", bit_mask);
-
- /* check if the pipe is empty */
- if (!(list_empty(&ep->queue))) {
- /* add dTD to the end of linked list */
- struct langwell_request *lastreq;
- lastreq = list_entry(ep->queue.prev,
- struct langwell_request, queue);
-
- lastreq->tail->dtd_next =
- cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK);
-
- /* read prime bit, if 1 goto out */
- if (readl(&dev->op_regs->endptprime) & bit_mask)
- goto out;
-
- do {
- /* set ATDTW bit in USBCMD */
- usbcmd = readl(&dev->op_regs->usbcmd);
- writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd);
-
- /* read correct status bit */
- endptstat = readl(&dev->op_regs->endptstat) & bit_mask;
-
- } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW));
-
- /* write ATDTW bit to 0 */
- usbcmd = readl(&dev->op_regs->usbcmd);
- writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd);
-
- if (endptstat)
- goto out;
- }
-
- /* write dQH next pointer and terminate bit to 0 */
- dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK;
- dqh->dtd_next = cpu_to_le32(dtd_dma);
-
- /* clear active and halt bit */
- dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED);
- dqh->dtd_status &= dtd_status;
- dev_vdbg(&dev->pdev->dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status);
-
- /* ensure that updates to the dQH will occur before priming */
- wmb();
-
- /* write 1 to endptprime register to PRIME endpoint */
- bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num);
- dev_vdbg(&dev->pdev->dev, "endprime bit_mask = 0x%08x\n", bit_mask);
- writel(bit_mask, &dev->op_regs->endptprime);
-out:
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* fill in the dTD structure to build a transfer descriptor */
-static struct langwell_dtd *build_dtd(struct langwell_request *req,
- unsigned *length, dma_addr_t *dma, int *is_last)
-{
- u32 buf_ptr;
- struct langwell_dtd *dtd;
- struct langwell_udc *dev;
- int i;
-
- dev = req->ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* the maximum transfer length, up to 16k bytes */
- *length = min(req->req.length - req->req.actual,
- (unsigned)DTD_MAX_TRANSFER_LENGTH);
-
- /* create dTD dma_pool resource */
- dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma);
- if (dtd == NULL)
- return dtd;
- dtd->dtd_dma = *dma;
-
- /* initialize buffer page pointers */
- buf_ptr = (u32)(req->req.dma + req->req.actual);
- for (i = 0; i < 5; i++)
- dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE);
-
- req->req.actual += *length;
-
- /* fill in total bytes with transfer size */
- dtd->dtd_total = cpu_to_le16(*length);
- dev_vdbg(&dev->pdev->dev, "dtd->dtd_total = %d\n", dtd->dtd_total);
-
- /* set is_last flag if req->req.zero is set or not */
- 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;
-
- if (*is_last == 0)
- dev_vdbg(&dev->pdev->dev, "multi-dtd request!\n");
-
- /* set interrupt on complete bit for the last dTD */
- if (*is_last && !req->req.no_interrupt)
- dtd->dtd_ioc = 1;
-
- /* set multiplier override 0 for non-ISO and non-TX endpoint */
- dtd->dtd_multo = 0;
-
- /* set the active bit of status field to 1 */
- dtd->dtd_status = DTD_STS_ACTIVE;
- dev_vdbg(&dev->pdev->dev, "dtd->dtd_status = 0x%02x\n",
- dtd->dtd_status);
-
- dev_vdbg(&dev->pdev->dev, "length = %d, dma addr= 0x%08x\n",
- *length, (int)*dma);
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return dtd;
-}
-
-
-/* generate dTD linked list for a request */
-static int req_to_dtd(struct langwell_request *req)
-{
- unsigned count;
- int is_last, is_first = 1;
- struct langwell_dtd *dtd, *last_dtd = NULL;
- struct langwell_udc *dev;
- dma_addr_t dma;
-
- dev = req->ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
- 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 = cpu_to_le32(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_TERM;
-
- req->tail = dtd;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* queue (submits) an I/O requests to an endpoint */
-static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
- gfp_t gfp_flags)
-{
- struct langwell_request *req;
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- unsigned long flags;
- int is_iso = 0, zlflag = 0;
-
- /* always require a cpu-view buffer */
- req = container_of(_req, struct langwell_request, req);
- ep = container_of(_ep, struct langwell_ep, ep);
-
- if (!_req || !_req->complete || !_req->buf
- || !list_empty(&req->queue)) {
- return -EINVAL;
- }
-
- if (unlikely(!_ep || !ep->desc))
- return -EINVAL;
-
- dev = ep->dev;
- req->ep = ep;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (usb_endpoint_xfer_isoc(ep->desc)) {
- if (req->req.length > ep->ep.maxpacket)
- return -EMSGSIZE;
- is_iso = 1;
- }
-
- if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
- return -ESHUTDOWN;
-
- /* set up dma mapping in case the caller didn't */
- if (_req->dma == DMA_ADDR_INVALID) {
- /* WORKAROUND: WARN_ON(size == 0) */
- if (_req->length == 0) {
- dev_vdbg(&dev->pdev->dev, "req->length: 0->1\n");
- zlflag = 1;
- _req->length++;
- }
-
- _req->dma = dma_map_single(&dev->pdev->dev,
- _req->buf, _req->length,
- is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (zlflag && (_req->length == 1)) {
- dev_vdbg(&dev->pdev->dev, "req->length: 1->0\n");
- zlflag = 0;
- _req->length = 0;
- }
-
- req->mapped = 1;
- dev_vdbg(&dev->pdev->dev, "req->mapped = 1\n");
- } else {
- dma_sync_single_for_device(&dev->pdev->dev,
- _req->dma, _req->length,
- is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = 0;
- dev_vdbg(&dev->pdev->dev, "req->mapped = 0\n");
- }
-
- dev_dbg(&dev->pdev->dev,
- "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
- _ep->name,
- _req, _req->length, _req->buf, (int)_req->dma);
-
- _req->status = -EINPROGRESS;
- _req->actual = 0;
- req->dtd_count = 0;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* build and put dTDs to endpoint queue */
- if (!req_to_dtd(req)) {
- queue_dtd(ep, req);
- } else {
- spin_unlock_irqrestore(&dev->lock, flags);
- return -ENOMEM;
- }
-
- /* update ep0 state */
- if (ep->ep_num == 0)
- dev->ep0_state = DATA_STATE_XMIT;
-
- if (likely(req != NULL)) {
- list_add_tail(&req->queue, &ep->queue);
- dev_vdbg(&dev->pdev->dev, "list_add_tail()\n");
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* dequeue (cancels, unlinks) an I/O request from an endpoint */
-static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- struct langwell_request *req;
- unsigned long flags;
- int stopped, ep_num, retval = 0;
- u32 endptctrl;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !ep->desc || !_req)
- return -EINVAL;
-
- if (!dev->driver)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&dev->lock, flags);
- stopped = ep->stopped;
-
- /* quiesce dma while we patch the queue */
- ep->stopped = 1;
- ep_num = ep->ep_num;
-
- /* disable endpoint control register */
- endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
- if (is_in(ep))
- endptctrl &= ~EPCTRL_TXE;
- else
- endptctrl &= ~EPCTRL_RXE;
- writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
-
- /* 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) {
- retval = -EINVAL;
- goto done;
- }
-
- /* queue head may be partially complete. */
- if (ep->queue.next == &req->queue) {
- dev_dbg(&dev->pdev->dev, "unlink (%s) dma\n", _ep->name);
- _req->status = -ECONNRESET;
- langwell_ep_fifo_flush(&ep->ep);
-
- /* not the last request in endpoint queue */
- if (likely(ep->queue.next == &req->queue)) {
- struct langwell_dqh *dqh;
- struct langwell_request *next_req;
-
- dqh = ep->dqh;
- next_req = list_entry(req->queue.next,
- struct langwell_request, queue);
-
- /* point the dQH to the first dTD of next request */
- writel((u32) next_req->head, &dqh->dqh_current);
- }
- } else {
- struct langwell_request *prev_req;
-
- prev_req = list_entry(req->queue.prev,
- struct langwell_request, queue);
- writel(readl(&req->tail->dtd_next),
- &prev_req->tail->dtd_next);
- }
-
- done(ep, req, -ECONNRESET);
-
-done:
- /* enable endpoint again */
- endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
- if (is_in(ep))
- endptctrl |= EPCTRL_TXE;
- else
- endptctrl |= EPCTRL_RXE;
- writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
-
- ep->stopped = stopped;
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return retval;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* endpoint set/clear halt */
-static void ep_set_halt(struct langwell_ep *ep, int value)
-{
- u32 endptctrl = 0;
- int ep_num;
- struct langwell_udc *dev = ep->dev;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- ep_num = ep->ep_num;
- endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
-
- /* value: 1 - set halt, 0 - clear halt */
- if (value) {
- /* set the stall bit */
- if (is_in(ep))
- endptctrl |= EPCTRL_TXS;
- else
- endptctrl |= EPCTRL_RXS;
- } else {
- /* clear the stall bit and reset data toggle */
- if (is_in(ep)) {
- endptctrl &= ~EPCTRL_TXS;
- endptctrl |= EPCTRL_TXR;
- } else {
- endptctrl &= ~EPCTRL_RXS;
- endptctrl |= EPCTRL_RXR;
- }
- }
-
- writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* set the endpoint halt feature */
-static int langwell_ep_set_halt(struct usb_ep *_ep, int value)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- unsigned long flags;
- int retval = 0;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !ep->desc)
- return -EINVAL;
-
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- if (usb_endpoint_xfer_isoc(ep->desc))
- return -EOPNOTSUPP;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /*
- * attempt to halt IN ep will fail if any transfer requests
- * are still queue
- */
- if (!list_empty(&ep->queue) && is_in(ep) && value) {
- /* IN endpoint FIFO holds bytes */
- dev_dbg(&dev->pdev->dev, "%s FIFO holds bytes\n", _ep->name);
- retval = -EAGAIN;
- goto done;
- }
-
- /* endpoint set/clear halt */
- if (ep->ep_num) {
- ep_set_halt(ep, value);
- } else { /* endpoint 0 */
- dev->ep0_state = WAIT_FOR_SETUP;
- dev->ep0_dir = USB_DIR_OUT;
- }
-done:
- spin_unlock_irqrestore(&dev->lock, flags);
- dev_dbg(&dev->pdev->dev, "%s %s halt\n",
- _ep->name, value ? "set" : "clear");
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return retval;
-}
-
-
-/* set the halt feature and ignores clear requests */
-static int langwell_ep_set_wedge(struct usb_ep *_ep)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !ep->desc)
- return -EINVAL;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return usb_ep_set_halt(_ep);
-}
-
-
-/* flush contents of a fifo */
-static void langwell_ep_fifo_flush(struct usb_ep *_ep)
-{
- struct langwell_ep *ep;
- struct langwell_udc *dev;
- u32 flush_bit;
- unsigned long timeout;
-
- ep = container_of(_ep, struct langwell_ep, ep);
- dev = ep->dev;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (!_ep || !ep->desc) {
- dev_vdbg(&dev->pdev->dev, "ep or ep->desc is NULL\n");
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return;
- }
-
- dev_vdbg(&dev->pdev->dev, "%s-%s fifo flush\n",
- _ep->name, DIR_STRING(ep));
-
- /* flush endpoint buffer */
- if (ep->ep_num == 0)
- flush_bit = (1 << 16) | 1;
- else if (is_in(ep))
- flush_bit = 1 << (ep->ep_num + 16); /* TX */
- else
- flush_bit = 1 << ep->ep_num; /* RX */
-
- /* wait until flush complete */
- timeout = jiffies + FLUSH_TIMEOUT;
- do {
- writel(flush_bit, &dev->op_regs->endptflush);
- while (readl(&dev->op_regs->endptflush)) {
- if (time_after(jiffies, timeout)) {
- dev_err(&dev->pdev->dev, "ep flush timeout\n");
- goto done;
- }
- cpu_relax();
- }
- } while (readl(&dev->op_regs->endptstat) & flush_bit);
-done:
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* endpoints operations structure */
-static const struct usb_ep_ops langwell_ep_ops = {
-
- /* configure endpoint, making it usable */
- .enable = langwell_ep_enable,
-
- /* endpoint is no longer usable */
- .disable = langwell_ep_disable,
-
- /* allocate a request object to use with this endpoint */
- .alloc_request = langwell_alloc_request,
-
- /* free a request object */
- .free_request = langwell_free_request,
-
- /* queue (submits) an I/O requests to an endpoint */
- .queue = langwell_ep_queue,
-
- /* dequeue (cancels, unlinks) an I/O request from an endpoint */
- .dequeue = langwell_ep_dequeue,
-
- /* set the endpoint halt feature */
- .set_halt = langwell_ep_set_halt,
-
- /* set the halt feature and ignores clear requests */
- .set_wedge = langwell_ep_set_wedge,
-
- /* flush contents of a fifo */
- .fifo_flush = langwell_ep_fifo_flush,
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-/* device controller usb_gadget_ops structure */
-
-/* returns the current frame number */
-static int langwell_get_frame(struct usb_gadget *_gadget)
-{
- struct langwell_udc *dev;
- u16 retval;
-
- if (!_gadget)
- return -ENODEV;
-
- dev = container_of(_gadget, struct langwell_udc, gadget);
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return retval;
-}
-
-
-/* enter or exit PHY low power state */
-static void langwell_phy_low_power(struct langwell_udc *dev, bool flag)
-{
- u32 devlc;
- u8 devlc_byte2;
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- devlc = readl(&dev->op_regs->devlc);
- dev_vdbg(&dev->pdev->dev, "devlc = 0x%08x\n", devlc);
-
- if (flag)
- devlc |= LPM_PHCD;
- else
- devlc &= ~LPM_PHCD;
-
- /* FIXME: workaround for Langwell A1/A2/A3 sighting */
- devlc_byte2 = (devlc >> 16) & 0xff;
- writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2);
-
- devlc = readl(&dev->op_regs->devlc);
- dev_vdbg(&dev->pdev->dev,
- "%s PHY low power suspend, devlc = 0x%08x\n",
- flag ? "enter" : "exit", devlc);
-}
-
-
-/* tries to wake up the host connected to this gadget */
-static int langwell_wakeup(struct usb_gadget *_gadget)
-{
- struct langwell_udc *dev;
- u32 portsc1;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
-
- dev = container_of(_gadget, struct langwell_udc, gadget);
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* remote wakeup feature not enabled by host */
- if (!dev->remote_wakeup) {
- dev_info(&dev->pdev->dev, "remote wakeup is disabled\n");
- return -ENOTSUPP;
- }
-
- spin_lock_irqsave(&dev->lock, flags);
-
- portsc1 = readl(&dev->op_regs->portsc1);
- if (!(portsc1 & PORTS_SUSP)) {
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
-
- /* LPM L1 to L0 or legacy remote wakeup */
- if (dev->lpm && dev->lpm_state == LPM_L1)
- dev_info(&dev->pdev->dev, "LPM L1 to L0 remote wakeup\n");
- else
- dev_info(&dev->pdev->dev, "device remote wakeup\n");
-
- /* exit PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 0);
-
- /* force port resume */
- portsc1 |= PORTS_FPR;
- writel(portsc1, &dev->op_regs->portsc1);
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* notify controller that VBUS is powered or not */
-static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active)
-{
- struct langwell_udc *dev;
- unsigned long flags;
- u32 usbcmd;
-
- if (!_gadget)
- return -ENODEV;
-
- dev = container_of(_gadget, struct langwell_udc, gadget);
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- spin_lock_irqsave(&dev->lock, flags);
- dev_vdbg(&dev->pdev->dev, "VBUS status: %s\n",
- is_active ? "on" : "off");
-
- dev->vbus_active = (is_active != 0);
- if (dev->driver && dev->softconnected && dev->vbus_active) {
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd |= CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
- } else {
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd &= ~CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* constrain controller's VBUS power usage */
-static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
-{
- struct langwell_udc *dev;
-
- if (!_gadget)
- return -ENODEV;
-
- dev = container_of(_gadget, struct langwell_udc, gadget);
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (dev->transceiver) {
- dev_vdbg(&dev->pdev->dev, "otg_set_power\n");
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return otg_set_power(dev->transceiver, mA);
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return -ENOTSUPP;
-}
-
-
-/* D+ pullup, software-controlled connect/disconnect to USB host */
-static int langwell_pullup(struct usb_gadget *_gadget, int is_on)
-{
- struct langwell_udc *dev;
- u32 usbcmd;
- unsigned long flags;
-
- if (!_gadget)
- return -ENODEV;
-
- dev = container_of(_gadget, struct langwell_udc, gadget);
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- spin_lock_irqsave(&dev->lock, flags);
- dev->softconnected = (is_on != 0);
-
- if (dev->driver && dev->softconnected && dev->vbus_active) {
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd |= CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
- } else {
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd &= ~CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-static int langwell_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-
-static int langwell_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-
-/* device controller usb_gadget_ops structure */
-static const struct usb_gadget_ops langwell_ops = {
-
- /* returns the current frame number */
- .get_frame = langwell_get_frame,
-
- /* tries to wake up the host connected to this gadget */
- .wakeup = langwell_wakeup,
-
- /* set the device selfpowered feature, always selfpowered */
- /* .set_selfpowered = langwell_set_selfpowered, */
-
- /* notify controller that VBUS is powered or not */
- .vbus_session = langwell_vbus_session,
-
- /* constrain controller's VBUS power usage */
- .vbus_draw = langwell_vbus_draw,
-
- /* D+ pullup, software-controlled connect/disconnect to USB host */
- .pullup = langwell_pullup,
-
- .udc_start = langwell_start,
- .udc_stop = langwell_stop,
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-/* device controller operations */
-
-/* reset device controller */
-static int langwell_udc_reset(struct langwell_udc *dev)
-{
- u32 usbcmd, usbmode, devlc, endpointlistaddr;
- u8 devlc_byte0, devlc_byte2;
- unsigned long timeout;
-
- if (!dev)
- return -EINVAL;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* set controller to stop state */
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd &= ~CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
-
- /* reset device controller */
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd |= CMD_RST;
- writel(usbcmd, &dev->op_regs->usbcmd);
-
- /* wait for reset to complete */
- timeout = jiffies + RESET_TIMEOUT;
- while (readl(&dev->op_regs->usbcmd) & CMD_RST) {
- if (time_after(jiffies, timeout)) {
- dev_err(&dev->pdev->dev, "device reset timeout\n");
- return -ETIMEDOUT;
- }
- cpu_relax();
- }
-
- /* set controller to device mode */
- usbmode = readl(&dev->op_regs->usbmode);
- usbmode |= MODE_DEVICE;
-
- /* turn setup lockout off, require setup tripwire in usbcmd */
- usbmode |= MODE_SLOM;
-
- writel(usbmode, &dev->op_regs->usbmode);
- usbmode = readl(&dev->op_regs->usbmode);
- dev_vdbg(&dev->pdev->dev, "usbmode=0x%08x\n", usbmode);
-
- /* Write-Clear setup status */
- writel(0, &dev->op_regs->usbsts);
-
- /* if support USB LPM, ACK all LPM token */
- if (dev->lpm) {
- devlc = readl(&dev->op_regs->devlc);
- dev_vdbg(&dev->pdev->dev, "devlc = 0x%08x\n", devlc);
- /* FIXME: workaround for Langwell A1/A2/A3 sighting */
- devlc &= ~LPM_STL; /* don't STALL LPM token */
- devlc &= ~LPM_NYT_ACK; /* ACK LPM token */
- devlc_byte0 = devlc & 0xff;
- devlc_byte2 = (devlc >> 16) & 0xff;
- writeb(devlc_byte0, (u8 *)&dev->op_regs->devlc);
- writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2);
- devlc = readl(&dev->op_regs->devlc);
- dev_vdbg(&dev->pdev->dev,
- "ACK LPM token, devlc = 0x%08x\n", devlc);
- }
-
- /* fill endpointlistaddr register */
- endpointlistaddr = dev->ep_dqh_dma;
- endpointlistaddr &= ENDPOINTLISTADDR_MASK;
- writel(endpointlistaddr, &dev->op_regs->endpointlistaddr);
-
- dev_vdbg(&dev->pdev->dev,
- "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n",
- dev->ep_dqh, endpointlistaddr,
- readl(&dev->op_regs->endpointlistaddr));
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* reinitialize device controller endpoints */
-static int eps_reinit(struct langwell_udc *dev)
-{
- struct langwell_ep *ep;
- char name[14];
- int i;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* initialize ep0 */
- ep = &dev->ep[0];
- ep->dev = dev;
- strncpy(ep->name, "ep0", sizeof(ep->name));
- ep->ep.name = ep->name;
- ep->ep.ops = &langwell_ep_ops;
- ep->stopped = 0;
- ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
- ep->ep_num = 0;
- ep->desc = &langwell_ep0_desc;
- INIT_LIST_HEAD(&ep->queue);
-
- ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
-
- /* initialize other endpoints */
- for (i = 2; i < dev->ep_max; i++) {
- ep = &dev->ep[i];
- if (i % 2)
- snprintf(name, sizeof(name), "ep%din", i / 2);
- else
- snprintf(name, sizeof(name), "ep%dout", i / 2);
- ep->dev = dev;
- strncpy(ep->name, name, sizeof(ep->name));
- ep->ep.name = ep->name;
-
- ep->ep.ops = &langwell_ep_ops;
- ep->stopped = 0;
- ep->ep.maxpacket = (unsigned short) ~0;
- ep->ep_num = i / 2;
-
- INIT_LIST_HEAD(&ep->queue);
- list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* enable interrupt and set controller to run state */
-static void langwell_udc_start(struct langwell_udc *dev)
-{
- u32 usbintr, usbcmd;
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* enable interrupts */
- usbintr = INTR_ULPIE /* ULPI */
- | INTR_SLE /* suspend */
- /* | INTR_SRE SOF received */
- | INTR_URE /* USB reset */
- | INTR_AAE /* async advance */
- | INTR_SEE /* system error */
- | INTR_FRE /* frame list rollover */
- | INTR_PCE /* port change detect */
- | INTR_UEE /* USB error interrupt */
- | INTR_UE; /* USB interrupt */
- writel(usbintr, &dev->op_regs->usbintr);
-
- /* clear stopped bit */
- dev->stopped = 0;
-
- /* set controller to run */
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd |= CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* disable interrupt and set controller to stop state */
-static void langwell_udc_stop(struct langwell_udc *dev)
-{
- u32 usbcmd;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* disable all interrupts */
- writel(0, &dev->op_regs->usbintr);
-
- /* set stopped bit */
- dev->stopped = 1;
-
- /* set controller to stop state */
- usbcmd = readl(&dev->op_regs->usbcmd);
- usbcmd &= ~CMD_RUNSTOP;
- writel(usbcmd, &dev->op_regs->usbcmd);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* stop all USB activities */
-static void stop_activity(struct langwell_udc *dev)
-{
- struct langwell_ep *ep;
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- nuke(&dev->ep[0], -ESHUTDOWN);
-
- list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
- nuke(ep, -ESHUTDOWN);
- }
-
- /* report disconnect; the driver is already quiesced */
- if (dev->driver) {
- spin_unlock(&dev->lock);
- dev->driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* device "function" sysfs attribute file */
-static ssize_t show_function(struct device *_dev,
- struct device_attribute *attr, char *buf)
-{
- struct langwell_udc *dev = dev_get_drvdata(_dev);
-
- if (!dev->driver || !dev->driver->function
- || strlen(dev->driver->function) > PAGE_SIZE)
- return 0;
-
- return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
-
-
-static inline enum usb_device_speed lpm_device_speed(u32 reg)
-{
- switch (LPM_PSPD(reg)) {
- case LPM_SPEED_HIGH:
- return USB_SPEED_HIGH;
- case LPM_SPEED_FULL:
- return USB_SPEED_FULL;
- case LPM_SPEED_LOW:
- return USB_SPEED_LOW;
- default:
- return USB_SPEED_UNKNOWN;
- }
-}
-
-/* device "langwell_udc" sysfs attribute file */
-static ssize_t show_langwell_udc(struct device *_dev,
- struct device_attribute *attr, char *buf)
-{
- struct langwell_udc *dev = dev_get_drvdata(_dev);
- struct langwell_request *req;
- struct langwell_ep *ep = NULL;
- char *next;
- unsigned size;
- unsigned t;
- unsigned i;
- unsigned long flags;
- u32 tmp_reg;
-
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave(&dev->lock, flags);
-
- /* driver basic information */
- t = scnprintf(next, size,
- DRIVER_DESC "\n"
- "%s version: %s\n"
- "Gadget driver: %s\n\n",
- driver_name, DRIVER_VERSION,
- dev->driver ? dev->driver->driver.name : "(none)");
- size -= t;
- next += t;
-
- /* device registers */
- tmp_reg = readl(&dev->op_regs->usbcmd);
- t = scnprintf(next, size,
- "USBCMD reg:\n"
- "SetupTW: %d\n"
- "Run/Stop: %s\n\n",
- (tmp_reg & CMD_SUTW) ? 1 : 0,
- (tmp_reg & CMD_RUNSTOP) ? "Run" : "Stop");
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->usbsts);
- t = scnprintf(next, size,
- "USB Status Reg:\n"
- "Device Suspend: %d\n"
- "Reset Received: %d\n"
- "System Error: %s\n"
- "USB Error Interrupt: %s\n\n",
- (tmp_reg & STS_SLI) ? 1 : 0,
- (tmp_reg & STS_URI) ? 1 : 0,
- (tmp_reg & STS_SEI) ? "Error" : "No error",
- (tmp_reg & STS_UEI) ? "Error detected" : "No error");
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->usbintr);
- t = scnprintf(next, size,
- "USB Intrrupt Enable Reg:\n"
- "Sleep Enable: %d\n"
- "SOF Received Enable: %d\n"
- "Reset Enable: %d\n"
- "System Error Enable: %d\n"
- "Port Change Dectected Enable: %d\n"
- "USB Error Intr Enable: %d\n"
- "USB Intr Enable: %d\n\n",
- (tmp_reg & INTR_SLE) ? 1 : 0,
- (tmp_reg & INTR_SRE) ? 1 : 0,
- (tmp_reg & INTR_URE) ? 1 : 0,
- (tmp_reg & INTR_SEE) ? 1 : 0,
- (tmp_reg & INTR_PCE) ? 1 : 0,
- (tmp_reg & INTR_UEE) ? 1 : 0,
- (tmp_reg & INTR_UE) ? 1 : 0);
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->frindex);
- t = scnprintf(next, size,
- "USB Frame Index Reg:\n"
- "Frame Number is 0x%08x\n\n",
- (tmp_reg & FRINDEX_MASK));
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->deviceaddr);
- t = scnprintf(next, size,
- "USB Device Address Reg:\n"
- "Device Addr is 0x%x\n\n",
- USBADR(tmp_reg));
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->endpointlistaddr);
- t = scnprintf(next, size,
- "USB Endpoint List Address Reg:\n"
- "Endpoint List Pointer is 0x%x\n\n",
- EPBASE(tmp_reg));
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->portsc1);
- t = scnprintf(next, size,
- "USB Port Status & Control Reg:\n"
- "Port Reset: %s\n"
- "Port Suspend Mode: %s\n"
- "Over-current Change: %s\n"
- "Port Enable/Disable Change: %s\n"
- "Port Enabled/Disabled: %s\n"
- "Current Connect Status: %s\n"
- "LPM Suspend Status: %s\n\n",
- (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset",
- (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend",
- (tmp_reg & PORTS_OCC) ? "Detected" : "No",
- (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed",
- (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct",
- (tmp_reg & PORTS_CCS) ? "Attached" : "Not Attached",
- (tmp_reg & PORTS_SLP) ? "LPM L1" : "LPM L0");
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->devlc);
- t = scnprintf(next, size,
- "Device LPM Control Reg:\n"
- "Parallel Transceiver : %d\n"
- "Serial Transceiver : %d\n"
- "Port Speed: %s\n"
- "Port Force Full Speed Connenct: %s\n"
- "PHY Low Power Suspend Clock: %s\n"
- "BmAttributes: %d\n\n",
- LPM_PTS(tmp_reg),
- (tmp_reg & LPM_STS) ? 1 : 0,
- usb_speed_string(lpm_device_speed(tmp_reg)),
- (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force",
- (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled",
- LPM_BA(tmp_reg));
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->usbmode);
- t = scnprintf(next, size,
- "USB Mode Reg:\n"
- "Controller Mode is : %s\n\n", ({
- char *s;
- switch (MODE_CM(tmp_reg)) {
- case MODE_IDLE:
- s = "Idle"; break;
- case MODE_DEVICE:
- s = "Device Controller"; break;
- case MODE_HOST:
- s = "Host Controller"; break;
- default:
- s = "None"; break;
- }
- s;
- }));
- size -= t;
- next += t;
-
- tmp_reg = readl(&dev->op_regs->endptsetupstat);
- t = scnprintf(next, size,
- "Endpoint Setup Status Reg:\n"
- "SETUP on ep 0x%04x\n\n",
- tmp_reg & SETUPSTAT_MASK);
- size -= t;
- next += t;
-
- for (i = 0; i < dev->ep_max / 2; i++) {
- tmp_reg = readl(&dev->op_regs->endptctrl[i]);
- t = scnprintf(next, size, "EP Ctrl Reg [%d]: 0x%08x\n",
- i, tmp_reg);
- size -= t;
- next += t;
- }
- tmp_reg = readl(&dev->op_regs->endptprime);
- t = scnprintf(next, size, "EP Prime Reg: 0x%08x\n\n", tmp_reg);
- size -= t;
- next += t;
-
- /* langwell_udc, langwell_ep, langwell_request structure information */
- ep = &dev->ep[0];
- t = scnprintf(next, size, "%s MaxPacketSize: 0x%x, ep_num: %d\n",
- ep->ep.name, ep->ep.maxpacket, ep->ep_num);
- size -= t;
- next += t;
-
- if (list_empty(&ep->queue)) {
- t = scnprintf(next, size, "its req queue is empty\n\n");
- size -= t;
- next += t;
- } else {
- list_for_each_entry(req, &ep->queue, queue) {
- t = scnprintf(next, size,
- "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, &dev->gadget.ep_list, ep.ep_list) {
- if (ep->desc) {
- t = scnprintf(next, size,
- "\n%s MaxPacketSize: 0x%x, "
- "ep_num: %d\n",
- ep->ep.name, ep->ep.maxpacket,
- ep->ep_num);
- size -= t;
- next += t;
-
- if (list_empty(&ep->queue)) {
- t = scnprintf(next, size,
- "its req queue is empty\n\n");
- size -= t;
- next += t;
- } else {
- list_for_each_entry(req, &ep->queue, queue) {
- t = scnprintf(next, size,
- "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;
- }
- }
- }
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL);
-
-
-/* device "remote_wakeup" sysfs attribute file */
-static ssize_t store_remote_wakeup(struct device *_dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct langwell_udc *dev = dev_get_drvdata(_dev);
- unsigned long flags;
- ssize_t rc = count;
-
- if (count > 2)
- return -EINVAL;
-
- if (count > 0 && buf[count-1] == '\n')
- ((char *) buf)[count-1] = 0;
-
- if (buf[0] != '1')
- return -EINVAL;
-
- /* force remote wakeup enabled in case gadget driver doesn't support */
- spin_lock_irqsave(&dev->lock, flags);
- dev->remote_wakeup = 1;
- dev->dev_status |= (1 << USB_DEVICE_REMOTE_WAKEUP);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- langwell_wakeup(&dev->gadget);
-
- return rc;
-}
-static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup);
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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 langwell_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct langwell_udc *dev = gadget_to_langwell(g);
- unsigned long flags;
- int retval;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* hook up the driver ... */
- driver->driver.bus = NULL;
- dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
- if (retval)
- goto err;
-
- dev->usb_state = USB_STATE_ATTACHED;
- dev->ep0_state = WAIT_FOR_SETUP;
- dev->ep0_dir = USB_DIR_OUT;
-
- /* enable interrupt and set controller to run state */
- if (dev->got_irq)
- langwell_udc_start(dev);
-
- dev_vdbg(&dev->pdev->dev,
- "After langwell_udc_start(), print all registers:\n");
- print_all_registers(dev);
-
- dev_info(&dev->pdev->dev, "register driver: %s\n",
- driver->driver.name);
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-
- return 0;
-
-err:
- dev->gadget.dev.driver = NULL;
- dev->driver = NULL;
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-
- return retval;
-}
-
-/* unregister gadget driver */
-static int langwell_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct langwell_udc *dev = gadget_to_langwell(g);
- unsigned long flags;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* exit PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 0);
-
- /* unbind OTG transceiver */
- if (dev->transceiver)
- (void)otg_set_peripheral(dev->transceiver, 0);
-
- /* disable interrupt and set controller to stop state */
- langwell_udc_stop(dev);
-
- dev->usb_state = USB_STATE_ATTACHED;
- dev->ep0_state = WAIT_FOR_SETUP;
- dev->ep0_dir = USB_DIR_OUT;
-
- spin_lock_irqsave(&dev->lock, flags);
-
- /* stop all usb activities */
- dev->gadget.speed = USB_SPEED_UNKNOWN;
- dev->gadget.dev.driver = NULL;
- dev->driver = NULL;
- stop_activity(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- device_remove_file(&dev->pdev->dev, &dev_attr_function);
-
- dev_info(&dev->pdev->dev, "unregistered driver '%s'\n",
- driver->driver.name);
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * setup tripwire is used as a semaphore to ensure that the setup data
- * payload is extracted from a dQH without being corrupted
- */
-static void setup_tripwire(struct langwell_udc *dev)
-{
- u32 usbcmd,
- endptsetupstat;
- unsigned long timeout;
- struct langwell_dqh *dqh;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* ep0 OUT dQH */
- dqh = &dev->ep_dqh[EP_DIR_OUT];
-
- /* Write-Clear endptsetupstat */
- endptsetupstat = readl(&dev->op_regs->endptsetupstat);
- writel(endptsetupstat, &dev->op_regs->endptsetupstat);
-
- /* wait until endptsetupstat is cleared */
- timeout = jiffies + SETUPSTAT_TIMEOUT;
- while (readl(&dev->op_regs->endptsetupstat)) {
- if (time_after(jiffies, timeout)) {
- dev_err(&dev->pdev->dev, "setup_tripwire timeout\n");
- break;
- }
- cpu_relax();
- }
-
- /* while a hazard exists when setup packet arrives */
- do {
- /* set setup tripwire bit */
- usbcmd = readl(&dev->op_regs->usbcmd);
- writel(usbcmd | CMD_SUTW, &dev->op_regs->usbcmd);
-
- /* copy the setup packet to local buffer */
- memcpy(&dev->local_setup_buff, &dqh->dqh_setup, 8);
- } while (!(readl(&dev->op_regs->usbcmd) & CMD_SUTW));
-
- /* Write-Clear setup tripwire bit */
- usbcmd = readl(&dev->op_regs->usbcmd);
- writel(usbcmd & ~CMD_SUTW, &dev->op_regs->usbcmd);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* protocol ep0 stall, will automatically be cleared on new transaction */
-static void ep0_stall(struct langwell_udc *dev)
-{
- u32 endptctrl;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* set TX and RX to stall */
- endptctrl = readl(&dev->op_regs->endptctrl[0]);
- endptctrl |= EPCTRL_TXS | EPCTRL_RXS;
- writel(endptctrl, &dev->op_regs->endptctrl[0]);
-
- /* update ep0 state */
- dev->ep0_state = WAIT_FOR_SETUP;
- dev->ep0_dir = USB_DIR_OUT;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* PRIME a status phase for ep0 */
-static int prime_status_phase(struct langwell_udc *dev, int dir)
-{
- struct langwell_request *req;
- struct langwell_ep *ep;
- int status = 0;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (dir == EP_DIR_IN)
- dev->ep0_dir = USB_DIR_IN;
- else
- dev->ep0_dir = USB_DIR_OUT;
-
- ep = &dev->ep[0];
- dev->ep0_state = WAIT_FOR_OUT_STATUS;
-
- req = dev->status_req;
-
- req->ep = ep;
- req->req.length = 0;
- req->req.status = -EINPROGRESS;
- req->req.actual = 0;
- req->req.complete = NULL;
- req->dtd_count = 0;
-
- if (!req_to_dtd(req))
- status = queue_dtd(ep, req);
- else
- return -ENOMEM;
-
- if (status)
- dev_err(&dev->pdev->dev, "can't queue ep0 status request\n");
-
- list_add_tail(&req->queue, &ep->queue);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return status;
-}
-
-
-/* SET_ADDRESS request routine */
-static void set_address(struct langwell_udc *dev, u16 value,
- u16 index, u16 length)
-{
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* save the new address to device struct */
- dev->dev_addr = (u8) value;
- dev_vdbg(&dev->pdev->dev, "dev->dev_addr = %d\n", dev->dev_addr);
-
- /* update usb state */
- dev->usb_state = USB_STATE_ADDRESS;
-
- /* STATUS phase */
- if (prime_status_phase(dev, EP_DIR_IN))
- ep0_stall(dev);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* return endpoint by windex */
-static struct langwell_ep *get_ep_by_windex(struct langwell_udc *dev,
- u16 wIndex)
-{
- struct langwell_ep *ep;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- 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 & USB_ENDPOINT_NUMBER_MASK)
- == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK))
- return ep;
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return NULL;
-}
-
-
-/* return whether endpoint is stalled, 0: not stalled; 1: stalled */
-static int ep_is_stall(struct langwell_ep *ep)
-{
- struct langwell_udc *dev = ep->dev;
- u32 endptctrl;
- int retval;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- endptctrl = readl(&dev->op_regs->endptctrl[ep->ep_num]);
- if (is_in(ep))
- retval = endptctrl & EPCTRL_TXS ? 1 : 0;
- else
- retval = endptctrl & EPCTRL_RXS ? 1 : 0;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return retval;
-}
-
-
-/* GET_STATUS request routine */
-static void get_status(struct langwell_udc *dev, u8 request_type, u16 value,
- u16 index, u16 length)
-{
- struct langwell_request *req;
- struct langwell_ep *ep;
- u16 status_data = 0; /* 16 bits cpu view status data */
- int status = 0;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- ep = &dev->ep[0];
-
- if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
- /* get device status */
- status_data = dev->dev_status;
- } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
- /* get interface status */
- status_data = 0;
- } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
- /* get endpoint status */
- struct langwell_ep *epn;
- epn = get_ep_by_windex(dev, index);
- /* stall if endpoint doesn't exist */
- if (!epn)
- goto stall;
-
- status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT;
- }
-
- dev_dbg(&dev->pdev->dev, "get status data: 0x%04x\n", status_data);
-
- dev->ep0_dir = USB_DIR_IN;
-
- /* borrow the per device status_req */
- req = dev->status_req;
-
- /* fill in the reqest structure */
- *((u16 *) req->req.buf) = cpu_to_le16(status_data);
- req->ep = ep;
- req->req.length = 2;
- req->req.status = -EINPROGRESS;
- req->req.actual = 0;
- req->req.complete = NULL;
- req->dtd_count = 0;
-
- /* prime the data phase */
- if (!req_to_dtd(req))
- status = queue_dtd(ep, req);
- else /* no mem */
- goto stall;
-
- if (status) {
- dev_err(&dev->pdev->dev,
- "response error on GET_STATUS request\n");
- goto stall;
- }
-
- list_add_tail(&req->queue, &ep->queue);
- dev->ep0_state = DATA_STATE_XMIT;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return;
-stall:
- ep0_stall(dev);
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* setup packet interrupt handler */
-static void handle_setup_packet(struct langwell_udc *dev,
- struct usb_ctrlrequest *setup)
-{
- u16 wValue = le16_to_cpu(setup->wValue);
- u16 wIndex = le16_to_cpu(setup->wIndex);
- u16 wLength = le16_to_cpu(setup->wLength);
- u32 portsc1;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* ep0 fifo flush */
- nuke(&dev->ep[0], -ESHUTDOWN);
-
- dev_dbg(&dev->pdev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
- setup->bRequestType, setup->bRequest,
- wValue, wIndex, wLength);
-
- /* RNDIS gadget delegate */
- if ((setup->bRequestType == 0x21) && (setup->bRequest == 0x00)) {
- /* USB_CDC_SEND_ENCAPSULATED_COMMAND */
- goto delegate;
- }
-
- /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
- if ((setup->bRequestType == 0xa1) && (setup->bRequest == 0x01)) {
- /* USB_CDC_GET_ENCAPSULATED_RESPONSE */
- goto delegate;
- }
-
- /* We process some stardard setup requests here */
- switch (setup->bRequest) {
- case USB_REQ_GET_STATUS:
- dev_dbg(&dev->pdev->dev, "SETUP: USB_REQ_GET_STATUS\n");
- /* get status, DATA and STATUS phase */
- if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
- != (USB_DIR_IN | USB_TYPE_STANDARD))
- break;
- get_status(dev, setup->bRequestType, wValue, wIndex, wLength);
- goto end;
-
- case USB_REQ_SET_ADDRESS:
- dev_dbg(&dev->pdev->dev, "SETUP: USB_REQ_SET_ADDRESS\n");
- /* STATUS phase */
- if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
- | USB_RECIP_DEVICE))
- break;
- set_address(dev, wValue, wIndex, wLength);
- goto end;
-
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- /* STATUS phase */
- {
- int rc = -EOPNOTSUPP;
- if (setup->bRequest == USB_REQ_SET_FEATURE)
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_SET_FEATURE\n");
- else if (setup->bRequest == USB_REQ_CLEAR_FEATURE)
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_CLEAR_FEATURE\n");
-
- if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
- == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
- struct langwell_ep *epn;
- epn = get_ep_by_windex(dev, wIndex);
- /* stall if endpoint doesn't exist */
- if (!epn) {
- ep0_stall(dev);
- goto end;
- }
-
- if (wValue != 0 || wLength != 0
- || epn->ep_num > dev->ep_max)
- break;
-
- spin_unlock(&dev->lock);
- rc = langwell_ep_set_halt(&epn->ep,
- (setup->bRequest == USB_REQ_SET_FEATURE)
- ? 1 : 0);
- spin_lock(&dev->lock);
-
- } else if ((setup->bRequestType & (USB_RECIP_MASK
- | USB_TYPE_MASK)) == (USB_RECIP_DEVICE
- | USB_TYPE_STANDARD)) {
- rc = 0;
- switch (wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
- if (setup->bRequest == USB_REQ_SET_FEATURE) {
- dev->remote_wakeup = 1;
- dev->dev_status |= (1 << wValue);
- } else {
- dev->remote_wakeup = 0;
- dev->dev_status &= ~(1 << wValue);
- }
- break;
- case USB_DEVICE_TEST_MODE:
- dev_dbg(&dev->pdev->dev, "SETUP: TEST MODE\n");
- if ((wIndex & 0xff) ||
- (dev->gadget.speed != USB_SPEED_HIGH))
- ep0_stall(dev);
-
- switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
- if (prime_status_phase(dev, EP_DIR_IN))
- ep0_stall(dev);
- portsc1 = readl(&dev->op_regs->portsc1);
- portsc1 |= (wIndex & 0xf00) << 8;
- writel(portsc1, &dev->op_regs->portsc1);
- goto end;
- default:
- rc = -EOPNOTSUPP;
- }
- break;
- default:
- rc = -EOPNOTSUPP;
- break;
- }
-
- if (!gadget_is_otg(&dev->gadget))
- break;
- else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
- dev->gadget.b_hnp_enable = 1;
- else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
- dev->gadget.a_hnp_support = 1;
- else if (setup->bRequest ==
- USB_DEVICE_A_ALT_HNP_SUPPORT)
- dev->gadget.a_alt_hnp_support = 1;
- else
- break;
- } else
- break;
-
- if (rc == 0) {
- if (prime_status_phase(dev, EP_DIR_IN))
- ep0_stall(dev);
- }
- goto end;
- }
-
- case USB_REQ_GET_DESCRIPTOR:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_GET_DESCRIPTOR\n");
- goto delegate;
-
- case USB_REQ_SET_DESCRIPTOR:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_SET_DESCRIPTOR unsupported\n");
- goto delegate;
-
- case USB_REQ_GET_CONFIGURATION:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_GET_CONFIGURATION\n");
- goto delegate;
-
- case USB_REQ_SET_CONFIGURATION:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_SET_CONFIGURATION\n");
- goto delegate;
-
- case USB_REQ_GET_INTERFACE:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_GET_INTERFACE\n");
- goto delegate;
-
- case USB_REQ_SET_INTERFACE:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_SET_INTERFACE\n");
- goto delegate;
-
- case USB_REQ_SYNCH_FRAME:
- dev_dbg(&dev->pdev->dev,
- "SETUP: USB_REQ_SYNCH_FRAME unsupported\n");
- goto delegate;
-
- default:
- /* delegate USB standard requests to the gadget driver */
- goto delegate;
-delegate:
- /* USB requests handled by gadget */
- if (wLength) {
- /* DATA phase from gadget, STATUS phase from udc */
- dev->ep0_dir = (setup->bRequestType & USB_DIR_IN)
- ? USB_DIR_IN : USB_DIR_OUT;
- dev_vdbg(&dev->pdev->dev,
- "dev->ep0_dir = 0x%x, wLength = %d\n",
- dev->ep0_dir, wLength);
- spin_unlock(&dev->lock);
- if (dev->driver->setup(&dev->gadget,
- &dev->local_setup_buff) < 0)
- ep0_stall(dev);
- spin_lock(&dev->lock);
- dev->ep0_state = (setup->bRequestType & USB_DIR_IN)
- ? DATA_STATE_XMIT : DATA_STATE_RECV;
- } else {
- /* no DATA phase, IN STATUS phase from gadget */
- dev->ep0_dir = USB_DIR_IN;
- dev_vdbg(&dev->pdev->dev,
- "dev->ep0_dir = 0x%x, wLength = %d\n",
- dev->ep0_dir, wLength);
- spin_unlock(&dev->lock);
- if (dev->driver->setup(&dev->gadget,
- &dev->local_setup_buff) < 0)
- ep0_stall(dev);
- spin_lock(&dev->lock);
- dev->ep0_state = WAIT_FOR_OUT_STATUS;
- }
- break;
- }
-end:
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* transfer completion, process endpoint request and free the completed dTDs
- * for this request
- */
-static int process_ep_req(struct langwell_udc *dev, int index,
- struct langwell_request *curr_req)
-{
- struct langwell_dtd *curr_dtd;
- struct langwell_dqh *curr_dqh;
- int td_complete, actual, remaining_length;
- int i, dir;
- u8 dtd_status = 0;
- int retval = 0;
-
- curr_dqh = &dev->ep_dqh[index];
- dir = index % 2;
-
- curr_dtd = curr_req->head;
- td_complete = 0;
- actual = curr_req->req.length;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- for (i = 0; i < curr_req->dtd_count; i++) {
-
- /* command execution states by dTD */
- dtd_status = curr_dtd->dtd_status;
-
- barrier();
- remaining_length = le16_to_cpu(curr_dtd->dtd_total);
- actual -= remaining_length;
-
- if (!dtd_status) {
- /* transfers completed successfully */
- if (!remaining_length) {
- td_complete++;
- dev_vdbg(&dev->pdev->dev,
- "dTD transmitted successfully\n");
- } else {
- if (dir) {
- dev_vdbg(&dev->pdev->dev,
- "TX dTD remains data\n");
- retval = -EPROTO;
- break;
-
- } else {
- td_complete++;
- break;
- }
- }
- } else {
- /* transfers completed with errors */
- if (dtd_status & DTD_STS_ACTIVE) {
- dev_dbg(&dev->pdev->dev,
- "dTD status ACTIVE dQH[%d]\n", index);
- retval = 1;
- return retval;
- } else if (dtd_status & DTD_STS_HALTED) {
- dev_err(&dev->pdev->dev,
- "dTD error %08x dQH[%d]\n",
- dtd_status, index);
- /* clear the errors and halt condition */
- curr_dqh->dtd_status = 0;
- retval = -EPIPE;
- break;
- } else if (dtd_status & DTD_STS_DBE) {
- dev_dbg(&dev->pdev->dev,
- "data buffer (overflow) error\n");
- retval = -EPROTO;
- break;
- } else if (dtd_status & DTD_STS_TRE) {
- dev_dbg(&dev->pdev->dev,
- "transaction(ISO) error\n");
- retval = -EILSEQ;
- break;
- } else
- dev_err(&dev->pdev->dev,
- "unknown error (0x%x)!\n",
- dtd_status);
- }
-
- if (i != curr_req->dtd_count - 1)
- curr_dtd = (struct langwell_dtd *)
- curr_dtd->next_dtd_virt;
- }
-
- if (retval)
- return retval;
-
- curr_req->req.actual = actual;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* complete DATA or STATUS phase of ep0 prime status phase if needed */
-static void ep0_req_complete(struct langwell_udc *dev,
- struct langwell_ep *ep0, struct langwell_request *req)
-{
- u32 new_addr;
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (dev->usb_state == USB_STATE_ADDRESS) {
- /* set the new address */
- new_addr = (u32)dev->dev_addr;
- writel(new_addr << USBADR_SHIFT, &dev->op_regs->deviceaddr);
-
- new_addr = USBADR(readl(&dev->op_regs->deviceaddr));
- dev_vdbg(&dev->pdev->dev, "new_addr = %d\n", new_addr);
- }
-
- done(ep0, req, 0);
-
- switch (dev->ep0_state) {
- case DATA_STATE_XMIT:
- /* receive status phase */
- if (prime_status_phase(dev, EP_DIR_OUT))
- ep0_stall(dev);
- break;
- case DATA_STATE_RECV:
- /* send status phase */
- if (prime_status_phase(dev, EP_DIR_IN))
- ep0_stall(dev);
- break;
- case WAIT_FOR_OUT_STATUS:
- dev->ep0_state = WAIT_FOR_SETUP;
- break;
- case WAIT_FOR_SETUP:
- dev_err(&dev->pdev->dev, "unexpect ep0 packets\n");
- break;
- default:
- ep0_stall(dev);
- break;
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* USB transfer completion interrupt */
-static void handle_trans_complete(struct langwell_udc *dev)
-{
- u32 complete_bits;
- int i, ep_num, dir, bit_mask, status;
- struct langwell_ep *epn;
- struct langwell_request *curr_req, *temp_req;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- complete_bits = readl(&dev->op_regs->endptcomplete);
- dev_vdbg(&dev->pdev->dev, "endptcomplete register: 0x%08x\n",
- complete_bits);
-
- /* Write-Clear the bits in endptcomplete register */
- writel(complete_bits, &dev->op_regs->endptcomplete);
-
- if (!complete_bits) {
- dev_dbg(&dev->pdev->dev, "complete_bits = 0\n");
- goto done;
- }
-
- for (i = 0; i < dev->ep_max; i++) {
- ep_num = i / 2;
- dir = i % 2;
-
- bit_mask = 1 << (ep_num + 16 * dir);
-
- if (!(complete_bits & bit_mask))
- continue;
-
- /* ep0 */
- if (i == 1)
- epn = &dev->ep[0];
- else
- epn = &dev->ep[i];
-
- if (epn->name == NULL) {
- dev_warn(&dev->pdev->dev, "invalid endpoint\n");
- continue;
- }
-
- if (i < 2)
- /* ep0 in and out */
- dev_dbg(&dev->pdev->dev, "%s-%s transfer completed\n",
- epn->name,
- is_in(epn) ? "in" : "out");
- else
- dev_dbg(&dev->pdev->dev, "%s transfer completed\n",
- epn->name);
-
- /* process the req queue until an uncomplete request */
- list_for_each_entry_safe(curr_req, temp_req,
- &epn->queue, queue) {
- status = process_ep_req(dev, i, curr_req);
- dev_vdbg(&dev->pdev->dev, "%s req status: %d\n",
- epn->name, status);
-
- if (status)
- break;
-
- /* write back status to req */
- curr_req->req.status = status;
-
- /* ep0 request completion */
- if (ep_num == 0) {
- ep0_req_complete(dev, epn, curr_req);
- break;
- } else {
- done(epn, curr_req, status);
- }
- }
- }
-done:
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-/* port change detect interrupt handler */
-static void handle_port_change(struct langwell_udc *dev)
-{
- u32 portsc1, devlc;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (dev->bus_reset)
- dev->bus_reset = 0;
-
- portsc1 = readl(&dev->op_regs->portsc1);
- devlc = readl(&dev->op_regs->devlc);
- dev_vdbg(&dev->pdev->dev, "portsc1 = 0x%08x, devlc = 0x%08x\n",
- portsc1, devlc);
-
- /* bus reset is finished */
- if (!(portsc1 & PORTS_PR)) {
- /* get the speed */
- dev->gadget.speed = lpm_device_speed(devlc);
- dev_vdbg(&dev->pdev->dev, "dev->gadget.speed = %d\n",
- dev->gadget.speed);
- }
-
- /* LPM L0 to L1 */
- if (dev->lpm && dev->lpm_state == LPM_L0)
- if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) {
- dev_info(&dev->pdev->dev, "LPM L0 to L1\n");
- dev->lpm_state = LPM_L1;
- }
-
- /* LPM L1 to L0, force resume or remote wakeup finished */
- if (dev->lpm && dev->lpm_state == LPM_L1)
- if (!(portsc1 & PORTS_SUSP)) {
- dev_info(&dev->pdev->dev, "LPM L1 to L0\n");
- dev->lpm_state = LPM_L0;
- }
-
- /* update USB state */
- if (!dev->resume_state)
- dev->usb_state = USB_STATE_DEFAULT;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* USB reset interrupt handler */
-static void handle_usb_reset(struct langwell_udc *dev)
-{
- u32 deviceaddr,
- endptsetupstat,
- endptcomplete;
- unsigned long timeout;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* Write-Clear the device address */
- deviceaddr = readl(&dev->op_regs->deviceaddr);
- writel(deviceaddr & ~USBADR_MASK, &dev->op_regs->deviceaddr);
-
- dev->dev_addr = 0;
-
- /* clear usb state */
- dev->resume_state = 0;
-
- /* LPM L1 to L0, reset */
- if (dev->lpm)
- dev->lpm_state = LPM_L0;
-
- dev->ep0_dir = USB_DIR_OUT;
- dev->ep0_state = WAIT_FOR_SETUP;
-
- /* remote wakeup reset to 0 when the device is reset */
- dev->remote_wakeup = 0;
- dev->dev_status = 1 << USB_DEVICE_SELF_POWERED;
- dev->gadget.b_hnp_enable = 0;
- dev->gadget.a_hnp_support = 0;
- dev->gadget.a_alt_hnp_support = 0;
-
- /* Write-Clear all the setup token semaphores */
- endptsetupstat = readl(&dev->op_regs->endptsetupstat);
- writel(endptsetupstat, &dev->op_regs->endptsetupstat);
-
- /* Write-Clear all the endpoint complete status bits */
- endptcomplete = readl(&dev->op_regs->endptcomplete);
- writel(endptcomplete, &dev->op_regs->endptcomplete);
-
- /* wait until all endptprime bits cleared */
- timeout = jiffies + PRIME_TIMEOUT;
- while (readl(&dev->op_regs->endptprime)) {
- if (time_after(jiffies, timeout)) {
- dev_err(&dev->pdev->dev, "USB reset timeout\n");
- break;
- }
- cpu_relax();
- }
-
- /* write 1s to endptflush register to clear any primed buffers */
- writel((u32) ~0, &dev->op_regs->endptflush);
-
- if (readl(&dev->op_regs->portsc1) & PORTS_PR) {
- dev_vdbg(&dev->pdev->dev, "USB bus reset\n");
- /* bus is reseting */
- dev->bus_reset = 1;
-
- /* reset all the queues, stop all USB activities */
- stop_activity(dev);
- dev->usb_state = USB_STATE_DEFAULT;
- } else {
- dev_vdbg(&dev->pdev->dev, "device controller reset\n");
- /* controller reset */
- langwell_udc_reset(dev);
-
- /* reset all the queues, stop all USB activities */
- stop_activity(dev);
-
- /* reset ep0 dQH and endptctrl */
- ep0_reset(dev);
-
- /* enable interrupt and set controller to run state */
- langwell_udc_start(dev);
-
- dev->usb_state = USB_STATE_ATTACHED;
- }
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* USB bus suspend/resume interrupt */
-static void handle_bus_suspend(struct langwell_udc *dev)
-{
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- dev->resume_state = dev->usb_state;
- dev->usb_state = USB_STATE_SUSPENDED;
-
- /* report suspend to the driver */
- if (dev->driver) {
- if (dev->driver->suspend) {
- spin_unlock(&dev->lock);
- dev->driver->suspend(&dev->gadget);
- spin_lock(&dev->lock);
- dev_dbg(&dev->pdev->dev, "suspend %s\n",
- dev->driver->driver.name);
- }
- }
-
- /* enter PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 0);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-static void handle_bus_resume(struct langwell_udc *dev)
-{
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- dev->usb_state = dev->resume_state;
- dev->resume_state = 0;
-
- /* exit PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 0);
-
- /* report resume to the driver */
- if (dev->driver) {
- if (dev->driver->resume) {
- spin_unlock(&dev->lock);
- dev->driver->resume(&dev->gadget);
- spin_lock(&dev->lock);
- dev_dbg(&dev->pdev->dev, "resume %s\n",
- dev->driver->driver.name);
- }
- }
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* USB device controller interrupt handler */
-static irqreturn_t langwell_irq(int irq, void *_dev)
-{
- struct langwell_udc *dev = _dev;
- u32 usbsts,
- usbintr,
- irq_sts,
- portsc1;
-
- dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- if (dev->stopped) {
- dev_vdbg(&dev->pdev->dev, "handle IRQ_NONE\n");
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return IRQ_NONE;
- }
-
- spin_lock(&dev->lock);
-
- /* USB status */
- usbsts = readl(&dev->op_regs->usbsts);
-
- /* USB interrupt enable */
- usbintr = readl(&dev->op_regs->usbintr);
-
- irq_sts = usbsts & usbintr;
- dev_vdbg(&dev->pdev->dev,
- "usbsts = 0x%08x, usbintr = 0x%08x, irq_sts = 0x%08x\n",
- usbsts, usbintr, irq_sts);
-
- if (!irq_sts) {
- dev_vdbg(&dev->pdev->dev, "handle IRQ_NONE\n");
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- spin_unlock(&dev->lock);
- return IRQ_NONE;
- }
-
- /* Write-Clear interrupt status bits */
- writel(irq_sts, &dev->op_regs->usbsts);
-
- /* resume from suspend */
- portsc1 = readl(&dev->op_regs->portsc1);
- if (dev->usb_state == USB_STATE_SUSPENDED)
- if (!(portsc1 & PORTS_SUSP))
- handle_bus_resume(dev);
-
- /* USB interrupt */
- if (irq_sts & STS_UI) {
- dev_vdbg(&dev->pdev->dev, "USB interrupt\n");
-
- /* setup packet received from ep0 */
- if (readl(&dev->op_regs->endptsetupstat)
- & EP0SETUPSTAT_MASK) {
- dev_vdbg(&dev->pdev->dev,
- "USB SETUP packet received interrupt\n");
- /* setup tripwire semaphone */
- setup_tripwire(dev);
- handle_setup_packet(dev, &dev->local_setup_buff);
- }
-
- /* USB transfer completion */
- if (readl(&dev->op_regs->endptcomplete)) {
- dev_vdbg(&dev->pdev->dev,
- "USB transfer completion interrupt\n");
- handle_trans_complete(dev);
- }
- }
-
- /* SOF received interrupt (for ISO transfer) */
- if (irq_sts & STS_SRI) {
- /* FIXME */
- /* dev_vdbg(&dev->pdev->dev, "SOF received interrupt\n"); */
- }
-
- /* port change detect interrupt */
- if (irq_sts & STS_PCI) {
- dev_vdbg(&dev->pdev->dev, "port change detect interrupt\n");
- handle_port_change(dev);
- }
-
- /* suspend interrupt */
- if (irq_sts & STS_SLI) {
- dev_vdbg(&dev->pdev->dev, "suspend interrupt\n");
- handle_bus_suspend(dev);
- }
-
- /* USB reset interrupt */
- if (irq_sts & STS_URI) {
- dev_vdbg(&dev->pdev->dev, "USB reset interrupt\n");
- handle_usb_reset(dev);
- }
-
- /* USB error or system error interrupt */
- if (irq_sts & (STS_UEI | STS_SEI)) {
- /* FIXME */
- dev_warn(&dev->pdev->dev, "error IRQ, irq_sts: %x\n", irq_sts);
- }
-
- spin_unlock(&dev->lock);
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return IRQ_HANDLED;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* release device structure */
-static void gadget_release(struct device *_dev)
-{
- struct langwell_udc *dev = dev_get_drvdata(_dev);
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- complete(dev->done);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- kfree(dev);
-}
-
-
-/* enable SRAM caching if SRAM detected */
-static void sram_init(struct langwell_udc *dev)
-{
- struct pci_dev *pdev = dev->pdev;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- dev->sram_addr = pci_resource_start(pdev, 1);
- dev->sram_size = pci_resource_len(pdev, 1);
- dev_info(&dev->pdev->dev, "Found private SRAM at %x size:%x\n",
- dev->sram_addr, dev->sram_size);
- dev->got_sram = 1;
-
- if (pci_request_region(pdev, 1, kobject_name(&pdev->dev.kobj))) {
- dev_warn(&dev->pdev->dev, "SRAM request failed\n");
- dev->got_sram = 0;
- } else if (!dma_declare_coherent_memory(&pdev->dev, dev->sram_addr,
- dev->sram_addr, dev->sram_size, DMA_MEMORY_MAP)) {
- dev_warn(&dev->pdev->dev, "SRAM DMA declare failed\n");
- pci_release_region(pdev, 1);
- dev->got_sram = 0;
- }
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* release SRAM caching */
-static void sram_deinit(struct langwell_udc *dev)
-{
- struct pci_dev *pdev = dev->pdev;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- dma_release_declared_memory(&pdev->dev);
- pci_release_region(pdev, 1);
-
- dev->got_sram = 0;
-
- dev_info(&dev->pdev->dev, "release SRAM caching\n");
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-
-/* tear down the binding between this driver and the pci device */
-static void langwell_udc_remove(struct pci_dev *pdev)
-{
- struct langwell_udc *dev = pci_get_drvdata(pdev);
-
- DECLARE_COMPLETION(done);
-
- BUG_ON(dev->driver);
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- dev->done = &done;
-
- /* free dTD dma_pool and dQH */
- if (dev->dtd_pool)
- dma_pool_destroy(dev->dtd_pool);
-
- if (dev->ep_dqh)
- dma_free_coherent(&pdev->dev, dev->ep_dqh_size,
- dev->ep_dqh, dev->ep_dqh_dma);
-
- /* release SRAM caching */
- if (dev->has_sram && dev->got_sram)
- sram_deinit(dev);
-
- if (dev->status_req) {
- kfree(dev->status_req->req.buf);
- kfree(dev->status_req);
- }
-
- kfree(dev->ep);
-
- /* disable IRQ handler */
- if (dev->got_irq)
- free_irq(pdev->irq, dev);
-
- if (dev->cap_regs)
- iounmap(dev->cap_regs);
-
- if (dev->region)
- release_mem_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
-
- if (dev->enabled)
- pci_disable_device(pdev);
-
- dev->cap_regs = NULL;
-
- dev_info(&dev->pdev->dev, "unbind\n");
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-
- device_unregister(&dev->gadget.dev);
- device_remove_file(&pdev->dev, &dev_attr_langwell_udc);
- device_remove_file(&pdev->dev, &dev_attr_remote_wakeup);
-
- pci_set_drvdata(pdev, NULL);
-
- /* free dev, wait for the release() finished */
- wait_for_completion(&done);
-}
-
-
-/*
- * wrap this driver around the specified device, but
- * don't respond over USB until a gadget driver binds to us.
- */
-static int langwell_udc_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct langwell_udc *dev;
- unsigned long resource, len;
- void __iomem *base = NULL;
- size_t size;
- int retval;
-
- /* alloc, and start init */
- dev = kzalloc(sizeof *dev, GFP_KERNEL);
- if (dev == NULL) {
- retval = -ENOMEM;
- goto error;
- }
-
- /* initialize device spinlock */
- spin_lock_init(&dev->lock);
-
- dev->pdev = pdev;
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- pci_set_drvdata(pdev, dev);
-
- /* now all the pci goodies ... */
- if (pci_enable_device(pdev) < 0) {
- retval = -ENODEV;
- goto error;
- }
- dev->enabled = 1;
-
- /* control register: BAR 0 */
- resource = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
- if (!request_mem_region(resource, len, driver_name)) {
- dev_err(&dev->pdev->dev, "controller already in use\n");
- retval = -EBUSY;
- goto error;
- }
- dev->region = 1;
-
- base = ioremap_nocache(resource, len);
- if (base == NULL) {
- dev_err(&dev->pdev->dev, "can't map memory\n");
- retval = -EFAULT;
- goto error;
- }
-
- dev->cap_regs = (struct langwell_cap_regs __iomem *) base;
- dev_vdbg(&dev->pdev->dev, "dev->cap_regs: %p\n", dev->cap_regs);
- dev->op_regs = (struct langwell_op_regs __iomem *)
- (base + OP_REG_OFFSET);
- dev_vdbg(&dev->pdev->dev, "dev->op_regs: %p\n", dev->op_regs);
-
- /* irq setup after old hardware is cleaned up */
- if (!pdev->irq) {
- dev_err(&dev->pdev->dev, "No IRQ. Check PCI setup!\n");
- retval = -ENODEV;
- goto error;
- }
-
- dev->has_sram = 1;
- dev->got_sram = 0;
- dev_vdbg(&dev->pdev->dev, "dev->has_sram: %d\n", dev->has_sram);
-
- /* enable SRAM caching if detected */
- if (dev->has_sram && !dev->got_sram)
- sram_init(dev);
-
- dev_info(&dev->pdev->dev,
- "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n",
- pdev->irq, resource, len, base);
- /* enables bus-mastering for device dev */
- pci_set_master(pdev);
-
- if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED,
- driver_name, dev) != 0) {
- dev_err(&dev->pdev->dev,
- "request interrupt %d failed\n", pdev->irq);
- retval = -EBUSY;
- goto error;
- }
- dev->got_irq = 1;
-
- /* set stopped bit */
- dev->stopped = 1;
-
- /* capabilities and endpoint number */
- dev->lpm = (readl(&dev->cap_regs->hccparams) & HCC_LEN) ? 1 : 0;
- dev->dciversion = readw(&dev->cap_regs->dciversion);
- dev->devcap = (readl(&dev->cap_regs->dccparams) & DEVCAP) ? 1 : 0;
- dev_vdbg(&dev->pdev->dev, "dev->lpm: %d\n", dev->lpm);
- dev_vdbg(&dev->pdev->dev, "dev->dciversion: 0x%04x\n",
- dev->dciversion);
- dev_vdbg(&dev->pdev->dev, "dccparams: 0x%08x\n",
- readl(&dev->cap_regs->dccparams));
- dev_vdbg(&dev->pdev->dev, "dev->devcap: %d\n", dev->devcap);
- if (!dev->devcap) {
- dev_err(&dev->pdev->dev, "can't support device mode\n");
- retval = -ENODEV;
- goto error;
- }
-
- /* a pair of endpoints (out/in) for each address */
- dev->ep_max = DEN(readl(&dev->cap_regs->dccparams)) * 2;
- dev_vdbg(&dev->pdev->dev, "dev->ep_max: %d\n", dev->ep_max);
-
- /* allocate endpoints memory */
- dev->ep = kzalloc(sizeof(struct langwell_ep) * dev->ep_max,
- GFP_KERNEL);
- if (!dev->ep) {
- dev_err(&dev->pdev->dev, "allocate endpoints memory failed\n");
- retval = -ENOMEM;
- goto error;
- }
-
- /* allocate device dQH memory */
- size = dev->ep_max * sizeof(struct langwell_dqh);
- dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size);
- if (size < DQH_ALIGNMENT)
- size = DQH_ALIGNMENT;
- else if ((size % DQH_ALIGNMENT) != 0) {
- size += DQH_ALIGNMENT + 1;
- size &= ~(DQH_ALIGNMENT - 1);
- }
- dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
- &dev->ep_dqh_dma, GFP_KERNEL);
- if (!dev->ep_dqh) {
- dev_err(&dev->pdev->dev, "allocate dQH memory failed\n");
- retval = -ENOMEM;
- goto error;
- }
- dev->ep_dqh_size = size;
- dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %zd\n", dev->ep_dqh_size);
-
- /* initialize ep0 status request structure */
- dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL);
- if (!dev->status_req) {
- dev_err(&dev->pdev->dev,
- "allocate status_req memory failed\n");
- retval = -ENOMEM;
- goto error;
- }
- INIT_LIST_HEAD(&dev->status_req->queue);
-
- /* allocate a small amount of memory to get valid address */
- dev->status_req->req.buf = kmalloc(8, GFP_KERNEL);
- dev->status_req->req.dma = virt_to_phys(dev->status_req->req.buf);
-
- dev->resume_state = USB_STATE_NOTATTACHED;
- dev->usb_state = USB_STATE_POWERED;
- dev->ep0_dir = USB_DIR_OUT;
-
- /* remote wakeup reset to 0 when the device is reset */
- dev->remote_wakeup = 0;
- dev->dev_status = 1 << USB_DEVICE_SELF_POWERED;
-
- /* reset device controller */
- langwell_udc_reset(dev);
-
- /* initialize gadget structure */
- dev->gadget.ops = &langwell_ops; /* usb_gadget_ops */
- dev->gadget.ep0 = &dev->ep[0].ep; /* gadget ep0 */
- INIT_LIST_HEAD(&dev->gadget.ep_list); /* ep_list */
- dev->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
- dev->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */
-
- /* 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; /* gadget name */
-
- /* controller endpoints reinit */
- eps_reinit(dev);
-
- /* reset ep0 dQH and endptctrl */
- ep0_reset(dev);
-
- /* create dTD dma_pool resource */
- dev->dtd_pool = dma_pool_create("langwell_dtd",
- &dev->pdev->dev,
- sizeof(struct langwell_dtd),
- DTD_ALIGNMENT,
- DMA_BOUNDARY);
-
- if (!dev->dtd_pool) {
- retval = -ENOMEM;
- goto error;
- }
-
- /* done */
- dev_info(&dev->pdev->dev, "%s\n", driver_desc);
- dev_info(&dev->pdev->dev, "irq %d, pci mem %p\n", pdev->irq, base);
- dev_info(&dev->pdev->dev, "Driver version: " DRIVER_VERSION "\n");
- dev_info(&dev->pdev->dev, "Support (max) %d endpoints\n", dev->ep_max);
- dev_info(&dev->pdev->dev, "Device interface version: 0x%04x\n",
- dev->dciversion);
- dev_info(&dev->pdev->dev, "Controller mode: %s\n",
- dev->devcap ? "Device" : "Host");
- dev_info(&dev->pdev->dev, "Support USB LPM: %s\n",
- dev->lpm ? "Yes" : "No");
-
- dev_vdbg(&dev->pdev->dev,
- "After langwell_udc_probe(), print all registers:\n");
- print_all_registers(dev);
-
- retval = device_register(&dev->gadget.dev);
- if (retval)
- goto error;
-
- retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
- if (retval)
- goto error;
-
- retval = device_create_file(&pdev->dev, &dev_attr_langwell_udc);
- if (retval)
- goto error;
-
- retval = device_create_file(&pdev->dev, &dev_attr_remote_wakeup);
- if (retval)
- goto error_attr1;
-
- dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-
-error_attr1:
- device_remove_file(&pdev->dev, &dev_attr_langwell_udc);
-error:
- if (dev) {
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- langwell_udc_remove(pdev);
- }
-
- return retval;
-}
-
-
-/* device controller suspend */
-static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct langwell_udc *dev = pci_get_drvdata(pdev);
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- usb_del_gadget_udc(&dev->gadget);
- /* disable interrupt and set controller to stop state */
- langwell_udc_stop(dev);
-
- /* disable IRQ handler */
- if (dev->got_irq)
- free_irq(pdev->irq, dev);
- dev->got_irq = 0;
-
- /* save PCI state */
- pci_save_state(pdev);
-
- spin_lock_irq(&dev->lock);
- /* stop all usb activities */
- stop_activity(dev);
- spin_unlock_irq(&dev->lock);
-
- /* free dTD dma_pool and dQH */
- if (dev->dtd_pool)
- dma_pool_destroy(dev->dtd_pool);
-
- if (dev->ep_dqh)
- dma_free_coherent(&pdev->dev, dev->ep_dqh_size,
- dev->ep_dqh, dev->ep_dqh_dma);
-
- /* release SRAM caching */
- if (dev->has_sram && dev->got_sram)
- sram_deinit(dev);
-
- /* set device power state */
- pci_set_power_state(pdev, PCI_D3hot);
-
- /* enter PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 1);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* device controller resume */
-static int langwell_udc_resume(struct pci_dev *pdev)
-{
- struct langwell_udc *dev = pci_get_drvdata(pdev);
- size_t size;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* exit PHY low power suspend */
- if (dev->pdev->device != 0x0829)
- langwell_phy_low_power(dev, 0);
-
- /* set device D0 power state */
- pci_set_power_state(pdev, PCI_D0);
-
- /* enable SRAM caching if detected */
- if (dev->has_sram && !dev->got_sram)
- sram_init(dev);
-
- /* allocate device dQH memory */
- size = dev->ep_max * sizeof(struct langwell_dqh);
- dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size);
- if (size < DQH_ALIGNMENT)
- size = DQH_ALIGNMENT;
- else if ((size % DQH_ALIGNMENT) != 0) {
- size += DQH_ALIGNMENT + 1;
- size &= ~(DQH_ALIGNMENT - 1);
- }
- dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
- &dev->ep_dqh_dma, GFP_KERNEL);
- if (!dev->ep_dqh) {
- dev_err(&dev->pdev->dev, "allocate dQH memory failed\n");
- return -ENOMEM;
- }
- dev->ep_dqh_size = size;
- dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %zd\n", dev->ep_dqh_size);
-
- /* create dTD dma_pool resource */
- dev->dtd_pool = dma_pool_create("langwell_dtd",
- &dev->pdev->dev,
- sizeof(struct langwell_dtd),
- DTD_ALIGNMENT,
- DMA_BOUNDARY);
-
- if (!dev->dtd_pool)
- return -ENOMEM;
-
- /* restore PCI state */
- pci_restore_state(pdev);
-
- /* enable IRQ handler */
- if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED,
- driver_name, dev) != 0) {
- dev_err(&dev->pdev->dev, "request interrupt %d failed\n",
- pdev->irq);
- return -EBUSY;
- }
- dev->got_irq = 1;
-
- /* reset and start controller to run state */
- if (dev->stopped) {
- /* reset device controller */
- langwell_udc_reset(dev);
-
- /* reset ep0 dQH and endptctrl */
- ep0_reset(dev);
-
- /* start device if gadget is loaded */
- if (dev->driver)
- langwell_udc_start(dev);
- }
-
- /* reset USB status */
- dev->usb_state = USB_STATE_ATTACHED;
- dev->ep0_state = WAIT_FOR_SETUP;
- dev->ep0_dir = USB_DIR_OUT;
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
- return 0;
-}
-
-
-/* pci driver shutdown */
-static void langwell_udc_shutdown(struct pci_dev *pdev)
-{
- struct langwell_udc *dev = pci_get_drvdata(pdev);
- u32 usbmode;
-
- dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__);
-
- /* reset controller mode to IDLE */
- usbmode = readl(&dev->op_regs->usbmode);
- dev_dbg(&dev->pdev->dev, "usbmode = 0x%08x\n", usbmode);
- usbmode &= (~3 | MODE_IDLE);
- writel(usbmode, &dev->op_regs->usbmode);
-
- dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id pci_ids[] = { {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x8086,
- .device = 0x0811,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-}, { /* end: all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
-
-static struct pci_driver langwell_pci_driver = {
- .name = (char *) driver_name,
- .id_table = pci_ids,
-
- .probe = langwell_udc_probe,
- .remove = langwell_udc_remove,
-
- /* device controller suspend/resume */
- .suspend = langwell_udc_suspend,
- .resume = langwell_udc_resume,
-
- .shutdown = langwell_udc_shutdown,
-};
-
-
-static int __init init(void)
-{
- return pci_register_driver(&langwell_pci_driver);
-}
-module_init(init);
-
-
-static void __exit cleanup(void)
-{
- pci_unregister_driver(&langwell_pci_driver);
-}
-module_exit(cleanup);
-
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Xiaochen Shen <xiaochen.shen@intel.com>");
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h
deleted file mode 100644
index d6e78accaff..00000000000
--- a/drivers/usb/gadget/langwell_udc.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Intel Langwell USB Device Controller driver
- * Copyright (C) 2008-2009, Intel Corporation.
- *
- * 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/usb/langwell_udc.h>
-
-/*-------------------------------------------------------------------------*/
-
-/* driver data structures and utilities */
-
-/*
- * dTD: Device Endpoint Transfer Descriptor
- * describe to the device controller the location and quantity of
- * data to be send/received for given transfer
- */
-struct langwell_dtd {
- u32 dtd_next;
-/* bits 31:5, next transfer element pointer */
-#define DTD_NEXT(d) (((d)>>5)&0x7ffffff)
-#define DTD_NEXT_MASK (0x7ffffff << 5)
-/* terminate */
-#define DTD_TERM BIT(0)
- /* bits 7:0, execution back states */
- u32 dtd_status:8;
-#define DTD_STATUS(d) (((d)>>0)&0xff)
-#define DTD_STS_ACTIVE BIT(7) /* active */
-#define DTD_STS_HALTED BIT(6) /* halted */
-#define DTD_STS_DBE BIT(5) /* data buffer error */
-#define DTD_STS_TRE BIT(3) /* transaction error */
- /* bits 9:8 */
- u32 dtd_res0:2;
- /* bits 11:10, multipier override */
- u32 dtd_multo:2;
-#define DTD_MULTO (BIT(11) | BIT(10))
- /* bits 14:12 */
- u32 dtd_res1:3;
- /* bit 15, interrupt on complete */
- u32 dtd_ioc:1;
-#define DTD_IOC BIT(15)
- /* bits 30:16, total bytes */
- u32 dtd_total:15;
-#define DTD_TOTAL(d) (((d)>>16)&0x7fff)
-#define DTD_MAX_TRANSFER_LENGTH 0x4000
- /* bit 31 */
- u32 dtd_res2:1;
- /* dTD buffer pointer page 0 to 4 */
- u32 dtd_buf[5];
-#define DTD_OFFSET_MASK 0xfff
-/* bits 31:12, buffer pointer */
-#define DTD_BUFFER(d) (((d)>>12)&0x3ff)
-/* bits 11:0, current offset */
-#define DTD_C_OFFSET(d) (((d)>>0)&0xfff)
-/* bits 10:0, frame number */
-#define DTD_FRAME(d) (((d)>>0)&0x7ff)
-
- /* driver-private parts */
-
- /* dtd dma address */
- dma_addr_t dtd_dma;
- /* next dtd virtual address */
- struct langwell_dtd *next_dtd_virt;
-};
-
-
-/*
- * dQH: Device Endpoint Queue Head
- * describe where all transfers are managed
- * 48-byte data structure, aligned on 64-byte boundary
- *
- * These are associated with dTD structure
- */
-struct langwell_dqh {
- /* endpoint capabilities and characteristics */
- u32 dqh_res0:15; /* bits 14:0 */
- u32 dqh_ios:1; /* bit 15, interrupt on setup */
-#define DQH_IOS BIT(15)
- u32 dqh_mpl:11; /* bits 26:16, maximum packet length */
-#define DQH_MPL (0x7ff << 16)
- u32 dqh_res1:2; /* bits 28:27 */
- u32 dqh_zlt:1; /* bit 29, zero length termination */
-#define DQH_ZLT BIT(29)
- u32 dqh_mult:2; /* bits 31:30 */
-#define DQH_MULT (BIT(30) | BIT(31))
-
- /* current dTD pointer */
- u32 dqh_current; /* locate the transfer in progress */
-#define DQH_C_DTD(e) \
- (((e)>>5)&0x7ffffff) /* bits 31:5, current dTD pointer */
-
- /* transfer overlay, hardware parts of a struct langwell_dtd */
- u32 dtd_next;
- u32 dtd_status:8; /* bits 7:0, execution back states */
- u32 dtd_res0:2; /* bits 9:8 */
- u32 dtd_multo:2; /* bits 11:10, multipier override */
- u32 dtd_res1:3; /* bits 14:12 */
- u32 dtd_ioc:1; /* bit 15, interrupt on complete */
- u32 dtd_total:15; /* bits 30:16, total bytes */
- u32 dtd_res2:1; /* bit 31 */
- u32 dtd_buf[5]; /* dTD buffer pointer page 0 to 4 */
-
- u32 dqh_res2;
- struct usb_ctrlrequest dqh_setup; /* setup packet buffer */
-} __attribute__ ((aligned(64)));
-
-
-/* endpoint data structure */
-struct langwell_ep {
- struct usb_ep ep;
- dma_addr_t dma;
- struct langwell_udc *dev;
- unsigned long irqs;
- struct list_head queue;
- struct langwell_dqh *dqh;
- const struct usb_endpoint_descriptor *desc;
- char name[14];
- unsigned stopped:1,
- ep_type:2,
- ep_num:8;
-};
-
-
-/* request data structure */
-struct langwell_request {
- struct usb_request req;
- struct langwell_dtd *dtd, *head, *tail;
- struct langwell_ep *ep;
- dma_addr_t dtd_dma;
- struct list_head queue;
- unsigned dtd_count;
- unsigned mapped:1;
-};
-
-
-/* ep0 transfer state */
-enum ep0_state {
- WAIT_FOR_SETUP,
- DATA_STATE_XMIT,
- DATA_STATE_NEED_ZLP,
- WAIT_FOR_OUT_STATUS,
- DATA_STATE_RECV,
-};
-
-
-/* device suspend state */
-enum lpm_state {
- LPM_L0, /* on */
- LPM_L1, /* LPM L1 sleep */
- LPM_L2, /* suspend */
- LPM_L3, /* off */
-};
-
-
-/* device data structure */
-struct langwell_udc {
- /* each pci device provides one gadget, several endpoints */
- struct usb_gadget gadget;
- spinlock_t lock; /* device lock */
- struct langwell_ep *ep;
- struct usb_gadget_driver *driver;
- struct otg_transceiver *transceiver;
- u8 dev_addr;
- u32 usb_state;
- u32 resume_state;
- u32 bus_reset;
- enum lpm_state lpm_state;
- enum ep0_state ep0_state;
- u32 ep0_dir;
- u16 dciversion;
- unsigned ep_max;
- unsigned devcap:1,
- enabled:1,
- region:1,
- got_irq:1,
- powered:1,
- remote_wakeup:1,
- rate:1,
- is_reset:1,
- softconnected:1,
- vbus_active:1,
- suspended:1,
- stopped:1,
- lpm:1, /* LPM capability */
- has_sram:1, /* SRAM caching */
- got_sram:1;
-
- /* pci state used to access those endpoints */
- struct pci_dev *pdev;
-
- /* Langwell otg transceiver */
- struct langwell_otg *lotg;
-
- /* control registers */
- struct langwell_cap_regs __iomem *cap_regs;
- struct langwell_op_regs __iomem *op_regs;
-
- struct usb_ctrlrequest local_setup_buff;
- struct langwell_dqh *ep_dqh;
- size_t ep_dqh_size;
- dma_addr_t ep_dqh_dma;
-
- /* ep0 status request */
- struct langwell_request *status_req;
-
- /* dma pool */
- struct dma_pool *dtd_pool;
-
- /* make sure release() is done */
- struct completion *done;
-
- /* for private SRAM caching */
- unsigned int sram_addr;
- unsigned int sram_size;
-
- /* device status data for get_status request */
- u16 dev_status;
-};
-
-#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget)
-
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 3608b3bd573..0d17174b86f 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -390,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);
@@ -558,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);
@@ -734,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);
}
}
@@ -917,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);
@@ -1377,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)
@@ -1463,41 +1463,14 @@ static struct usb_ep_ops m66592_ep_ops = {
};
/*-------------------------------------------------------------------------*/
-static struct m66592 *the_controller;
-
-static int m66592_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int m66592_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct m66592 *m66592 = the_controller;
- int retval;
-
- if (!driver
- || driver->max_speed < USB_SPEED_HIGH
- || !bind
- || !driver->setup)
- return -EINVAL;
- if (!m66592)
- return -ENODEV;
- if (m66592->driver)
- return -EBUSY;
+ struct m66592 *m66592 = to_m66592(g);
/* hook up the driver */
driver->driver.bus = NULL;
m66592->driver = driver;
- m66592->gadget.dev.driver = &driver->driver;
-
- retval = device_add(&m66592->gadget.dev);
- if (retval) {
- pr_err("device_add error (%d)\n", retval);
- goto error;
- }
-
- retval = bind(&m66592->gadget);
- if (retval) {
- pr_err("bind to driver error (%d)\n", retval);
- device_del(&m66592->gadget.dev);
- goto error;
- }
m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
@@ -1510,37 +1483,22 @@ static int m66592_start(struct usb_gadget_driver *driver,
}
return 0;
-
-error:
- m66592->driver = NULL;
- m66592->gadget.dev.driver = NULL;
-
- return retval;
}
-static int m66592_stop(struct usb_gadget_driver *driver)
+static int m66592_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct m66592 *m66592 = the_controller;
- unsigned long flags;
-
- if (driver != m66592->driver || !driver->unbind)
- return -EINVAL;
-
- spin_lock_irqsave(&m66592->lock, flags);
- if (m66592->gadget.speed != USB_SPEED_UNKNOWN)
- m66592_usb_disconnect(m66592);
- spin_unlock_irqrestore(&m66592->lock, flags);
+ struct m66592 *m66592 = to_m66592(g);
m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
driver->unbind(&m66592->gadget);
- m66592->gadget.dev.driver = NULL;
init_controller(m66592);
disable_controller(m66592);
- device_del(&m66592->gadget.dev);
m66592->driver = NULL;
+
return 0;
}
@@ -1566,16 +1524,16 @@ static int m66592_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static struct usb_gadget_ops m66592_gadget_ops = {
+static const struct usb_gadget_ops m66592_gadget_ops = {
.get_frame = m66592_get_frame,
- .start = m66592_start,
- .stop = m66592_stop,
+ .udc_start = m66592_udc_start,
+ .udc_stop = m66592_udc_stop,
.pullup = m66592_pullup,
};
static int __exit m66592_remove(struct platform_device *pdev)
{
- struct m66592 *m66592 = dev_get_drvdata(&pdev->dev);
+ struct m66592 *m66592 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&m66592->gadget);
@@ -1583,12 +1541,10 @@ static int __exit m66592_remove(struct platform_device *pdev)
iounmap(m66592->reg);
free_irq(platform_get_irq(pdev, 0), m66592);
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);
clk_put(m66592->clk);
}
-#endif
kfree(m66592);
return 0;
}
@@ -1602,9 +1558,7 @@ static int __init m66592_probe(struct platform_device *pdev)
struct resource *res, *ires;
void __iomem *reg = NULL;
struct m66592 *m66592 = NULL;
-#ifdef CONFIG_HAVE_CLK
char clk_name[8];
-#endif
int ret = 0;
int i;
@@ -1630,7 +1584,7 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
- if (pdev->dev.platform_data == NULL) {
+ if (dev_get_platdata(&pdev->dev) == NULL) {
dev_err(&pdev->dev, "no platform data\n");
ret = -ENODEV;
goto clean_up;
@@ -1640,23 +1594,17 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
if (m66592 == NULL) {
ret = -ENOMEM;
- pr_err("kzalloc error\n");
goto clean_up;
}
- m66592->pdata = pdev->dev.platform_data;
+ m66592->pdata = dev_get_platdata(&pdev->dev);
m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
spin_lock_init(&m66592->lock);
- dev_set_drvdata(&pdev->dev, m66592);
+ platform_set_drvdata(pdev, m66592);
m66592->gadget.ops = &m66592_gadget_ops;
- device_initialize(&m66592->gadget.dev);
- dev_set_name(&m66592->gadget.dev, "gadget");
m66592->gadget.max_speed = USB_SPEED_HIGH;
- m66592->gadget.dev.parent = &pdev->dev;
- m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
- m66592->gadget.dev.release = pdev->dev.release;
m66592->gadget.name = udc_name;
init_timer(&m66592->timer);
@@ -1671,7 +1619,6 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
m66592->clk = clk_get(&pdev->dev, clk_name);
@@ -1683,7 +1630,7 @@ static int __init m66592_probe(struct platform_device *pdev)
}
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);
@@ -1699,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;
@@ -1711,11 +1658,11 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->pipenum2ep[0] = &m66592->ep[0];
m66592->epaddr2ep[0] = &m66592->ep[0];
- the_controller = m66592;
-
m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
- if (m66592->ep0_req == NULL)
+ if (m66592->ep0_req == NULL) {
+ ret = -ENOMEM;
goto clean_up3;
+ }
m66592->ep0_req->complete = nop_completion;
init_controller(m66592);
@@ -1731,13 +1678,11 @@ err_add_udc:
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
clean_up3:
-#ifdef CONFIG_HAVE_CLK
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);
clk_put(m66592->clk);
}
clean_up2:
-#endif
free_irq(ires->start, m66592);
clean_up:
if (m66592) {
@@ -1760,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 9d9f7e39f03..96d49d7bfb6 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -13,10 +13,7 @@
#ifndef __M66592_UDC_H__
#define __M66592_UDC_H__
-#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
-#endif
-
#include <linux/usb/m66592.h>
#define M66592_SYSCFG 0x00
@@ -456,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;
@@ -468,9 +465,7 @@ struct m66592_ep {
struct m66592 {
spinlock_t lock;
void __iomem *reg;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
-#endif
struct m66592_platdata *pdata;
unsigned long irq_trigger;
@@ -497,6 +492,7 @@ struct m66592 {
int isochronous;
int num_dma;
};
+#define to_m66592(g) (container_of((g), struct m66592, gadget))
#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
#define m66592_to_gadget(m66592) (&m66592->gadget)
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index e24f72f82a4..8e27a8c9644 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2003-2008 Alan Stern
* Copyright (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * Author: Michal Nazarewicz <mina86@mina86.com>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -29,32 +29,27 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/usb/ch9.h>
-
+#include <linux/module.h>
/*-------------------------------------------------------------------------*/
#define DRIVER_DESC "Mass Storage Gadget"
#define DRIVER_VERSION "2009/09/11"
-/*-------------------------------------------------------------------------*/
-
/*
- * kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
*/
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-#include "f_mass_storage.c"
+#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor msg_device_desc = {
.bLength = sizeof msg_device_desc,
@@ -85,12 +80,45 @@ static const struct usb_descriptor_header *otg_desc[] = {
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;
@@ -104,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common)
static int __init msg_do_config(struct usb_configuration *c)
{
- static const struct fsg_operations ops = {
- .thread_exits = msg_thread_exits,
- };
- static struct fsg_common common;
-
- struct fsg_common *retp;
- struct fsg_config config;
+ struct fsg_opts *opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -118,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- fsg_config_from_params(&config, &mod_data);
- config.ops = &ops;
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ f_msg = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg))
+ return PTR_ERR(f_msg);
- retp = fsg_common_init(&common, c->cdev, &config);
- if (IS_ERR(retp))
- return PTR_ERR(retp);
+ ret = fsg_common_run_thread(opts->common);
+ if (ret)
+ goto put_func;
- ret = fsg_bind_config(c->cdev, c, &common);
- fsg_common_put(&common);
+ ret = usb_add_function(c, f_msg);
+ if (ret)
+ goto put_func;
+
+ return 0;
+
+put_func:
+ usb_put_function(f_msg);
return ret;
}
@@ -141,27 +172,90 @@ static struct usb_configuration msg_config_driver = {
static int __init msg_bind(struct usb_composite_dev *cdev)
{
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
+ struct fsg_opts *opts;
+ struct fsg_config config;
int status;
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg))
+ return PTR_ERR(fi_msg);
+
+ fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
+ opts = fsg_opts_from_func_inst(fi_msg);
+
+ opts->no_configfs = true;
+ status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
+ if (status)
+ goto fail;
+
+ status = fsg_common_set_nluns(opts->common, config.nluns);
+ if (status)
+ goto fail_set_nluns;
+
+ fsg_common_set_ops(opts->common, &ops);
+
+ status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_sysfs(opts->common, true);
+ status = fsg_common_create_luns(opts->common, &config);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_inquiry_string(opts->common, config.vendor_name,
+ config.product_name);
+
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ goto fail_string_ids;
+ msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
if (status < 0)
- return status;
+ goto fail_string_ids;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
set_bit(0, &msg_registered);
return 0;
+
+fail_string_ids:
+ fsg_common_remove_luns(opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(opts->common);
+fail:
+ usb_put_function_instance(fi_msg);
+ return status;
}
+static int msg_unbind(struct usb_composite_dev *cdev)
+{
+ if (!IS_ERR(f_msg))
+ usb_put_function(f_msg);
+
+ if (!IS_ERR(fi_msg))
+ usb_put_function_instance(fi_msg);
+
+ return 0;
+}
/****************************** Some noise ******************************/
-static struct usb_composite_driver msg_driver = {
+static __refdata struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
- .iProduct = DRIVER_DESC,
.max_speed = USB_SPEED_SUPER,
.needs_serial = 1,
+ .strings = dev_strings,
+ .bind = msg_bind,
+ .unbind = msg_unbind,
};
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -170,7 +264,7 @@ MODULE_LICENSE("GPL");
static int __init msg_init(void)
{
- return usb_composite_probe(&msg_driver, msg_bind);
+ return usb_composite_probe(&msg_driver);
}
module_init(msg_init);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 7e7f515b8b1..940f6cde8e8 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ * 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
@@ -14,10 +14,10 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
+#include <linux/netdevice.h>
-
+#include "u_serial.h"
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
@@ -33,35 +33,18 @@ MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
-/***************************** All the files... *****************************/
-
-/*
- * 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_mass_storage.c"
+#include "f_mass_storage.h"
-#include "u_serial.c"
-#include "f_acm.c"
-
-#include "f_ecm.c"
-#include "f_subset.c"
+#include "u_ecm.h"
#ifdef USB_ETH_RNDIS
-# include "f_rndis.c"
-# include "rndis.c"
+# include "u_rndis.h"
+# include "rndis.h"
#endif
-#include "u_ether.c"
+#include "u_ether.h"
+USB_GADGET_COMPOSITE_OPTIONS();
+USB_ETHERNET_MODULE_PARAMETERS();
/***************************** Device Descriptor ****************************/
@@ -112,21 +95,16 @@ static const struct usb_descriptor_header *otg_desc[] = {
enum {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
- MULTI_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
MULTI_STRING_CDC_CONFIG_IDX,
-#endif
};
static struct usb_string strings_dev[] = {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
+ [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",
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
[MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
-#endif
{ } /* end of list */
};
@@ -144,19 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
-FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+
+#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
-static struct fsg_common fsg_common;
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-static u8 hostaddr[ETH_ALEN];
+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)) {
@@ -164,22 +159,54 @@ static __init int rndis_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- ret = rndis_bind_config(c, hostaddr);
- if (ret < 0)
- return ret;
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
- ret = acm_bind_config(c, 0);
+ ret = usb_add_function(c, f_rndis);
if (ret < 0)
- return ret;
+ goto err_func_rndis;
- ret = fsg_bind_config(c->cdev, c, &fsg_common);
- if (ret < 0)
- return ret;
+ f_acm_rndis = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm_rndis)) {
+ ret = PTR_ERR(f_acm_rndis);
+ goto err_func_acm;
+ }
+
+ ret = usb_add_function(c, f_acm_rndis);
+ if (ret)
+ goto err_conf;
+
+ f_msg_rndis = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg_rndis)) {
+ ret = PTR_ERR(f_msg_rndis);
+ goto err_fsg;
+ }
+
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
+ ret = fsg_common_run_thread(fsg_opts->common);
+ if (ret)
+ goto err_run;
+
+ ret = usb_add_function(c, f_msg_rndis);
+ if (ret)
+ goto err_run;
return 0;
+err_run:
+ usb_put_function(f_msg_rndis);
+err_fsg:
+ usb_remove_function(c, f_acm_rndis);
+err_conf:
+ usb_put_function(f_acm_rndis);
+err_func_acm:
+ usb_remove_function(c, f_rndis);
+err_func_rndis:
+ usb_put_function(f_rndis);
+ return ret;
}
-static int rndis_config_register(struct usb_composite_dev *cdev)
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
{
static struct usb_configuration config = {
.bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
@@ -194,7 +221,7 @@ static int rndis_config_register(struct usb_composite_dev *cdev)
#else
-static int rndis_config_register(struct usb_composite_dev *cdev)
+static __ref int rndis_config_register(struct usb_composite_dev *cdev)
{
return 0;
}
@@ -205,9 +232,14 @@ static int rndis_config_register(struct usb_composite_dev *cdev)
/********** CDC ECM **********/
#ifdef CONFIG_USB_G_MULTI_CDC
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_acm_multi;
+static struct usb_function *f_ecm;
+static struct usb_function *f_msg_multi;
static __init int cdc_do_config(struct usb_configuration *c)
{
+ struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -215,22 +247,55 @@ static __init int cdc_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- ret = ecm_bind_config(c, hostaddr);
- if (ret < 0)
- return ret;
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
- ret = acm_bind_config(c, 0);
+ ret = usb_add_function(c, f_ecm);
if (ret < 0)
- return ret;
+ goto err_func_ecm;
- ret = fsg_bind_config(c->cdev, c, &fsg_common);
- if (ret < 0)
- return ret;
+ /* implicit port_num is zero */
+ f_acm_multi = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm_multi)) {
+ ret = PTR_ERR(f_acm_multi);
+ goto err_func_acm;
+ }
+
+ ret = usb_add_function(c, f_acm_multi);
+ if (ret)
+ goto err_conf;
+
+ f_msg_multi = usb_get_function(fi_msg);
+ if (IS_ERR(f_msg_multi)) {
+ ret = PTR_ERR(f_msg_multi);
+ goto err_fsg;
+ }
+
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
+ ret = fsg_common_run_thread(fsg_opts->common);
+ if (ret)
+ goto err_run;
+
+ ret = usb_add_function(c, f_msg_multi);
+ if (ret)
+ goto err_run;
return 0;
+err_run:
+ usb_put_function(f_msg_multi);
+err_fsg:
+ usb_remove_function(c, f_acm_multi);
+err_conf:
+ usb_put_function(f_acm_multi);
+err_func_acm:
+ usb_remove_function(c, f_ecm);
+err_func_ecm:
+ usb_put_function(f_ecm);
+ return ret;
}
-static int cdc_config_register(struct usb_composite_dev *cdev)
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
{
static struct usb_configuration config = {
.bConfigurationValue = MULTI_CDC_CONFIG_NUM,
@@ -245,7 +310,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev)
#else
-static int cdc_config_register(struct usb_composite_dev *cdev)
+static __ref int cdc_config_register(struct usb_composite_dev *cdev)
{
return 0;
}
@@ -256,81 +321,175 @@ static int cdc_config_register(struct usb_composite_dev *cdev)
/****************************** Gadget Bind ******************************/
-
static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
- int status, gcnum;
+#ifdef CONFIG_USB_G_MULTI_CDC
+ struct f_ecm_opts *ecm_opts;
+#endif
+#ifdef USB_ETH_RNDIS
+ struct f_rndis_opts *rndis_opts;
+#endif
+ struct fsg_opts *fsg_opts;
+ struct fsg_config config;
+ int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
- gadget->name);
+ gadget->name);
return -EINVAL;
}
- /* set up network link layer */
- status = gether_setup(cdev->gadget, hostaddr);
- if (status < 0)
- return status;
+#ifdef CONFIG_USB_G_MULTI_CDC
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+
+ gether_set_qmult(ecm_opts->net, qmult);
+ if (!gether_set_host_addr(ecm_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#ifdef USB_ETH_RNDIS
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ status = PTR_ERR(fi_rndis);
+ goto fail;
+ }
+
+ rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
+
+ gether_set_qmult(rndis_opts->net, qmult);
+ if (!gether_set_host_addr(rndis_opts->net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
+
+#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
+ /*
+ * If both ecm and rndis are selected then:
+ * 1) rndis borrows the net interface from ecm
+ * 2) since the interface is shared it must not be bound
+ * twice - in ecm's _and_ rndis' binds, so do it here.
+ */
+ gether_set_gadget(ecm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ecm_opts->net);
+ if (status)
+ goto fail0;
+
+ rndis_borrow_net(fi_rndis, ecm_opts->net);
+ ecm_opts->bound = true;
+#endif
/* set up serial link layer */
- status = gserial_setup(cdev->gadget, 1);
- if (status < 0)
+ fi_acm = usb_get_function_instance("acm");
+ if (IS_ERR(fi_acm)) {
+ status = PTR_ERR(fi_acm);
goto fail0;
+ }
/* set up mass storage function */
- {
- void *retp;
- retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
- if (IS_ERR(retp)) {
- status = PTR_ERR(retp);
- goto fail1;
- }
+ fi_msg = usb_get_function_instance("mass_storage");
+ if (IS_ERR(fi_msg)) {
+ status = PTR_ERR(fi_msg);
+ goto fail1;
}
+ fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
+ fsg_opts = fsg_opts_from_func_inst(fi_msg);
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
- }
+ fsg_opts->no_configfs = true;
+ status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
+ if (status)
+ goto fail2;
+
+ status = fsg_common_set_nluns(fsg_opts->common, config.nluns);
+ if (status)
+ goto fail_set_nluns;
+
+ status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_sysfs(fsg_opts->common, true);
+ status = fsg_common_create_luns(fsg_opts->common, &config);
+ if (status)
+ goto fail_set_cdev;
+
+ fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
+ config.product_name);
/* allocate string IDs */
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
- goto fail2;
+ goto fail_string_ids;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register configurations */
status = rndis_config_register(cdev);
if (unlikely(status < 0))
- goto fail2;
+ goto fail_string_ids;
status = cdc_config_register(cdev);
if (unlikely(status < 0))
- goto fail2;
+ goto fail_string_ids;
+ usb_composite_overwrite_options(cdev, &coverwrite);
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC "\n");
- fsg_common_put(&fsg_common);
return 0;
/* error recovery */
+fail_string_ids:
+ fsg_common_remove_luns(fsg_opts->common);
+fail_set_cdev:
+ fsg_common_free_luns(fsg_opts->common);
+fail_set_nluns:
+ fsg_common_free_buffers(fsg_opts->common);
fail2:
- fsg_common_put(&fsg_common);
+ usb_put_function_instance(fi_msg);
fail1:
- gserial_cleanup();
+ usb_put_function_instance(fi_acm);
fail0:
- gether_cleanup();
+#ifdef USB_ETH_RNDIS
+ usb_put_function_instance(fi_rndis);
+fail:
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function_instance(fi_ecm);
+#endif
return status;
}
static int __exit multi_unbind(struct usb_composite_dev *cdev)
{
- gserial_cleanup();
- gether_cleanup();
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_msg_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_msg_rndis);
+#endif
+ usb_put_function_instance(fi_msg);
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_acm_multi);
+#endif
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_acm_rndis);
+#endif
+ usb_put_function_instance(fi_acm);
+#ifdef USB_ETH_RNDIS
+ usb_put_function(f_rndis);
+ usb_put_function_instance(fi_rndis);
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ usb_put_function(f_ecm);
+ usb_put_function_instance(fi_ecm);
+#endif
return 0;
}
@@ -338,20 +497,20 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
/****************************** Some noise ******************************/
-static struct usb_composite_driver multi_driver = {
+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),
- .iProduct = DRIVER_DESC,
.needs_serial = 1,
};
static int __init multi_init(void)
{
- return usb_composite_probe(&multi_driver, multi_bind);
+ return usb_composite_probe(&multi_driver);
}
module_init(multi_init);
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
index 34aadfae723..be77f207dba 100644
--- a/drivers/usb/gadget/mv_udc.h
+++ b/drivers/usb/gadget/mv_udc.h
@@ -217,13 +217,12 @@ struct mv_udc {
struct work_struct vbus_work;
struct workqueue_struct *qwork;
- struct otg_transceiver *transceiver;
+ struct usb_phy *transceiver;
struct mv_usb_platform_data *pdata;
/* some SOC has mutiple clock sources for USB*/
- unsigned int clknum;
- struct clk *clk[0];
+ struct clk *clk;
};
/* endpoint data structure */
@@ -232,7 +231,6 @@ struct mv_ep {
struct mv_udc *udc;
struct list_head queue;
struct mv_dqh *dqh;
- const struct usb_endpoint_descriptor *desc;
u32 direction;
char name[14];
unsigned stopped:1,
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index f97e737d26f..fcff3a571b4 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -19,7 +19,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/init.h>
+#include <linux/err.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -34,7 +34,6 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/platform_data/mv_usb.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include "mv_udc.h"
@@ -51,9 +50,8 @@
#define EPSTATUS_TIMEOUT 10000
#define PRIME_TIMEOUT 10000
#define READSAFE_TIMEOUT 1000
-#define DTD_TIMEOUT 1000
-#define LOOPS_USEC_SHIFT 4
+#define LOOPS_USEC_SHIFT 1
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
@@ -62,10 +60,6 @@ static DECLARE_COMPLETION(release_done);
static const char driver_name[] = "mv_udc";
static const char driver_desc[] = DRIVER_DESC;
-/* controller device global variable */
-static struct mv_udc *the_controller;
-int mv_usb_otgsc;
-
static void nuke(struct mv_ep *ep, int status);
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
@@ -217,6 +211,8 @@ static int process_ep_req(struct mv_udc *udc, int index,
* request is still in progress.
*/
static void done(struct mv_ep *ep, struct mv_req *req, int status)
+ __releases(&ep->udc->lock)
+ __acquires(&ep->udc->lock)
{
struct mv_udc *udc = NULL;
unsigned char stopped = ep->stopped;
@@ -242,18 +238,7 @@ static void done(struct mv_ep *ep, struct mv_req *req, int status)
dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma);
}
- if (req->mapped) {
- dma_unmap_single(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ((ep_dir(ep) == EP_DIR_IN) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE));
- req->req.dma = DMA_ADDR_INVALID;
- req->mapped = 0;
- } else
- dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ((ep_dir(ep) == EP_DIR_IN) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
if (status && (status != -ESHUTDOWN))
dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u",
@@ -357,17 +342,24 @@ done:
return retval;
}
-
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
dma_addr_t *dma, int *is_last)
{
- u32 temp;
struct mv_dtd *dtd;
struct mv_udc *udc;
+ struct mv_dqh *dqh;
+ u32 temp, mult = 0;
/* how big will this transfer be? */
- *length = min(req->req.length - req->req.actual,
- (unsigned)EP_MAX_LENGTH_TRANSFER);
+ 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;
@@ -375,7 +367,7 @@ static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
* Be careful that no _GFP_HIGHMEM is set,
* or we can not use dma_to_virt
*/
- dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, dma);
+ dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
if (dtd == NULL)
return dtd;
@@ -409,6 +401,8 @@ static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
if (*is_last && !req->req.no_interrupt)
temp |= DTD_IOC;
+ temp |= mult << 10;
+
dtd->size_ioc_sts = temp;
mb();
@@ -465,7 +459,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
ep = container_of(_ep, struct mv_ep, ep);
udc = ep->udc;
- if (!_ep || !desc || ep->desc
+ if (!_ep || !desc
|| desc->bDescriptorType != USB_DT_ENDPOINT)
return -EINVAL;
@@ -529,7 +523,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
dqh->size_ioc_int_sts = 0;
ep->ep.maxpacket = max;
- ep->desc = desc;
+ ep->ep.desc = desc;
ep->stopped = 0;
/* Enable the endpoint for Rx or Tx and set the endpoint type */
@@ -581,7 +575,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
unsigned long flags;
ep = container_of(_ep, struct mv_ep, ep);
- if ((_ep == NULL) || !ep->desc)
+ if ((_ep == NULL) || !ep->ep.desc)
return -EINVAL;
udc = ep->udc;
@@ -607,7 +601,7 @@ static int mv_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);
@@ -651,7 +645,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep)
return;
ep = container_of(_ep, struct mv_ep, ep);
- if (!ep->desc)
+ if (!ep->ep.desc)
return;
udc = ep->udc;
@@ -708,6 +702,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
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
@@ -715,14 +710,10 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
dev_err(&udc->dev->dev, "%s, bad params", __func__);
return -EINVAL;
}
- if (unlikely(!_ep || !ep->desc)) {
+ if (unlikely(!_ep || !ep->ep.desc)) {
dev_err(&udc->dev->dev, "%s, bad ep", __func__);
return -EINVAL;
}
- if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- if (req->req.length > ep->ep.maxpacket)
- return -EMSGSIZE;
- }
udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@@ -731,21 +722,9 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
req->ep = ep;
/* map virtual address to hardware */
- if (req->req.dma == DMA_ADDR_INVALID) {
- req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
- req->req.buf,
- req->req.length, ep_dir(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
- req->mapped = 1;
- } else {
- dma_sync_single_for_device(ep->udc->gadget.dev.parent,
- req->req.dma, req->req.length,
- ep_dir(ep)
- ? DMA_TO_DEVICE
- : DMA_FROM_DEVICE);
- req->mapped = 0;
- }
+ retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep));
+ if (retval)
+ return retval;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
@@ -755,15 +734,17 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/* build dtds and push them to device queue */
if (!req_to_dtd(req)) {
- int retval;
retval = queue_dtd(ep, req);
if (retval) {
spin_unlock_irqrestore(&udc->lock, flags);
- return retval;
+ dev_err(&udc->dev->dev, "Failed to queue dtd\n");
+ goto err_unmap_dma;
}
} else {
spin_unlock_irqrestore(&udc->lock, flags);
- return -ENOMEM;
+ dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
+ retval = -ENOMEM;
+ goto err_unmap_dma;
}
/* Update ep0 state */
@@ -771,11 +752,15 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
udc->ep0_state = DATA_STATE_XMIT;
/* irq handler advances the queue */
- if (req != NULL)
- list_add_tail(&req->queue, &ep->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)
@@ -926,12 +911,12 @@ static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
ep = container_of(_ep, struct mv_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 (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
status = -EOPNOTSUPP;
goto out;
}
@@ -988,18 +973,12 @@ static struct usb_ep_ops mv_ep_ops = {
static void udc_clock_enable(struct mv_udc *udc)
{
- unsigned int i;
-
- for (i = 0; i < udc->clknum; i++)
- clk_enable(udc->clk[i]);
+ clk_prepare_enable(udc->clk);
}
static void udc_clock_disable(struct mv_udc *udc)
{
- unsigned int i;
-
- for (i = 0; i < udc->clknum; i++)
- clk_disable(udc->clk[i]);
+ clk_disable_unprepare(udc->clk);
}
static void udc_stop(struct mv_udc *udc)
@@ -1066,7 +1045,7 @@ static int udc_reset(struct mv_udc *udc)
tmp |= USBMODE_CTRL_MODE_DEVICE;
/* turn setup lockout off, require setup tripwire in usbcmd */
- tmp |= USBMODE_SETUP_LOCK_OFF | USBMODE_STREAM_DISABLE;
+ tmp |= USBMODE_SETUP_LOCK_OFF;
writel(tmp, &udc->op_regs->usbmode);
@@ -1200,12 +1179,16 @@ static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
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;
}
@@ -1243,9 +1226,8 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
return retval;
}
-static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int mv_udc_stop(struct usb_gadget_driver *driver);
+static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
+static int mv_udc_stop(struct usb_gadget *, struct usb_gadget_driver *);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops mv_ops = {
@@ -1260,8 +1242,8 @@ static const struct usb_gadget_ops mv_ops = {
/* D+ pullup, software-controlled connect/disconnect to USB host */
.pullup = mv_udc_pullup,
- .start = mv_udc_start,
- .stop = mv_udc_stop,
+ .udc_start = mv_udc_start,
+ .udc_stop = mv_udc_stop,
};
static int eps_init(struct mv_udc *udc)
@@ -1278,9 +1260,9 @@ static int eps_init(struct mv_udc *udc)
ep->ep.ops = &mv_ep_ops;
ep->wedge = 0;
ep->stopped = 0;
- ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE);
ep->ep_num = 0;
- ep->desc = &mv_ep0_desc;
+ ep->ep.desc = &mv_ep0_desc;
INIT_LIST_HEAD(&ep->queue);
ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
@@ -1301,7 +1283,7 @@ static int eps_init(struct mv_udc *udc)
ep->ep.ops = &mv_ep_ops;
ep->stopped = 0;
- ep->ep.maxpacket = (unsigned short) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
ep->ep_num = i / 2;
INIT_LIST_HEAD(&ep->queue);
@@ -1348,15 +1330,14 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
}
}
-static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int mv_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
int retval = 0;
unsigned long flags;
- if (!udc)
- return -ENODEV;
+ udc = container_of(gadget, struct mv_udc, gadget);
if (udc->driver)
return -EBUSY;
@@ -1366,7 +1347,6 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
/* hook up the driver ... */
driver->driver.bus = NULL;
udc->driver = driver;
- udc->gadget.dev.driver = &driver->driver;
udc->usb_state = USB_STATE_ATTACHED;
udc->ep0_state = WAIT_FOR_SETUP;
@@ -1374,25 +1354,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
spin_unlock_irqrestore(&udc->lock, flags);
- retval = bind(&udc->gadget);
- if (retval) {
- dev_err(&udc->dev->dev, "bind to driver %s --> %d\n",
- driver->driver.name, retval);
- udc->driver = NULL;
- udc->gadget.dev.driver = NULL;
- return retval;
- }
-
if (udc->transceiver) {
- retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
+ retval = otg_set_peripheral(udc->transceiver->otg,
+ &udc->gadget);
if (retval) {
dev_err(&udc->dev->dev,
"unable to register peripheral to otg\n");
- if (driver->unbind) {
- driver->unbind(&udc->gadget);
- udc->gadget.dev.driver = NULL;
- udc->driver = NULL;
- }
+ udc->driver = NULL;
return retval;
}
}
@@ -1407,13 +1375,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
return 0;
}
-static int mv_udc_stop(struct usb_gadget_driver *driver)
+static int mv_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
unsigned long flags;
- if (!udc)
- return -ENODEV;
+ udc = container_of(gadget, struct mv_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
@@ -1428,8 +1396,6 @@ static int mv_udc_stop(struct usb_gadget_driver *driver)
spin_unlock_irqrestore(&udc->lock, flags);
/* unbind gadget driver */
- driver->unbind(&udc->gadget);
- udc->gadget.dev.driver = NULL;
udc->driver = NULL;
return 0;
@@ -1446,10 +1412,13 @@ static void mv_set_ptc(struct mv_udc *udc, u32 mode)
static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req)
{
- struct mv_udc *udc = the_controller;
+ struct mv_ep *mvep = container_of(ep, struct mv_ep, ep);
struct mv_req *req = container_of(_req, struct mv_req, req);
+ struct mv_udc *udc;
unsigned long flags;
+ udc = mvep->udc;
+
dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode);
spin_lock_irqsave(&udc->lock, flags);
@@ -1499,15 +1468,17 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
}
/* prime the data phase */
- if (!req_to_dtd(req))
+ if (!req_to_dtd(req)) {
retval = queue_dtd(ep, req);
- else{ /* no mem */
+ if (retval) {
+ dev_err(&udc->dev->dev,
+ "Failed to queue dtd when prime status\n");
+ goto out;
+ }
+ } else{ /* no mem */
retval = -ENOMEM;
- goto out;
- }
-
- if (retval) {
- dev_err(&udc->dev->dev, "response error on GET_STATUS request\n");
+ dev_err(&udc->dev->dev,
+ "Failed to dma_pool_alloc when prime status\n");
goto out;
}
@@ -1515,6 +1486,8 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
return 0;
out:
+ usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
+
return retval;
}
@@ -1673,6 +1646,8 @@ out:
static void handle_setup_packet(struct mv_udc *udc, u8 ep_num,
struct usb_ctrlrequest *setup)
+ __releases(&ep->udc->lock)
+ __acquires(&ep->udc->lock)
{
bool delegate = false;
@@ -1869,7 +1844,7 @@ static void irq_process_tr_complete(struct mv_udc *udc)
}
}
-void irq_process_reset(struct mv_udc *udc)
+static void irq_process_reset(struct mv_udc *udc)
{
u32 tmp;
unsigned int loops;
@@ -2086,15 +2061,18 @@ static void mv_udc_vbus_work(struct work_struct *work)
/* release device structure */
static void gadget_release(struct device *_dev)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
+
+ udc = dev_get_drvdata(_dev);
complete(udc->done);
}
-static int __devexit mv_udc_remove(struct platform_device *dev)
+static int mv_udc_remove(struct platform_device *pdev)
{
- struct mv_udc *udc = the_controller;
- int clk_i;
+ struct mv_udc *udc;
+
+ udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
@@ -2103,129 +2081,95 @@ static int __devexit mv_udc_remove(struct platform_device *dev)
destroy_workqueue(udc->qwork);
}
- /*
- * If we have transceiver inited,
- * then vbus irq will not be requested in udc driver.
- */
- if (udc->pdata && udc->pdata->vbus
- && udc->clock_gating && udc->transceiver == NULL)
- free_irq(udc->pdata->vbus->irq, &dev->dev);
-
/* free memory allocated in probe */
if (udc->dtd_pool)
dma_pool_destroy(udc->dtd_pool);
if (udc->ep_dqh)
- dma_free_coherent(&dev->dev, udc->ep_dqh_size,
+ dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
udc->ep_dqh, udc->ep_dqh_dma);
- kfree(udc->eps);
-
- if (udc->irq)
- free_irq(udc->irq, &dev->dev);
-
mv_udc_disable(udc);
- if (udc->cap_regs)
- iounmap(udc->cap_regs);
-
- if (udc->phy_regs)
- iounmap(udc->phy_regs);
-
- if (udc->status_req) {
- kfree(udc->status_req->req.buf);
- kfree(udc->status_req);
- }
-
- for (clk_i = 0; clk_i <= udc->clknum; clk_i++)
- clk_put(udc->clk[clk_i]);
-
- device_unregister(&udc->gadget.dev);
-
/* free dev, wait for the release() finished */
wait_for_completion(udc->done);
- kfree(udc);
-
- the_controller = NULL;
return 0;
}
-static int __devinit mv_udc_probe(struct platform_device *dev)
+static int mv_udc_probe(struct platform_device *pdev)
{
- struct mv_usb_platform_data *pdata = dev->dev.platform_data;
+ struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mv_udc *udc;
int retval = 0;
- int clk_i = 0;
struct resource *r;
size_t size;
if (pdata == NULL) {
- dev_err(&dev->dev, "missing platform_data\n");
+ dev_err(&pdev->dev, "missing platform_data\n");
return -ENODEV;
}
- size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum;
- udc = kzalloc(size, GFP_KERNEL);
+ udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
if (udc == NULL) {
- dev_err(&dev->dev, "failed to allocate memory for udc\n");
+ dev_err(&pdev->dev, "failed to allocate memory for udc\n");
return -ENOMEM;
}
- the_controller = udc;
udc->done = &release_done;
- udc->pdata = dev->dev.platform_data;
+ udc->pdata = dev_get_platdata(&pdev->dev);
spin_lock_init(&udc->lock);
- udc->dev = dev;
+ udc->dev = pdev;
-#ifdef CONFIG_USB_OTG_UTILS
- if (pdata->mode == MV_USB_MODE_OTG)
- udc->transceiver = otg_get_transceiver();
-#endif
+ if (pdata->mode == MV_USB_MODE_OTG) {
+ udc->transceiver = devm_usb_get_phy(&pdev->dev,
+ USB_PHY_TYPE_USB2);
+ if (IS_ERR(udc->transceiver)) {
+ retval = PTR_ERR(udc->transceiver);
- udc->clknum = pdata->clknum;
- for (clk_i = 0; clk_i < udc->clknum; clk_i++) {
- udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]);
- if (IS_ERR(udc->clk[clk_i])) {
- retval = PTR_ERR(udc->clk[clk_i]);
- goto err_put_clk;
+ 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(&dev->dev, "no I/O memory resource defined\n");
- retval = -ENODEV;
- goto err_put_clk;
+ dev_err(&pdev->dev, "no I/O memory resource defined\n");
+ return -ENODEV;
}
udc->cap_regs = (struct mv_cap_regs __iomem *)
- ioremap(r->start, resource_size(r));
+ devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (udc->cap_regs == NULL) {
- dev_err(&dev->dev, "failed to map I/O memory\n");
- retval = -EBUSY;
- goto err_put_clk;
+ dev_err(&pdev->dev, "failed to map I/O memory\n");
+ return -EBUSY;
}
r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
if (r == NULL) {
- dev_err(&dev->dev, "no phy I/O memory resource defined\n");
- retval = -ENODEV;
- goto err_iounmap_capreg;
+ dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
+ return -ENODEV;
}
udc->phy_regs = ioremap(r->start, resource_size(r));
if (udc->phy_regs == NULL) {
- dev_err(&dev->dev, "failed to map phy I/O memory\n");
- retval = -EBUSY;
- goto err_iounmap_capreg;
+ dev_err(&pdev->dev, "failed to map phy I/O memory\n");
+ return -EBUSY;
}
/* we will acces controller register, so enable the clk */
retval = mv_udc_enable_internal(udc);
if (retval)
- goto err_iounmap_phyreg;
+ return retval;
udc->op_regs =
(struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
@@ -2242,11 +2186,11 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
size = udc->max_eps * sizeof(struct mv_dqh) *2;
size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
- udc->ep_dqh = dma_alloc_coherent(&dev->dev, size,
+ udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
&udc->ep_dqh_dma, GFP_KERNEL);
if (udc->ep_dqh == NULL) {
- dev_err(&dev->dev, "allocate dQH memory failed\n");
+ dev_err(&pdev->dev, "allocate dQH memory failed\n");
retval = -ENOMEM;
goto err_disable_clock;
}
@@ -2254,7 +2198,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
/* create dTD dma_pool resource */
udc->dtd_pool = dma_pool_create("mv_dtd",
- &dev->dev,
+ &pdev->dev,
sizeof(struct mv_dtd),
DTD_ALIGNMENT,
DMA_BOUNDARY);
@@ -2265,19 +2209,20 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
}
size = udc->max_eps * sizeof(struct mv_ep) *2;
- udc->eps = kzalloc(size, GFP_KERNEL);
+ udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (udc->eps == NULL) {
- dev_err(&dev->dev, "allocate ep memory failed\n");
+ dev_err(&pdev->dev, "allocate ep memory failed\n");
retval = -ENOMEM;
goto err_destroy_dma;
}
/* initialize ep0 status request structure */
- udc->status_req = kzalloc(sizeof(struct mv_req), GFP_KERNEL);
+ udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
+ GFP_KERNEL);
if (!udc->status_req) {
- dev_err(&dev->dev, "allocate status_req memory failed\n");
+ dev_err(&pdev->dev, "allocate status_req memory failed\n");
retval = -ENOMEM;
- goto err_free_eps;
+ goto err_destroy_dma;
}
INIT_LIST_HEAD(&udc->status_req->queue);
@@ -2292,17 +2237,17 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0);
if (r == NULL) {
- dev_err(&dev->dev, "no IRQ resource defined\n");
+ dev_err(&pdev->dev, "no IRQ resource defined\n");
retval = -ENODEV;
- goto err_free_status_req;
+ goto err_destroy_dma;
}
udc->irq = r->start;
- if (request_irq(udc->irq, mv_udc_irq,
+ if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq,
IRQF_SHARED, driver_name, udc)) {
- dev_err(&dev->dev, "Request irq %d for UDC failed\n",
+ dev_err(&pdev->dev, "Request irq %d for UDC failed\n",
udc->irq);
retval = -ENODEV;
- goto err_free_status_req;
+ goto err_destroy_dma;
}
/* initialize gadget structure */
@@ -2313,16 +2258,8 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */
/* the "gadget" abstracts/virtualizes the controller */
- dev_set_name(&udc->gadget.dev, "gadget");
- udc->gadget.dev.parent = &dev->dev;
- udc->gadget.dev.dma_mask = dev->dev.dma_mask;
- udc->gadget.dev.release = gadget_release;
udc->gadget.name = driver_name; /* gadget name */
- retval = device_register(&udc->gadget.dev);
- if (retval)
- goto err_free_irq;
-
eps_init(udc);
/* VBUS detect: we can disable/enable clock on demand.*/
@@ -2330,10 +2267,11 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
udc->clock_gating = 1;
else if (pdata->vbus) {
udc->clock_gating = 1;
- retval = request_threaded_irq(pdata->vbus->irq, NULL,
+ retval = devm_request_threaded_irq(&pdev->dev,
+ pdata->vbus->irq, NULL,
mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
if (retval) {
- dev_info(&dev->dev,
+ dev_info(&pdev->dev,
"Can not request irq for VBUS, "
"disable clock gating\n");
udc->clock_gating = 0;
@@ -2341,9 +2279,9 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
udc->qwork = create_singlethread_workqueue("mv_udc_queue");
if (!udc->qwork) {
- dev_err(&dev->dev, "cannot create workqueue\n");
+ dev_err(&pdev->dev, "cannot create workqueue\n");
retval = -ENOMEM;
- goto err_unregister;
+ goto err_destroy_dma;
}
INIT_WORK(&udc->vbus_work, mv_udc_vbus_work);
@@ -2359,50 +2297,36 @@ static int __devinit mv_udc_probe(struct platform_device *dev)
else
udc->vbus_active = 1;
- retval = usb_add_gadget_udc(&dev->dev, &udc->gadget);
+ retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+ gadget_release);
if (retval)
- goto err_unregister;
+ goto err_create_workqueue;
- dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n",
+ platform_set_drvdata(pdev, udc);
+ dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n",
udc->clock_gating ? "with" : "without");
return 0;
-err_unregister:
- if (udc->pdata && udc->pdata->vbus
- && udc->clock_gating && udc->transceiver == NULL)
- free_irq(pdata->vbus->irq, &dev->dev);
- device_unregister(&udc->gadget.dev);
-err_free_irq:
- free_irq(udc->irq, &dev->dev);
-err_free_status_req:
- kfree(udc->status_req->req.buf);
- kfree(udc->status_req);
-err_free_eps:
- kfree(udc->eps);
+err_create_workqueue:
+ destroy_workqueue(udc->qwork);
err_destroy_dma:
dma_pool_destroy(udc->dtd_pool);
err_free_dma:
- dma_free_coherent(&dev->dev, udc->ep_dqh_size,
+ dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
udc->ep_dqh, udc->ep_dqh_dma);
err_disable_clock:
mv_udc_disable_internal(udc);
-err_iounmap_phyreg:
- iounmap(udc->phy_regs);
-err_iounmap_capreg:
- iounmap(udc->cap_regs);
-err_put_clk:
- for (clk_i--; clk_i >= 0; clk_i--)
- clk_put(udc->clk[clk_i]);
- the_controller = NULL;
- kfree(udc);
+
return retval;
}
#ifdef CONFIG_PM
-static int mv_udc_suspend(struct device *_dev)
+static int mv_udc_suspend(struct device *dev)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
+
+ udc = dev_get_drvdata(dev);
/* if OTG is enabled, the following will be done in OTG driver*/
if (udc->transceiver)
@@ -2432,11 +2356,13 @@ static int mv_udc_suspend(struct device *_dev)
return 0;
}
-static int mv_udc_resume(struct device *_dev)
+static int mv_udc_resume(struct device *dev)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
int retval;
+ udc = dev_get_drvdata(dev);
+
/* if OTG is enabled, the following will be done in OTG driver*/
if (udc->transceiver)
return 0;
@@ -2462,20 +2388,23 @@ static const struct dev_pm_ops mv_udc_pm_ops = {
};
#endif
-static void mv_udc_shutdown(struct platform_device *dev)
+static void mv_udc_shutdown(struct platform_device *pdev)
{
- struct mv_udc *udc = the_controller;
+ struct mv_udc *udc;
u32 mode;
+ udc = platform_get_drvdata(pdev);
/* reset controller mode to IDLE */
+ mv_udc_enable(udc);
mode = readl(&udc->op_regs->usbmode);
mode &= ~3;
writel(mode, &udc->op_regs->usbmode);
+ mv_udc_disable(udc);
}
static struct platform_driver udc_driver = {
.probe = mv_udc_probe,
- .remove = __exit_p(mv_udc_remove),
+ .remove = mv_udc_remove,
.shutdown = mv_udc_shutdown,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
index 89530034dff..81956feca1b 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/ncm.c
@@ -20,32 +20,16 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
+#include <linux/module.h>
+#include <linux/usb/composite.h>
#include "u_ether.h"
+#include "u_ncm.h"
#define DRIVER_DESC "NCM Gadget"
/*-------------------------------------------------------------------------*/
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
-#include "f_ncm.c"
-#include "u_ether.c"
-
-/*-------------------------------------------------------------------------*/
-
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
@@ -57,6 +41,9 @@
#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,
@@ -97,17 +84,11 @@ 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 = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -121,12 +102,15 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
-static u8 hostaddr[ETH_ALEN];
+static struct usb_function_instance *f_ncm_inst;
+static struct usb_function *f_ncm;
/*-------------------------------------------------------------------------*/
static int __init ncm_do_config(struct usb_configuration *c)
{
+ int status;
+
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
@@ -134,7 +118,19 @@ static int __init ncm_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- return ncm_bind_config(c, hostaddr);
+ f_ncm = usb_get_function(f_ncm_inst);
+ if (IS_ERR(f_ncm)) {
+ status = PTR_ERR(f_ncm);
+ return status;
+ }
+
+ status = usb_add_function(c, f_ncm);
+ if (status < 0) {
+ usb_put_function(f_ncm);
+ return status;
+ }
+
+ return 0;
}
static struct usb_configuration ncm_config_driver = {
@@ -149,77 +145,62 @@ static struct usb_configuration ncm_config_driver = {
static int __init gncm_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
+ struct f_ncm_opts *ncm_opts;
int status;
- /* set up network link layer */
- status = gether_setup(cdev->gadget, hostaddr);
- if (status < 0)
- return status;
+ f_ncm_inst = usb_get_function_instance("ncm");
+ if (IS_ERR(f_ncm_inst))
+ return PTR_ERR(f_ncm_inst);
- 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,
- ncm_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
+ 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.
*/
- /* 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;
+ 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:
- gether_cleanup();
+ usb_put_function_instance(f_ncm_inst);
return status;
}
static int __exit gncm_unbind(struct usb_composite_dev *cdev)
{
- gether_cleanup();
+ if (!IS_ERR_OR_NULL(f_ncm))
+ usb_put_function(f_ncm);
+ if (!IS_ERR_OR_NULL(f_ncm_inst))
+ usb_put_function_instance(f_ncm_inst);
return 0;
}
-static struct usb_composite_driver ncm_driver = {
+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),
};
@@ -229,7 +210,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&ncm_driver, gncm_bind);
+ return usb_composite_probe(&ncm_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h
index b0e52fc277b..a19f72dec0c 100644
--- a/drivers/usb/gadget/ndis.h
+++ b/drivers/usb/gadget/ndis.h
@@ -15,11 +15,6 @@
#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,
@@ -35,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;
@@ -54,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
index 7322d293213..ca15405583e 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/net2272.c
@@ -42,7 +42,6 @@
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include "net2272.h"
@@ -59,8 +58,7 @@ static const char * const ep_name[] = {
"ep-a", "ep-b", "ep-c",
};
-#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-#ifdef CONFIG_USB_GADGET_NET2272_DMA
+#ifdef CONFIG_USB_NET2272_DMA
/*
* use_dma: the NET2272 can use an external DMA controller.
* Note that since there is no generic DMA api, some functions,
@@ -268,7 +266,7 @@ static void net2272_ep_reset(struct net2272_ep *ep)
ep->desc = NULL;
INIT_LIST_HEAD(&ep->queue);
- ep->ep.maxpacket = ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
ep->ep.ops = &net2272_ep_ops;
/* disable irqs, endpoint */
@@ -342,7 +340,6 @@ net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
if (!req)
return NULL;
- req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue);
return &req->req;
@@ -385,12 +382,9 @@ net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status)
status = req->req.status;
dev = ep->dev;
- if (use_dma && req->mapped) {
- dma_unmap_single(dev->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 (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",
@@ -850,10 +844,11 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
return -ESHUTDOWN;
/* set up dma mapping in case the caller didn't */
- if (use_dma && ep->dma && _req->dma == DMA_ADDR_INVALID) {
- _req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
- ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = 1;
+ 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",
@@ -916,7 +911,7 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
}
}
}
- if (likely(req != 0))
+ if (likely(req))
list_add_tail(&req->queue, &ep->queue);
if (likely(!list_empty(&ep->queue)))
@@ -1189,7 +1184,7 @@ static const struct usb_gadget_ops net2272_ops = {
/*---------------------------------------------------------------------------*/
static ssize_t
-net2272_show_registers(struct device *_dev, struct device_attribute *attr, char *buf)
+registers_show(struct device *_dev, struct device_attribute *attr, char *buf)
{
struct net2272 *dev;
char *next;
@@ -1313,7 +1308,7 @@ net2272_show_registers(struct device *_dev, struct device_attribute *attr, char
return PAGE_SIZE - size;
}
-static DEVICE_ATTR(registers, S_IRUGO, net2272_show_registers, NULL);
+static DEVICE_ATTR_RO(registers);
/*---------------------------------------------------------------------------*/
@@ -1414,7 +1409,7 @@ net2272_usb_reinit(struct net2272 *dev)
ep->fifo_size = 64;
net2272_ep_reset(ep);
}
- dev->ep[0].ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
dev->gadget.ep0 = &dev->ep[0].ep;
dev->ep[0].stopped = 0;
@@ -1470,7 +1465,6 @@ static int net2272_start(struct usb_gadget *_gadget,
dev->softconnect = 1;
driver->driver.bus = NULL;
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
@@ -1498,6 +1492,13 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
for (i = 0; i < 4; ++i)
net2272_dequeue_all(&dev->ep[i]);
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
net2272_usb_reinit(dev);
}
@@ -1513,7 +1514,6 @@ static int net2272_stop(struct usb_gadget *_gadget,
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name);
@@ -1545,7 +1545,7 @@ net2272_handle_dma(struct net2272_ep *ep)
| (ep->dev->dma_eot_polarity << EOT_POLARITY)
| (ep->dev->dma_dack_polarity << DACK_POLARITY)
| (ep->dev->dma_dreq_polarity << DREQ_POLARITY)
- | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT));
+ | (ep->dma << DMA_ENDPOINT_SELECT));
ep->dev->dma_busy = 0;
@@ -1618,7 +1618,7 @@ net2272_handle_ep(struct net2272_ep *ep)
ep->irqs++;
dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n",
- ep->ep.name, stat0, stat1, req ? &req->req : 0);
+ ep->ep.name, stat0, stat1, req ? &req->req : NULL);
net2272_ep_write(ep, EP_STAT0, stat0 &
~((1 << NAK_OUT_PACKETS)
@@ -2072,8 +2072,10 @@ static irqreturn_t net2272_irq(int irq, void *_dev)
#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))
+ if (!intcsr & (1 << NET2272_PCI_IRQ)) {
+ spin_unlock(&dev->lock);
return IRQ_NONE;
+ }
/* check dma interrupts */
#endif
/* Platform/devcice interrupt handler */
@@ -2194,7 +2196,7 @@ net2272_gadget_release(struct device *_dev)
/*---------------------------------------------------------------------------*/
-static void __devexit
+static void
net2272_remove(struct net2272 *dev)
{
usb_del_gadget_udc(&dev->gadget);
@@ -2210,14 +2212,12 @@ net2272_remove(struct net2272 *dev)
free_irq(dev->irq, dev);
iounmap(dev->base_addr);
- device_unregister(&dev->gadget.dev);
device_remove_file(dev->dev, &dev_attr_registers);
dev_info(dev->dev, "unbind\n");
}
-static struct net2272 * __devinit
-net2272_probe_init(struct device *dev, unsigned int irq)
+static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq)
{
struct net2272 *ret;
@@ -2238,16 +2238,12 @@ net2272_probe_init(struct device *dev, unsigned int irq)
ret->gadget.max_speed = USB_SPEED_HIGH;
/* the "gadget" abstracts/virtualizes the controller */
- dev_set_name(&ret->gadget.dev, "gadget");
- ret->gadget.dev.parent = dev;
- ret->gadget.dev.dma_mask = dev->dma_mask;
- ret->gadget.dev.release = net2272_gadget_release;
ret->gadget.name = driver_name;
return ret;
}
-static int __devinit
+static int
net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
{
int ret;
@@ -2277,14 +2273,12 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
dma_mode_string());
dev_info(dev->dev, "version: %s\n", driver_vers);
- ret = device_register(&dev->gadget.dev);
- if (ret)
- goto err_irq;
ret = device_create_file(dev->dev, &dev_attr_registers);
if (ret)
- goto err_dev_reg;
+ goto err_irq;
- ret = usb_add_gadget_udc(dev->dev, &dev->gadget);
+ ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget,
+ net2272_gadget_release);
if (ret)
goto err_add_udc;
@@ -2292,8 +2286,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
err_add_udc:
device_remove_file(dev->dev, &dev_attr_registers);
- err_dev_reg:
- device_unregister(&dev->gadget.dev);
err_irq:
free_irq(dev->irq, dev);
err:
@@ -2307,7 +2299,7 @@ err_add_udc:
* don't respond over USB until a gadget driver binds to us
*/
-static int __devinit
+static int
net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev)
{
unsigned long resource, len, tmp;
@@ -2390,7 +2382,7 @@ net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev)
return ret;
}
-static int __devinit
+static int
net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev)
{
unsigned long resource, len;
@@ -2448,7 +2440,7 @@ net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev)
return ret;
}
-static int __devinit
+static int
net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct net2272 *dev;
@@ -2490,7 +2482,7 @@ net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;
}
-static void __devexit
+static void
net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev)
{
int i;
@@ -2512,7 +2504,7 @@ net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev)
}
}
-static void __devexit
+static void
net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev)
{
int i;
@@ -2531,7 +2523,7 @@ net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev)
pci_resource_len(pdev, i));
}
-static void __devexit
+static void
net2272_pci_remove(struct pci_dev *pdev)
{
struct net2272 *dev = pci_get_drvdata(pdev);
@@ -2550,7 +2542,7 @@ net2272_pci_remove(struct pci_dev *pdev)
}
/* Table of matching PCI IDs */
-static struct pci_device_id __devinitdata pci_ids[] = {
+static struct pci_device_id pci_ids[] = {
{ /* RDK 1 card */
.class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
.class_mask = 0,
@@ -2576,7 +2568,7 @@ static struct pci_driver net2272_pci_driver = {
.id_table = pci_ids,
.probe = net2272_pci_probe,
- .remove = __devexit_p(net2272_pci_remove),
+ .remove = net2272_pci_remove,
};
static int net2272_pci_register(void)
@@ -2596,7 +2588,7 @@ static inline void net2272_pci_unregister(void) { }
/*---------------------------------------------------------------------------*/
-static int __devinit
+static int
net2272_plat_probe(struct platform_device *pdev)
{
struct net2272 *dev;
@@ -2662,7 +2654,7 @@ net2272_plat_probe(struct platform_device *pdev)
return ret;
}
-static int __devexit
+static int
net2272_plat_remove(struct platform_device *pdev)
{
struct net2272 *dev = platform_get_drvdata(pdev);
@@ -2679,7 +2671,7 @@ net2272_plat_remove(struct platform_device *pdev)
static struct platform_driver net2272_plat_driver = {
.probe = net2272_plat_probe,
- .remove = __devexit_p(net2272_plat_remove),
+ .remove = net2272_plat_remove,
.driver = {
.name = driver_name,
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index cdedd133674..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
@@ -59,14 +59,12 @@
#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 */
@@ -117,6 +115,10 @@ static bool enable_suspend = 0;
/* "modprobe net2280 enable_suspend=1" etc */
module_param (enable_suspend, bool, S_IRUGO);
+/* force full-speed operation */
+static bool full_speed;
+module_param(full_speed, bool, 0444);
+MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
@@ -127,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
@@ -291,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... */
@@ -403,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 */
@@ -417,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;
}
@@ -806,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",
@@ -857,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
@@ -1425,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);
@@ -1436,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;
@@ -1591,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;
@@ -1631,7 +1630,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
val = "intr"; break;
default:
val = "iso"; break;
- }; val; }),
+ } val; }),
usb_endpoint_maxp (d) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
);
@@ -1691,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
@@ -1806,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;
@@ -1894,13 +1893,16 @@ static int net2280_start(struct usb_gadget *_gadget,
dev->softconnect = 1;
driver->driver.bus = NULL;
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
if (retval) goto err_unbind;
retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
if (retval) goto err_func;
+ /* Enable force-full-speed testing mode, if desired */
+ if (full_speed)
+ writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag);
+
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
@@ -1918,8 +1920,6 @@ static int net2280_start(struct usb_gadget *_gadget,
err_func:
device_remove_file (&dev->pdev->dev, &dev_attr_function);
err_unbind:
- driver->unbind (&dev->gadget);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
return retval;
}
@@ -1940,6 +1940,13 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
for (i = 0; i < 7; i++)
nuke (&dev->ep [i]);
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
usb_reinit (dev);
}
@@ -1955,14 +1962,19 @@ static int net2280_stop(struct usb_gadget *_gadget,
stop_activity (dev, driver);
spin_unlock_irqrestore (&dev->lock, flags);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
net2280_led_active (dev, 0);
+
+ /* Disable full-speed test mode */
+ writel(0, &dev->usb->xcvrdiag);
+
device_remove_file (&dev->pdev->dev, &dev_attr_function);
device_remove_file (&dev->pdev->dev, &dev_attr_queues);
- DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
+ DEBUG(dev, "unregistered driver '%s'\n",
+ driver ? driver->driver.name : "");
+
return 0;
}
@@ -2056,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;
@@ -2314,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;
@@ -2342,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",
@@ -2364,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;
@@ -2669,9 +2681,7 @@ static void net2280_remove (struct pci_dev *pdev)
pci_resource_len (pdev, 0));
if (dev->enabled)
pci_disable_device (pdev);
- device_unregister (&dev->gadget.dev);
device_remove_file (&pdev->dev, &dev_attr_registers);
- pci_set_drvdata (pdev, NULL);
INFO (dev, "unbind\n");
}
@@ -2701,10 +2711,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.max_speed = USB_SPEED_HIGH;
/* the "gadget" abstracts/virtualizes the controller */
- dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.dev.parent = &pdev->dev;
- dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
- dev->gadget.dev.release = gadget_release;
dev->gadget.name = driver_name;
/* now all the pci goodies ... */
@@ -2786,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;
}
@@ -2813,12 +2818,11 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
use_dma
? (use_dma_chaining ? "chaining" : "enabled")
: "disabled");
- retval = device_register (&dev->gadget.dev);
- if (retval) goto done;
retval = device_create_file (&pdev->dev, &dev_attr_registers);
if (retval) goto done;
- retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+ gadget_release);
if (retval)
goto done;
return 0;
@@ -2843,6 +2847,9 @@ static void net2280_shutdown (struct pci_dev *pdev)
/* disable the pullup so the host will think we're gone */
writel (0, &dev->usb->usbctl);
+
+ /* Disable full-speed test mode */
+ writel(0, &dev->usb->xcvrdiag);
}
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c
index c7fb7723c01..3ab38616751 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/nokia.c
@@ -16,12 +16,13 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.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 */
@@ -29,46 +30,25 @@
#define NOKIA_VERSION_NUM 0x0211
#define NOKIA_LONG_NAME "N900 (PC-Suite Mode)"
-/*-------------------------------------------------------------------------*/
+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 "f_obex.c"
-#include "f_serial.c"
-#include "f_phonet.c"
-#include "u_ether.c"
-
-/*-------------------------------------------------------------------------*/
+USB_ETHERNET_MODULE_PARAMETERS();
#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#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[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer_nokia,
- [STRING_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [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 */
};
@@ -90,6 +70,7 @@ static struct usb_device_descriptor device_desc = {
.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,
@@ -103,42 +84,24 @@ MODULE_AUTHOR("Felipe Balbi");
MODULE_LICENSE("GPL");
/*-------------------------------------------------------------------------*/
+static struct usb_function *f_acm_cfg1;
+static struct usb_function *f_acm_cfg2;
+static struct usb_function *f_ecm_cfg1;
+static struct usb_function *f_ecm_cfg2;
+static struct usb_function *f_obex1_cfg1;
+static struct usb_function *f_obex2_cfg1;
+static struct usb_function *f_obex1_cfg2;
+static struct usb_function *f_obex2_cfg2;
+static struct usb_function *f_phonet_cfg1;
+static struct usb_function *f_phonet_cfg2;
-static u8 hostaddr[ETH_ALEN];
-
-static int __init nokia_bind_config(struct usb_configuration *c)
-{
- int status = 0;
-
- status = phonet_bind_config(c);
- if (status)
- printk(KERN_DEBUG "could not bind phonet config\n");
-
- status = obex_bind_config(c, 0);
- if (status)
- printk(KERN_DEBUG "could not bind obex config %d\n", 0);
-
- status = obex_bind_config(c, 1);
- if (status)
- printk(KERN_DEBUG "could not bind obex config %d\n", 0);
-
- status = acm_bind_config(c, 2);
- if (status)
- printk(KERN_DEBUG "could not bind acm config\n");
-
- status = ecm_bind_config(c, hostaddr);
- if (status)
- printk(KERN_DEBUG "could not bind ecm config\n");
-
- return status;
-}
static struct usb_configuration nokia_config_500ma_driver = {
.label = "Bus Powered",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE,
- .bMaxPower = 250, /* 500mA */
+ .MaxPower = 500,
};
static struct usb_configuration nokia_config_100ma_driver = {
@@ -146,108 +109,247 @@ static struct usb_configuration nokia_config_100ma_driver = {
.bConfigurationValue = 2,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 50, /* 100 mA */
+ .MaxPower = 100,
};
-static int __init nokia_bind(struct usb_composite_dev *cdev)
+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)
{
- int gcnum;
- struct usb_gadget *gadget = cdev->gadget;
- int status;
+ 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");
+ }
- status = gphonet_setup(cdev->gadget);
- if (status < 0)
- goto err_phonet;
+ if (!IS_ERR(fi_obex1)) {
+ f_obex1 = usb_get_function(fi_obex1);
+ if (IS_ERR(f_obex1))
+ pr_debug("could not get obex function 0\n");
+ }
- status = gserial_setup(cdev->gadget, 3);
- if (status < 0)
- goto err_serial;
+ 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");
+ }
- status = gether_setup(cdev->gadget, hostaddr);
- if (status < 0)
- goto err_ether;
+ f_acm = usb_get_function(fi_acm);
+ if (IS_ERR(f_acm)) {
+ status = PTR_ERR(f_acm);
+ goto err_get_acm;
+ }
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm)) {
+ status = PTR_ERR(f_ecm);
+ goto err_get_ecm;
+ }
- device_desc.iManufacturer = status;
+ 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");
+ }
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_PRODUCT_IDX].id = status;
+ 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;
+}
- device_desc.iProduct = status;
+static int __init nokia_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
- /* config description */
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto err_usb;
- 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;
nokia_config_500ma_driver.iConfiguration = status;
nokia_config_100ma_driver.iConfiguration = status;
- /* set up other descriptors */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM);
- else {
- /* this should only work with hw that supports altsettings
- * and several endpoints, anything else, panic.
- */
- pr_err("nokia_bind: controller '%s' not recognized\n",
- gadget->name);
+ if (!gadget_supports_altsettings(gadget)) {
+ status = -ENODEV;
goto err_usb;
}
+ fi_phonet = usb_get_function_instance("phonet");
+ if (IS_ERR(fi_phonet))
+ pr_debug("could not find phonet function\n");
+
+ fi_obex1 = usb_get_function_instance("obex");
+ if (IS_ERR(fi_obex1))
+ pr_debug("could not find obex function 1\n");
+
+ fi_obex2 = usb_get_function_instance("obex");
+ if (IS_ERR(fi_obex2))
+ pr_debug("could not find obex function 2\n");
+
+ fi_acm = usb_get_function_instance("acm");
+ if (IS_ERR(fi_acm)) {
+ status = PTR_ERR(fi_acm);
+ goto err_obex2_inst;
+ }
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm)) {
+ status = PTR_ERR(fi_ecm);
+ goto err_acm_inst;
+ }
+
/* finally register the configuration */
status = usb_add_config(cdev, &nokia_config_500ma_driver,
nokia_bind_config);
if (status < 0)
- goto err_usb;
+ goto err_ecm_inst;
status = usb_add_config(cdev, &nokia_config_100ma_driver,
nokia_bind_config);
if (status < 0)
- goto err_usb;
+ goto err_put_cfg1;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
return 0;
+err_put_cfg1:
+ usb_put_function(f_acm_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+ usb_put_function(f_obex1_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+ usb_put_function(f_obex2_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+ usb_put_function(f_phonet_cfg1);
+ usb_put_function(f_ecm_cfg1);
+err_ecm_inst:
+ usb_put_function_instance(fi_ecm);
+err_acm_inst:
+ usb_put_function_instance(fi_acm);
+err_obex2_inst:
+ if (!IS_ERR(fi_obex2))
+ usb_put_function_instance(fi_obex2);
+ if (!IS_ERR(fi_obex1))
+ usb_put_function_instance(fi_obex1);
+ if (!IS_ERR(fi_phonet))
+ usb_put_function_instance(fi_phonet);
err_usb:
- gether_cleanup();
-err_ether:
- gserial_cleanup();
-err_serial:
- gphonet_cleanup();
-err_phonet:
return status;
}
static int __exit nokia_unbind(struct usb_composite_dev *cdev)
{
- gphonet_cleanup();
- gserial_cleanup();
- gether_cleanup();
+ if (!IS_ERR_OR_NULL(f_obex1_cfg2))
+ usb_put_function(f_obex1_cfg2);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg2))
+ usb_put_function(f_obex2_cfg2);
+ if (!IS_ERR_OR_NULL(f_obex1_cfg1))
+ usb_put_function(f_obex1_cfg1);
+ if (!IS_ERR_OR_NULL(f_obex2_cfg1))
+ usb_put_function(f_obex2_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg1))
+ usb_put_function(f_phonet_cfg1);
+ if (!IS_ERR_OR_NULL(f_phonet_cfg2))
+ usb_put_function(f_phonet_cfg2);
+ usb_put_function(f_acm_cfg1);
+ usb_put_function(f_acm_cfg2);
+ usb_put_function(f_ecm_cfg1);
+ usb_put_function(f_ecm_cfg2);
+
+ usb_put_function_instance(fi_ecm);
+ if (!IS_ERR(fi_obex2))
+ usb_put_function_instance(fi_obex2);
+ if (!IS_ERR(fi_obex1))
+ usb_put_function_instance(fi_obex1);
+ if (!IS_ERR(fi_phonet))
+ usb_put_function_instance(fi_phonet);
+ usb_put_function_instance(fi_acm);
return 0;
}
-static struct usb_composite_driver nokia_driver = {
+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, nokia_bind);
+ return usb_composite_probe(&nokia_driver);
}
module_init(nokia_init);
@@ -256,4 +358,3 @@ static void __exit nokia_cleanup(void)
usb_composite_unregister(&nokia_driver);
}
module_exit(nokia_cleanup);
-
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 576cd8578b4..2ae4f6d69f7 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -22,7 +22,6 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -35,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 <plat/dma.h>
-#include <plat/usb.h>
+#include <linux/omap-dma.h>
+
+#include <mach/usb.h>
#include "omap_udc.h"
@@ -60,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
@@ -88,14 +86,14 @@
#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 bool use_dma = 1;
@@ -103,8 +101,8 @@ 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 */
@@ -112,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;
/*-------------------------------------------------------------------------*/
@@ -154,7 +152,7 @@ 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 < usb_endpoint_maxp(desc)) {
@@ -201,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;
@@ -243,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);
@@ -272,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;
}
@@ -284,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);
}
/*-------------------------------------------------------------------------*/
@@ -293,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);
@@ -302,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)
@@ -365,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;
@@ -430,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;
@@ -537,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,
@@ -601,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);
@@ -684,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);
@@ -703,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);
@@ -761,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) {
@@ -781,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) {
@@ -809,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);
}
@@ -917,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;
}
@@ -929,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;
@@ -945,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);
@@ -985,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;
}
@@ -1012,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);
@@ -1024,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;
@@ -1072,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;
}
@@ -1121,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)
@@ -1179,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 */
};
/*-------------------------------------------------------------------------*/
@@ -1212,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);
@@ -1344,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;
}
@@ -1365,19 +1309,20 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int omap_udc_stop(struct usb_gadget_driver *driver);
+static int omap_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int omap_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
-static struct usb_gadget_ops omap_gadget_ops = {
+static const struct usb_gadget_ops omap_gadget_ops = {
.get_frame = omap_get_frame,
.wakeup = omap_wakeup,
.set_selfpowered = omap_set_selfpowered,
.vbus_session = omap_vbus_session,
.vbus_draw = omap_vbus_draw,
.pullup = omap_pullup,
- .start = omap_udc_start,
- .stop = omap_udc_stop,
+ .udc_start = omap_udc_start,
+ .udc_stop = omap_udc_stop,
};
/*-------------------------------------------------------------------------*/
@@ -1410,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);
}
@@ -1526,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;
@@ -1625,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);
@@ -1653,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);
@@ -1688,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 */
@@ -1750,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;
}
@@ -1793,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);
@@ -1827,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);
@@ -1838,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);
@@ -2028,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;
@@ -2087,41 +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()
- || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */
- );
+ /* No known omap7xx boards with vbus sense */
+ || cpu_is_omap7xx();
}
-static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int omap_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
int status = -ENODEV;
struct omap_ep *ep;
unsigned long flags;
- /* basic sanity tests */
- if (!udc)
- return -ENODEV;
- if (!driver
- // FIXME if otg, check: driver->is_otg
- || driver->max_speed < USB_SPEED_FULL
- || !bind || !driver->setup)
- return -EINVAL;
spin_lock_irqsave(&udc->lock, flags);
- if (udc->driver) {
- spin_unlock_irqrestore(&udc->lock, flags);
- return -EBUSY;
- }
-
/* reset state */
- list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) {
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
ep->irqs = 0;
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
continue;
@@ -2135,40 +2066,30 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
/* hook up the driver */
driver->driver.bus = NULL;
udc->driver = driver;
- udc->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(&udc->lock, flags);
if (udc->dc_clk != NULL)
omap_udc_enable_clock(1);
- status = bind(&udc->gadget);
- 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;
@@ -2180,27 +2101,24 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
done:
if (udc->dc_clk != NULL)
omap_udc_enable_clock(0);
+
return status;
}
-static int omap_udc_stop(struct usb_gadget_driver *driver)
+static int omap_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
unsigned long flags;
int status = -ENODEV;
- if (!udc)
- return -ENODEV;
- if (!driver || driver != udc->driver || !driver->unbind)
- return -EINVAL;
-
if (udc->dc_clk != NULL)
omap_udc_enable_clock(1);
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);
@@ -2208,13 +2126,11 @@ static int omap_udc_stop(struct usb_gadget_driver *driver)
udc_quiesce(udc);
spin_unlock_irqrestore(&udc->lock, flags);
- driver->unbind(&udc->gadget);
- udc->gadget.dev.driver = NULL;
udc->driver = NULL;
if (udc->dc_clk != NULL)
omap_udc_enable_clock(0);
- DBG("unregistered driver '%s'\n", driver->driver.name);
+
return status;
}
@@ -2227,7 +2143,7 @@ static int omap_udc_stop(struct usb_gadget_driver *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)
{
@@ -2249,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 " : "",
@@ -2270,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]) {
@@ -2291,11 +2216,16 @@ 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";
}
}
@@ -2305,12 +2235,9 @@ static int proc_otg_show(struct seq_file *s)
u32 trans = 0;
char *ctrl_name = "(UNKNOWN)";
- /* XXX This needs major revision for OMAP2+ */
tmp = omap_readl(OTG_REV);
- if (cpu_class_is_omap1()) {
- 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);
@@ -2330,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" : "",
@@ -2397,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())
@@ -2420,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)) {
@@ -2504,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);
}
}
@@ -2555,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)
{
@@ -2573,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;
@@ -2589,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;
@@ -2636,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;
}
@@ -2645,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;
@@ -2663,13 +2613,13 @@ 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;
@@ -2678,14 +2628,6 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.max_speed = USB_SPEED_FULL;
udc->gadget.name = driver_name;
-
- device_initialize(&udc->gadget.dev);
- dev_set_name(&udc->gadget.dev, "gadget");
- udc->gadget.dev.release = omap_udc_release;
- udc->gadget.dev.parent = &odev->dev;
- if (use_dma)
- udc->gadget.dev.dma_mask = odev->dev.dma_mask;
-
udc->transceiver = xceiv;
/* ep0 is special; put it right after the SETUP buffer */
@@ -2699,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);
@@ -2786,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,
@@ -2814,16 +2759,6 @@ 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");
- BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
- /* can't use omap_udc_enable_clock yet */
- clk_enable(dc_clk);
- clk_enable(hhc_clk);
- udelay(100);
- }
-
if (cpu_is_omap7xx()) {
dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
@@ -2863,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");
@@ -2873,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:
@@ -2896,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";
}
@@ -2919,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);
@@ -2942,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);
@@ -2951,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);
@@ -2973,25 +2900,14 @@ known:
clk_disable(dc_clk);
}
- if (cpu_is_omap24xx()) {
- 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);
+ status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+ omap_udc_release);
if (status)
goto cleanup4;
- status = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
- if (!status)
- return status;
- /* If fail, fall through */
+ return 0;
+
cleanup4:
remove_proc_file();
@@ -3004,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() || cpu_is_omap7xx()) {
+ if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
clk_disable(hhc_clk);
clk_disable(dc_clk);
clk_put(hhc_clk);
@@ -3024,7 +2940,7 @@ 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);
@@ -3038,8 +2954,8 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
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);
@@ -3062,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;
@@ -3109,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 = {
@@ -3118,27 +3034,7 @@ static struct platform_driver udc_driver = {
},
};
-static int __init udc_init(void)
-{
- /* Disable DMA for omap7xx -- it doesn't work right. */
- if (cpu_is_omap7xx())
- use_dma = 0;
-
- 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
index a3fcaae4bc2..eb8c3bedb57 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -15,6 +15,14 @@
#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 */
@@ -287,7 +295,6 @@ struct pch_udc_ep {
struct pch_udc_data_dma_desc *td_data;
struct pch_udc_dev *dev;
unsigned long offset_addr;
- const struct usb_endpoint_descriptor *desc;
struct list_head queue;
unsigned num:5,
in:1,
@@ -296,6 +303,21 @@ struct pch_udc_ep {
};
/**
+ * 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
@@ -311,6 +333,7 @@ struct pch_udc_ep {
* @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
@@ -322,6 +345,7 @@ struct pch_udc_ep {
* @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;
@@ -334,9 +358,9 @@ struct pch_udc_dev {
prot_stall:1,
irq_registered:1,
mem_region:1,
- registered:1,
suspended:1,
connected:1,
+ vbus_session:1,
set_cfg_not_acked:1,
waiting_zlp_ack:1;
struct pci_pool *data_requests;
@@ -347,8 +371,10 @@ struct pch_udc_dev {
unsigned long phys_addr;
void __iomem *base_addr;
unsigned irq;
- struct pch_udc_cfg_data cfg_data;
+ 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
@@ -358,7 +384,6 @@ struct pch_udc_dev {
static const char ep0_string[] = "ep0in";
static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */
-struct pch_udc_dev *pch_udc; /* pointer to device object */
static bool speed_fs;
module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
@@ -554,6 +579,29 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
}
/**
+ * 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
@@ -563,10 +611,18 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
int is_active)
{
- if (is_active)
- pch_udc_clear_disconnect(dev);
- else
+ 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;
+ }
}
/**
@@ -1126,7 +1182,17 @@ static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
if (!gadget)
return -EINVAL;
dev = container_of(gadget, struct pch_udc_dev, gadget);
- pch_udc_vbus_session(dev, is_on);
+ 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;
}
@@ -1168,9 +1234,10 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
return -EOPNOTSUPP;
}
-static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int pch_udc_stop(struct usb_gadget_driver *driver);
+static int pch_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int pch_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pch_udc_ops = {
.get_frame = pch_udc_pcd_get_frame,
.wakeup = pch_udc_pcd_wakeup,
@@ -1178,11 +1245,193 @@ static const struct usb_gadget_ops pch_udc_ops = {
.pullup = pch_udc_pcd_pullup,
.vbus_session = pch_udc_pcd_vbus_session,
.vbus_draw = pch_udc_pcd_vbus_draw,
- .start = pch_udc_start,
- .stop = pch_udc_stop,
+ .udc_start = pch_udc_start,
+ .udc_stop = pch_udc_stop,
};
/**
+ * 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
@@ -1191,6 +1440,8 @@ static const struct usb_gadget_ops pch_udc_ops = {
*/
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;
@@ -1455,7 +1706,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
spin_lock_irqsave(&dev->lock, iflags);
- ep->desc = desc;
+ ep->ep.desc = desc;
ep->halted = 0;
pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
ep->ep.maxpacket = usb_endpoint_maxp(desc);
@@ -1484,7 +1735,7 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev;
- if ((usbep->name == ep0_string) || !ep->desc)
+ if ((usbep->name == ep0_string) || !ep->ep.desc)
return -EINVAL;
spin_lock_irqsave(&ep->dev->lock, iflags);
@@ -1492,7 +1743,7 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
ep->halted = 1;
pch_udc_ep_disable(ep);
pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
- ep->desc = NULL;
+ ep->ep.desc = NULL;
INIT_LIST_HEAD(&ep->queue);
spin_unlock_irqrestore(&ep->dev->lock, iflags);
return 0;
@@ -1598,7 +1849,7 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev;
- if (!ep->desc && ep->num)
+ if (!ep->ep.desc && ep->num)
return -EINVAL;
req = container_of(usbreq, struct pch_udc_request, req);
if (!list_empty(&req->queue))
@@ -1698,7 +1949,7 @@ static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev;
- if (!usbep || !usbreq || (!ep->desc && ep->num))
+ 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);
@@ -1737,7 +1988,7 @@ static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev;
- if (!ep->desc && !ep->num)
+ if (!ep->ep.desc && !ep->num)
return -EINVAL;
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
@@ -1782,7 +2033,7 @@ static int pch_udc_pcd_set_wedge(struct usb_ep *usbep)
return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev;
- if (!ep->desc && !ep->num)
+ if (!ep->ep.desc && !ep->num)
return -EINVAL;
if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
@@ -1814,7 +2065,7 @@ static void pch_udc_pcd_fifo_flush(struct usb_ep *usbep)
return;
ep = container_of(usbep, struct pch_udc_ep, ep);
- if (ep->desc || !ep->num)
+ if (ep->ep.desc || !ep->num)
pch_udc_ep_fifo_flush(ep, ep->in);
}
@@ -1959,7 +2210,7 @@ static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
return;
}
if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE)
- if (td->status | PCH_UDC_DMA_LAST) {
+ if (td->status & PCH_UDC_DMA_LAST) {
count = td->status & PCH_UDC_RXTX_BYTES;
break;
}
@@ -2132,6 +2383,8 @@ static void pch_udc_svc_control_in(struct pch_udc_dev *dev)
* @dev: Reference to the device structure
*/
static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
+ __releases(&dev->lock)
+ __acquires(&dev->lock)
{
u32 stat;
int setup_supported;
@@ -2335,8 +2588,11 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
/* Complete request queue */
empty_req_queue(ep);
}
- if (dev->driver && dev->driver->disconnect)
+ if (dev->driver && dev->driver->disconnect) {
+ spin_unlock(&dev->lock);
dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
}
/**
@@ -2371,6 +2627,11 @@ static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev)
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);
}
/**
@@ -2459,12 +2720,18 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
*/
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)
+ 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)
+ 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);
@@ -2472,8 +2739,30 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
if (dev_intr & UDC_DEVINT_SC)
pch_udc_svc_cfg_interrupt(dev);
/* USB Suspend interrupt */
- if (dev_intr & UDC_DEVINT_US)
+ 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");
@@ -2499,6 +2788,14 @@ static irqreturn_t pch_udc_isr(int irq, void *pdev)
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);
@@ -2599,12 +2896,12 @@ static void pch_udc_pcd_reinit(struct pch_udc_dev *dev)
ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) *
UDC_EP_REG_SHIFT;
/* need to set ep->ep.maxpacket and set Default Configuration?*/
- ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, UDC_BULK_MAX_PKT_SIZE);
list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
INIT_LIST_HEAD(&ep->queue);
}
- dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
- dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IDX].ep, UDC_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IDX].ep, UDC_EP0OUT_MAX_PKT_SIZE);
/* remove ep0 in and out from the list. They have own pointer */
list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list);
@@ -2625,6 +2922,7 @@ 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;
}
@@ -2687,74 +2985,39 @@ static int init_dma_pools(struct pch_udc_dev *dev)
return 0;
}
-static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int pch_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct pch_udc_dev *dev = pch_udc;
- int retval;
+ struct pch_udc_dev *dev = to_pch_udc(g);
- if (!driver || (driver->max_speed == USB_SPEED_UNKNOWN) || !bind ||
- !driver->setup || !driver->unbind || !driver->disconnect) {
- dev_err(&dev->pdev->dev,
- "%s: invalid driver parameter\n", __func__);
- return -EINVAL;
- }
-
- if (!dev)
- return -ENODEV;
-
- if (dev->driver) {
- dev_err(&dev->pdev->dev, "%s: already bound\n", __func__);
- return -EBUSY;
- }
driver->driver.bus = NULL;
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
- /* Invoke the bind routine of the gadget driver */
- retval = bind(&dev->gadget);
-
- if (retval) {
- dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n",
- __func__, driver->driver.name, retval);
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
/* get ready for ep0 traffic */
pch_udc_setup_ep0(dev);
/* clear SD */
- pch_udc_clear_disconnect(dev);
+ 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_driver *driver)
+static int pch_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct pch_udc_dev *dev = pch_udc;
-
- if (!dev)
- return -ENODEV;
-
- if (!driver || (driver != dev->driver)) {
- dev_err(&dev->pdev->dev,
- "%s: invalid driver parameter\n", __func__);
- return -EINVAL;
- }
+ struct pch_udc_dev *dev = to_pch_udc(g);
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
/* Assures that there are no pending requests with this driver */
- driver->disconnect(&dev->gadget);
- driver->unbind(&dev->gadget);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
dev->connected = 0;
/* set SD */
pch_udc_set_disconnect(dev);
+
return 0;
}
@@ -2803,6 +3066,8 @@ static void pch_udc_remove(struct pci_dev *pdev)
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)
@@ -2814,10 +3079,7 @@ static void pch_udc_remove(struct pci_dev *pdev)
pci_resource_len(pdev, PCH_UDC_PCI_BAR));
if (dev->active)
pci_disable_device(pdev);
- if (dev->registered)
- device_unregister(&dev->gadget.dev);
kfree(dev);
- pci_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
@@ -2867,11 +3129,6 @@ static int pch_udc_probe(struct pci_dev *pdev,
int retval;
struct pch_udc_dev *dev;
- /* one udc only */
- if (pch_udc) {
- pr_err("%s: already probed\n", __func__);
- return -EBUSY;
- }
/* init */
dev = kzalloc(sizeof *dev, GFP_KERNEL);
if (!dev) {
@@ -2910,10 +3167,11 @@ static int pch_udc_probe(struct pci_dev *pdev,
retval = -ENODEV;
goto finished;
}
- pch_udc = dev;
/* initialize the hardware */
- if (pch_udc_pcd_init(dev))
+ 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__,
@@ -2936,21 +3194,13 @@ static int pch_udc_probe(struct pci_dev *pdev,
if (retval)
goto finished;
- dev_set_name(&dev->gadget.dev, "gadget");
- dev->gadget.dev.parent = &pdev->dev;
- dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
- dev->gadget.dev.release = gadget_release;
dev->gadget.name = KBUILD_MODNAME;
dev->gadget.max_speed = USB_SPEED_HIGH;
- retval = device_register(&dev->gadget.dev);
- if (retval)
- goto finished;
- dev->registered = 1;
-
/* Put the device in disconnected state till a driver is bound */
pch_udc_set_disconnect(dev);
- retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget);
+ retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+ gadget_release);
if (retval)
goto finished;
return 0;
@@ -2960,7 +3210,7 @@ finished:
return retval;
}
-static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = {
+static const struct pci_device_id pch_udc_pcidev_id[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
@@ -2981,7 +3231,6 @@ static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = {
MODULE_DEVICE_TABLE(pci, pch_udc_pcidev_id);
-
static struct pci_driver pch_udc_driver = {
.name = KBUILD_MODNAME,
.id_table = pch_udc_pcidev_id,
@@ -2992,17 +3241,7 @@ static struct pci_driver pch_udc_driver = {
.shutdown = pch_udc_shutdown,
};
-static int __init pch_udc_pci_init(void)
-{
- return pci_register_driver(&pch_udc_driver);
-}
-module_init(pch_udc_pci_init);
-
-static void __exit pch_udc_pci_exit(void)
-{
- pci_unregister_driver(&pch_udc_driver);
-}
-module_exit(pch_udc_pci_exit);
+module_pci_driver(pch_udc_driver);
MODULE_DESCRIPTION("Intel EG20T USB Device Controller");
MODULE_AUTHOR("LAPIS Semiconductor, <tomoya-linux@dsn.lapis-semi.com>");
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index d83134b0f78..6474081dcba 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -22,7 +22,6 @@
#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>
@@ -34,29 +33,17 @@
#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"
@@ -76,8 +63,6 @@ struct printer_dev {
/* lock buffer lists during read/write calls */
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;
@@ -101,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;
@@ -121,28 +107,7 @@ static struct printer_dev usb_printer_gadget;
* 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 *iSerialNum;
-module_param(iSerialNum, charp, S_IRUGO);
+module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
MODULE_PARM_DESC(iSerialNum, "1");
static char *iPNPstring;
@@ -155,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,
@@ -222,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 */
@@ -271,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.
@@ -308,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] =
@@ -338,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 *
@@ -527,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);
}
}
@@ -788,7 +693,7 @@ static int
printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
{
struct printer_dev *dev = fd->private_data;
- struct inode *inode = fd->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(fd);
unsigned long flags;
int tx_list_empty;
@@ -938,105 +843,19 @@ static void printer_reset_interface(struct printer_dev *dev)
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;
-
- 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 {
- unsigned power;
-
- power = 2 * config_desc.bMaxPower;
- usb_gadget_vbus_draw(dev->gadget, power);
-
- dev->config = number;
- INFO(dev, "%s config #%d: %d mA, %s\n",
- usb_speed_string(gadget->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;
/* 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);
@@ -1044,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;
@@ -1108,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);
@@ -1121,107 +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:
- device_desc.bMaxPacketSize0 =
- gadget->ep0->maxpacket;
- 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(gadget))
- break;
- /*
- * assumes ep0 uses the same value for both
- * speeds
- */
- dev_qualifier.bMaxPacketSize0 =
- gadget->ep0->maxpacket;
- value = min(wLength,
- (u16) sizeof dev_qualifier);
- memcpy(req->buf, &dev_qualifier, value);
- break;
-
- case USB_DT_OTHER_SPEED_CONFIG:
- if (!gadget_is_dualspeed(gadget))
- break;
- /* FALLTHROUGH */
-#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);
- if (!value)
- value = set_interface(dev, PRINTER_INTERFACE);
- 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];
@@ -1232,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;
@@ -1241,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);
@@ -1262,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 = 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 = get_gadget_data(gadget);
+ 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__);
@@ -1337,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;
}
@@ -1380,46 +1154,6 @@ printer_bind(struct usb_gadget *gadget)
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);
@@ -1427,33 +1161,12 @@ 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 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 */
-
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);
@@ -1467,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;
@@ -1475,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) {
@@ -1514,45 +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 = {
- .max_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)
{
@@ -1561,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_probe_driver(&printer_driver, printer_bind);
+ status = usb_composite_probe(&printer_driver);
if (status) {
class_destroy(usb_gadget_class);
unregister_chrdev_region(g_printer_devno, 1);
- DBG(dev, "usb_gadget_probe_driver %x\n", status);
+ pr_err("usb_gadget_probe_driver %x\n", status);
}
return status;
@@ -1587,15 +1292,14 @@ module_init(init);
static void __exit
cleanup(void)
{
- int status;
-
mutex_lock(&usb_printer_gadget.lock_printer_io);
- status = usb_gadget_unregister_driver(&printer_driver);
- if (status)
- ERROR(dev, "usb_gadget_unregister_driver %x\n", status);
-
+ usb_composite_unregister(&printer_driver);
unregister_chrdev_region(g_printer_devno, 1);
class_destroy(usb_gadget_class);
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 dd470635f4f..9984437d295 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -21,18 +21,18 @@
#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>
@@ -41,7 +41,6 @@
#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>
@@ -54,15 +53,13 @@
*/
#ifdef CONFIG_ARCH_PXA
#include <mach/pxa25x-udc.h>
+#include <mach/hardware.h>
#endif
#ifdef CONFIG_ARCH_LUBBOCK
#include <mach/lubbock.h>
#endif
-#include <asm/mach/udc_pxa2xx.h>
-
-
/*
* This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
* series processors. The UDC for the IXP 4xx series is very similar.
@@ -219,7 +216,7 @@ 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 < usb_endpoint_maxp (desc)) {
@@ -250,7 +247,7 @@ 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 = usb_endpoint_maxp (desc);
@@ -270,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;
@@ -282,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);
@@ -390,7 +387,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
{
unsigned max;
- max = usb_endpoint_maxp(ep->desc);
+ max = usb_endpoint_maxp(ep->ep.desc);
do {
unsigned count;
int is_last, is_short;
@@ -644,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;
}
@@ -660,7 +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 > usb_endpoint_maxp (ep->desc)))
+ && req->req.length > usb_endpoint_maxp(ep->ep.desc)))
return -EMSGSIZE;
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
@@ -673,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) {
@@ -722,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);
}
@@ -749,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);
}
@@ -792,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;
@@ -820,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;
@@ -994,14 +991,15 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
udc = container_of(_gadget, struct pxa25x_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 pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int pxa25x_stop(struct usb_gadget_driver *driver);
+static int pxa25x_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int pxa25x_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa25x_udc_ops = {
.get_frame = pxa25x_udc_get_frame,
@@ -1009,8 +1007,8 @@ static const struct usb_gadget_ops pxa25x_udc_ops = {
.vbus_session = pxa25x_udc_vbus_session,
.pullup = pxa25x_udc_pullup,
.vbus_draw = pxa25x_udc_vbus_draw,
- .start = pxa25x_start,
- .stop = pxa25x_stop,
+ .udc_start = pxa25x_udc_start,
+ .udc_stop = pxa25x_udc_stop,
};
/*-------------------------------------------------------------------------*/
@@ -1087,7 +1085,7 @@ 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;
@@ -1191,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 */
@@ -1257,57 +1256,25 @@ static void udc_enable (struct pxa25x_udc *dev)
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
-static int pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int pxa25x_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct pxa25x_udc *dev = the_controller;
+ struct pxa25x_udc *dev = to_pxa25x(g);
int retval;
- if (!driver
- || driver->max_speed < USB_SPEED_FULL
- || !bind
- || !driver->disconnect
- || !driver->setup)
- return -EINVAL;
- if (!dev)
- return -ENODEV;
- if (dev->driver)
- return -EBUSY;
-
/* first hook up the driver ... */
dev->driver = driver;
- dev->gadget.dev.driver = &driver->driver;
dev->pullup = 1;
- retval = device_add (&dev->gadget.dev);
- if (retval) {
-fail:
- dev->driver = NULL;
- dev->gadget.dev.driver = NULL;
- return retval;
- }
- retval = bind(&dev->gadget);
- 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 (dev->transceiver) {
- retval = otg_set_peripheral(dev->transceiver, &dev->gadget);
- if (retval) {
- DMSG("can't bind to transceiver\n");
- if (driver->unbind)
- driver->unbind(&dev->gadget);
+ if (!IS_ERR_OR_NULL(dev->transceiver)) {
+ retval = otg_set_peripheral(dev->transceiver->otg,
+ &dev->gadget);
+ if (retval)
goto bind_fail;
- }
}
pullup(dev);
@@ -1344,14 +1311,10 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
udc_reinit(dev);
}
-static int pxa25x_stop(struct usb_gadget_driver *driver)
+static int pxa25x_udc_stop(struct usb_gadget*g,
+ struct usb_gadget_driver *driver)
{
- struct pxa25x_udc *dev = the_controller;
-
- if (!dev)
- return -ENODEV;
- if (!driver || driver != dev->driver || !driver->unbind)
- return -EINVAL;
+ struct pxa25x_udc *dev = to_pxa25x(g);
local_irq_disable();
dev->pullup = 0;
@@ -1359,17 +1322,13 @@ static int pxa25x_stop(struct usb_gadget_driver *driver)
stop_activity(dev, driver);
local_irq_enable();
- if (dev->transceiver)
- (void) otg_set_peripheral(dev->transceiver, NULL);
+ if (!IS_ERR_OR_NULL(dev->transceiver))
+ (void) otg_set_peripheral(dev->transceiver->otg, NULL);
- driver->unbind(&dev->gadget);
- dev->gadget.dev.driver = NULL;
dev->driver = NULL;
- device_del (&dev->gadget.dev);
-
- DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
dump_state(dev);
+
return 0;
}
@@ -2096,12 +2055,14 @@ static struct pxa25x_udc memory = {
/*
* probe - binds to the platform device
*/
-static int __init pxa25x_udc_probe(struct platform_device *pdev)
+static int pxa25x_udc_probe(struct platform_device *pdev)
{
struct pxa25x_udc *dev = &memory;
int retval, irq;
u32 chiprev;
+ pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
+
/* insist on Intel/ARM/XScale */
asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
@@ -2157,9 +2118,9 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
/* other non-static parts of init */
dev->dev = &pdev->dev;
- dev->mach = pdev->dev.platform_data;
+ dev->mach = dev_get_platdata(&pdev->dev);
- dev->transceiver = otg_get_transceiver();
+ dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (gpio_is_valid(dev->mach->gpio_pullup)) {
if ((retval = gpio_request(dev->mach->gpio_pullup,
@@ -2176,10 +2137,6 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
dev->timer.function = udc_watchdog;
dev->timer.data = (unsigned long) dev;
- device_initialize(&dev->gadget.dev);
- dev->gadget.dev.parent = &pdev->dev;
- dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
-
the_controller = dev;
platform_set_drvdata(pdev, dev);
@@ -2200,19 +2157,15 @@ 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_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);
goto err_irq_lub;
}
- retval = request_irq(LUBBOCK_USB_IRQ,
- lubbock_vbus_irq,
- 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);
@@ -2237,8 +2190,8 @@ lubbock_fail0:
if (gpio_is_valid(dev->mach->gpio_pullup))
gpio_free(dev->mach->gpio_pullup);
err_gpio_pullup:
- if (dev->transceiver) {
- otg_put_transceiver(dev->transceiver);
+ if (!IS_ERR_OR_NULL(dev->transceiver)) {
+ usb_put_phy(dev->transceiver);
dev->transceiver = NULL;
}
clk_put(dev->clk);
@@ -2251,14 +2204,14 @@ static void pxa25x_udc_shutdown(struct platform_device *_dev)
pullup_off();
}
-static int __exit pxa25x_udc_remove(struct platform_device *pdev)
+static int pxa25x_udc_remove(struct platform_device *pdev)
{
struct pxa25x_udc *dev = platform_get_drvdata(pdev);
- usb_del_gadget_udc(&dev->gadget);
if (dev->driver)
return -EBUSY;
+ usb_del_gadget_udc(&dev->gadget);
dev->pullup = 0;
pullup(dev);
@@ -2279,12 +2232,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
clk_put(dev->clk);
- if (dev->transceiver) {
- otg_put_transceiver(dev->transceiver);
+ if (!IS_ERR_OR_NULL(dev->transceiver)) {
+ usb_put_phy(dev->transceiver);
dev->transceiver = NULL;
}
- platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
}
@@ -2343,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 = {
@@ -2352,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 8eaf4e43726..3fe5931dc21 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -41,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;
@@ -119,7 +118,7 @@ struct pxa25x_udc {
struct device *dev;
struct clk *clk;
struct pxa2xx_udc_mach_info *mach;
- struct otg_transceiver *transceiver;
+ struct usb_phy *transceiver;
u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
@@ -127,6 +126,7 @@ struct pxa25x_udc {
struct dentry *debugfs_udc;
#endif
};
+#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
/*-------------------------------------------------------------------------*/
@@ -226,7 +226,7 @@ dump_state(struct pxa25x_udc *dev)
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 f4c44eb806c..cdf4d678be9 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -13,6 +13,7 @@
#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>
@@ -23,14 +24,12 @@
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
-
-#include <asm/byteorder.h>
-#include <mach/hardware.h>
+#include <linux/byteorder/generic.h>
+#include <linux/platform_data/pxa2xx_udc.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <mach/udc.h>
#include "pxa27x_udc.h"
@@ -610,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;
@@ -632,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;
@@ -1573,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;
}
@@ -1594,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;
}
@@ -1665,14 +1664,15 @@ 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_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int pxa27x_udc_stop(struct usb_gadget_driver *driver);
+static int pxa27x_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int pxa27x_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa_udc_ops = {
.get_frame = pxa_udc_get_frame,
@@ -1680,8 +1680,8 @@ static const struct usb_gadget_ops pxa_udc_ops = {
.pullup = pxa_udc_pullup,
.vbus_session = pxa_udc_vbus_session,
.vbus_draw = pxa_udc_vbus_draw,
- .start = pxa27x_udc_start,
- .stop = pxa27x_udc_stop,
+ .udc_start = pxa27x_udc_start,
+ .udc_stop = pxa27x_udc_stop,
};
/**
@@ -1716,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;
@@ -1737,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);
+ }
}
/**
@@ -1801,44 +1804,22 @@ static void udc_enable(struct pxa_udc *udc)
*
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
*/
-static int pxa27x_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int pxa27x_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct pxa_udc *udc = the_controller;
+ struct pxa_udc *udc = to_pxa(g);
int retval;
- if (!driver || driver->max_speed < USB_SPEED_FULL || !bind
- || !driver->disconnect || !driver->setup)
- return -EINVAL;
- if (!udc)
- return -ENODEV;
- if (udc->driver)
- return -EBUSY;
-
/* first hook up the driver ... */
udc->driver = driver;
- udc->gadget.dev.driver = &driver->driver;
dplus_pullup(udc, 1);
- retval = device_add(&udc->gadget.dev);
- if (retval) {
- dev_err(udc->dev, "device_add error %d\n", retval);
- goto add_fail;
- }
- retval = bind(&udc->gadget);
- 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;
}
}
@@ -1846,14 +1827,8 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
udc_enable(udc);
return 0;
-transceiver_fail:
- if (driver->unbind)
- driver->unbind(&udc->gadget);
-bind_fail:
- device_del(&udc->gadget.dev);
-add_fail:
+fail:
udc->driver = NULL;
- udc->gadget.dev.driver = NULL;
return retval;
}
@@ -1876,9 +1851,6 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
for (i = 0; i < NR_USB_ENDPOINTS; i++)
pxa_ep_disable(&udc->udc_usb_ep[i].usb_ep);
-
- if (driver)
- driver->disconnect(&udc->gadget);
}
/**
@@ -1887,28 +1859,19 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
*
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
*/
-static int pxa27x_udc_stop(struct usb_gadget_driver *driver)
+static int pxa27x_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct pxa_udc *udc = the_controller;
-
- if (!udc)
- return -ENODEV;
- if (!driver || driver != udc->driver || !driver->unbind)
- return -EINVAL;
+ struct pxa_udc *udc = to_pxa(g);
stop_activity(udc, driver);
udc_disable(udc);
dplus_pullup(udc, 0);
- driver->unbind(&udc->gadget);
udc->driver = NULL;
- device_del(&udc->gadget.dev);
- dev_info(udc->dev, "unregistered gadget driver '%s'\n",
- driver->driver.name);
-
- if (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;
}
@@ -2448,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;
@@ -2462,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)) {
@@ -2491,9 +2454,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
goto err_map;
}
- device_initialize(&udc->gadget.dev);
- udc->gadget.dev.parent = &pdev->dev;
- udc->gadget.dev.dma_mask = NULL;
udc->vbus_sensed = 0;
the_controller = udc;
@@ -2506,15 +2466,18 @@ 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:
@@ -2530,7 +2493,7 @@ err_clk:
* pxa_udc_remove - removes the udc device driver
* @_dev: platform device
*/
-static int __exit pxa_udc_remove(struct platform_device *_dev)
+static int pxa_udc_remove(struct platform_device *_dev)
{
struct pxa_udc *udc = platform_get_drvdata(_dev);
int gpio = udc->mach->gpio_pullup;
@@ -2542,10 +2505,9 @@ static int __exit pxa_udc_remove(struct platform_device *_dev)
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);
@@ -2648,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,
@@ -2656,22 +2619,7 @@ static struct platform_driver udc_driver = {
#endif
};
-static int __init udc_init(void)
-{
- if (!cpu_is_pxa27x() && !cpu_is_pxa3xx())
- return -ENODEV;
-
- printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
- return platform_driver_probe(&udc_driver, pxa_udc_probe);
-}
-module_init(udc_init);
-
-
-static void __exit udc_exit(void)
-{
- platform_driver_unregister(&udc_driver);
-}
-module_exit(udc_exit);
+module_platform_driver(udc_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Robert Jarzmik");
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
index 7f4e8f424e8..28f2b53530f 100644
--- a/drivers/usb/gadget/pxa27x_udc.h
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -418,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
@@ -447,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;
@@ -473,6 +473,7 @@ struct pxa_udc {
struct dentry *debugfs_eps;
#endif
};
+#define to_pxa(g) (container_of((g), struct pxa_udc, gadget))
static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
{
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index f5b8d215e1d..b698a490cc7 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -459,7 +459,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
unsigned char *counter;
int ret;
- ep->desc = desc;
+ ep->ep.desc = desc;
if (ep->pipenum) /* already allocated pipe */
return 0;
@@ -648,7 +648,7 @@ static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
/* set SUDMAC parameters */
dma = &r8a66597->dma;
dma->used = 1;
- if (ep->desc->bEndpointAddress & USB_DIR_IN) {
+ if (ep->ep.desc->bEndpointAddress & USB_DIR_IN) {
dma->dir = 1;
} else {
dma->dir = 0;
@@ -663,11 +663,7 @@ static int sudmac_alloc_channel(struct r8a66597 *r8a66597,
ep->fifoctr = D0FIFOCTR;
/* dma mapping */
- req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597),
- req->req.buf, req->req.length,
- dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
-
- return 0;
+ return usb_gadget_map_request(&r8a66597->gadget, &req->req, dma->dir);
}
static void sudmac_free_channel(struct r8a66597 *r8a66597,
@@ -677,9 +673,7 @@ static void sudmac_free_channel(struct r8a66597 *r8a66597,
if (!r8a66597_is_sudmac(r8a66597))
return;
- dma_unmap_single(r8a66597_to_dev(ep->r8a66597),
- req->req.dma, req->req.length,
- ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ 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);
@@ -776,7 +770,7 @@ static void start_packet_read(struct r8a66597_ep *ep,
static void start_packet(struct r8a66597_ep *ep, struct r8a66597_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);
@@ -936,7 +930,7 @@ __acquires(r8a66597->lock)
if (restart) {
req = get_request_from_ep(ep);
- if (ep->desc)
+ if (ep->ep.desc)
start_packet(ep, req);
}
}
@@ -1122,7 +1116,7 @@ static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb)
r8a66597_write(r8a66597, ~check, BRDYSTS);
ep = r8a66597->pipenum2ep[pipenum];
req = get_request_from_ep(ep);
- 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);
@@ -1176,7 +1170,7 @@ __acquires(r8a66597->lock)
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- status = 1 << USB_DEVICE_SELF_POWERED;
+ status = r8a66597->device_status;
break;
case USB_RECIP_INTERFACE:
status = 0;
@@ -1475,11 +1469,11 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
u16 savepipe;
u16 mask0;
+ spin_lock(&r8a66597->lock);
+
if (r8a66597_is_sudmac(r8a66597))
r8a66597_sudmac_irq(r8a66597);
- spin_lock(&r8a66597->lock);
-
intsts0 = r8a66597_read(r8a66597, INTSTS0);
intenb0 = r8a66597_read(r8a66597, INTENB0);
@@ -1633,7 +1627,7 @@ static int r8a66597_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)
@@ -1698,7 +1692,7 @@ static int r8a66597_set_wedge(struct usb_ep *_ep)
ep = container_of(_ep, struct r8a66597_ep, ep);
- if (!ep || !ep->desc)
+ if (!ep || !ep->ep.desc)
return -EINVAL;
spin_lock_irqsave(&ep->r8a66597->lock, flags);
@@ -1806,16 +1800,29 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static struct usb_gadget_ops r8a66597_gadget_ops = {
+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 = dev_get_drvdata(&pdev->dev);
+ struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&r8a66597->gadget);
del_timer_sync(&r8a66597->timer);
@@ -1824,13 +1831,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
iounmap(r8a66597->sudmac_reg);
free_irq(platform_get_irq(pdev, 0), r8a66597);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
-#ifdef CONFIG_HAVE_CLK
+
if (r8a66597->pdata->on_chip) {
- clk_disable(r8a66597->clk);
+ clk_disable_unprepare(r8a66597->clk);
clk_put(r8a66597->clk);
}
-#endif
- device_unregister(&r8a66597->gadget.dev);
+
kfree(r8a66597);
return 0;
}
@@ -1861,9 +1867,7 @@ static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
static int __init r8a66597_probe(struct platform_device *pdev)
{
-#ifdef CONFIG_HAVE_CLK
char clk_name[8];
-#endif
struct resource *res, *ires;
int irq;
void __iomem *reg = NULL;
@@ -1900,34 +1904,23 @@ static int __init r8a66597_probe(struct platform_device *pdev)
r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
if (r8a66597 == NULL) {
ret = -ENOMEM;
- dev_err(&pdev->dev, "kzalloc error\n");
goto clean_up;
}
spin_lock_init(&r8a66597->lock);
- dev_set_drvdata(&pdev->dev, r8a66597);
- r8a66597->pdata = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, r8a66597);
+ r8a66597->pdata = dev_get_platdata(&pdev->dev);
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
r8a66597->gadget.ops = &r8a66597_gadget_ops;
- dev_set_name(&r8a66597->gadget.dev, "gadget");
r8a66597->gadget.max_speed = USB_SPEED_HIGH;
- r8a66597->gadget.dev.parent = &pdev->dev;
- r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask;
- r8a66597->gadget.dev.release = pdev->dev.release;
r8a66597->gadget.name = udc_name;
- ret = device_register(&r8a66597->gadget.dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "device_register failed\n");
- goto clean_up;
- }
init_timer(&r8a66597->timer);
r8a66597->timer.function = r8a66597_timer;
r8a66597->timer.data = (unsigned long)r8a66597;
r8a66597->reg = reg;
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
r8a66597->clk = clk_get(&pdev->dev, clk_name);
@@ -1935,11 +1928,11 @@ static int __init r8a66597_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
clk_name);
ret = PTR_ERR(r8a66597->clk);
- goto clean_up_dev;
+ goto clean_up;
}
- clk_enable(r8a66597->clk);
+ clk_prepare_enable(r8a66597->clk);
}
-#endif
+
if (r8a66597->pdata->sudmac) {
ret = r8a66597_sudmac_ioremap(r8a66597, pdev);
if (ret < 0)
@@ -1970,9 +1963,9 @@ static int __init r8a66597_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = r8a66597_ep_name[i];
ep->ep.ops = &r8a66597_ep_ops;
- ep->ep.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&ep->ep, 512);
}
- r8a66597->ep[0].ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&r8a66597->ep[0].ep, 64);
r8a66597->ep[0].pipenum = 0;
r8a66597->ep[0].fifoaddr = CFIFO;
r8a66597->ep[0].fifosel = CFIFOSEL;
@@ -1983,8 +1976,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep,
GFP_KERNEL);
- if (r8a66597->ep0_req == NULL)
+ if (r8a66597->ep0_req == NULL) {
+ ret = -ENOMEM;
goto clean_up3;
+ }
r8a66597->ep0_req->complete = nop_completion;
ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget);
@@ -1999,14 +1994,10 @@ err_add_udc:
clean_up3:
free_irq(irq, r8a66597);
clean_up2:
-#ifdef CONFIG_HAVE_CLK
if (r8a66597->pdata->on_chip) {
- clk_disable(r8a66597->clk);
+ clk_disable_unprepare(r8a66597->clk);
clk_put(r8a66597->clk);
}
-clean_up_dev:
-#endif
- device_unregister(&r8a66597->gadget.dev);
clean_up:
if (r8a66597) {
if (r8a66597->sudmac_reg)
@@ -2029,21 +2020,10 @@ static struct platform_driver r8a66597_driver = {
.name = (char *) udc_name,
},
};
-MODULE_ALIAS("platform:r8a66597_udc");
-static int __init r8a66597_udc_init(void)
-{
- return platform_driver_probe(&r8a66597_driver, r8a66597_probe);
-}
-module_init(r8a66597_udc_init);
-
-static void __exit r8a66597_udc_cleanup(void)
-{
- platform_driver_unregister(&r8a66597_driver);
-}
-module_exit(r8a66597_udc_cleanup);
+module_platform_driver_probe(r8a66597_driver, r8a66597_probe);
MODULE_DESCRIPTION("R8A66597 USB gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yoshihiro Shimoda");
-
+MODULE_ALIAS("platform:r8a66597_udc");
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h
index 8e3de61cd4b..45c4b2df178 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/r8a66597-udc.h
@@ -13,10 +13,7 @@
#ifndef __R8A66597_H__
#define __R8A66597_H__
-#ifdef CONFIG_HAVE_CLK
#include <linux/clk.h>
-#endif
-
#include <linux/usb/r8a66597.h>
#define R8A66597_MAX_SAMPLING 10
@@ -72,7 +69,7 @@ struct r8a66597_ep {
unsigned use_dma:1;
u16 pipenum;
u16 type;
- const struct usb_endpoint_descriptor *desc;
+
/* register address */
unsigned char fifoaddr;
unsigned char fifosel;
@@ -92,9 +89,7 @@ struct r8a66597 {
void __iomem *reg;
void __iomem *sudmac_reg;
-#ifdef CONFIG_HAVE_CLK
struct clk *clk;
-#endif
struct r8a66597_platdata *pdata;
struct usb_gadget gadget;
@@ -111,6 +106,7 @@ struct r8a66597 {
u16 old_vbus;
u16 scount;
u16 old_dvsq;
+ u16 device_status; /* for GET_STATUS */
/* pipe config */
unsigned char bulk;
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index d3cdffea9c8..95d2324f697 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -25,7 +25,6 @@
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
@@ -34,9 +33,9 @@
#include <asm/io.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
+#include "u_rndis.h"
#undef VERBOSE_DEBUG
@@ -74,65 +73,65 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length);
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
@@ -201,8 +200,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* general oids (table 4-1) */
/* mandatory */
- case OID_GEN_SUPPORTED_LIST:
- pr_debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__);
+ 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++)
@@ -211,8 +210,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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:
@@ -224,23 +223,23 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_GEN_MEDIA_SUPPORTED:
- pr_debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);
+ 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);
retval = 0;
break;
/* mandatory */
- case OID_GEN_MAXIMUM_FRAME_SIZE:
- pr_debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+ 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);
@@ -249,11 +248,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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__);
+ pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
if (rndis_per_dev_params[configNr].media_state
- == NDIS_MEDIA_STATE_DISCONNECTED)
+ == RNDIS_MEDIA_STATE_DISCONNECTED)
*outbuf = cpu_to_le32(0);
else
*outbuf = cpu_to_le32(
@@ -262,8 +261,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_GEN_TRANSMIT_BLOCK_SIZE:
- pr_debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+ 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);
@@ -272,8 +271,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_GEN_RECEIVE_BLOCK_SIZE:
- pr_debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+ 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);
@@ -282,16 +281,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_GEN_VENDOR_ID:
- pr_debug("%s: OID_GEN_VENDOR_ID\n", __func__);
+ 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__);
+ 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);
@@ -304,38 +303,38 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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__);
+ 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__);
+ 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__);
+ case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
+ pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
*outbuf = cpu_to_le32(0);
retval = 0;
break;
@@ -344,20 +343,20 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
* 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);
@@ -366,9 +365,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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);
@@ -377,9 +376,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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;
@@ -387,9 +386,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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;
@@ -397,8 +396,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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;
@@ -408,8 +407,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* ieee802.3 OIDs (table 4-3) */
/* mandatory */
- case OID_802_3_PERMANENT_ADDRESS:
- pr_debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);
+ 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,
@@ -420,8 +419,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_802_3_CURRENT_ADDRESS:
- pr_debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);
+ 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,
@@ -432,23 +431,23 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
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);
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);
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;
@@ -456,8 +455,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* 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;
@@ -465,15 +464,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
break;
/* mandatory */
- case OID_802_3_XMIT_ONE_COLLISION:
- pr_debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+ 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__);
+ 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;
@@ -517,7 +516,7 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
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
@@ -526,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
@@ -546,9 +545,9 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
}
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;
@@ -578,7 +577,7 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
return -ENOMEM;
resp = (rndis_init_cmplt_type *)r->buf;
- resp->MessageType = cpu_to_le32(REMOTE_NDIS_INITIALIZE_CMPLT);
+ 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);
@@ -622,7 +621,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
return -ENOMEM;
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),
@@ -669,7 +668,7 @@ 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->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),
@@ -693,7 +692,7 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
return -ENOMEM;
resp = (rndis_reset_cmplt_type *)r->buf;
- resp->MessageType = cpu_to_le32(REMOTE_NDIS_RESET_CMPLT);
+ 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 */
@@ -717,8 +716,7 @@ static int rndis_keepalive_response(int configNr,
return -ENOMEM;
resp = (rndis_keepalive_cmplt_type *)r->buf;
- resp->MessageType = cpu_to_le32(
- REMOTE_NDIS_KEEPALIVE_CMPLT);
+ 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);
@@ -746,7 +744,7 @@ static int rndis_indicate_status_msg(int configNr, u32 status)
return -ENOMEM;
resp = (rndis_indicate_status_msg_type *)r->buf;
- resp->MessageType = cpu_to_le32(REMOTE_NDIS_INDICATE_STATUS_MSG);
+ 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);
@@ -759,18 +757,20 @@ static int rndis_indicate_status_msg(int configNr, u32 status)
int rndis_signal_connect(int configNr)
{
rndis_per_dev_params[configNr].media_state
- = NDIS_MEDIA_STATE_CONNECTED;
+ = 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)
{
rndis_per_dev_params[configNr].media_state
- = NDIS_MEDIA_STATE_DISCONNECTED;
+ = 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)
{
@@ -785,11 +785,13 @@ void rndis_uninit(int configNr)
while ((buf = rndis_get_next_response(configNr, &length)))
rndis_free_response(configNr, buf);
}
+EXPORT_SYMBOL_GPL(rndis_uninit);
void rndis_set_host_mac(int configNr, const u8 *addr)
{
rndis_per_dev_params[configNr].host_mac = addr;
}
+EXPORT_SYMBOL_GPL(rndis_set_host_mac);
/*
* Message Parser
@@ -818,15 +820,15 @@ 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",
+ 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);
- case REMOTE_NDIS_HALT_MSG:
- pr_debug("%s: REMOTE_NDIS_HALT_MSG\n",
+ case RNDIS_MSG_HALT:
+ pr_debug("%s: RNDIS_MSG_HALT\n",
__func__);
params->state = RNDIS_UNINITIALIZED;
if (params->dev) {
@@ -835,24 +837,24 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
}
return 0;
- case REMOTE_NDIS_QUERY_MSG:
+ case RNDIS_MSG_QUERY:
return rndis_query_response(configNr,
(rndis_query_msg_type *)buf);
- case REMOTE_NDIS_SET_MSG:
+ 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",
+ 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",
+ pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
__func__);
return rndis_keepalive_response(configNr,
(rndis_keepalive_msg_type *)
@@ -865,31 +867,14 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
*/
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]);
- }
- }
+ 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)
{
@@ -911,6 +896,7 @@ int rndis_register(void (*resp_avail)(void *v), void *v)
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(rndis_register);
void rndis_deregister(int configNr)
{
@@ -919,6 +905,7 @@ void rndis_deregister(int configNr)
if (configNr >= RNDIS_MAX_CONFIGS) return;
rndis_per_dev_params[configNr].used = 0;
}
+EXPORT_SYMBOL_GPL(rndis_deregister);
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
{
@@ -932,6 +919,7 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
return 0;
}
+EXPORT_SYMBOL_GPL(rndis_set_param_dev);
int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
{
@@ -944,6 +932,7 @@ int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
return 0;
}
+EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
{
@@ -955,6 +944,7 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
return 0;
}
+EXPORT_SYMBOL_GPL(rndis_set_param_medium);
void rndis_add_hdr(struct sk_buff *skb)
{
@@ -964,11 +954,12 @@ void rndis_add_hdr(struct sk_buff *skb)
return;
header = (void *)skb_push(skb, sizeof(*header));
memset(header, 0, sizeof *header);
- header->MessageType = cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
+ 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));
}
+EXPORT_SYMBOL_GPL(rndis_add_hdr);
void rndis_free_response(int configNr, u8 *buf)
{
@@ -985,6 +976,7 @@ void rndis_free_response(int configNr, u8 *buf)
}
}
}
+EXPORT_SYMBOL_GPL(rndis_free_response);
u8 *rndis_get_next_response(int configNr, u32 *length)
{
@@ -1006,6 +998,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length)
return NULL;
}
+EXPORT_SYMBOL_GPL(rndis_get_next_response);
static rndis_resp_t *rndis_add_response(int configNr, u32 length)
{
@@ -1032,7 +1025,7 @@ int rndis_rm_hdr(struct gether *port,
__le32 *tmp = (void *)skb->data;
/* MessageType, MessageLength */
- if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
+ if (cpu_to_le32(RNDIS_MSG_PACKET)
!= get_unaligned(tmp++)) {
dev_kfree_skb_any(skb);
return -EINVAL;
@@ -1049,6 +1042,7 @@ int rndis_rm_hdr(struct gether *port,
skb_queue_tail(list, skb);
return 0;
}
+EXPORT_SYMBOL_GPL(rndis_rm_hdr);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
@@ -1074,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",
@@ -1085,7 +1079,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
+ rndis_params *p = PDE_DATA(file_inode(file));
u32 speed = 0;
int i, fl_speed = 0;
@@ -1129,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 = {
@@ -1174,7 +1168,7 @@ int rndis_init(void)
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;
+ = RNDIS_MEDIA_STATE_DISCONNECTED;
INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
}
@@ -1193,3 +1187,4 @@ void rndis_exit(void)
}
#endif
}
+
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 907c3300811..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;
@@ -262,7 +217,4 @@ int rndis_signal_disconnect (int configNr);
int rndis_state (int configNr);
extern void rndis_set_host_mac (int configNr, const u8 *addr);
-int rndis_init(void);
-void rndis_exit (void);
-
#endif /* _LINUX_RNDIS_H */
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
deleted file mode 100644
index 69295ba9d99..00000000000
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ /dev/null
@@ -1,3475 +0,0 @@
-/* linux/drivers/usb/gadget/s3c-hsotg.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Copyright 2008 Openmoko, Inc.
- * Copyright 2008 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * S3C USB2.0 High-speed / OtG driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-#include <mach/map.h>
-
-#include <plat/regs-usb-hsotg-phy.h>
-#include <plat/regs-usb-hsotg.h>
-#include <mach/regs-sys.h>
-#include <plat/udc-hs.h>
-#include <plat/cpu.h>
-
-#define DMA_ADDR_INVALID (~((dma_addr_t)0))
-
-/* EP0_MPS_LIMIT
- *
- * Unfortunately there seems to be a limit of the amount of data that can
- * be transferred by IN transactions on EP0. This is either 127 bytes or 3
- * packets (which practically means 1 packet and 63 bytes of data) when the
- * MPS is set to 64.
- *
- * This means if we are wanting to move >127 bytes of data, we need to
- * split the transactions up, but just doing one packet at a time does
- * not work (this may be an implicit DATA0 PID on first packet of the
- * transaction) and doing 2 packets is outside the controller's limits.
- *
- * If we try to lower the MPS size for EP0, then no transfers work properly
- * for EP0, and the system will fail basic enumeration. As no cause for this
- * has currently been found, we cannot support any large IN transfers for
- * EP0.
- */
-#define EP0_MPS_LIMIT 64
-
-struct s3c_hsotg;
-struct s3c_hsotg_req;
-
-/**
- * struct s3c_hsotg_ep - driver endpoint definition.
- * @ep: The gadget layer representation of the endpoint.
- * @name: The driver generated name for the endpoint.
- * @queue: Queue of requests for this endpoint.
- * @parent: Reference back to the parent device structure.
- * @req: The current request that the endpoint is processing. This is
- * used to indicate an request has been loaded onto the endpoint
- * and has yet to be completed (maybe due to data move, or simply
- * awaiting an ack from the core all the data has been completed).
- * @debugfs: File entry for debugfs file for this endpoint.
- * @lock: State lock to protect contents of endpoint.
- * @dir_in: Set to true if this endpoint is of the IN direction, which
- * means that it is sending data to the Host.
- * @index: The index for the endpoint registers.
- * @name: The name array passed to the USB core.
- * @halted: Set if the endpoint has been halted.
- * @periodic: Set if this is a periodic ep, such as Interrupt
- * @sent_zlp: Set if we've sent a zero-length packet.
- * @total_data: The total number of data bytes done.
- * @fifo_size: The size of the FIFO (for periodic IN endpoints)
- * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
- * @last_load: The offset of data for the last start of request.
- * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
- *
- * This is the driver's state for each registered enpoint, allowing it
- * to keep track of transactions that need doing. Each endpoint has a
- * lock to protect the state, to try and avoid using an overall lock
- * for the host controller as much as possible.
- *
- * For periodic IN endpoints, we have fifo_size and fifo_load to try
- * and keep track of the amount of data in the periodic FIFO for each
- * of these as we don't have a status register that tells us how much
- * is in each of them. (note, this may actually be useless information
- * as in shared-fifo mode periodic in acts like a single-frame packet
- * buffer than a fifo)
- */
-struct s3c_hsotg_ep {
- struct usb_ep ep;
- struct list_head queue;
- struct s3c_hsotg *parent;
- struct s3c_hsotg_req *req;
- struct dentry *debugfs;
-
- spinlock_t lock;
-
- unsigned long total_data;
- unsigned int size_loaded;
- unsigned int last_load;
- unsigned int fifo_load;
- unsigned short fifo_size;
-
- unsigned char dir_in;
- unsigned char index;
-
- unsigned int halted:1;
- unsigned int periodic:1;
- unsigned int sent_zlp:1;
-
- char name[10];
-};
-
-#define S3C_HSOTG_EPS (8+1) /* limit to 9 for the moment */
-
-/**
- * struct s3c_hsotg - driver state.
- * @dev: The parent device supplied to the probe function
- * @driver: USB gadget driver
- * @plat: The platform specific configuration data.
- * @regs: The memory area mapped for accessing registers.
- * @regs_res: The resource that was allocated when claiming register space.
- * @irq: The IRQ number we are using
- * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
- * @debug_root: root directrory for debugfs.
- * @debug_file: main status file for debugfs.
- * @debug_fifo: FIFO status file for debugfs.
- * @ep0_reply: Request used for ep0 reply.
- * @ep0_buff: Buffer for EP0 reply data, if needed.
- * @ctrl_buff: Buffer for EP0 control requests.
- * @ctrl_req: Request for EP0 control packets.
- * @eps: The endpoints being supplied to the gadget framework
- */
-struct s3c_hsotg {
- struct device *dev;
- struct usb_gadget_driver *driver;
- struct s3c_hsotg_plat *plat;
-
- void __iomem *regs;
- struct resource *regs_res;
- int irq;
- struct clk *clk;
-
- unsigned int dedicated_fifos:1;
-
- struct dentry *debug_root;
- struct dentry *debug_file;
- struct dentry *debug_fifo;
-
- struct usb_request *ep0_reply;
- struct usb_request *ctrl_req;
- u8 ep0_buff[8];
- u8 ctrl_buff[8];
-
- struct usb_gadget gadget;
- struct s3c_hsotg_ep eps[];
-};
-
-/**
- * struct s3c_hsotg_req - data transfer request
- * @req: The USB gadget request
- * @queue: The list of requests for the endpoint this is queued for.
- * @in_progress: Has already had size/packets written to core
- * @mapped: DMA buffer for this request has been mapped via dma_map_single().
- */
-struct s3c_hsotg_req {
- struct usb_request req;
- struct list_head queue;
- unsigned char in_progress;
- unsigned char mapped;
-};
-
-/* conversion functions */
-static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
-{
- return container_of(req, struct s3c_hsotg_req, req);
-}
-
-static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
-{
- return container_of(ep, struct s3c_hsotg_ep, ep);
-}
-
-static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
-{
- return container_of(gadget, struct s3c_hsotg, gadget);
-}
-
-static inline void __orr32(void __iomem *ptr, u32 val)
-{
- writel(readl(ptr) | val, ptr);
-}
-
-static inline void __bic32(void __iomem *ptr, u32 val)
-{
- writel(readl(ptr) & ~val, ptr);
-}
-
-/* forward decleration of functions */
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
-
-/**
- * using_dma - return the DMA status of the driver.
- * @hsotg: The driver state.
- *
- * Return true if we're using DMA.
- *
- * Currently, we have the DMA support code worked into everywhere
- * that needs it, but the AMBA DMA implementation in the hardware can
- * only DMA from 32bit aligned addresses. This means that gadgets such
- * as the CDC Ethernet cannot work as they often pass packets which are
- * not 32bit aligned.
- *
- * Unfortunately the choice to use DMA or not is global to the controller
- * and seems to be only settable when the controller is being put through
- * a core reset. This means we either need to fix the gadgets to take
- * account of DMA alignment, or add bounce buffers (yuerk).
- *
- * Until this issue is sorted out, we always return 'false'.
- */
-static inline bool using_dma(struct s3c_hsotg *hsotg)
-{
- return false; /* support is not complete */
-}
-
-/**
- * s3c_hsotg_en_gsint - enable one or more of the general interrupt
- * @hsotg: The device state
- * @ints: A bitmask of the interrupts to enable
- */
-static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
-{
- u32 gsintmsk = readl(hsotg->regs + S3C_GINTMSK);
- u32 new_gsintmsk;
-
- new_gsintmsk = gsintmsk | ints;
-
- if (new_gsintmsk != gsintmsk) {
- dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
- writel(new_gsintmsk, hsotg->regs + S3C_GINTMSK);
- }
-}
-
-/**
- * s3c_hsotg_disable_gsint - disable one or more of the general interrupt
- * @hsotg: The device state
- * @ints: A bitmask of the interrupts to enable
- */
-static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
-{
- u32 gsintmsk = readl(hsotg->regs + S3C_GINTMSK);
- u32 new_gsintmsk;
-
- new_gsintmsk = gsintmsk & ~ints;
-
- if (new_gsintmsk != gsintmsk)
- writel(new_gsintmsk, hsotg->regs + S3C_GINTMSK);
-}
-
-/**
- * s3c_hsotg_ctrl_epint - enable/disable an endpoint irq
- * @hsotg: The device state
- * @ep: The endpoint index
- * @dir_in: True if direction is in.
- * @en: The enable value, true to enable
- *
- * Set or clear the mask for an individual endpoint's interrupt
- * request.
- */
-static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
- unsigned int ep, unsigned int dir_in,
- unsigned int en)
-{
- unsigned long flags;
- u32 bit = 1 << ep;
- u32 daint;
-
- if (!dir_in)
- bit <<= 16;
-
- local_irq_save(flags);
- daint = readl(hsotg->regs + S3C_DAINTMSK);
- if (en)
- daint |= bit;
- else
- daint &= ~bit;
- writel(daint, hsotg->regs + S3C_DAINTMSK);
- local_irq_restore(flags);
-}
-
-/**
- * s3c_hsotg_init_fifo - initialise non-periodic FIFOs
- * @hsotg: The device instance.
- */
-static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
-{
- unsigned int ep;
- unsigned int addr;
- unsigned int size;
- int timeout;
- u32 val;
-
- /* the ryu 2.6.24 release ahs
- writel(0x1C0, hsotg->regs + S3C_GRXFSIZ);
- writel(S3C_GNPTXFSIZ_NPTxFStAddr(0x200) |
- S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
- hsotg->regs + S3C_GNPTXFSIZ);
- */
-
- /* set FIFO sizes to 2048/1024 */
-
- writel(2048, hsotg->regs + S3C_GRXFSIZ);
- writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
- S3C_GNPTXFSIZ_NPTxFDep(1024),
- hsotg->regs + S3C_GNPTXFSIZ);
-
- /* arange all the rest of the TX FIFOs, as some versions of this
- * block have overlapping default addresses. This also ensures
- * that if the settings have been changed, then they are set to
- * known values. */
-
- /* start at the end of the GNPTXFSIZ, rounded up */
- addr = 2048 + 1024;
- size = 768;
-
- /* currently we allocate TX FIFOs for all possible endpoints,
- * and assume that they are all the same size. */
-
- for (ep = 0; ep <= 15; ep++) {
- val = addr;
- val |= size << S3C_DPTXFSIZn_DPTxFSize_SHIFT;
- addr += size;
-
- writel(val, hsotg->regs + S3C_DPTXFSIZn(ep));
- }
-
- /* according to p428 of the design guide, we need to ensure that
- * all fifos are flushed before continuing */
-
- writel(S3C_GRSTCTL_TxFNum(0x10) | S3C_GRSTCTL_TxFFlsh |
- S3C_GRSTCTL_RxFFlsh, hsotg->regs + S3C_GRSTCTL);
-
- /* wait until the fifos are both flushed */
- timeout = 100;
- while (1) {
- val = readl(hsotg->regs + S3C_GRSTCTL);
-
- if ((val & (S3C_GRSTCTL_TxFFlsh | S3C_GRSTCTL_RxFFlsh)) == 0)
- break;
-
- if (--timeout == 0) {
- dev_err(hsotg->dev,
- "%s: timeout flushing fifos (GRSTCTL=%08x)\n",
- __func__, val);
- }
-
- udelay(1);
- }
-
- dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout);
-}
-
-/**
- * @ep: USB endpoint to allocate request for.
- * @flags: Allocation flags
- *
- * Allocate a new USB request structure appropriate for the specified endpoint
- */
-static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep,
- gfp_t flags)
-{
- struct s3c_hsotg_req *req;
-
- req = kzalloc(sizeof(struct s3c_hsotg_req), flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD(&req->queue);
-
- req->req.dma = DMA_ADDR_INVALID;
- return &req->req;
-}
-
-/**
- * is_ep_periodic - return true if the endpoint is in periodic mode.
- * @hs_ep: The endpoint to query.
- *
- * Returns true if the endpoint is in periodic mode, meaning it is being
- * used for an Interrupt or ISO transfer.
- */
-static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
-{
- return hs_ep->periodic;
-}
-
-/**
- * s3c_hsotg_unmap_dma - unmap the DMA memory being used for the request
- * @hsotg: The device state.
- * @hs_ep: The endpoint for the request
- * @hs_req: The request being processed.
- *
- * This is the reverse of s3c_hsotg_map_dma(), called for the completion
- * of a request to ensure the buffer is ready for access by the caller.
-*/
-static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct s3c_hsotg_req *hs_req)
-{
- struct usb_request *req = &hs_req->req;
- enum dma_data_direction dir;
-
- dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
- /* ignore this if we're not moving any data */
- if (hs_req->req.length == 0)
- return;
-
- if (hs_req->mapped) {
- /* we mapped this, so unmap and remove the dma */
-
- dma_unmap_single(hsotg->dev, req->dma, req->length, dir);
-
- req->dma = DMA_ADDR_INVALID;
- hs_req->mapped = 0;
- } else {
- dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir);
- }
-}
-
-/**
- * s3c_hsotg_write_fifo - write packet Data to the TxFIFO
- * @hsotg: The controller state.
- * @hs_ep: The endpoint we're going to write for.
- * @hs_req: The request to write data for.
- *
- * This is called when the TxFIFO has some space in it to hold a new
- * transmission and we have something to give it. The actual setup of
- * the data size is done elsewhere, so all we have to do is to actually
- * write the data.
- *
- * The return value is zero if there is more space (or nothing was done)
- * otherwise -ENOSPC is returned if the FIFO space was used up.
- *
- * This routine is only needed for PIO
-*/
-static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct s3c_hsotg_req *hs_req)
-{
- bool periodic = is_ep_periodic(hs_ep);
- u32 gnptxsts = readl(hsotg->regs + S3C_GNPTXSTS);
- int buf_pos = hs_req->req.actual;
- int to_write = hs_ep->size_loaded;
- void *data;
- int can_write;
- int pkt_round;
-
- to_write -= (buf_pos - hs_ep->last_load);
-
- /* if there's nothing to write, get out early */
- if (to_write == 0)
- return 0;
-
- if (periodic && !hsotg->dedicated_fifos) {
- u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
- int size_left;
- int size_done;
-
- /* work out how much data was loaded so we can calculate
- * how much data is left in the fifo. */
-
- size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
-
- /* if shared fifo, we cannot write anything until the
- * previous data has been completely sent.
- */
- if (hs_ep->fifo_load != 0) {
- s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
- return -ENOSPC;
- }
-
- dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
- __func__, size_left,
- hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
-
- /* how much of the data has moved */
- size_done = hs_ep->size_loaded - size_left;
-
- /* how much data is left in the fifo */
- can_write = hs_ep->fifo_load - size_done;
- dev_dbg(hsotg->dev, "%s: => can_write1=%d\n",
- __func__, can_write);
-
- can_write = hs_ep->fifo_size - can_write;
- dev_dbg(hsotg->dev, "%s: => can_write2=%d\n",
- __func__, can_write);
-
- if (can_write <= 0) {
- s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
- return -ENOSPC;
- }
- } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
- can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index));
-
- can_write &= 0xffff;
- can_write *= 4;
- } else {
- if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
- dev_dbg(hsotg->dev,
- "%s: no queue slots available (0x%08x)\n",
- __func__, gnptxsts);
-
- s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_NPTxFEmp);
- return -ENOSPC;
- }
-
- can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
- can_write *= 4; /* fifo size is in 32bit quantities. */
- }
-
- dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
- __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket);
-
- /* limit to 512 bytes of data, it seems at least on the non-periodic
- * FIFO, requests of >512 cause the endpoint to get stuck with a
- * fragment of the end of the transfer in it.
- */
- if (can_write > 512)
- can_write = 512;
-
- /* limit the write to one max-packet size worth of data, but allow
- * the transfer to return that it did not run out of fifo space
- * doing it. */
- if (to_write > hs_ep->ep.maxpacket) {
- to_write = hs_ep->ep.maxpacket;
-
- s3c_hsotg_en_gsint(hsotg,
- periodic ? S3C_GINTSTS_PTxFEmp :
- S3C_GINTSTS_NPTxFEmp);
- }
-
- /* see if we can write data */
-
- if (to_write > can_write) {
- to_write = can_write;
- pkt_round = to_write % hs_ep->ep.maxpacket;
-
- /* Not sure, but we probably shouldn't be writing partial
- * packets into the FIFO, so round the write down to an
- * exact number of packets.
- *
- * Note, we do not currently check to see if we can ever
- * write a full packet or not to the FIFO.
- */
-
- if (pkt_round)
- to_write -= pkt_round;
-
- /* enable correct FIFO interrupt to alert us when there
- * is more room left. */
-
- s3c_hsotg_en_gsint(hsotg,
- periodic ? S3C_GINTSTS_PTxFEmp :
- S3C_GINTSTS_NPTxFEmp);
- }
-
- dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
- to_write, hs_req->req.length, can_write, buf_pos);
-
- if (to_write <= 0)
- return -ENOSPC;
-
- hs_req->req.actual = buf_pos + to_write;
- hs_ep->total_data += to_write;
-
- if (periodic)
- hs_ep->fifo_load += to_write;
-
- to_write = DIV_ROUND_UP(to_write, 4);
- data = hs_req->req.buf + buf_pos;
-
- writesl(hsotg->regs + S3C_EPFIFO(hs_ep->index), data, to_write);
-
- return (to_write >= can_write) ? -ENOSPC : 0;
-}
-
-/**
- * get_ep_limit - get the maximum data legnth for this endpoint
- * @hs_ep: The endpoint
- *
- * Return the maximum data that can be queued in one go on a given endpoint
- * so that transfers that are too long can be split.
- */
-static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
-{
- int index = hs_ep->index;
- unsigned maxsize;
- unsigned maxpkt;
-
- if (index != 0) {
- maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
- maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
- } else {
- maxsize = 64+64;
- if (hs_ep->dir_in)
- maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
- else
- maxpkt = 2;
- }
-
- /* we made the constant loading easier above by using +1 */
- maxpkt--;
- maxsize--;
-
- /* constrain by packet count if maxpkts*pktsize is greater
- * than the length register size. */
-
- if ((maxpkt * hs_ep->ep.maxpacket) < maxsize)
- maxsize = maxpkt * hs_ep->ep.maxpacket;
-
- return maxsize;
-}
-
-/**
- * s3c_hsotg_start_req - start a USB request from an endpoint's queue
- * @hsotg: The controller state.
- * @hs_ep: The endpoint to process a request for
- * @hs_req: The request to start.
- * @continuing: True if we are doing more for the current request.
- *
- * Start the given request running by setting the endpoint registers
- * appropriately, and writing any data to the FIFOs.
- */
-static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct s3c_hsotg_req *hs_req,
- bool continuing)
-{
- struct usb_request *ureq = &hs_req->req;
- int index = hs_ep->index;
- int dir_in = hs_ep->dir_in;
- u32 epctrl_reg;
- u32 epsize_reg;
- u32 epsize;
- u32 ctrl;
- unsigned length;
- unsigned packets;
- unsigned maxreq;
-
- if (index != 0) {
- if (hs_ep->req && !continuing) {
- dev_err(hsotg->dev, "%s: active request\n", __func__);
- WARN_ON(1);
- return;
- } else if (hs_ep->req != hs_req && continuing) {
- dev_err(hsotg->dev,
- "%s: continue different req\n", __func__);
- WARN_ON(1);
- return;
- }
- }
-
- epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
- epsize_reg = dir_in ? S3C_DIEPTSIZ(index) : S3C_DOEPTSIZ(index);
-
- dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
- __func__, readl(hsotg->regs + epctrl_reg), index,
- hs_ep->dir_in ? "in" : "out");
-
- /* If endpoint is stalled, we will restart request later */
- ctrl = readl(hsotg->regs + epctrl_reg);
-
- if (ctrl & S3C_DxEPCTL_Stall) {
- dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
- return;
- }
-
- length = ureq->length - ureq->actual;
-
- if (0)
- dev_dbg(hsotg->dev,
- "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
- ureq->buf, length, ureq->dma,
- ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
-
- maxreq = get_ep_limit(hs_ep);
- if (length > maxreq) {
- int round = maxreq % hs_ep->ep.maxpacket;
-
- dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n",
- __func__, length, maxreq, round);
-
- /* round down to multiple of packets */
- if (round)
- maxreq -= round;
-
- length = maxreq;
- }
-
- if (length)
- packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket);
- else
- packets = 1; /* send one packet if length is zero. */
-
- if (dir_in && index != 0)
- epsize = S3C_DxEPTSIZ_MC(1);
- else
- epsize = 0;
-
- if (index != 0 && ureq->zero) {
- /* test for the packets being exactly right for the
- * transfer */
-
- if (length == (packets * hs_ep->ep.maxpacket))
- packets++;
- }
-
- epsize |= S3C_DxEPTSIZ_PktCnt(packets);
- epsize |= S3C_DxEPTSIZ_XferSize(length);
-
- dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n",
- __func__, packets, length, ureq->length, epsize, epsize_reg);
-
- /* store the request as the current one we're doing */
- hs_ep->req = hs_req;
-
- /* write size / packets */
- writel(epsize, hsotg->regs + epsize_reg);
-
- if (using_dma(hsotg)) {
- unsigned int dma_reg;
-
- /* write DMA address to control register, buffer already
- * synced by s3c_hsotg_ep_queue(). */
-
- dma_reg = dir_in ? S3C_DIEPDMA(index) : S3C_DOEPDMA(index);
- writel(ureq->dma, hsotg->regs + dma_reg);
-
- dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
- __func__, ureq->dma, dma_reg);
- }
-
- ctrl |= S3C_DxEPCTL_EPEna; /* ensure ep enabled */
- ctrl |= S3C_DxEPCTL_USBActEp;
- ctrl |= S3C_DxEPCTL_CNAK; /* clear NAK set by core */
-
- dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
- writel(ctrl, hsotg->regs + epctrl_reg);
-
- /* set these, it seems that DMA support increments past the end
- * of the packet buffer so we need to calculate the length from
- * this information. */
- hs_ep->size_loaded = length;
- hs_ep->last_load = ureq->actual;
-
- if (dir_in && !using_dma(hsotg)) {
- /* set these anyway, we may need them for non-periodic in */
- hs_ep->fifo_load = 0;
-
- s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req);
- }
-
- /* clear the INTknTXFEmpMsk when we start request, more as a aide
- * to debugging to see what is going on. */
- if (dir_in)
- writel(S3C_DIEPMSK_INTknTXFEmpMsk,
- hsotg->regs + S3C_DIEPINT(index));
-
- /* Note, trying to clear the NAK here causes problems with transmit
- * on the S3C6400 ending up with the TXFIFO becoming full. */
-
- /* check ep is enabled */
- if (!(readl(hsotg->regs + epctrl_reg) & S3C_DxEPCTL_EPEna))
- dev_warn(hsotg->dev,
- "ep%d: failed to become enabled (DxEPCTL=0x%08x)?\n",
- index, readl(hsotg->regs + epctrl_reg));
-
- dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n",
- __func__, readl(hsotg->regs + epctrl_reg));
-}
-
-/**
- * s3c_hsotg_map_dma - map the DMA memory being used for the request
- * @hsotg: The device state.
- * @hs_ep: The endpoint the request is on.
- * @req: The request being processed.
- *
- * We've been asked to queue a request, so ensure that the memory buffer
- * is correctly setup for DMA. If we've been passed an extant DMA address
- * then ensure the buffer has been synced to memory. If our buffer has no
- * DMA memory, then we map the memory and mark our request to allow us to
- * cleanup on completion.
-*/
-static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct usb_request *req)
-{
- enum dma_data_direction dir;
- struct s3c_hsotg_req *hs_req = our_req(req);
-
- dir = hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
- /* if the length is zero, ignore the DMA data */
- if (hs_req->req.length == 0)
- return 0;
-
- if (req->dma == DMA_ADDR_INVALID) {
- dma_addr_t dma;
-
- dma = dma_map_single(hsotg->dev, req->buf, req->length, dir);
-
- if (unlikely(dma_mapping_error(hsotg->dev, dma)))
- goto dma_error;
-
- if (dma & 3) {
- dev_err(hsotg->dev, "%s: unaligned dma buffer\n",
- __func__);
-
- dma_unmap_single(hsotg->dev, dma, req->length, dir);
- return -EINVAL;
- }
-
- hs_req->mapped = 1;
- req->dma = dma;
- } else {
- dma_sync_single_for_cpu(hsotg->dev, req->dma, req->length, dir);
- hs_req->mapped = 0;
- }
-
- return 0;
-
-dma_error:
- dev_err(hsotg->dev, "%s: failed to map buffer %p, %d bytes\n",
- __func__, req->buf, req->length);
-
- return -EIO;
-}
-
-static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
- gfp_t gfp_flags)
-{
- struct s3c_hsotg_req *hs_req = our_req(req);
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
- unsigned long irqflags;
- bool first;
-
- dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
- ep->name, req, req->length, req->buf, req->no_interrupt,
- req->zero, req->short_not_ok);
-
- /* initialise status of the request */
- INIT_LIST_HEAD(&hs_req->queue);
- req->actual = 0;
- req->status = -EINPROGRESS;
-
- /* if we're using DMA, sync the buffers as necessary */
- if (using_dma(hs)) {
- int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
- if (ret)
- return ret;
- }
-
- spin_lock_irqsave(&hs_ep->lock, irqflags);
-
- first = list_empty(&hs_ep->queue);
- list_add_tail(&hs_req->queue, &hs_ep->queue);
-
- if (first)
- s3c_hsotg_start_req(hs, hs_ep, hs_req, false);
-
- spin_unlock_irqrestore(&hs_ep->lock, irqflags);
-
- return 0;
-}
-
-static void s3c_hsotg_ep_free_request(struct usb_ep *ep,
- struct usb_request *req)
-{
- struct s3c_hsotg_req *hs_req = our_req(req);
-
- kfree(hs_req);
-}
-
-/**
- * s3c_hsotg_complete_oursetup - setup completion callback
- * @ep: The endpoint the request was on.
- * @req: The request completed.
- *
- * Called on completion of any requests the driver itself
- * submitted that need cleaning up.
- */
-static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
- struct usb_request *req)
-{
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
-
- dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
-
- s3c_hsotg_ep_free_request(ep, req);
-}
-
-/**
- * ep_from_windex - convert control wIndex value to endpoint
- * @hsotg: The driver state.
- * @windex: The control request wIndex field (in host order).
- *
- * Convert the given wIndex into a pointer to an driver endpoint
- * structure, or return NULL if it is not a valid endpoint.
-*/
-static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
- u32 windex)
-{
- struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
- int dir = (windex & USB_DIR_IN) ? 1 : 0;
- int idx = windex & 0x7F;
-
- if (windex >= 0x100)
- return NULL;
-
- if (idx > S3C_HSOTG_EPS)
- return NULL;
-
- if (idx && ep->dir_in != dir)
- return NULL;
-
- return ep;
-}
-
-/**
- * s3c_hsotg_send_reply - send reply to control request
- * @hsotg: The device state
- * @ep: Endpoint 0
- * @buff: Buffer for request
- * @length: Length of reply.
- *
- * Create a request and queue it on the given endpoint. This is useful as
- * an internal method of sending replies to certain control requests, etc.
- */
-static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *ep,
- void *buff,
- int length)
-{
- struct usb_request *req;
- int ret;
-
- dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length);
-
- req = s3c_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC);
- hsotg->ep0_reply = req;
- if (!req) {
- dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__);
- return -ENOMEM;
- }
-
- req->buf = hsotg->ep0_buff;
- req->length = length;
- req->zero = 1; /* always do zero-length final transfer */
- req->complete = s3c_hsotg_complete_oursetup;
-
- if (length)
- memcpy(req->buf, buff, length);
- else
- ep->sent_zlp = 1;
-
- ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
- if (ret) {
- dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__);
- return ret;
- }
-
- return 0;
-}
-
-/**
- * s3c_hsotg_process_req_status - process request GET_STATUS
- * @hsotg: The device state
- * @ctrl: USB control request
- */
-static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
-{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
- struct s3c_hsotg_ep *ep;
- __le16 reply;
- int ret;
-
- dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__);
-
- if (!ep0->dir_in) {
- dev_warn(hsotg->dev, "%s: direction out?\n", __func__);
- return -EINVAL;
- }
-
- switch (ctrl->bRequestType & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- reply = cpu_to_le16(0); /* bit 0 => self powered,
- * bit 1 => remote wakeup */
- break;
-
- case USB_RECIP_INTERFACE:
- /* currently, the data result should be zero */
- reply = cpu_to_le16(0);
- break;
-
- case USB_RECIP_ENDPOINT:
- ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
- if (!ep)
- return -ENOENT;
-
- reply = cpu_to_le16(ep->halted ? 1 : 0);
- break;
-
- default:
- return 0;
- }
-
- if (le16_to_cpu(ctrl->wLength) != 2)
- return -EINVAL;
-
- ret = s3c_hsotg_send_reply(hsotg, ep0, &reply, 2);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to send reply\n", __func__);
- return ret;
- }
-
- return 1;
-}
-
-static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value);
-
-/**
- * get_ep_head - return the first request on the endpoint
- * @hs_ep: The controller endpoint to get
- *
- * Get the first request on the endpoint.
- */
-static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
-{
- if (list_empty(&hs_ep->queue))
- return NULL;
-
- return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue);
-}
-
-/**
- * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
- * @hsotg: The device state
- * @ctrl: USB control request
- */
-static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
-{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
- struct s3c_hsotg_req *hs_req;
- bool restart;
- bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
- struct s3c_hsotg_ep *ep;
- int ret;
-
- dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
- __func__, set ? "SET" : "CLEAR");
-
- if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
- ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
- if (!ep) {
- dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
- __func__, le16_to_cpu(ctrl->wIndex));
- return -ENOENT;
- }
-
- switch (le16_to_cpu(ctrl->wValue)) {
- case USB_ENDPOINT_HALT:
- s3c_hsotg_ep_sethalt(&ep->ep, set);
-
- ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
- if (ret) {
- dev_err(hsotg->dev,
- "%s: failed to send reply\n", __func__);
- return ret;
- }
-
- if (!set) {
- /*
- * If we have request in progress,
- * then complete it
- */
- if (ep->req) {
- hs_req = ep->req;
- ep->req = NULL;
- list_del_init(&hs_req->queue);
- hs_req->req.complete(&ep->ep,
- &hs_req->req);
- }
-
- /* If we have pending request, then start it */
- restart = !list_empty(&ep->queue);
- if (restart) {
- hs_req = get_ep_head(ep);
- s3c_hsotg_start_req(hsotg, ep,
- hs_req, false);
- }
- }
-
- break;
-
- default:
- return -ENOENT;
- }
- } else
- return -ENOENT; /* currently only deal with endpoint */
-
- return 1;
-}
-
-/**
- * s3c_hsotg_process_control - process a control request
- * @hsotg: The device state
- * @ctrl: The control request received
- *
- * The controller has received the SETUP phase of a control request, and
- * needs to work out what to do next (and whether to pass it on to the
- * gadget driver).
- */
-static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
-{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
- int ret = 0;
- u32 dcfg;
-
- ep0->sent_zlp = 0;
-
- dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
- ctrl->bRequest, ctrl->bRequestType,
- ctrl->wValue, ctrl->wLength);
-
- /* record the direction of the request, for later use when enquing
- * packets onto EP0. */
-
- ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
- dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
-
- /* if we've no data with this request, then the last part of the
- * transaction is going to implicitly be IN. */
- if (ctrl->wLength == 0)
- ep0->dir_in = 1;
-
- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- switch (ctrl->bRequest) {
- case USB_REQ_SET_ADDRESS:
- dcfg = readl(hsotg->regs + S3C_DCFG);
- dcfg &= ~S3C_DCFG_DevAddr_MASK;
- dcfg |= ctrl->wValue << S3C_DCFG_DevAddr_SHIFT;
- writel(dcfg, hsotg->regs + S3C_DCFG);
-
- dev_info(hsotg->dev, "new address %d\n", ctrl->wValue);
-
- ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
- return;
-
- case USB_REQ_GET_STATUS:
- ret = s3c_hsotg_process_req_status(hsotg, ctrl);
- break;
-
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- ret = s3c_hsotg_process_req_feature(hsotg, ctrl);
- break;
- }
- }
-
- /* as a fallback, try delivering it to the driver to deal with */
-
- if (ret == 0 && hsotg->driver) {
- ret = hsotg->driver->setup(&hsotg->gadget, ctrl);
- if (ret < 0)
- dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret);
- }
-
- /* the request is either unhandlable, or is not formatted correctly
- * so respond with a STALL for the status stage to indicate failure.
- */
-
- if (ret < 0) {
- u32 reg;
- u32 ctrl;
-
- dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
- reg = (ep0->dir_in) ? S3C_DIEPCTL0 : S3C_DOEPCTL0;
-
- /* S3C_DxEPCTL_Stall will be cleared by EP once it has
- * taken effect, so no need to clear later. */
-
- ctrl = readl(hsotg->regs + reg);
- ctrl |= S3C_DxEPCTL_Stall;
- ctrl |= S3C_DxEPCTL_CNAK;
- writel(ctrl, hsotg->regs + reg);
-
- dev_dbg(hsotg->dev,
- "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
- ctrl, reg, readl(hsotg->regs + reg));
-
- /* don't believe we need to anything more to get the EP
- * to reply with a STALL packet */
- }
-}
-
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
-
-/**
- * s3c_hsotg_complete_setup - completion of a setup transfer
- * @ep: The endpoint the request was on.
- * @req: The request completed.
- *
- * Called on completion of any requests the driver itself submitted for
- * EP0 setup packets
- */
-static void s3c_hsotg_complete_setup(struct usb_ep *ep,
- struct usb_request *req)
-{
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
-
- if (req->status < 0) {
- dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
- return;
- }
-
- if (req->actual == 0)
- s3c_hsotg_enqueue_setup(hsotg);
- else
- s3c_hsotg_process_control(hsotg, req->buf);
-}
-
-/**
- * s3c_hsotg_enqueue_setup - start a request for EP0 packets
- * @hsotg: The device state.
- *
- * Enqueue a request on EP0 if necessary to received any SETUP packets
- * received from the host.
- */
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
-{
- struct usb_request *req = hsotg->ctrl_req;
- struct s3c_hsotg_req *hs_req = our_req(req);
- int ret;
-
- dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__);
-
- req->zero = 0;
- req->length = 8;
- req->buf = hsotg->ctrl_buff;
- req->complete = s3c_hsotg_complete_setup;
-
- if (!list_empty(&hs_req->queue)) {
- dev_dbg(hsotg->dev, "%s already queued???\n", __func__);
- return;
- }
-
- hsotg->eps[0].dir_in = 0;
-
- ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC);
- if (ret < 0) {
- dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
- /* Don't think there's much we can do other than watch the
- * driver fail. */
- }
-}
-
-/**
- * s3c_hsotg_complete_request - complete a request given to us
- * @hsotg: The device state.
- * @hs_ep: The endpoint the request was on.
- * @hs_req: The request to complete.
- * @result: The result code (0 => Ok, otherwise errno)
- *
- * The given request has finished, so call the necessary completion
- * if it has one and then look to see if we can start a new request
- * on the endpoint.
- *
- * Note, expects the ep to already be locked as appropriate.
-*/
-static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct s3c_hsotg_req *hs_req,
- int result)
-{
- bool restart;
-
- if (!hs_req) {
- dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__);
- return;
- }
-
- dev_dbg(hsotg->dev, "complete: ep %p %s, req %p, %d => %p\n",
- hs_ep, hs_ep->ep.name, hs_req, result, hs_req->req.complete);
-
- /* only replace the status if we've not already set an error
- * from a previous transaction */
-
- if (hs_req->req.status == -EINPROGRESS)
- hs_req->req.status = result;
-
- hs_ep->req = NULL;
- list_del_init(&hs_req->queue);
-
- if (using_dma(hsotg))
- s3c_hsotg_unmap_dma(hsotg, hs_ep, hs_req);
-
- /* call the complete request with the locks off, just in case the
- * request tries to queue more work for this endpoint. */
-
- if (hs_req->req.complete) {
- spin_unlock(&hs_ep->lock);
- hs_req->req.complete(&hs_ep->ep, &hs_req->req);
- spin_lock(&hs_ep->lock);
- }
-
- /* Look to see if there is anything else to do. Note, the completion
- * of the previous request may have caused a new request to be started
- * so be careful when doing this. */
-
- if (!hs_ep->req && result >= 0) {
- restart = !list_empty(&hs_ep->queue);
- if (restart) {
- hs_req = get_ep_head(hs_ep);
- s3c_hsotg_start_req(hsotg, hs_ep, hs_req, false);
- }
- }
-}
-
-/**
- * s3c_hsotg_complete_request_lock - complete a request given to us (locked)
- * @hsotg: The device state.
- * @hs_ep: The endpoint the request was on.
- * @hs_req: The request to complete.
- * @result: The result code (0 => Ok, otherwise errno)
- *
- * See s3c_hsotg_complete_request(), but called with the endpoint's
- * lock held.
-*/
-static void s3c_hsotg_complete_request_lock(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- struct s3c_hsotg_req *hs_req,
- int result)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&hs_ep->lock, flags);
- s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
- spin_unlock_irqrestore(&hs_ep->lock, flags);
-}
-
-/**
- * s3c_hsotg_rx_data - receive data from the FIFO for an endpoint
- * @hsotg: The device state.
- * @ep_idx: The endpoint index for the data
- * @size: The size of data in the fifo, in bytes
- *
- * The FIFO status shows there is data to read from the FIFO for a given
- * endpoint, so sort out whether we need to read the data into a request
- * that has been made for that endpoint.
- */
-static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
-{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
- struct s3c_hsotg_req *hs_req = hs_ep->req;
- void __iomem *fifo = hsotg->regs + S3C_EPFIFO(ep_idx);
- int to_read;
- int max_req;
- int read_ptr;
-
- if (!hs_req) {
- u32 epctl = readl(hsotg->regs + S3C_DOEPCTL(ep_idx));
- int ptr;
-
- dev_warn(hsotg->dev,
- "%s: FIFO %d bytes on ep%d but no req (DxEPCTl=0x%08x)\n",
- __func__, size, ep_idx, epctl);
-
- /* dump the data from the FIFO, we've nothing we can do */
- for (ptr = 0; ptr < size; ptr += 4)
- (void)readl(fifo);
-
- return;
- }
-
- spin_lock(&hs_ep->lock);
-
- to_read = size;
- read_ptr = hs_req->req.actual;
- max_req = hs_req->req.length - read_ptr;
-
- dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
- __func__, to_read, max_req, read_ptr, hs_req->req.length);
-
- if (to_read > max_req) {
- /* more data appeared than we where willing
- * to deal with in this request.
- */
-
- /* currently we don't deal this */
- WARN_ON_ONCE(1);
- }
-
- hs_ep->total_data += to_read;
- hs_req->req.actual += to_read;
- to_read = DIV_ROUND_UP(to_read, 4);
-
- /* note, we might over-write the buffer end by 3 bytes depending on
- * alignment of the data. */
- readsl(fifo, hs_req->req.buf + read_ptr, to_read);
-
- spin_unlock(&hs_ep->lock);
-}
-
-/**
- * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
- * @hsotg: The device instance
- * @req: The request currently on this endpoint
- *
- * Generate a zero-length IN packet request for terminating a SETUP
- * transaction.
- *
- * Note, since we don't write any data to the TxFIFO, then it is
- * currently believed that we do not need to wait for any space in
- * the TxFIFO.
- */
-static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_req *req)
-{
- u32 ctrl;
-
- if (!req) {
- dev_warn(hsotg->dev, "%s: no request?\n", __func__);
- return;
- }
-
- if (req->req.length == 0) {
- hsotg->eps[0].sent_zlp = 1;
- s3c_hsotg_enqueue_setup(hsotg);
- return;
- }
-
- hsotg->eps[0].dir_in = 1;
- hsotg->eps[0].sent_zlp = 1;
-
- dev_dbg(hsotg->dev, "sending zero-length packet\n");
-
- /* issue a zero-sized packet to terminate this */
- writel(S3C_DxEPTSIZ_MC(1) | S3C_DxEPTSIZ_PktCnt(1) |
- S3C_DxEPTSIZ_XferSize(0), hsotg->regs + S3C_DIEPTSIZ(0));
-
- ctrl = readl(hsotg->regs + S3C_DIEPCTL0);
- ctrl |= S3C_DxEPCTL_CNAK; /* clear NAK set by core */
- ctrl |= S3C_DxEPCTL_EPEna; /* ensure ep enabled */
- ctrl |= S3C_DxEPCTL_USBActEp;
- writel(ctrl, hsotg->regs + S3C_DIEPCTL0);
-}
-
-/**
- * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
- * @hsotg: The device instance
- * @epnum: The endpoint received from
- * @was_setup: Set if processing a SetupDone event.
- *
- * The RXFIFO has delivered an OutDone event, which means that the data
- * transfer for an OUT endpoint has been completed, either by a short
- * packet or by the finish of a transfer.
-*/
-static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
- int epnum, bool was_setup)
-{
- u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
- struct s3c_hsotg_req *hs_req = hs_ep->req;
- struct usb_request *req = &hs_req->req;
- unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
- int result = 0;
-
- if (!hs_req) {
- dev_dbg(hsotg->dev, "%s: no request active\n", __func__);
- return;
- }
-
- if (using_dma(hsotg)) {
- unsigned size_done;
-
- /* Calculate the size of the transfer by checking how much
- * is left in the endpoint size register and then working it
- * out from the amount we loaded for the transfer.
- *
- * We need to do this as DMA pointers are always 32bit aligned
- * so may overshoot/undershoot the transfer.
- */
-
- size_done = hs_ep->size_loaded - size_left;
- size_done += hs_ep->last_load;
-
- req->actual = size_done;
- }
-
- /* if there is more request to do, schedule new transfer */
- if (req->actual < req->length && size_left == 0) {
- s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
- return;
- }
-
- if (req->actual < req->length && req->short_not_ok) {
- dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
- __func__, req->actual, req->length);
-
- /* todo - what should we return here? there's no one else
- * even bothering to check the status. */
- }
-
- if (epnum == 0) {
- if (!was_setup && req->complete != s3c_hsotg_complete_setup)
- s3c_hsotg_send_zlp(hsotg, hs_req);
- }
-
- s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, result);
-}
-
-/**
- * s3c_hsotg_read_frameno - read current frame number
- * @hsotg: The device instance
- *
- * Return the current frame number
-*/
-static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
-{
- u32 dsts;
-
- dsts = readl(hsotg->regs + S3C_DSTS);
- dsts &= S3C_DSTS_SOFFN_MASK;
- dsts >>= S3C_DSTS_SOFFN_SHIFT;
-
- return dsts;
-}
-
-/**
- * s3c_hsotg_handle_rx - RX FIFO has data
- * @hsotg: The device instance
- *
- * The IRQ handler has detected that the RX FIFO has some data in it
- * that requires processing, so find out what is in there and do the
- * appropriate read.
- *
- * The RXFIFO is a true FIFO, the packets coming out are still in packet
- * chunks, so if you have x packets received on an endpoint you'll get x
- * FIFO events delivered, each with a packet's worth of data in it.
- *
- * When using DMA, we should not be processing events from the RXFIFO
- * as the actual data should be sent to the memory directly and we turn
- * on the completion interrupts to get notifications of transfer completion.
- */
-static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
-{
- u32 grxstsr = readl(hsotg->regs + S3C_GRXSTSP);
- u32 epnum, status, size;
-
- WARN_ON(using_dma(hsotg));
-
- epnum = grxstsr & S3C_GRXSTS_EPNum_MASK;
- status = grxstsr & S3C_GRXSTS_PktSts_MASK;
-
- size = grxstsr & S3C_GRXSTS_ByteCnt_MASK;
- size >>= S3C_GRXSTS_ByteCnt_SHIFT;
-
- if (1)
- dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
- __func__, grxstsr, size, epnum);
-
-#define __status(x) ((x) >> S3C_GRXSTS_PktSts_SHIFT)
-
- switch (status >> S3C_GRXSTS_PktSts_SHIFT) {
- case __status(S3C_GRXSTS_PktSts_GlobalOutNAK):
- dev_dbg(hsotg->dev, "GlobalOutNAK\n");
- break;
-
- case __status(S3C_GRXSTS_PktSts_OutDone):
- dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n",
- s3c_hsotg_read_frameno(hsotg));
-
- if (!using_dma(hsotg))
- s3c_hsotg_handle_outdone(hsotg, epnum, false);
- break;
-
- case __status(S3C_GRXSTS_PktSts_SetupDone):
- dev_dbg(hsotg->dev,
- "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
- s3c_hsotg_read_frameno(hsotg),
- readl(hsotg->regs + S3C_DOEPCTL(0)));
-
- s3c_hsotg_handle_outdone(hsotg, epnum, true);
- break;
-
- case __status(S3C_GRXSTS_PktSts_OutRX):
- s3c_hsotg_rx_data(hsotg, epnum, size);
- break;
-
- case __status(S3C_GRXSTS_PktSts_SetupRX):
- dev_dbg(hsotg->dev,
- "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
- s3c_hsotg_read_frameno(hsotg),
- readl(hsotg->regs + S3C_DOEPCTL(0)));
-
- s3c_hsotg_rx_data(hsotg, epnum, size);
- break;
-
- default:
- dev_warn(hsotg->dev, "%s: unknown status %08x\n",
- __func__, grxstsr);
-
- s3c_hsotg_dump(hsotg);
- break;
- }
-}
-
-/**
- * s3c_hsotg_ep0_mps - turn max packet size into register setting
- * @mps: The maximum packet size in bytes.
-*/
-static u32 s3c_hsotg_ep0_mps(unsigned int mps)
-{
- switch (mps) {
- case 64:
- return S3C_D0EPCTL_MPS_64;
- case 32:
- return S3C_D0EPCTL_MPS_32;
- case 16:
- return S3C_D0EPCTL_MPS_16;
- case 8:
- return S3C_D0EPCTL_MPS_8;
- }
-
- /* bad max packet size, warn and return invalid result */
- WARN_ON(1);
- return (u32)-1;
-}
-
-/**
- * s3c_hsotg_set_ep_maxpacket - set endpoint's max-packet field
- * @hsotg: The driver state.
- * @ep: The index number of the endpoint
- * @mps: The maximum packet size in bytes
- *
- * Configure the maximum packet size for the given endpoint, updating
- * the hardware control registers to reflect this.
- */
-static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
- unsigned int ep, unsigned int mps)
-{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
- void __iomem *regs = hsotg->regs;
- u32 mpsval;
- u32 reg;
-
- if (ep == 0) {
- /* EP0 is a special case */
- mpsval = s3c_hsotg_ep0_mps(mps);
- if (mpsval > 3)
- goto bad_mps;
- } else {
- if (mps >= S3C_DxEPCTL_MPS_LIMIT+1)
- goto bad_mps;
-
- mpsval = mps;
- }
-
- hs_ep->ep.maxpacket = mps;
-
- /* update both the in and out endpoint controldir_ registers, even
- * if one of the directions may not be in use. */
-
- reg = readl(regs + S3C_DIEPCTL(ep));
- reg &= ~S3C_DxEPCTL_MPS_MASK;
- reg |= mpsval;
- writel(reg, regs + S3C_DIEPCTL(ep));
-
- reg = readl(regs + S3C_DOEPCTL(ep));
- reg &= ~S3C_DxEPCTL_MPS_MASK;
- reg |= mpsval;
- writel(reg, regs + S3C_DOEPCTL(ep));
-
- return;
-
-bad_mps:
- dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps);
-}
-
-/**
- * s3c_hsotg_txfifo_flush - flush Tx FIFO
- * @hsotg: The driver state
- * @idx: The index for the endpoint (0..15)
- */
-static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
-{
- int timeout;
- int val;
-
- writel(S3C_GRSTCTL_TxFNum(idx) | S3C_GRSTCTL_TxFFlsh,
- hsotg->regs + S3C_GRSTCTL);
-
- /* wait until the fifo is flushed */
- timeout = 100;
-
- while (1) {
- val = readl(hsotg->regs + S3C_GRSTCTL);
-
- if ((val & (S3C_GRSTCTL_TxFFlsh)) == 0)
- break;
-
- if (--timeout == 0) {
- dev_err(hsotg->dev,
- "%s: timeout flushing fifo (GRSTCTL=%08x)\n",
- __func__, val);
- }
-
- udelay(1);
- }
-}
-
-/**
- * s3c_hsotg_trytx - check to see if anything needs transmitting
- * @hsotg: The driver state
- * @hs_ep: The driver endpoint to check.
- *
- * Check to see if there is a request that has data to send, and if so
- * make an attempt to write data into the FIFO.
- */
-static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep)
-{
- struct s3c_hsotg_req *hs_req = hs_ep->req;
-
- if (!hs_ep->dir_in || !hs_req)
- return 0;
-
- if (hs_req->req.actual < hs_req->req.length) {
- dev_dbg(hsotg->dev, "trying to write more for ep%d\n",
- hs_ep->index);
- return s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req);
- }
-
- return 0;
-}
-
-/**
- * s3c_hsotg_complete_in - complete IN transfer
- * @hsotg: The device state.
- * @hs_ep: The endpoint that has just completed.
- *
- * An IN transfer has been completed, update the transfer's state and then
- * call the relevant completion routines.
- */
-static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep)
-{
- struct s3c_hsotg_req *hs_req = hs_ep->req;
- u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
- int size_left, size_done;
-
- if (!hs_req) {
- dev_dbg(hsotg->dev, "XferCompl but no req\n");
- return;
- }
-
- /* Calculate the size of the transfer by checking how much is left
- * in the endpoint size register and then working it out from
- * the amount we loaded for the transfer.
- *
- * We do this even for DMA, as the transfer may have incremented
- * past the end of the buffer (DMA transfers are always 32bit
- * aligned).
- */
-
- size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
-
- size_done = hs_ep->size_loaded - size_left;
- size_done += hs_ep->last_load;
-
- if (hs_req->req.actual != size_done)
- dev_dbg(hsotg->dev, "%s: adjusting size done %d => %d\n",
- __func__, hs_req->req.actual, size_done);
-
- hs_req->req.actual = size_done;
-
- /* if we did all of the transfer, and there is more data left
- * around, then try restarting the rest of the request */
-
- if (!size_left && hs_req->req.actual < hs_req->req.length) {
- dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
- s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
- } else
- s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
-}
-
-/**
- * s3c_hsotg_epint - handle an in/out endpoint interrupt
- * @hsotg: The driver state
- * @idx: The index for the endpoint (0..15)
- * @dir_in: Set if this is an IN endpoint
- *
- * Process and clear any interrupt pending for an individual endpoint
-*/
-static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
- int dir_in)
-{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
- u32 epint_reg = dir_in ? S3C_DIEPINT(idx) : S3C_DOEPINT(idx);
- u32 epctl_reg = dir_in ? S3C_DIEPCTL(idx) : S3C_DOEPCTL(idx);
- u32 epsiz_reg = dir_in ? S3C_DIEPTSIZ(idx) : S3C_DOEPTSIZ(idx);
- u32 ints;
-
- ints = readl(hsotg->regs + epint_reg);
-
- /* Clear endpoint interrupts */
- writel(ints, hsotg->regs + epint_reg);
-
- dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
- __func__, idx, dir_in ? "in" : "out", ints);
-
- if (ints & S3C_DxEPINT_XferCompl) {
- dev_dbg(hsotg->dev,
- "%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n",
- __func__, readl(hsotg->regs + epctl_reg),
- readl(hsotg->regs + epsiz_reg));
-
- /* we get OutDone from the FIFO, so we only need to look
- * at completing IN requests here */
- if (dir_in) {
- s3c_hsotg_complete_in(hsotg, hs_ep);
-
- if (idx == 0 && !hs_ep->req)
- s3c_hsotg_enqueue_setup(hsotg);
- } else if (using_dma(hsotg)) {
- /* We're using DMA, we need to fire an OutDone here
- * as we ignore the RXFIFO. */
-
- s3c_hsotg_handle_outdone(hsotg, idx, false);
- }
- }
-
- if (ints & S3C_DxEPINT_EPDisbld) {
- dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
-
- if (dir_in) {
- int epctl = readl(hsotg->regs + epctl_reg);
-
- s3c_hsotg_txfifo_flush(hsotg, idx);
-
- if ((epctl & S3C_DxEPCTL_Stall) &&
- (epctl & S3C_DxEPCTL_EPType_Bulk)) {
- int dctl = readl(hsotg->regs + S3C_DCTL);
-
- dctl |= S3C_DCTL_CGNPInNAK;
- writel(dctl, hsotg->regs + S3C_DCTL);
- }
- }
- }
-
- if (ints & S3C_DxEPINT_AHBErr)
- dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__);
-
- if (ints & S3C_DxEPINT_Setup) { /* Setup or Timeout */
- dev_dbg(hsotg->dev, "%s: Setup/Timeout\n", __func__);
-
- if (using_dma(hsotg) && idx == 0) {
- /* this is the notification we've received a
- * setup packet. In non-DMA mode we'd get this
- * from the RXFIFO, instead we need to process
- * the setup here. */
-
- if (dir_in)
- WARN_ON_ONCE(1);
- else
- s3c_hsotg_handle_outdone(hsotg, 0, true);
- }
- }
-
- if (ints & S3C_DxEPINT_Back2BackSetup)
- dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
-
- if (dir_in) {
- /* not sure if this is important, but we'll clear it anyway
- */
- if (ints & S3C_DIEPMSK_INTknTXFEmpMsk) {
- dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
- __func__, idx);
- }
-
- /* this probably means something bad is happening */
- if (ints & S3C_DIEPMSK_INTknEPMisMsk) {
- dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n",
- __func__, idx);
- }
-
- /* FIFO has space or is empty (see GAHBCFG) */
- if (hsotg->dedicated_fifos &&
- ints & S3C_DIEPMSK_TxFIFOEmpty) {
- dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
- __func__, idx);
- s3c_hsotg_trytx(hsotg, hs_ep);
- }
- }
-}
-
-/**
- * s3c_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done)
- * @hsotg: The device state.
- *
- * Handle updating the device settings after the enumeration phase has
- * been completed.
-*/
-static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
-{
- u32 dsts = readl(hsotg->regs + S3C_DSTS);
- int ep0_mps = 0, ep_mps;
-
- /* This should signal the finish of the enumeration phase
- * of the USB handshaking, so we should now know what rate
- * we connected at. */
-
- dev_dbg(hsotg->dev, "EnumDone (DSTS=0x%08x)\n", dsts);
-
- /* note, since we're limited by the size of transfer on EP0, and
- * it seems IN transfers must be a even number of packets we do
- * not advertise a 64byte MPS on EP0. */
-
- /* catch both EnumSpd_FS and EnumSpd_FS48 */
- switch (dsts & S3C_DSTS_EnumSpd_MASK) {
- case S3C_DSTS_EnumSpd_FS:
- case S3C_DSTS_EnumSpd_FS48:
- hsotg->gadget.speed = USB_SPEED_FULL;
- ep0_mps = EP0_MPS_LIMIT;
- ep_mps = 64;
- break;
-
- case S3C_DSTS_EnumSpd_HS:
- hsotg->gadget.speed = USB_SPEED_HIGH;
- ep0_mps = EP0_MPS_LIMIT;
- ep_mps = 512;
- break;
-
- case S3C_DSTS_EnumSpd_LS:
- hsotg->gadget.speed = USB_SPEED_LOW;
- /* note, we don't actually support LS in this driver at the
- * moment, and the documentation seems to imply that it isn't
- * supported by the PHYs on some of the devices.
- */
- break;
- }
- dev_info(hsotg->dev, "new device is %s\n",
- usb_speed_string(hsotg->gadget.speed));
-
- /* we should now know the maximum packet size for an
- * endpoint, so set the endpoints to a default value. */
-
- if (ep0_mps) {
- int i;
- s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps);
- for (i = 1; i < S3C_HSOTG_EPS; i++)
- s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps);
- }
-
- /* ensure after enumeration our EP0 is active */
-
- s3c_hsotg_enqueue_setup(hsotg);
-
- dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- readl(hsotg->regs + S3C_DIEPCTL0),
- readl(hsotg->regs + S3C_DOEPCTL0));
-}
-
-/**
- * kill_all_requests - remove all requests from the endpoint's queue
- * @hsotg: The device state.
- * @ep: The endpoint the requests may be on.
- * @result: The result code to use.
- * @force: Force removal of any current requests
- *
- * Go through the requests on the given endpoint and mark them
- * completed with the given result code.
- */
-static void kill_all_requests(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *ep,
- int result, bool force)
-{
- struct s3c_hsotg_req *req, *treq;
- unsigned long flags;
-
- spin_lock_irqsave(&ep->lock, flags);
-
- list_for_each_entry_safe(req, treq, &ep->queue, queue) {
- /* currently, we can't do much about an already
- * running request on an in endpoint */
-
- if (ep->req == req && ep->dir_in && !force)
- continue;
-
- s3c_hsotg_complete_request(hsotg, ep, req,
- result);
- }
-
- spin_unlock_irqrestore(&ep->lock, flags);
-}
-
-#define call_gadget(_hs, _entry) \
- if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
- (_hs)->driver && (_hs)->driver->_entry) \
- (_hs)->driver->_entry(&(_hs)->gadget);
-
-/**
- * s3c_hsotg_disconnect_irq - disconnect irq service
- * @hsotg: The device state.
- *
- * A disconnect IRQ has been received, meaning that the host has
- * lost contact with the bus. Remove all current transactions
- * and signal the gadget driver that this has happened.
-*/
-static void s3c_hsotg_disconnect_irq(struct s3c_hsotg *hsotg)
-{
- unsigned ep;
-
- for (ep = 0; ep < S3C_HSOTG_EPS; ep++)
- kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
-
- call_gadget(hsotg, disconnect);
-}
-
-/**
- * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
- * @hsotg: The device state:
- * @periodic: True if this is a periodic FIFO interrupt
- */
-static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
-{
- struct s3c_hsotg_ep *ep;
- int epno, ret;
-
- /* look through for any more data to transmit */
-
- for (epno = 0; epno < S3C_HSOTG_EPS; epno++) {
- ep = &hsotg->eps[epno];
-
- if (!ep->dir_in)
- continue;
-
- if ((periodic && !ep->periodic) ||
- (!periodic && ep->periodic))
- continue;
-
- ret = s3c_hsotg_trytx(hsotg, ep);
- if (ret < 0)
- break;
- }
-}
-
-static struct s3c_hsotg *our_hsotg;
-
-/* IRQ flags which will trigger a retry around the IRQ loop */
-#define IRQ_RETRY_MASK (S3C_GINTSTS_NPTxFEmp | \
- S3C_GINTSTS_PTxFEmp | \
- S3C_GINTSTS_RxFLvl)
-
-/**
- * s3c_hsotg_irq - handle device interrupt
- * @irq: The IRQ number triggered
- * @pw: The pw value when registered the handler.
- */
-static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
-{
- struct s3c_hsotg *hsotg = pw;
- int retry_count = 8;
- u32 gintsts;
- u32 gintmsk;
-
-irq_retry:
- gintsts = readl(hsotg->regs + S3C_GINTSTS);
- gintmsk = readl(hsotg->regs + S3C_GINTMSK);
-
- dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n",
- __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count);
-
- gintsts &= gintmsk;
-
- if (gintsts & S3C_GINTSTS_OTGInt) {
- u32 otgint = readl(hsotg->regs + S3C_GOTGINT);
-
- dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
-
- writel(otgint, hsotg->regs + S3C_GOTGINT);
- }
-
- if (gintsts & S3C_GINTSTS_DisconnInt) {
- dev_dbg(hsotg->dev, "%s: DisconnInt\n", __func__);
- writel(S3C_GINTSTS_DisconnInt, hsotg->regs + S3C_GINTSTS);
-
- s3c_hsotg_disconnect_irq(hsotg);
- }
-
- if (gintsts & S3C_GINTSTS_SessReqInt) {
- dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
- writel(S3C_GINTSTS_SessReqInt, hsotg->regs + S3C_GINTSTS);
- }
-
- if (gintsts & S3C_GINTSTS_EnumDone) {
- writel(S3C_GINTSTS_EnumDone, hsotg->regs + S3C_GINTSTS);
-
- s3c_hsotg_irq_enumdone(hsotg);
- }
-
- if (gintsts & S3C_GINTSTS_ConIDStsChng) {
- dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
- readl(hsotg->regs + S3C_DSTS),
- readl(hsotg->regs + S3C_GOTGCTL));
-
- writel(S3C_GINTSTS_ConIDStsChng, hsotg->regs + S3C_GINTSTS);
- }
-
- if (gintsts & (S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt)) {
- u32 daint = readl(hsotg->regs + S3C_DAINT);
- u32 daint_out = daint >> S3C_DAINT_OutEP_SHIFT;
- u32 daint_in = daint & ~(daint_out << S3C_DAINT_OutEP_SHIFT);
- int ep;
-
- dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
-
- for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
- if (daint_out & 1)
- s3c_hsotg_epint(hsotg, ep, 0);
- }
-
- for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) {
- if (daint_in & 1)
- s3c_hsotg_epint(hsotg, ep, 1);
- }
- }
-
- if (gintsts & S3C_GINTSTS_USBRst) {
- dev_info(hsotg->dev, "%s: USBRst\n", __func__);
- dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
- readl(hsotg->regs + S3C_GNPTXSTS));
-
- writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS);
-
- kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
-
- /* it seems after a reset we can end up with a situation
- * where the TXFIFO still has data in it... the docs
- * suggest resetting all the fifos, so use the init_fifo
- * code to relayout and flush the fifos.
- */
-
- s3c_hsotg_init_fifo(hsotg);
-
- s3c_hsotg_enqueue_setup(hsotg);
- }
-
- /* check both FIFOs */
-
- if (gintsts & S3C_GINTSTS_NPTxFEmp) {
- dev_dbg(hsotg->dev, "NPTxFEmp\n");
-
- /* Disable the interrupt to stop it happening again
- * unless one of these endpoint routines decides that
- * it needs re-enabling */
-
- s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_NPTxFEmp);
- s3c_hsotg_irq_fifoempty(hsotg, false);
- }
-
- if (gintsts & S3C_GINTSTS_PTxFEmp) {
- dev_dbg(hsotg->dev, "PTxFEmp\n");
-
- /* See note in S3C_GINTSTS_NPTxFEmp */
-
- s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
- s3c_hsotg_irq_fifoempty(hsotg, true);
- }
-
- if (gintsts & S3C_GINTSTS_RxFLvl) {
- /* note, since GINTSTS_RxFLvl doubles as FIFO-not-empty,
- * we need to retry s3c_hsotg_handle_rx if this is still
- * set. */
-
- s3c_hsotg_handle_rx(hsotg);
- }
-
- if (gintsts & S3C_GINTSTS_ModeMis) {
- dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
- writel(S3C_GINTSTS_ModeMis, hsotg->regs + S3C_GINTSTS);
- }
-
- if (gintsts & S3C_GINTSTS_USBSusp) {
- dev_info(hsotg->dev, "S3C_GINTSTS_USBSusp\n");
- writel(S3C_GINTSTS_USBSusp, hsotg->regs + S3C_GINTSTS);
-
- call_gadget(hsotg, suspend);
- }
-
- if (gintsts & S3C_GINTSTS_WkUpInt) {
- dev_info(hsotg->dev, "S3C_GINTSTS_WkUpIn\n");
- writel(S3C_GINTSTS_WkUpInt, hsotg->regs + S3C_GINTSTS);
-
- call_gadget(hsotg, resume);
- }
-
- if (gintsts & S3C_GINTSTS_ErlySusp) {
- dev_dbg(hsotg->dev, "S3C_GINTSTS_ErlySusp\n");
- writel(S3C_GINTSTS_ErlySusp, hsotg->regs + S3C_GINTSTS);
- }
-
- /* these next two seem to crop-up occasionally causing the core
- * to shutdown the USB transfer, so try clearing them and logging
- * the occurrence. */
-
- if (gintsts & S3C_GINTSTS_GOUTNakEff) {
- dev_info(hsotg->dev, "GOUTNakEff triggered\n");
-
- writel(S3C_DCTL_CGOUTNak, hsotg->regs + S3C_DCTL);
-
- s3c_hsotg_dump(hsotg);
- }
-
- if (gintsts & S3C_GINTSTS_GINNakEff) {
- dev_info(hsotg->dev, "GINNakEff triggered\n");
-
- writel(S3C_DCTL_CGNPInNAK, hsotg->regs + S3C_DCTL);
-
- s3c_hsotg_dump(hsotg);
- }
-
- /* if we've had fifo events, we should try and go around the
- * loop again to see if there's any point in returning yet. */
-
- if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
- goto irq_retry;
-
- return IRQ_HANDLED;
-}
-
-/**
- * s3c_hsotg_ep_enable - enable the given endpoint
- * @ep: The USB endpint to configure
- * @desc: The USB endpoint descriptor to configure with.
- *
- * This is called from the USB gadget code's usb_ep_enable().
-*/
-static int s3c_hsotg_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
- unsigned long flags;
- int index = hs_ep->index;
- u32 epctrl_reg;
- u32 epctrl;
- u32 mps;
- int dir_in;
- int ret = 0;
-
- dev_dbg(hsotg->dev,
- "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
- __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes,
- desc->wMaxPacketSize, desc->bInterval);
-
- /* not to be called for EP0 */
- WARN_ON(index == 0);
-
- dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
- if (dir_in != hs_ep->dir_in) {
- dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__);
- return -EINVAL;
- }
-
- mps = usb_endpoint_maxp(desc);
-
- /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */
-
- epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
- epctrl = readl(hsotg->regs + epctrl_reg);
-
- dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
- __func__, epctrl, epctrl_reg);
-
- spin_lock_irqsave(&hs_ep->lock, flags);
-
- epctrl &= ~(S3C_DxEPCTL_EPType_MASK | S3C_DxEPCTL_MPS_MASK);
- epctrl |= S3C_DxEPCTL_MPS(mps);
-
- /* mark the endpoint as active, otherwise the core may ignore
- * transactions entirely for this endpoint */
- epctrl |= S3C_DxEPCTL_USBActEp;
-
- /* set the NAK status on the endpoint, otherwise we might try and
- * do something with data that we've yet got a request to process
- * since the RXFIFO will take data for an endpoint even if the
- * size register hasn't been set.
- */
-
- epctrl |= S3C_DxEPCTL_SNAK;
-
- /* update the endpoint state */
- hs_ep->ep.maxpacket = mps;
-
- /* default, set to non-periodic */
- hs_ep->periodic = 0;
-
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_ISOC:
- dev_err(hsotg->dev, "no current ISOC support\n");
- ret = -EINVAL;
- goto out;
-
- case USB_ENDPOINT_XFER_BULK:
- epctrl |= S3C_DxEPCTL_EPType_Bulk;
- break;
-
- case USB_ENDPOINT_XFER_INT:
- if (dir_in) {
- /* Allocate our TxFNum by simply using the index
- * of the endpoint for the moment. We could do
- * something better if the host indicates how
- * many FIFOs we are expecting to use. */
-
- hs_ep->periodic = 1;
- epctrl |= S3C_DxEPCTL_TxFNum(index);
- }
-
- epctrl |= S3C_DxEPCTL_EPType_Intterupt;
- break;
-
- case USB_ENDPOINT_XFER_CONTROL:
- epctrl |= S3C_DxEPCTL_EPType_Control;
- break;
- }
-
- /* if the hardware has dedicated fifos, we must give each IN EP
- * a unique tx-fifo even if it is non-periodic.
- */
- if (dir_in && hsotg->dedicated_fifos)
- epctrl |= S3C_DxEPCTL_TxFNum(index);
-
- /* for non control endpoints, set PID to D0 */
- if (index)
- epctrl |= S3C_DxEPCTL_SetD0PID;
-
- dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
- __func__, epctrl);
-
- writel(epctrl, hsotg->regs + epctrl_reg);
- dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n",
- __func__, readl(hsotg->regs + epctrl_reg));
-
- /* enable the endpoint interrupt */
- s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
-
-out:
- spin_unlock_irqrestore(&hs_ep->lock, flags);
- return ret;
-}
-
-static int s3c_hsotg_ep_disable(struct usb_ep *ep)
-{
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
- int dir_in = hs_ep->dir_in;
- int index = hs_ep->index;
- unsigned long flags;
- u32 epctrl_reg;
- u32 ctrl;
-
- dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep);
-
- if (ep == &hsotg->eps[0].ep) {
- dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
- return -EINVAL;
- }
-
- epctrl_reg = dir_in ? S3C_DIEPCTL(index) : S3C_DOEPCTL(index);
-
- /* terminate all requests with shutdown */
- kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
-
- spin_lock_irqsave(&hs_ep->lock, flags);
-
- ctrl = readl(hsotg->regs + epctrl_reg);
- ctrl &= ~S3C_DxEPCTL_EPEna;
- ctrl &= ~S3C_DxEPCTL_USBActEp;
- ctrl |= S3C_DxEPCTL_SNAK;
-
- dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
- writel(ctrl, hsotg->regs + epctrl_reg);
-
- /* disable endpoint interrupts */
- s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
-
- spin_unlock_irqrestore(&hs_ep->lock, flags);
- return 0;
-}
-
-/**
- * on_list - check request is on the given endpoint
- * @ep: The endpoint to check.
- * @test: The request to test if it is on the endpoint.
-*/
-static bool on_list(struct s3c_hsotg_ep *ep, struct s3c_hsotg_req *test)
-{
- struct s3c_hsotg_req *req, *treq;
-
- list_for_each_entry_safe(req, treq, &ep->queue, queue) {
- if (req == test)
- return true;
- }
-
- return false;
-}
-
-static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
-{
- struct s3c_hsotg_req *hs_req = our_req(req);
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
- unsigned long flags;
-
- dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
-
- spin_lock_irqsave(&hs_ep->lock, flags);
-
- if (!on_list(hs_ep, hs_req)) {
- spin_unlock_irqrestore(&hs_ep->lock, flags);
- return -EINVAL;
- }
-
- s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
- spin_unlock_irqrestore(&hs_ep->lock, flags);
-
- return 0;
-}
-
-static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
-{
- struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
- int index = hs_ep->index;
- unsigned long irqflags;
- u32 epreg;
- u32 epctl;
- u32 xfertype;
-
- dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
-
- spin_lock_irqsave(&hs_ep->lock, irqflags);
-
- /* write both IN and OUT control registers */
-
- epreg = S3C_DIEPCTL(index);
- epctl = readl(hs->regs + epreg);
-
- if (value) {
- epctl |= S3C_DxEPCTL_Stall + S3C_DxEPCTL_SNAK;
- if (epctl & S3C_DxEPCTL_EPEna)
- epctl |= S3C_DxEPCTL_EPDis;
- } else {
- epctl &= ~S3C_DxEPCTL_Stall;
- xfertype = epctl & S3C_DxEPCTL_EPType_MASK;
- if (xfertype == S3C_DxEPCTL_EPType_Bulk ||
- xfertype == S3C_DxEPCTL_EPType_Intterupt)
- epctl |= S3C_DxEPCTL_SetD0PID;
- }
-
- writel(epctl, hs->regs + epreg);
-
- epreg = S3C_DOEPCTL(index);
- epctl = readl(hs->regs + epreg);
-
- if (value)
- epctl |= S3C_DxEPCTL_Stall;
- else {
- epctl &= ~S3C_DxEPCTL_Stall;
- xfertype = epctl & S3C_DxEPCTL_EPType_MASK;
- if (xfertype == S3C_DxEPCTL_EPType_Bulk ||
- xfertype == S3C_DxEPCTL_EPType_Intterupt)
- epctl |= S3C_DxEPCTL_SetD0PID;
- }
-
- writel(epctl, hs->regs + epreg);
-
- spin_unlock_irqrestore(&hs_ep->lock, irqflags);
-
- return 0;
-}
-
-static struct usb_ep_ops s3c_hsotg_ep_ops = {
- .enable = s3c_hsotg_ep_enable,
- .disable = s3c_hsotg_ep_disable,
- .alloc_request = s3c_hsotg_ep_alloc_request,
- .free_request = s3c_hsotg_ep_free_request,
- .queue = s3c_hsotg_ep_queue,
- .dequeue = s3c_hsotg_ep_dequeue,
- .set_halt = s3c_hsotg_ep_sethalt,
- /* note, don't believe we have any call for the fifo routines */
-};
-
-/**
- * s3c_hsotg_corereset - issue softreset to the core
- * @hsotg: The device state
- *
- * Issue a soft reset to the core, and await the core finishing it.
-*/
-static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
-{
- int timeout;
- u32 grstctl;
-
- dev_dbg(hsotg->dev, "resetting core\n");
-
- /* issue soft reset */
- writel(S3C_GRSTCTL_CSftRst, hsotg->regs + S3C_GRSTCTL);
-
- timeout = 1000;
- do {
- grstctl = readl(hsotg->regs + S3C_GRSTCTL);
- } while ((grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0);
-
- if (grstctl & S3C_GRSTCTL_CSftRst) {
- dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
- return -EINVAL;
- }
-
- timeout = 1000;
-
- while (1) {
- u32 grstctl = readl(hsotg->regs + S3C_GRSTCTL);
-
- if (timeout-- < 0) {
- dev_info(hsotg->dev,
- "%s: reset failed, GRSTCTL=%08x\n",
- __func__, grstctl);
- return -ETIMEDOUT;
- }
-
- if (!(grstctl & S3C_GRSTCTL_AHBIdle))
- continue;
-
- break; /* reset done */
- }
-
- dev_dbg(hsotg->dev, "reset successful\n");
- return 0;
-}
-
-static int s3c_hsotg_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
-{
- struct s3c_hsotg *hsotg = our_hsotg;
- int ret;
-
- if (!hsotg) {
- printk(KERN_ERR "%s: called with no device\n", __func__);
- return -ENODEV;
- }
-
- if (!driver) {
- dev_err(hsotg->dev, "%s: no driver\n", __func__);
- return -EINVAL;
- }
-
- if (driver->max_speed < USB_SPEED_FULL)
- dev_err(hsotg->dev, "%s: bad speed\n", __func__);
-
- if (!bind || !driver->setup) {
- dev_err(hsotg->dev, "%s: missing entry points\n", __func__);
- return -EINVAL;
- }
-
- WARN_ON(hsotg->driver);
-
- driver->driver.bus = NULL;
- hsotg->driver = driver;
- hsotg->gadget.dev.driver = &driver->driver;
- hsotg->gadget.dev.dma_mask = hsotg->dev->dma_mask;
- hsotg->gadget.speed = USB_SPEED_UNKNOWN;
-
- ret = device_add(&hsotg->gadget.dev);
- if (ret) {
- dev_err(hsotg->dev, "failed to register gadget device\n");
- goto err;
- }
-
- ret = bind(&hsotg->gadget);
- if (ret) {
- dev_err(hsotg->dev, "failed bind %s\n", driver->driver.name);
-
- hsotg->gadget.dev.driver = NULL;
- hsotg->driver = NULL;
- goto err;
- }
-
- /* we must now enable ep0 ready for host detection and then
- * set configuration. */
-
- s3c_hsotg_corereset(hsotg);
-
- /* set the PLL on, remove the HNP/SRP and set the PHY */
- writel(S3C_GUSBCFG_PHYIf16 | S3C_GUSBCFG_TOutCal(7) |
- (0x5 << 10), hsotg->regs + S3C_GUSBCFG);
-
- /* looks like soft-reset changes state of FIFOs */
- s3c_hsotg_init_fifo(hsotg);
-
- __orr32(hsotg->regs + S3C_DCTL, S3C_DCTL_SftDiscon);
-
- writel(1 << 18 | S3C_DCFG_DevSpd_HS, hsotg->regs + S3C_DCFG);
-
- /* Clear any pending OTG interrupts */
- writel(0xffffffff, hsotg->regs + S3C_GOTGINT);
-
- /* Clear any pending interrupts */
- writel(0xffffffff, hsotg->regs + S3C_GINTSTS);
-
- writel(S3C_GINTSTS_DisconnInt | S3C_GINTSTS_SessReqInt |
- S3C_GINTSTS_ConIDStsChng | S3C_GINTSTS_USBRst |
- S3C_GINTSTS_EnumDone | S3C_GINTSTS_OTGInt |
- S3C_GINTSTS_USBSusp | S3C_GINTSTS_WkUpInt |
- S3C_GINTSTS_GOUTNakEff | S3C_GINTSTS_GINNakEff |
- S3C_GINTSTS_ErlySusp,
- hsotg->regs + S3C_GINTMSK);
-
- if (using_dma(hsotg))
- writel(S3C_GAHBCFG_GlblIntrEn | S3C_GAHBCFG_DMAEn |
- S3C_GAHBCFG_HBstLen_Incr4,
- hsotg->regs + S3C_GAHBCFG);
- else
- writel(S3C_GAHBCFG_GlblIntrEn, hsotg->regs + S3C_GAHBCFG);
-
- /* Enabling INTknTXFEmpMsk here seems to be a big mistake, we end
- * up being flooded with interrupts if the host is polling the
- * endpoint to try and read data. */
-
- writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
- S3C_DIEPMSK_INTknEPMisMsk |
- S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk |
- ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0),
- hsotg->regs + S3C_DIEPMSK);
-
- /* don't need XferCompl, we get that from RXFIFO in slave mode. In
- * DMA mode we may need this. */
- writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk |
- S3C_DOEPMSK_EPDisbldMsk |
- (using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk |
- S3C_DIEPMSK_TimeOUTMsk) : 0),
- hsotg->regs + S3C_DOEPMSK);
-
- writel(0, hsotg->regs + S3C_DAINTMSK);
-
- dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- readl(hsotg->regs + S3C_DIEPCTL0),
- readl(hsotg->regs + S3C_DOEPCTL0));
-
- /* enable in and out endpoint interrupts */
- s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt);
-
- /* Enable the RXFIFO when in slave mode, as this is how we collect
- * the data. In DMA mode, we get events from the FIFO but also
- * things we cannot process, so do not use it. */
- if (!using_dma(hsotg))
- s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_RxFLvl);
-
- /* Enable interrupts for EP0 in and out */
- s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
- s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
-
- __orr32(hsotg->regs + S3C_DCTL, S3C_DCTL_PWROnPrgDone);
- udelay(10); /* see openiboot */
- __bic32(hsotg->regs + S3C_DCTL, S3C_DCTL_PWROnPrgDone);
-
- dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + S3C_DCTL));
-
- /* S3C_DxEPCTL_USBActEp says RO in manual, but seems to be set by
- writing to the EPCTL register.. */
-
- /* set to read 1 8byte packet */
- writel(S3C_DxEPTSIZ_MC(1) | S3C_DxEPTSIZ_PktCnt(1) |
- S3C_DxEPTSIZ_XferSize(8), hsotg->regs + DOEPTSIZ0);
-
- writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
- S3C_DxEPCTL_CNAK | S3C_DxEPCTL_EPEna |
- S3C_DxEPCTL_USBActEp,
- hsotg->regs + S3C_DOEPCTL0);
-
- /* enable, but don't activate EP0in */
- writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
- S3C_DxEPCTL_USBActEp, hsotg->regs + S3C_DIEPCTL0);
-
- s3c_hsotg_enqueue_setup(hsotg);
-
- dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- readl(hsotg->regs + S3C_DIEPCTL0),
- readl(hsotg->regs + S3C_DOEPCTL0));
-
- /* clear global NAKs */
- writel(S3C_DCTL_CGOUTNak | S3C_DCTL_CGNPInNAK,
- hsotg->regs + S3C_DCTL);
-
- /* must be at-least 3ms to allow bus to see disconnect */
- msleep(3);
-
- /* remove the soft-disconnect and let's go */
- __bic32(hsotg->regs + S3C_DCTL, S3C_DCTL_SftDiscon);
-
- /* report to the user, and return */
-
- dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
- return 0;
-
-err:
- hsotg->driver = NULL;
- hsotg->gadget.dev.driver = NULL;
- return ret;
-}
-
-static int s3c_hsotg_stop(struct usb_gadget_driver *driver)
-{
- struct s3c_hsotg *hsotg = our_hsotg;
- int ep;
-
- if (!hsotg)
- return -ENODEV;
-
- if (!driver || driver != hsotg->driver || !driver->unbind)
- return -EINVAL;
-
- /* all endpoints should be shutdown */
- for (ep = 0; ep < S3C_HSOTG_EPS; ep++)
- s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
-
- call_gadget(hsotg, disconnect);
-
- driver->unbind(&hsotg->gadget);
- hsotg->driver = NULL;
- hsotg->gadget.speed = USB_SPEED_UNKNOWN;
-
- device_del(&hsotg->gadget.dev);
-
- dev_info(hsotg->dev, "unregistered gadget driver '%s'\n",
- driver->driver.name);
-
- return 0;
-}
-
-static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
-{
- return s3c_hsotg_read_frameno(to_hsotg(gadget));
-}
-
-static struct usb_gadget_ops s3c_hsotg_gadget_ops = {
- .get_frame = s3c_hsotg_gadget_getframe,
- .start = s3c_hsotg_start,
- .stop = s3c_hsotg_stop,
-};
-
-/**
- * s3c_hsotg_initep - initialise a single endpoint
- * @hsotg: The device state.
- * @hs_ep: The endpoint to be initialised.
- * @epnum: The endpoint number
- *
- * Initialise the given endpoint (as part of the probe and device state
- * creation) to give to the gadget driver. Setup the endpoint name, any
- * direction information and other state that may be required.
- */
-static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
- struct s3c_hsotg_ep *hs_ep,
- int epnum)
-{
- u32 ptxfifo;
- char *dir;
-
- if (epnum == 0)
- dir = "";
- else if ((epnum % 2) == 0) {
- dir = "out";
- } else {
- dir = "in";
- hs_ep->dir_in = 1;
- }
-
- hs_ep->index = epnum;
-
- snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
-
- INIT_LIST_HEAD(&hs_ep->queue);
- INIT_LIST_HEAD(&hs_ep->ep.ep_list);
-
- spin_lock_init(&hs_ep->lock);
-
- /* add to the list of endpoints known by the gadget driver */
- if (epnum)
- list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
-
- hs_ep->parent = hsotg;
- hs_ep->ep.name = hs_ep->name;
- hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT;
- hs_ep->ep.ops = &s3c_hsotg_ep_ops;
-
- /* Read the FIFO size for the Periodic TX FIFO, even if we're
- * an OUT endpoint, we may as well do this if in future the
- * code is changed to make each endpoint's direction changeable.
- */
-
- ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
- hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4;
-
- /* if we're using dma, we need to set the next-endpoint pointer
- * to be something valid.
- */
-
- if (using_dma(hsotg)) {
- u32 next = S3C_DxEPCTL_NextEp((epnum + 1) % 15);
- writel(next, hsotg->regs + S3C_DIEPCTL(epnum));
- writel(next, hsotg->regs + S3C_DOEPCTL(epnum));
- }
-}
-
-/**
- * s3c_hsotg_otgreset - reset the OtG phy block
- * @hsotg: The host state.
- *
- * Power up the phy, set the basic configuration and start the PHY.
- */
-static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
-{
- struct clk *xusbxti;
- u32 pwr, osc;
-
- pwr = readl(S3C_PHYPWR);
- pwr &= ~0x19;
- writel(pwr, S3C_PHYPWR);
- mdelay(1);
-
- osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0;
-
- xusbxti = clk_get(hsotg->dev, "xusbxti");
- if (xusbxti && !IS_ERR(xusbxti)) {
- switch (clk_get_rate(xusbxti)) {
- case 12*MHZ:
- osc |= S3C_PHYCLK_CLKSEL_12M;
- break;
- case 24*MHZ:
- osc |= S3C_PHYCLK_CLKSEL_24M;
- break;
- default:
- case 48*MHZ:
- /* default reference clock */
- break;
- }
- clk_put(xusbxti);
- }
-
- writel(osc | 0x10, S3C_PHYCLK);
-
- /* issue a full set of resets to the otg and core */
-
- writel(S3C_RSTCON_PHY, S3C_RSTCON);
- udelay(20); /* at-least 10uS */
- writel(0, S3C_RSTCON);
-}
-
-
-static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
-{
- u32 cfg4;
-
- /* unmask subset of endpoint interrupts */
-
- writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
- S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
- hsotg->regs + S3C_DIEPMSK);
-
- writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk |
- S3C_DOEPMSK_EPDisbldMsk | S3C_DOEPMSK_XferComplMsk,
- hsotg->regs + S3C_DOEPMSK);
-
- writel(0, hsotg->regs + S3C_DAINTMSK);
-
- /* Be in disconnected state until gadget is registered */
- __orr32(hsotg->regs + S3C_DCTL, S3C_DCTL_SftDiscon);
-
- if (0) {
- /* post global nak until we're ready */
- writel(S3C_DCTL_SGNPInNAK | S3C_DCTL_SGOUTNak,
- hsotg->regs + S3C_DCTL);
- }
-
- /* setup fifos */
-
- dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
- readl(hsotg->regs + S3C_GRXFSIZ),
- readl(hsotg->regs + S3C_GNPTXFSIZ));
-
- s3c_hsotg_init_fifo(hsotg);
-
- /* set the PLL on, remove the HNP/SRP and set the PHY */
- writel(S3C_GUSBCFG_PHYIf16 | S3C_GUSBCFG_TOutCal(7) | (0x5 << 10),
- hsotg->regs + S3C_GUSBCFG);
-
- writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
- hsotg->regs + S3C_GAHBCFG);
-
- /* check hardware configuration */
-
- cfg4 = readl(hsotg->regs + 0x50);
- hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
-
- dev_info(hsotg->dev, "%s fifos\n",
- hsotg->dedicated_fifos ? "dedicated" : "shared");
-}
-
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
-{
-#ifdef DEBUG
- struct device *dev = hsotg->dev;
- void __iomem *regs = hsotg->regs;
- u32 val;
- int idx;
-
- dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n",
- readl(regs + S3C_DCFG), readl(regs + S3C_DCTL),
- readl(regs + S3C_DIEPMSK));
-
- dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
- readl(regs + S3C_GAHBCFG), readl(regs + 0x44));
-
- dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
- readl(regs + S3C_GRXFSIZ), readl(regs + S3C_GNPTXFSIZ));
-
- /* show periodic fifo settings */
-
- for (idx = 1; idx <= 15; idx++) {
- val = readl(regs + S3C_DPTXFSIZn(idx));
- dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
- val >> S3C_DPTXFSIZn_DPTxFSize_SHIFT,
- val & S3C_DPTXFSIZn_DPTxFStAddr_MASK);
- }
-
- for (idx = 0; idx < 15; idx++) {
- dev_info(dev,
- "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
- readl(regs + S3C_DIEPCTL(idx)),
- readl(regs + S3C_DIEPTSIZ(idx)),
- readl(regs + S3C_DIEPDMA(idx)));
-
- val = readl(regs + S3C_DOEPCTL(idx));
- dev_info(dev,
- "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n",
- idx, readl(regs + S3C_DOEPCTL(idx)),
- readl(regs + S3C_DOEPTSIZ(idx)),
- readl(regs + S3C_DOEPDMA(idx)));
-
- }
-
- dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
- readl(regs + S3C_DVBUSDIS), readl(regs + S3C_DVBUSPULSE));
-#endif
-}
-
-
-/**
- * state_show - debugfs: show overall driver and device state.
- * @seq: The seq file to write to.
- * @v: Unused parameter.
- *
- * This debugfs entry shows the overall state of the hardware and
- * some general information about each of the endpoints available
- * to the system.
- */
-static int state_show(struct seq_file *seq, void *v)
-{
- struct s3c_hsotg *hsotg = seq->private;
- void __iomem *regs = hsotg->regs;
- int idx;
-
- seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
- readl(regs + S3C_DCFG),
- readl(regs + S3C_DCTL),
- readl(regs + S3C_DSTS));
-
- seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
- readl(regs + S3C_DIEPMSK), readl(regs + S3C_DOEPMSK));
-
- seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
- readl(regs + S3C_GINTMSK),
- readl(regs + S3C_GINTSTS));
-
- seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
- readl(regs + S3C_DAINTMSK),
- readl(regs + S3C_DAINT));
-
- seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
- readl(regs + S3C_GNPTXSTS),
- readl(regs + S3C_GRXSTSR));
-
- seq_printf(seq, "\nEndpoint status:\n");
-
- for (idx = 0; idx < 15; idx++) {
- u32 in, out;
-
- in = readl(regs + S3C_DIEPCTL(idx));
- out = readl(regs + S3C_DOEPCTL(idx));
-
- seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
- idx, in, out);
-
- in = readl(regs + S3C_DIEPTSIZ(idx));
- out = readl(regs + S3C_DOEPTSIZ(idx));
-
- seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
- in, out);
-
- seq_printf(seq, "\n");
- }
-
- return 0;
-}
-
-static int state_open(struct inode *inode, struct file *file)
-{
- return single_open(file, state_show, inode->i_private);
-}
-
-static const struct file_operations state_fops = {
- .owner = THIS_MODULE,
- .open = state_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/**
- * fifo_show - debugfs: show the fifo information
- * @seq: The seq_file to write data to.
- * @v: Unused parameter.
- *
- * Show the FIFO information for the overall fifo and all the
- * periodic transmission FIFOs.
-*/
-static int fifo_show(struct seq_file *seq, void *v)
-{
- struct s3c_hsotg *hsotg = seq->private;
- void __iomem *regs = hsotg->regs;
- u32 val;
- int idx;
-
- seq_printf(seq, "Non-periodic FIFOs:\n");
- seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + S3C_GRXFSIZ));
-
- val = readl(regs + S3C_GNPTXFSIZ);
- seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
- val >> S3C_GNPTXFSIZ_NPTxFDep_SHIFT,
- val & S3C_GNPTXFSIZ_NPTxFStAddr_MASK);
-
- seq_printf(seq, "\nPeriodic TXFIFOs:\n");
-
- for (idx = 1; idx <= 15; idx++) {
- val = readl(regs + S3C_DPTXFSIZn(idx));
-
- seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
- val >> S3C_DPTXFSIZn_DPTxFSize_SHIFT,
- val & S3C_DPTXFSIZn_DPTxFStAddr_MASK);
- }
-
- return 0;
-}
-
-static int fifo_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fifo_show, inode->i_private);
-}
-
-static const struct file_operations fifo_fops = {
- .owner = THIS_MODULE,
- .open = fifo_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-
-static const char *decode_direction(int is_in)
-{
- return is_in ? "in" : "out";
-}
-
-/**
- * ep_show - debugfs: show the state of an endpoint.
- * @seq: The seq_file to write data to.
- * @v: Unused parameter.
- *
- * This debugfs entry shows the state of the given endpoint (one is
- * registered for each available).
-*/
-static int ep_show(struct seq_file *seq, void *v)
-{
- struct s3c_hsotg_ep *ep = seq->private;
- struct s3c_hsotg *hsotg = ep->parent;
- struct s3c_hsotg_req *req;
- void __iomem *regs = hsotg->regs;
- int index = ep->index;
- int show_limit = 15;
- unsigned long flags;
-
- seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
- ep->index, ep->ep.name, decode_direction(ep->dir_in));
-
- /* first show the register state */
-
- seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
- readl(regs + S3C_DIEPCTL(index)),
- readl(regs + S3C_DOEPCTL(index)));
-
- seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
- readl(regs + S3C_DIEPDMA(index)),
- readl(regs + S3C_DOEPDMA(index)));
-
- seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
- readl(regs + S3C_DIEPINT(index)),
- readl(regs + S3C_DOEPINT(index)));
-
- seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
- readl(regs + S3C_DIEPTSIZ(index)),
- readl(regs + S3C_DOEPTSIZ(index)));
-
- seq_printf(seq, "\n");
- seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
- seq_printf(seq, "total_data=%ld\n", ep->total_data);
-
- seq_printf(seq, "request list (%p,%p):\n",
- ep->queue.next, ep->queue.prev);
-
- spin_lock_irqsave(&ep->lock, flags);
-
- list_for_each_entry(req, &ep->queue, queue) {
- if (--show_limit < 0) {
- seq_printf(seq, "not showing more requests...\n");
- break;
- }
-
- seq_printf(seq, "%c req %p: %d bytes @%p, ",
- req == ep->req ? '*' : ' ',
- req, req->req.length, req->req.buf);
- seq_printf(seq, "%d done, res %d\n",
- req->req.actual, req->req.status);
- }
-
- spin_unlock_irqrestore(&ep->lock, flags);
-
- return 0;
-}
-
-static int ep_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ep_show, inode->i_private);
-}
-
-static const struct file_operations ep_fops = {
- .owner = THIS_MODULE,
- .open = ep_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/**
- * s3c_hsotg_create_debug - create debugfs directory and files
- * @hsotg: The driver state
- *
- * Create the debugfs files to allow the user to get information
- * about the state of the system. The directory name is created
- * with the same name as the device itself, in case we end up
- * with multiple blocks in future systems.
-*/
-static void __devinit s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
-{
- struct dentry *root;
- unsigned epidx;
-
- root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
- hsotg->debug_root = root;
- if (IS_ERR(root)) {
- dev_err(hsotg->dev, "cannot create debug root\n");
- return;
- }
-
- /* create general state file */
-
- hsotg->debug_file = debugfs_create_file("state", 0444, root,
- hsotg, &state_fops);
-
- if (IS_ERR(hsotg->debug_file))
- dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
-
- hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
- hsotg, &fifo_fops);
-
- if (IS_ERR(hsotg->debug_fifo))
- dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
-
- /* create one file for each endpoint */
-
- for (epidx = 0; epidx < S3C_HSOTG_EPS; epidx++) {
- struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
-
- ep->debugfs = debugfs_create_file(ep->name, 0444,
- root, ep, &ep_fops);
-
- if (IS_ERR(ep->debugfs))
- dev_err(hsotg->dev, "failed to create %s debug file\n",
- ep->name);
- }
-}
-
-/**
- * s3c_hsotg_delete_debug - cleanup debugfs entries
- * @hsotg: The driver state
- *
- * Cleanup (remove) the debugfs files for use on module exit.
-*/
-static void __devexit s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
-{
- unsigned epidx;
-
- for (epidx = 0; epidx < S3C_HSOTG_EPS; epidx++) {
- struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
- debugfs_remove(ep->debugfs);
- }
-
- debugfs_remove(hsotg->debug_file);
- debugfs_remove(hsotg->debug_fifo);
- debugfs_remove(hsotg->debug_root);
-}
-
-/**
- * s3c_hsotg_gate - set the hardware gate for the block
- * @pdev: The device we bound to
- * @on: On or off.
- *
- * Set the hardware gate setting into the block. If we end up on
- * something other than an S3C64XX, then we might need to change this
- * to using a platform data callback, or some other mechanism.
- */
-static void s3c_hsotg_gate(struct platform_device *pdev, bool on)
-{
- unsigned long flags;
- u32 others;
-
- local_irq_save(flags);
-
- others = __raw_readl(S3C64XX_OTHERS);
- if (on)
- others |= S3C64XX_OTHERS_USBMASK;
- else
- others &= ~S3C64XX_OTHERS_USBMASK;
- __raw_writel(others, S3C64XX_OTHERS);
-
- local_irq_restore(flags);
-}
-
-static struct s3c_hsotg_plat s3c_hsotg_default_pdata;
-
-static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
-{
- struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
- struct device *dev = &pdev->dev;
- struct s3c_hsotg *hsotg;
- struct resource *res;
- int epnum;
- int ret;
-
- if (!plat)
- plat = &s3c_hsotg_default_pdata;
-
- hsotg = kzalloc(sizeof(struct s3c_hsotg) +
- sizeof(struct s3c_hsotg_ep) * S3C_HSOTG_EPS,
- GFP_KERNEL);
- if (!hsotg) {
- dev_err(dev, "cannot get memory\n");
- return -ENOMEM;
- }
-
- hsotg->dev = dev;
- hsotg->plat = plat;
-
- hsotg->clk = clk_get(&pdev->dev, "otg");
- if (IS_ERR(hsotg->clk)) {
- dev_err(dev, "cannot get otg clock\n");
- ret = PTR_ERR(hsotg->clk);
- goto err_mem;
- }
-
- platform_set_drvdata(pdev, hsotg);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "cannot find register resource 0\n");
- ret = -EINVAL;
- goto err_clk;
- }
-
- hsotg->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!hsotg->regs_res) {
- dev_err(dev, "cannot reserve registers\n");
- ret = -ENOENT;
- goto err_clk;
- }
-
- hsotg->regs = ioremap(res->start, resource_size(res));
- if (!hsotg->regs) {
- dev_err(dev, "cannot map registers\n");
- ret = -ENXIO;
- goto err_regs_res;
- }
-
- ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "cannot find IRQ\n");
- goto err_regs;
- }
-
- hsotg->irq = ret;
-
- ret = request_irq(ret, s3c_hsotg_irq, 0, dev_name(dev), hsotg);
- if (ret < 0) {
- dev_err(dev, "cannot claim IRQ\n");
- goto err_regs;
- }
-
- dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
-
- device_initialize(&hsotg->gadget.dev);
-
- dev_set_name(&hsotg->gadget.dev, "gadget");
-
- hsotg->gadget.max_speed = USB_SPEED_HIGH;
- hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
- hsotg->gadget.name = dev_name(dev);
-
- hsotg->gadget.dev.parent = dev;
- hsotg->gadget.dev.dma_mask = dev->dma_mask;
-
- /* setup endpoint information */
-
- INIT_LIST_HEAD(&hsotg->gadget.ep_list);
- hsotg->gadget.ep0 = &hsotg->eps[0].ep;
-
- /* allocate EP0 request */
-
- hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
- GFP_KERNEL);
- if (!hsotg->ctrl_req) {
- dev_err(dev, "failed to allocate ctrl req\n");
- goto err_regs;
- }
-
- /* reset the system */
-
- clk_enable(hsotg->clk);
-
- s3c_hsotg_gate(pdev, true);
-
- s3c_hsotg_otgreset(hsotg);
- s3c_hsotg_corereset(hsotg);
- s3c_hsotg_init(hsotg);
-
- /* initialise the endpoints now the core has been initialised */
- for (epnum = 0; epnum < S3C_HSOTG_EPS; epnum++)
- s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
-
- ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
- if (ret)
- goto err_add_udc;
-
- s3c_hsotg_create_debug(hsotg);
-
- s3c_hsotg_dump(hsotg);
-
- our_hsotg = hsotg;
- return 0;
-
-err_add_udc:
- s3c_hsotg_gate(pdev, false);
- clk_disable(hsotg->clk);
- clk_put(hsotg->clk);
-
-err_regs:
- iounmap(hsotg->regs);
-
-err_regs_res:
- release_resource(hsotg->regs_res);
- kfree(hsotg->regs_res);
-err_clk:
- clk_put(hsotg->clk);
-err_mem:
- kfree(hsotg);
- return ret;
-}
-
-static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
-{
- struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
-
- usb_del_gadget_udc(&hsotg->gadget);
-
- s3c_hsotg_delete_debug(hsotg);
-
- usb_gadget_unregister_driver(hsotg->driver);
-
- free_irq(hsotg->irq, hsotg);
- iounmap(hsotg->regs);
-
- release_resource(hsotg->regs_res);
- kfree(hsotg->regs_res);
-
- s3c_hsotg_gate(pdev, false);
-
- clk_disable(hsotg->clk);
- clk_put(hsotg->clk);
-
- kfree(hsotg);
- return 0;
-}
-
-#if 1
-#define s3c_hsotg_suspend NULL
-#define s3c_hsotg_resume NULL
-#endif
-
-static struct platform_driver s3c_hsotg_driver = {
- .driver = {
- .name = "s3c-hsotg",
- .owner = THIS_MODULE,
- },
- .probe = s3c_hsotg_probe,
- .remove = __devexit_p(s3c_hsotg_remove),
- .suspend = s3c_hsotg_suspend,
- .resume = s3c_hsotg_resume,
-};
-
-module_platform_driver(s3c_hsotg_driver);
-
-MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c-hsotg");
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index df8661d266c..10c6a128250 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -24,12 +24,14 @@
#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>
@@ -109,7 +111,6 @@ struct s3c_hsudc_ep {
struct usb_ep ep;
char name[20];
struct s3c_hsudc *dev;
- const struct usb_endpoint_descriptor *desc;
struct list_head queue;
u8 stopped;
u8 wedge;
@@ -134,7 +135,6 @@ struct s3c_hsudc_req {
* @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.
- * @mem_rsrc: Device memory resource used for remapping device register space.
* irq: IRQ number used by the controller.
* uclk: Reference to the controller clock.
* ep0state: Current state of EP0.
@@ -145,11 +145,10 @@ struct s3c_hsudc {
struct usb_gadget_driver *driver;
struct device *dev;
struct s3c24xx_hsudc_platdata *pd;
- struct otg_transceiver *transceiver;
+ struct usb_phy *transceiver;
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
spinlock_t lock;
void __iomem *regs;
- struct resource *mem_rsrc;
int irq;
struct clk *uclk;
int ep0state;
@@ -284,7 +283,6 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
/**
* s3c_hsudc_stop_activity - Stop activity on all endpoints.
* @hsudc: Device controller for which EP activity is to be stopped.
- * @driver: Reference to the gadget driver which is currently active.
*
* All the endpoints are stopped and any pending transfer requests if any on
* the endpoint are terminated.
@@ -436,7 +434,7 @@ static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
struct s3c_hsudc_req *hsreq;
u32 csr;
- csr = readl((u32)hsudc->regs + S3C_ESR);
+ csr = readl(hsudc->regs + S3C_ESR);
if (csr & S3C_ESR_STALL) {
writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
return;
@@ -469,7 +467,7 @@ static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
struct s3c_hsudc_req *hsreq;
u32 csr;
- csr = readl((u32)hsudc->regs + S3C_ESR);
+ csr = readl(hsudc->regs + S3C_ESR);
if (csr & S3C_ESR_STALL) {
writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
return;
@@ -759,8 +757,8 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
unsigned long flags;
u32 ecr = 0;
- hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
- if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+ 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))
@@ -782,7 +780,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
writel(ecr, hsudc->regs + S3C_ECR);
hsep->stopped = hsep->wedge = 0;
- hsep->desc = desc;
+ hsep->ep.desc = desc;
hsep->ep.maxpacket = usb_endpoint_maxp(desc);
s3c_hsudc_set_halt(_ep, 0);
@@ -805,7 +803,7 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
struct s3c_hsudc *hsudc = hsep->dev;
unsigned long flags;
- if (!_ep || !hsep->desc)
+ if (!_ep || !hsep->ep.desc)
return -EINVAL;
spin_lock_irqsave(&hsudc->lock, flags);
@@ -815,7 +813,7 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
- hsep->desc = 0;
+ hsep->ep.desc = NULL;
hsep->stopped = 1;
spin_unlock_irqrestore(&hsudc->lock, flags);
@@ -834,9 +832,9 @@ static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
{
struct s3c_hsudc_req *hsreq;
- hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+ hsreq = kzalloc(sizeof(*hsreq), gfp_flags);
if (!hsreq)
- return 0;
+ return NULL;
INIT_LIST_HEAD(&hsreq->queue);
return &hsreq->req;
@@ -853,7 +851,7 @@ static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
{
struct s3c_hsudc_req *hsreq;
- hsreq = container_of(_req, struct s3c_hsudc_req, req);
+ hsreq = our_req(_req);
WARN_ON(!list_empty(&hsreq->queue));
kfree(hsreq);
}
@@ -876,12 +874,12 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
u32 offset;
u32 csr;
- hsreq = container_of(_req, struct s3c_hsudc_req, req);
+ hsreq = our_req(_req);
if ((!_req || !_req->complete || !_req->buf ||
!list_empty(&hsreq->queue)))
return -EINVAL;
- hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+ hsep = our_ep(_ep);
hsudc = hsep->dev;
if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
@@ -902,19 +900,19 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
if (list_empty(&hsep->queue) && !hsep->stopped) {
offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
if (ep_is_in(hsep)) {
- csr = readl((u32)hsudc->regs + offset);
+ csr = readl(hsudc->regs + offset);
if (!(csr & S3C_ESR_TX_SUCCESS) &&
(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
} else {
- csr = readl((u32)hsudc->regs + offset);
+ csr = readl(hsudc->regs + offset);
if ((csr & S3C_ESR_RX_SUCCESS)
&& (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
}
}
- if (hsreq != 0)
+ if (hsreq)
list_add_tail(&hsreq->queue, &hsep->queue);
spin_unlock_irqrestore(&hsudc->lock, flags);
@@ -935,7 +933,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
struct s3c_hsudc_req *hsreq;
unsigned long flags;
- hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+ hsep = our_ep(_ep);
if (!_ep || hsep->ep.name == ep0name)
return -EINVAL;
@@ -1001,10 +999,10 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
hsep->dev = hsudc;
hsep->ep.name = hsep->name;
- hsep->ep.maxpacket = epnum ? 512 : 64;
+ usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64);
hsep->ep.ops = &s3c_hsudc_ep_ops;
hsep->fifo = hsudc->regs + S3C_BR(epnum);
- hsep->desc = 0;
+ hsep->ep.desc = NULL;
hsep->stopped = 0;
hsep->wedge = 0;
@@ -1155,7 +1153,6 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
return -EBUSY;
hsudc->driver = driver;
- hsudc->gadget.dev.driver = &driver->driver;
ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies),
hsudc->supplies);
@@ -1165,8 +1162,9 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
}
/* connect to bus through transceiver */
- if (hsudc->transceiver) {
- ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget);
+ 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);
@@ -1178,6 +1176,9 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
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();
@@ -1187,7 +1188,6 @@ err_otg:
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
hsudc->driver = NULL;
- hsudc->gadget.dev.driver = NULL;
return ret;
}
@@ -1205,16 +1205,18 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
spin_lock_irqsave(&hsudc->lock, flags);
hsudc->driver = NULL;
- hsudc->gadget.dev.driver = NULL;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
s3c_hsudc_uninit_phy();
+
+ 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 (hsudc->transceiver)
- (void) otg_set_peripheral(hsudc->transceiver, NULL);
+ if (!IS_ERR_OR_NULL(hsudc->transceiver))
+ (void) otg_set_peripheral(hsudc->transceiver->otg, NULL);
disable_irq(hsudc->irq);
@@ -1242,28 +1244,28 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA)
if (!hsudc)
return -ENODEV;
- if (hsudc->transceiver)
- return otg_set_power(hsudc->transceiver, mA);
+ if (!IS_ERR_OR_NULL(hsudc->transceiver))
+ return usb_phy_set_power(hsudc->transceiver, mA);
return -EOPNOTSUPP;
}
-static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+static const struct usb_gadget_ops s3c_hsudc_gadget_ops = {
.get_frame = s3c_hsudc_gadget_getframe,
.udc_start = s3c_hsudc_start,
.udc_stop = s3c_hsudc_stop,
.vbus_draw = s3c_hsudc_vbus_draw,
};
-static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
+static int s3c_hsudc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct s3c_hsudc *hsudc;
- struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+ struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev);
int ret, i;
- hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+ hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
sizeof(struct s3c_hsudc_ep) * pd->epnum,
GFP_KERNEL);
if (!hsudc) {
@@ -1273,14 +1275,14 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
hsudc->dev = dev;
- hsudc->pd = pdev->dev.platform_data;
+ hsudc->pd = dev_get_platdata(&pdev->dev);
- hsudc->transceiver = otg_get_transceiver();
+ hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++)
hsudc->supplies[i].supply = s3c_hsudc_supply_names[i];
- ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
hsudc->supplies);
if (ret != 0) {
dev_err(dev, "failed to request supplies: %d\n", ret);
@@ -1288,38 +1290,19 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "unable to obtain driver resource data\n");
- ret = -ENODEV;
- goto err_res;
- }
- hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!hsudc->mem_rsrc) {
- dev_err(dev, "failed to reserve register area\n");
- ret = -ENODEV;
+ hsudc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hsudc->regs)) {
+ ret = PTR_ERR(hsudc->regs);
goto err_res;
}
- hsudc->regs = ioremap(res->start, resource_size(res));
- if (!hsudc->regs) {
- dev_err(dev, "error mapping device register area\n");
- ret = -EBUSY;
- goto err_remap;
- }
-
spin_lock_init(&hsudc->lock);
- dev_set_name(&hsudc->gadget.dev, "gadget");
-
hsudc->gadget.max_speed = USB_SPEED_HIGH;
hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
hsudc->gadget.name = dev_name(dev);
- hsudc->gadget.dev.parent = dev;
- hsudc->gadget.dev.dma_mask = dev->dma_mask;
hsudc->gadget.ep0 = &hsudc->ep[0].ep;
-
hsudc->gadget.is_otg = 0;
hsudc->gadget.is_a_peripheral = 0;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
@@ -1329,21 +1312,22 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
- goto err_irq;
+ goto err_res;
}
hsudc->irq = ret;
- ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+ 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_irq;
+ goto err_res;
}
- hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+ 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_clk;
+ goto err_res;
}
clk_enable(hsudc->uclk);
@@ -1352,36 +1336,20 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
disable_irq(hsudc->irq);
local_irq_enable();
- ret = device_register(&hsudc->gadget.dev);
- if (ret) {
- put_device(&hsudc->gadget.dev);
- goto err_add_device;
- }
-
ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget);
if (ret)
goto err_add_udc;
+ pm_runtime_enable(dev);
+
return 0;
err_add_udc:
- device_unregister(&hsudc->gadget.dev);
-err_add_device:
clk_disable(hsudc->uclk);
- clk_put(hsudc->uclk);
-err_clk:
- free_irq(hsudc->irq, hsudc);
-err_irq:
- iounmap(hsudc->regs);
-
-err_remap:
- release_mem_region(res->start, resource_size(res));
err_res:
- if (hsudc->transceiver)
- otg_put_transceiver(hsudc->transceiver);
+ if (!IS_ERR_OR_NULL(hsudc->transceiver))
+ usb_put_phy(hsudc->transceiver);
- regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
- kfree(hsudc);
return ret;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 3f87cb9344b..7987aa049fa 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -12,6 +12,8 @@
* (at your option) any later version.
*/
+#define pr_fmt(fmt) "s3c2410_udc: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -27,6 +29,7 @@
#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>
@@ -35,16 +38,14 @@
#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"
@@ -116,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, ...)
@@ -126,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);
@@ -165,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;
@@ -231,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);
}
@@ -264,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;
@@ -281,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);
}
@@ -390,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);
@@ -407,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 {
@@ -443,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;
@@ -511,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) {
@@ -543,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);
@@ -649,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;
@@ -658,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;
@@ -667,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;
@@ -680,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;
@@ -751,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]);
@@ -802,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:
@@ -837,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
@@ -859,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);
@@ -874,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);
}
}
@@ -1058,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;
@@ -1074,9 +1071,9 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
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;
@@ -1118,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;
@@ -1137,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;
@@ -1147,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);
@@ -1171,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;
}
@@ -1195,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);
}
@@ -1215,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))) {
@@ -1234,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));
}
@@ -1300,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);
@@ -1330,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;
@@ -1349,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;
}
@@ -1363,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;
@@ -1377,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);
@@ -1405,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;
}
@@ -1485,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;
}
@@ -1542,9 +1539,10 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
return -ENOTSUPP;
}
-static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int s3c2410_udc_stop(struct usb_gadget_driver *driver);
+static int s3c2410_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
+static int s3c2410_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver);
static const struct usb_gadget_ops s3c2410_ops = {
.get_frame = s3c2410_udc_get_frame,
@@ -1553,8 +1551,8 @@ static const struct usb_gadget_ops s3c2410_ops = {
.pullup = s3c2410_udc_pullup,
.vbus_session = s3c2410_udc_vbus_session,
.vbus_draw = s3c2410_vbus_draw,
- .start = s3c2410_udc_start,
- .stop = s3c2410_udc_stop,
+ .udc_start = s3c2410_udc_start,
+ .udc_stop = s3c2410_udc_stop,
};
static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd)
@@ -1618,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);
}
}
@@ -1668,82 +1667,27 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
s3c2410_udc_command(S3C2410_UDC_P_ENABLE);
}
-static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int s3c2410_udc_start(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct s3c2410_udc *udc = the_controller;
- int retval;
+ struct s3c2410_udc *udc = to_s3c2410(g);
dprintk(DEBUG_NORMAL, "%s() '%s'\n", __func__, driver->driver.name);
- /* Sanity checks */
- if (!udc)
- return -ENODEV;
-
- if (udc->driver)
- return -EBUSY;
-
- if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) {
- printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
- bind, driver->setup, driver->max_speed);
- return -EINVAL;
- }
-#if defined(MODULE)
- if (!driver->unbind) {
- printk(KERN_ERR "Invalid driver: no unbind method\n");
- return -EINVAL;
- }
-#endif
-
/* 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 = 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;
}
-static int s3c2410_udc_stop(struct usb_gadget_driver *driver)
+static int s3c2410_udc_stop(struct usb_gadget *g,
+ struct usb_gadget_driver *driver)
{
- struct s3c2410_udc *udc = the_controller;
-
- if (!udc)
- return -ENODEV;
-
- if (!driver || driver != udc->driver || !driver->unbind)
- return -EINVAL;
-
- dprintk(DEBUG_NORMAL, "usb_gadget_unregister_driver() '%s'\n",
- driver->driver.name);
+ struct s3c2410_udc *udc = to_s3c2410(g);
- /* report disconnect */
- if (driver->disconnect)
- driver->disconnect(&udc->gadget);
-
- driver->unbind(&udc->gadget);
-
- device_del(&udc->gadget.dev);
udc->driver = NULL;
/* Disable udc */
@@ -1866,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;
@@ -1881,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);
@@ -1913,6 +1853,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
irq = gpio_to_irq(udc_info->vbus_pin);
if (irq < 0) {
dev_err(dev, "no irq for gpio vbus pin\n");
+ retval = irq;
goto err_gpio_claim;
}
@@ -1990,10 +1931,10 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
dev_dbg(&pdev->dev, "%s()\n", __func__);
- usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
+ usb_del_gadget_udc(&udc->gadget);
debugfs_remove(udc->regs_info);
if (udc_info && !udc_info->udc_command &&
@@ -2010,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);
@@ -2029,7 +1968,8 @@ 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)
{
s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
@@ -2074,7 +2014,7 @@ 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;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h
index 1653bae08b8..93bf225f196 100644
--- a/drivers/usb/gadget/s3c2410_udc.h
+++ b/drivers/usb/gadget/s3c2410_udc.h
@@ -19,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;
@@ -96,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 ad9e5b2df64..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,
@@ -137,22 +116,6 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
/*-------------------------------------------------------------------------*/
-static int __init serial_bind_config(struct usb_configuration *c)
-{
- unsigned i;
- int status = 0;
-
- for (i = 0; i < n_ports && status == 0; i++) {
- if (use_acm)
- status = acm_bind_config(c, i);
- else if (use_obex)
- status = obex_bind_config(c, i);
- else
- status = gser_bind_config(c, i);
- }
- return status;
-}
-
static struct usb_configuration serial_config_driver = {
/* .label = f(use_acm) */
/* .bConfigurationValue = f(use_acm) */
@@ -160,89 +123,120 @@ static struct usb_configuration serial_config_driver = {
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
+static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
+static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
+
+static int serial_register_ports(struct usb_composite_dev *cdev,
+ struct usb_configuration *c, const char *f_name)
+{
+ int i;
+ int ret;
+
+ ret = usb_add_config_only(cdev, c);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < n_ports; i++) {
+
+ fi_serial[i] = usb_get_function_instance(f_name);
+ if (IS_ERR(fi_serial[i])) {
+ ret = PTR_ERR(fi_serial[i]);
+ goto fail;
+ }
+
+ f_serial[i] = usb_get_function(fi_serial[i]);
+ if (IS_ERR(f_serial[i])) {
+ ret = PTR_ERR(f_serial[i]);
+ goto err_get_func;
+ }
+
+ ret = usb_add_function(c, f_serial[i]);
+ if (ret)
+ goto err_add_func;
+ }
+
+ return 0;
+
+err_add_func:
+ usb_put_function(f_serial[i]);
+err_get_func:
+ usb_put_function_instance(fi_serial[i]);
+
+fail:
+ i--;
+ while (i >= 0) {
+ usb_remove_function(c, f_serial[i]);
+ usb_put_function(f_serial[i]);
+ usb_put_function_instance(fi_serial[i]);
+ i--;
+ }
+out:
+ return ret;
+}
+
static int __init gs_bind(struct usb_composite_dev *cdev)
{
- int 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,
- serial_bind_config);
+ if (use_acm) {
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "acm");
+ usb_ep_autoconfig_reset(cdev->gadget);
+ } else if (use_obex)
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "obex");
+ else {
+ status = serial_register_ports(cdev, &serial_config_driver,
+ "gser");
+ }
if (status < 0)
goto fail;
+ 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_HIGH,
+ .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_probe(&gserial_driver, gs_bind);
+ 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
index 85ea14e2545..648f9e489b3 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2003-2008 Alan Stern
* Copyeight (C) 2009 Samsung Electronics
- * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ * 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
@@ -11,36 +11,10 @@
* (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_manufacturer -- name of the manufacturer
- * - fsg_string_product -- name of the product
- * - fsg_string_config -- name of the configuration
* - fsg_string_interface -- name of the interface
- * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
- * macro is defined prior to including this file.
- */
-
-/*
- * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
- * fsg_hs_intr_in_desc objects as well as
- * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
- * macros are not defined.
- *
- * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
- * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
- * defined (as well as corresponding entries in string tables are
- * missing) and FSG_STRING_INTERFACE has value of zero.
- *
- * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
- */
-
-/*
- * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
- * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
- * characters rather then a pointer to void.
*/
/*
@@ -49,339 +23,17 @@
* The valid range of num_buffers is: num >= 2 && num <= 4.
*/
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/usb/composite.h>
-#include <linux/usb/storage.h>
-#include <scsi/scsi.h>
-#include <asm/unaligned.h>
-
-
-/*
- * Thanks to NetChip Technologies for donating this product ID.
- *
- * DO NOT REUSE THESE IDs with any other driver!! Ever!!
- * Instead: allocate your own, using normal USB-IF procedures.
- */
-#define FSG_VENDOR_ID 0x0525 /* NetChip */
-#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
-
-
-/*-------------------------------------------------------------------------*/
-
-
-#ifndef DEBUG
-#undef VERBOSE_DEBUG
-#undef DUMP_MSGS
-#endif /* !DEBUG */
-
-#ifdef VERBOSE_DEBUG
-#define VLDBG LDBG
-#else
-#define VLDBG(lun, fmt, args...) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
-#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
-#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
-#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
-
-/*
- * Keep those macros in sync with those in
- * include/linux/usb/composite.h or else GCC will complain. If they
- * are identical (the same names of arguments, white spaces in the
- * same places) GCC will allow redefinition otherwise (even if some
- * white space is removed or added) warning will be issued.
- *
- * Those macros are needed here because File Storage Gadget does not
- * include the composite.h header. For composite gadgets those macros
- * are redundant since composite.h is included any way.
- *
- * One could check whether those macros are already defined (which
- * would indicate composite.h had been included) or not (which would
- * indicate we were in FSG) but this is not done because a warning is
- * desired if definitions here differ from the ones in composite.h.
- *
- * We want the definitions to match and be the same in File Storage
- * Gadget as well as Mass Storage Function (and so composite gadgets
- * using MSF). If someone changes them in composite.h it will produce
- * a warning in this file when building MSF.
- */
-#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)
-
-
-
-#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 */
-
-
-
-
-
-/*-------------------------------------------------------------------------*/
-
-/* Bulk-only data structures */
-
-/* Command Block Wrapper */
-struct fsg_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
-
-
-/* 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;
-};
-
-#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
-
-static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
-{
- return container_of(dev, struct fsg_lun, dev);
-}
-
-
-/* Big enough to hold our biggest descriptor */
-#define EP0_BUFSIZE 256
-#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
-module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
-MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
-
-#else
-
-/*
- * Number of buffers we will use.
- * 2 is usually enough for good buffering pipeline
- */
-#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
-
-#endif /* CONFIG_USB_DEBUG */
-
-/* check if fsg_num_buffers is within a valid range */
-static inline int fsg_num_buffers_validate(void)
-{
- if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
- return 0;
- pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
- fsg_num_buffers, 2 ,4);
- return -EINVAL;
-}
-
-/* Default size of buffer length. */
-#define FSG_BUFLEN ((u32)16384)
-
-/* Maximal number of LUNs supported in mass storage function */
-#define FSG_MAX_LUNS 8
-
-enum fsg_buffer_state {
- BUF_STATE_EMPTY = 0,
- BUF_STATE_FULL,
- BUF_STATE_BUSY
-};
-
-struct fsg_buffhd {
-#ifdef FSG_BUFFHD_STATIC_BUFFER
- char buf[FSG_BUFLEN];
-#else
- void *buf;
-#endif
- enum fsg_buffer_state state;
- struct fsg_buffhd *next;
-
- /*
- * The NetChip 2280 is faster, and handles some protocol faults
- * better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here.
- */
- unsigned int bulk_out_intended_length;
-
- struct usb_request *inreq;
- int inreq_busy;
- struct usb_request *outreq;
- int outreq_busy;
-};
-
-enum fsg_state {
- /* This one isn't used anywhere */
- FSG_STATE_COMMAND_PHASE = -10,
- FSG_STATE_DATA_PHASE,
- FSG_STATE_STATUS_PHASE,
-
- FSG_STATE_IDLE = 0,
- FSG_STATE_ABORT_BULK_OUT,
- FSG_STATE_RESET,
- FSG_STATE_INTERFACE_CHANGE,
- FSG_STATE_CONFIG_CHANGE,
- FSG_STATE_DISCONNECT,
- FSG_STATE_EXIT,
- FSG_STATE_TERMINATED
-};
-
-enum data_direction {
- DATA_DIR_UNKNOWN = 0,
- DATA_DIR_FROM_HOST,
- DATA_DIR_TO_HOST,
- DATA_DIR_NONE
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-
-static inline u32 get_unaligned_be24(u8 *buf)
-{
- return 0xffffff & (u32) get_unaligned_be32(buf - 1);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-
-enum {
-#ifndef FSG_NO_DEVICE_STRINGS
- FSG_STRING_MANUFACTURER = 1,
- FSG_STRING_PRODUCT,
- FSG_STRING_SERIAL,
- FSG_STRING_CONFIG,
-#endif
- FSG_STRING_INTERFACE
-};
-
-
-#ifndef FSG_NO_OTG
-static struct usb_otg_descriptor
-fsg_otg_desc = {
- .bLength = sizeof fsg_otg_desc,
- .bDescriptorType = USB_DT_OTG,
-
- .bmAttributes = USB_OTG_SRP,
-};
-#endif
+#include "storage_common.h"
/* There is only one interface. */
-static struct usb_interface_descriptor
-fsg_intf_desc = {
+struct usb_interface_descriptor fsg_intf_desc = {
.bLength = sizeof fsg_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
@@ -391,14 +43,14 @@ fsg_intf_desc = {
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
.iInterface = FSG_STRING_INTERFACE,
};
+EXPORT_SYMBOL_GPL(fsg_intf_desc);
/*
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
* interrupt-in.
*/
-static struct usb_endpoint_descriptor
-fsg_fs_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -406,9 +58,9 @@ fsg_fs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
-static struct usb_endpoint_descriptor
-fsg_fs_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -416,40 +68,15 @@ fsg_fs_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
+EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
-#ifndef FSG_NO_INTR_EP
-
-static struct usb_endpoint_descriptor
-fsg_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 */
-};
-
-#ifndef FSG_NO_OTG
-# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
-#else
-# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
-#endif
-
-#endif
-
-static struct usb_descriptor_header *fsg_fs_function[] = {
-#ifndef FSG_NO_OTG
- (struct usb_descriptor_header *) &fsg_otg_desc,
-#endif
+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,
-#ifndef FSG_NO_INTR_EP
- (struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
-#endif
NULL,
};
+EXPORT_SYMBOL_GPL(fsg_fs_function);
/*
@@ -460,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = {
* and a "device qualifier" ... plus more construction options
* for the configuration descriptor.
*/
-static struct usb_endpoint_descriptor
-fsg_hs_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -469,9 +95,9 @@ fsg_hs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
-static struct usb_endpoint_descriptor
-fsg_hs_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -480,43 +106,18 @@ fsg_hs_bulk_out_desc = {
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 1, /* NAK every 1 uframe */
};
+EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
-#ifndef FSG_NO_INTR_EP
-
-static struct usb_endpoint_descriptor
-fsg_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 */
-};
-
-#ifndef FSG_NO_OTG
-# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
-#else
-# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
-#endif
-#endif
-
-static struct usb_descriptor_header *fsg_hs_function[] = {
-#ifndef FSG_NO_OTG
- (struct usb_descriptor_header *) &fsg_otg_desc,
-#endif
+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,
-#ifndef FSG_NO_INTR_EP
- (struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
-#endif
NULL,
};
+EXPORT_SYMBOL_GPL(fsg_hs_function);
-static struct usb_endpoint_descriptor
-fsg_ss_bulk_in_desc = {
+struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -524,16 +125,17 @@ fsg_ss_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
-static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
-static struct usb_endpoint_descriptor
-fsg_ss_bulk_out_desc = {
+struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -541,123 +143,25 @@ fsg_ss_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
-static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
+EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
-#ifndef FSG_NO_INTR_EP
-
-static struct usb_endpoint_descriptor
-fsg_ss_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 struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
- .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-
- .wBytesPerInterval = cpu_to_le16(2),
-};
-
-#ifndef FSG_NO_OTG
-# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2
-#else
-# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1
-#endif
-
-#endif
-
-static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = {
- .bLength = USB_DT_USB_EXT_CAP_SIZE,
- .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
- .bDevCapabilityType = USB_CAP_TYPE_EXT,
-
- .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT),
-};
-
-static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = {
- .bLength = USB_DT_USB_SS_CAP_SIZE,
- .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
- .bDevCapabilityType = USB_SS_CAP_TYPE,
-
- /* .bmAttributes = LTM is not supported yet */
-
- .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION
- | USB_FULL_SPEED_OPERATION
- | USB_HIGH_SPEED_OPERATION
- | USB_5GBPS_OPERATION),
- .bFunctionalitySupport = USB_LOW_SPEED_OPERATION,
- .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT,
- .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT),
-};
-
-static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = {
- .bLength = USB_DT_BOS_SIZE,
- .bDescriptorType = USB_DT_BOS,
-
- .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE
- + USB_DT_USB_EXT_CAP_SIZE
- + USB_DT_USB_SS_CAP_SIZE),
-
- .bNumDeviceCaps = 2,
-};
-
-static struct usb_descriptor_header *fsg_ss_function[] = {
-#ifndef FSG_NO_OTG
- (struct usb_descriptor_header *) &fsg_otg_desc,
-#endif
+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,
-#ifndef FSG_NO_INTR_EP
- (struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
- (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
-#endif
NULL,
};
-
-/* Maxpacket and other transfer characteristics vary by speed. */
-static __maybe_unused struct usb_endpoint_descriptor *
-fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs,
- struct usb_endpoint_descriptor *ss)
-{
- if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
- return ss;
- else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return hs;
- return fs;
-}
-
-
-/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
-static struct usb_string fsg_strings[] = {
-#ifndef FSG_NO_DEVICE_STRINGS
- {FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
- {FSG_STRING_PRODUCT, fsg_string_product},
- {FSG_STRING_SERIAL, ""},
- {FSG_STRING_CONFIG, fsg_string_config},
-#endif
- {FSG_STRING_INTERFACE, fsg_string_interface},
- {}
-};
-
-static struct usb_gadget_strings fsg_stringtab = {
- .language = 0x0409, /* en-us */
- .strings = fsg_strings,
-};
+EXPORT_SYMBOL_GPL(fsg_ss_function);
/*-------------------------------------------------------------------------*/
@@ -667,7 +171,17 @@ static struct usb_gadget_strings fsg_stringtab = {
* the caller must own fsg->filesem for writing.
*/
-static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+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;
@@ -676,6 +190,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
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;
@@ -694,9 +210,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (!(filp->f_mode & FMODE_WRITE))
ro = 1;
- if (filp->f_path.dentry)
- inode = filp->f_path.dentry->d_inode;
- if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ 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;
}
@@ -705,11 +220,11 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
* If we can't read the file, it's no good.
* If we can't write the file, use it read-only.
*/
- if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
+ if (!(filp->f_mode & FMODE_CAN_READ)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
}
- if (!(filp->f_op->write || filp->f_op->aio_write))
+ if (!(filp->f_mode & FMODE_CAN_WRITE))
ro = 1;
size = i_size_read(inode->i_mapping->host);
@@ -720,17 +235,17 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
}
if (curlun->cdrom) {
- curlun->blksize = 2048;
- curlun->blkbits = 11;
+ blksize = 2048;
+ blkbits = 11;
} else if (inode->i_bdev) {
- curlun->blksize = bdev_logical_block_size(inode->i_bdev);
- curlun->blkbits = blksize_bits(curlun->blksize);
+ blksize = bdev_logical_block_size(inode->i_bdev);
+ blkbits = blksize_bits(blksize);
} else {
- curlun->blksize = 512;
- curlun->blkbits = 9;
+ blksize = 512;
+ blkbits = 9;
}
- num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */
+ 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 */
@@ -747,28 +262,23 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
goto out;
}
- get_file(filp);
+ 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);
- rc = 0;
+ return 0;
out:
- filp_close(filp, current->files);
+ fput(filp);
return rc;
}
-
-
-static 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_open);
/*-------------------------------------------------------------------------*/
@@ -777,7 +287,7 @@ static void fsg_lun_close(struct fsg_lun *curlun)
* Sync the file data, don't bother with the metadata.
* This code was copied from fs/buffer.c:sys_fdatasync().
*/
-static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
@@ -785,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
return 0;
return vfs_fsync(filp, 1);
}
+EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
-static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+void store_cdrom_address(u8 *dest, int msf, u32 addr)
{
if (msf) {
/* Convert to Minutes-Seconds-Frames */
@@ -803,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
put_unaligned_be32(addr, dest);
}
}
-
+EXPORT_SYMBOL_GPL(store_cdrom_address);
/*-------------------------------------------------------------------------*/
-static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
- char *buf)
+ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
{
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
-
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
? curlun->ro
: curlun->initially_ro);
}
+EXPORT_SYMBOL_GPL(fsg_show_ro);
-static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
- char *buf)
+ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
{
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
-
return sprintf(buf, "%u\n", curlun->nofua);
}
+EXPORT_SYMBOL_GPL(fsg_show_nofua);
-static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
- char *buf)
+ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ char *buf)
{
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
- struct rw_semaphore *filesem = dev_get_drvdata(dev);
char *p;
ssize_t rc;
@@ -852,17 +357,44 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
up_read(filesem);
return rc;
}
+EXPORT_SYMBOL_GPL(fsg_show_file);
+
+ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
+{
+ return sprintf(buf, "%u\n", curlun->cdrom);
+}
+EXPORT_SYMBOL_GPL(fsg_show_cdrom);
+
+ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
+{
+ return sprintf(buf, "%u\n", curlun->removable);
+}
+EXPORT_SYMBOL_GPL(fsg_show_removable);
+
+/*
+ * 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;
+}
-static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ const char *buf, size_t count)
{
ssize_t rc;
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
- struct rw_semaphore *filesem = dev_get_drvdata(dev);
- unsigned ro;
+ bool ro;
- rc = kstrtouint(buf, 2, &ro);
+ rc = strtobool(buf, &ro);
if (rc)
return rc;
@@ -871,28 +403,21 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
* backing file is closed.
*/
down_read(filesem);
- if (fsg_lun_is_open(curlun)) {
- LDBG(curlun, "read-only status change prevented\n");
- rc = -EBUSY;
- } else {
- curlun->ro = ro;
- curlun->initially_ro = ro;
- LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+ rc = _fsg_store_ro(curlun, ro);
+ if (!rc)
rc = count;
- }
up_read(filesem);
+
return rc;
}
+EXPORT_SYMBOL_GPL(fsg_store_ro);
-static ssize_t fsg_store_nofua(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
{
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
- unsigned nofua;
+ bool nofua;
int ret;
- ret = kstrtouint(buf, 2, &nofua);
+ ret = strtobool(buf, &nofua);
if (ret)
return ret;
@@ -904,12 +429,11 @@ static ssize_t fsg_store_nofua(struct device *dev,
return count;
}
+EXPORT_SYMBOL_GPL(fsg_store_nofua);
-static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ const char *buf, size_t count)
{
- struct fsg_lun *curlun = fsg_lun_from_dev(dev);
- struct rw_semaphore *filesem = dev_get_drvdata(dev);
int rc = 0;
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
@@ -921,20 +445,60 @@ static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
if (count > 0 && buf[count-1] == '\n')
((char *) buf)[count-1] = 0; /* Ugh! */
- /* Eject current medium */
- down_write(filesem);
- if (fsg_lun_is_open(curlun)) {
- fsg_lun_close(curlun);
- curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
- }
-
/* 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 29c854bbca4..97b027724ee 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -14,11 +14,13 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.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"
@@ -48,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;
@@ -62,6 +63,8 @@ struct eth_dev {
struct sk_buff_head rx_frames;
+ unsigned qmult;
+
unsigned header_len;
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
int (*unwrap)(struct gether *,
@@ -75,6 +78,7 @@ struct eth_dev {
bool zlp;
u8 host_mac[ETH_ALEN];
+ u8 dev_mac[ETH_ALEN];
};
/*-------------------------------------------------------------------------*/
@@ -83,19 +87,8 @@ 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/super speed");
-
-#else /* full speed (low speed doesn't do bulk) */
-#define qmult 1
-#endif
-
/* for dual-speed hardware, use deeper queues at high/super speed */
-static inline int qlen(struct usb_gadget *gadget)
+static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
{
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
gadget->speed == USB_SPEED_SUPER))
@@ -164,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:
@@ -301,7 +294,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
while (skb2) {
if (status < 0
|| ETH_HLEN > skb2->len
- || skb2->len > ETH_FRAME_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);
@@ -594,7 +587,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
if (gadget_is_dualspeed(dev->gadget))
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
dev->gadget->speed == USB_SPEED_SUPER)
- ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
+ ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
: 0;
retval = usb_ep_queue(in, req, GFP_ATOMIC);
@@ -669,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);
@@ -682,10 +677,14 @@ 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");
+ link->in_ep->desc = in;
+ link->out_ep->desc = out;
usb_ep_enable(link->in_ep);
usb_ep_enable(link->out_ep);
}
@@ -697,16 +696,6 @@ static int eth_stop(struct net_device *net)
/*-------------------------------------------------------------------------*/
-/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
-static char *dev_addr;
-module_param(dev_addr, charp, S_IRUGO);
-MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
-
-/* this address is invisible to ifconfig */
-static char *host_addr;
-module_param(host_addr, charp, S_IRUGO);
-MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
-
static int get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
@@ -724,11 +713,20 @@ static int get_ether_addr(const char *str, u8 *dev_addr)
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,
@@ -744,30 +742,30 @@ static struct device_type gadget_type = {
};
/**
- * 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 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);
@@ -780,7 +778,8 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
/* 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,
@@ -794,13 +793,7 @@ int 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_carrier_off(net);
+ net->ethtool_ops = &ops;
dev->gadget = g;
SET_NETDEV_DEV(net, &g->dev);
@@ -810,15 +803,201 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
if (status < 0) {
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
free_netdev(net);
+ dev = ERR_PTR(status);
} else {
INFO(dev, "MAC %pM\n", net->dev_addr);
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
- the_dev = dev;
+ /*
+ * two kinds of host-initiated state changes:
+ * - iff DATA transfer is active, carrier is "on"
+ * - tx queueing enabled if open *and* carrier is "on"
+ */
+ netif_carrier_off(net);
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(gether_setup_name);
+
+struct net_device *gether_setup_name_default(const char *netname)
+{
+ struct net_device *net;
+ struct eth_dev *dev;
+
+ net = alloc_etherdev(sizeof(*dev));
+ if (!net)
+ return ERR_PTR(-ENOMEM);
+
+ dev = netdev_priv(net);
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->req_lock);
+ INIT_WORK(&dev->work, eth_work);
+ INIT_LIST_HEAD(&dev->tx_reqs);
+ INIT_LIST_HEAD(&dev->rx_reqs);
+
+ skb_queue_head_init(&dev->rx_frames);
+
+ /* network device setup */
+ dev->net = net;
+ dev->qmult = QMULT_DEFAULT;
+ snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+ eth_random_addr(dev->dev_mac);
+ pr_warn("using random %s ethernet address\n", "self");
+ eth_random_addr(dev->host_mac);
+ pr_warn("using random %s ethernet address\n", "host");
+
+ net->netdev_ops = &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
@@ -826,18 +1005,16 @@ int 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);
- flush_work_sync(&the_dev->work);
- free_netdev(the_dev->net);
-
- the_dev = NULL;
+ unregister_netdev(dev->net);
+ flush_work(&dev->work);
+ free_netdev(dev->net);
}
-
+EXPORT_SYMBOL_GPL(gether_cleanup);
/**
* gether_connect - notify network layer that USB link is active
@@ -857,7 +1034,7 @@ void gether_cleanup(void)
*/
struct net_device *gether_connect(struct gether *link)
{
- struct eth_dev *dev = the_dev;
+ struct eth_dev *dev = link->ioport;
int result = 0;
if (!dev)
@@ -880,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;
@@ -892,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);
@@ -918,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
@@ -942,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
@@ -986,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 8012357e98a..0f0290acea7 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -21,6 +21,27 @@
#include "gadget_chips.h"
+#define QMULT_DEFAULT 5
+
+/*
+ * dev_addr: initial value
+ * changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx"
+ * host_addr: this address is invisible to ifconfig
+ */
+#define USB_ETHERNET_MODULE_PARAMETERS() \
+ static unsigned qmult = QMULT_DEFAULT; \
+ module_param(qmult, uint, S_IRUGO|S_IWUSR); \
+ MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\
+ \
+ static char *dev_addr; \
+ module_param(dev_addr, charp, S_IRUGO); \
+ MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); \
+ \
+ static char *host_addr; \
+ module_param(host_addr, charp, S_IRUGO); \
+ MODULE_PARM_DESC(host_addr, "Host Ethernet Address")
+
+struct eth_dev;
/*
* This represents the USB side of an "ethernet" link, managed by a USB
@@ -69,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 *);
@@ -91,24 +267,4 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
return true;
}
-/* each configuration may bind one instance of an ethernet link */
-int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
-int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
-int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
-int eem_bind_config(struct usb_configuration *c);
-
-#ifdef USB_ETH_RNDIS
-
-int rndis_bind_config(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 6597a6813e4..ad0aca81200 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -26,6 +26,7 @@
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/module.h>
#include "u_serial.h"
@@ -35,11 +36,12 @@
* "serial port" functionality through the USB gadget stack. Each such
* port is exposed through a /dev/ttyGS* node.
*
- * After initialization (gserial_setup), these TTY port devices stay
- * available until they are removed (gserial_cleanup). Each one may be
- * connected to a USB function (gserial_connect), or disconnected (with
- * gserial_disconnect) when the USB host issues a config change event.
- * Data can only flow when the port is connected to the host.
+ * After this module has been loaded, the individual TTY port can be requested
+ * (gserial_alloc_line()) and it will stay available until they are removed
+ * (gserial_free_line()). Each one may be connected to a USB function
+ * (gserial_connect), or disconnected (with gserial_disconnect) when the USB
+ * host issues a config change event. Data can only flow when the port is
+ * connected to the host.
*
* A given TTY port can be made available in multiple configurations.
* For example, each one might expose a ttyGS0 node which provides a
@@ -94,17 +96,14 @@ 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;
@@ -122,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
/*-------------------------------------------------------------------------*/
@@ -308,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
@@ -319,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
@@ -412,8 +414,8 @@ __acquires(&port->port_lock)
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;
}
@@ -435,7 +437,7 @@ __acquires(&port->port_lock)
struct tty_struct *tty;
/* no more rx if closed */
- tty = port->port_tty;
+ tty = port->port.tty;
if (!tty)
break;
@@ -488,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) {
@@ -532,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) {
@@ -545,16 +544,16 @@ static void gs_rx_push(unsigned long _port)
}
port->n_read = 0;
}
-recycle:
+
list_move(&req->list, &port->read_pool);
port->read_started--;
}
- /* Push from tty to ldisc; without low_latency set this is handled by
- * a workqueue, so we won't get callbacks and can hold port_lock
+ /* Push from tty to ldisc; this is handled by a workqueue,
+ * so we won't get callbacks and can hold port_lock
*/
- if (tty && do_push)
- tty_flip_buffer_push(tty);
+ if (do_push)
+ tty_flip_buffer_push(&port->port);
/* We want our data queue to become empty ASAP, keeping data
@@ -699,7 +698,7 @@ 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, &port->read_allocated);
gs_free_requests(port->port_usb->in, &port->write_pool,
@@ -725,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;
@@ -737,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) {
@@ -796,9 +792,9 @@ 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;
/* if connected, start the I/O stream */
@@ -840,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;
}
@@ -854,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)
@@ -882,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);
}
@@ -920,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);
@@ -1028,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);
@@ -1051,107 +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) {
- 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)
@@ -1159,59 +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);
- put_tty_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
@@ -1228,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.
@@ -1240,11 +1170,18 @@ int gserial_connect(struct gserial *gser, u8 port_num)
unsigned long flags;
int status;
- if (!gs_tty_driver || port_num >= n_ports)
+ if (port_num >= MAX_U_SERIAL_PORTS)
return -ENXIO;
- /* we "know" gserial_cleanup() hasn't been called */
port = ports[port_num].port;
+ if (!port) {
+ pr_err("serial line %d not allocated.\n", port_num);
+ return -EINVAL;
+ }
+ if (port->port_usb) {
+ pr_err("serial line %d is in use.\n", port_num);
+ return -EBUSY;
+ }
/* activate the endpoints */
status = usb_ep_enable(gser->in);
@@ -1272,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)
@@ -1291,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
@@ -1319,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);
@@ -1335,7 +1272,7 @@ 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, NULL);
gs_free_requests(gser->out, &port->read_queue, NULL);
@@ -1346,3 +1283,65 @@ void gserial_disconnect(struct gserial *gser)
spin_unlock_irqrestore(&port->port_lock, flags);
}
+EXPORT_SYMBOL_GPL(gserial_disconnect);
+
+static int userial_init(void)
+{
+ unsigned i;
+ int status;
+
+ gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
+ if (!gs_tty_driver)
+ return -ENOMEM;
+
+ gs_tty_driver->driver_name = "g_serial";
+ gs_tty_driver->name = PREFIX;
+ /* uses dynamically assigned dev_t values */
+
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ gs_tty_driver->init_termios = tty_std_termios;
+
+ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+ * MS-Windows. Otherwise, most of these flags shouldn't affect
+ * anything unless we were to actually hook up to a serial line.
+ */
+ gs_tty_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ gs_tty_driver->init_termios.c_ispeed = 9600;
+ gs_tty_driver->init_termios.c_ospeed = 9600;
+
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
+ for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
+ mutex_init(&ports[i].lock);
+
+ /* export the driver ... */
+ status = tty_register_driver(gs_tty_driver);
+ if (status) {
+ pr_err("%s: cannot register, err %d\n",
+ __func__, status);
+ goto fail;
+ }
+
+ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+ MAX_U_SERIAL_PORTS,
+ (MAX_U_SERIAL_PORTS == 1) ? "" : "s");
+
+ return status;
+fail:
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+ return status;
+}
+module_init(userial_init);
+
+static void userial_cleanup(void)
+{
+ tty_unregister_driver(gs_tty_driver);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+}
+module_exit(userial_cleanup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index 9b0fe6450fb..c20210c0bab 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -15,6 +15,13 @@
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
+#define MAX_U_SERIAL_PORTS 4
+
+struct f_serial_opts {
+ struct usb_function_instance func_inst;
+ u8 port_num;
+};
+
/*
* One non-multiplexed "serial" I/O port ... there can be several of these
* on any given USB peripheral device, if it provides enough endpoints.
@@ -49,16 +56,15 @@ struct gserial {
struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
void gs_free_req(struct usb_ep *, struct usb_request *req);
-/* port setup/teardown is handled by gadget driver */
-int gserial_setup(struct usb_gadget *g, unsigned n_ports);
-void gserial_cleanup(void);
+/* management of individual TTY ports */
+int gserial_alloc_line(unsigned char *port_line);
+void gserial_free_line(unsigned char port_line);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
/* functions are bound to configurations by a config or gadget driver */
-int acm_bind_config(struct usb_configuration *c, u8 port_num);
int gser_bind_config(struct usb_configuration *c, u8 port_num);
int obex_bind_config(struct usb_configuration *c, u8 port_num);
diff --git a/drivers/usb/gadget/u_audio.c b/drivers/usb/gadget/u_uac1.c
index 59ffe1ecf1c..7a55fea4343 100644
--- a/drivers/usb/gadget/u_audio.c
+++ b/drivers/usb/gadget/u_uac1.c
@@ -1,5 +1,5 @@
/*
- * u_audio.c -- ALSA audio utilities for Gadget stack
+ * u_uac1.c -- ALSA audio utilities for Gadget stack
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
@@ -17,7 +17,7 @@
#include <linux/random.h>
#include <linux/syscalls.h>
-#include "u_audio.h"
+#include "u_uac1.h"
/*
* This component encapsulates the ALSA devices for USB audio gadget
@@ -191,7 +191,7 @@ try_again:
frames = bytes_to_frames(runtime, count);
old_fs = get_fs();
set_fs(KERNEL_DS);
- result = snd_pcm_lib_write(snd->substream, buf, frames);
+ result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
if (result != frames) {
ERROR(card, "Playback error: %d\n", (int)result);
set_fs(old_fs);
@@ -240,8 +240,11 @@ static int gaudio_open_snd_dev(struct gaudio *card)
snd = &card->playback;
snd->filp = filp_open(fn_play, O_WRONLY, 0);
if (IS_ERR(snd->filp)) {
+ int ret = PTR_ERR(snd->filp);
+
ERROR(card, "No such PCM playback device: %s\n", fn_play);
snd->filp = NULL;
+ return ret;
}
pcm_file = snd->filp->private_data;
snd->substream = pcm_file->substream;
@@ -275,17 +278,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
/* Close control device */
snd = &gau->control;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM playback device and setup substream */
snd = &gau->playback;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM capture device and setup substream */
snd = &gau->capture;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
return 0;
}
diff --git a/drivers/usb/gadget/u_audio.h b/drivers/usb/gadget/u_uac1.h
index 08ffce3298e..18c2e729faf 100644
--- a/drivers/usb/gadget/u_audio.h
+++ b/drivers/usb/gadget/u_uac1.h
@@ -1,5 +1,5 @@
/*
- * u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities
+ * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 0b0d12ccc48..b0d98172bc0 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -22,6 +22,8 @@
#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>
@@ -49,27 +51,77 @@ static DEFINE_MUTEX(udc_lock);
/* ------------------------------------------------------------------------- */
-/**
- * usb_gadget_start - tells usb device controller to start up
- * @gadget: The gadget we want to get started
- * @driver: The driver we want to bind to @gadget
- * @bind: The bind function for @driver
- *
- * This call is issued by the UDC Class driver when it's about
- * to register a gadget driver to the device controller, before
- * calling gadget driver's bind() method.
- *
- * It allows the controller to be powered off until strictly
- * necessary to have it powered on.
- *
- * Returns zero on success, else negative errno.
- */
-static inline int usb_gadget_start(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+#ifdef CONFIG_HAS_DMA
+
+int usb_gadget_map_request(struct usb_gadget *gadget,
+ struct usb_request *req, int is_in)
{
- return gadget->ops->start(driver, bind);
+ 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
@@ -92,24 +144,6 @@ static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
}
/**
- * usb_gadget_stop - tells usb device controller we don't need it anymore
- * @gadget: The device we want to stop activity
- * @driver: The driver to unbind from @gadget
- *
- * This call is issued by the UDC Class driver after calling
- * gadget driver's unbind() method.
- *
- * The details are implementation specific, but it can go as
- * far as powering off UDC completely and disable its data
- * line pullups.
- */
-static inline void usb_gadget_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
-{
- gadget->ops->stop(driver);
-}
-
-/**
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
* @gadget: The device we want to stop activity
* @driver: The driver to unbind from @gadget
@@ -144,15 +178,23 @@ static void usb_udc_release(struct device *dev)
}
static const struct attribute_group *usb_udc_attr_groups[];
+
+static void usb_udc_nop_release(struct device *dev)
+{
+ dev_vdbg(dev, "%s\n", __func__);
+}
+
/**
- * usb_add_gadget_udc - adds a new gadget to the udc class driver list
- * @parent: the parent device to this udc. Usually the controller
- * driver's device.
- * @gadget: the gadget to be added to the list
+ * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be added to the list.
+ * @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
*/
-int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
{
struct usb_udc *udc;
int ret = -ENOMEM;
@@ -161,6 +203,25 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
if (!udc)
goto err1;
+ dev_set_name(&gadget->dev, "gadget");
+ INIT_WORK(&gadget->work, usb_gadget_state_work);
+ gadget->dev.parent = parent;
+
+#ifdef CONFIG_HAS_DMA
+ dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask);
+ gadget->dev.dma_parms = parent->dma_parms;
+ gadget->dev.dma_mask = parent->dma_mask;
+#endif
+
+ if (release)
+ gadget->dev.release = release;
+ else
+ gadget->dev.release = usb_udc_nop_release;
+
+ ret = device_register(&gadget->dev);
+ if (ret)
+ goto err2;
+
device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
udc->dev.class = udc_class;
@@ -168,7 +229,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
udc->dev.parent = parent;
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
if (ret)
- goto err2;
+ goto err3;
udc->gadget = gadget;
@@ -177,30 +238,43 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
ret = device_add(&udc->dev);
if (ret)
- goto err3;
+ goto err4;
+
+ usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
mutex_unlock(&udc_lock);
return 0;
-err3:
+
+err4:
list_del(&udc->list);
mutex_unlock(&udc_lock);
-err2:
+err3:
put_device(&udc->dev);
+err2:
+ put_device(&gadget->dev);
+ kfree(udc);
+
err1:
return ret;
}
-EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
-static int udc_is_newstyle(struct usb_udc *udc)
+/**
+ * usb_add_gadget_udc - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller
+ * driver's device.
+ * @gadget: the gadget to be added to the list
+ *
+ * Returns zero on success, negative errno otherwise.
+ */
+int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{
- if (udc->gadget->ops->udc_start && udc->gadget->ops->udc_stop)
- return 1;
- return 0;
+ return usb_add_gadget_udc_release(parent, gadget, NULL);
}
-
+EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
@@ -209,17 +283,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
- if (udc_is_newstyle(udc)) {
- udc->driver->disconnect(udc->gadget);
- udc->driver->unbind(udc->gadget);
- usb_gadget_udc_stop(udc->gadget, udc->driver);
- usb_gadget_disconnect(udc->gadget);
- } else {
- usb_gadget_stop(udc->gadget, udc->driver);
- }
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
+ udc->driver->unbind(udc->gadget);
+ usb_gadget_udc_stop(udc->gadget, NULL);
udc->driver = NULL;
udc->dev.driver = NULL;
+ udc->gadget->dev.driver = NULL;
}
/**
@@ -253,19 +324,79 @@ found:
usb_gadget_remove_driver(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+ flush_work(&gadget->work);
device_unregister(&udc->dev);
+ device_unregister(&gadget->dev);
}
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+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 || !bind || !driver->setup)
+ if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
mutex_lock(&udc_lock);
@@ -278,41 +409,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
pr_debug("couldn't find an available UDC\n");
mutex_unlock(&udc_lock);
return -ENODEV;
-
found:
- dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
- driver->function);
-
- udc->driver = driver;
- udc->dev.driver = &driver->driver;
-
- if (udc_is_newstyle(udc)) {
- ret = bind(udc->gadget);
- if (ret)
- goto err1;
- ret = usb_gadget_udc_start(udc->gadget, driver);
- if (ret) {
- driver->unbind(udc->gadget);
- goto err1;
- }
- usb_gadget_connect(udc->gadget);
- } else {
-
- ret = usb_gadget_start(udc->gadget, driver, bind);
- if (ret)
- goto err1;
-
- }
-
- kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
- mutex_unlock(&udc_lock);
- return 0;
-
-err1:
- dev_err(&udc->dev, "failed to start %s: %d\n",
- udc->driver->function, ret);
- udc->driver = NULL;
- udc->dev.driver = NULL;
+ ret = udc_bind_to_driver(udc, driver);
mutex_unlock(&udc_lock);
return ret;
}
@@ -330,6 +428,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
list_for_each_entry(udc, &udc_list, list)
if (udc->driver == driver) {
usb_gadget_remove_driver(udc);
+ usb_gadget_set_state(udc->gadget,
+ USB_STATE_NOTATTACHED);
ret = 0;
break;
}
@@ -359,9 +459,11 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
if (sysfs_streq(buf, "connect")) {
+ 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;
@@ -371,31 +473,31 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
}
static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
+ struct usb_gadget *gadget = udc->gadget;
+
+ return sprintf(buf, "%s\n", usb_state_string(gadget->state));
+}
+static DEVICE_ATTR_RO(state);
+
#define USB_UDC_SPEED_ATTR(name, param) \
-ssize_t usb_udc_##param##_show(struct device *dev, \
+ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
return snprintf(buf, PAGE_SIZE, "%s\n", \
usb_speed_string(udc->gadget->param)); \
} \
-static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL)
+static DEVICE_ATTR_RO(name)
static USB_UDC_SPEED_ATTR(current_speed, speed);
static USB_UDC_SPEED_ATTR(maximum_speed, max_speed);
-/* TODO: Scheduled for removal in 3.8. */
-static ssize_t usb_udc_is_dualspeed_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, "%d\n",
- gadget_is_dualspeed(udc->gadget));
-}
-static DEVICE_ATTR(is_dualspeed, S_IRUSR, usb_udc_is_dualspeed_show, NULL);
-
#define USB_UDC_ATTR(name) \
-ssize_t usb_udc_##name##_show(struct device *dev, \
+ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \
@@ -403,7 +505,7 @@ ssize_t usb_udc_##name##_show(struct device *dev, \
\
return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
} \
-static DEVICE_ATTR(name, S_IRUGO, usb_udc_##name##_show, NULL)
+static DEVICE_ATTR_RO(name)
static USB_UDC_ATTR(is_otg);
static USB_UDC_ATTR(is_a_peripheral);
@@ -414,10 +516,10 @@ 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_dualspeed.attr,
&dev_attr_is_otg.attr,
&dev_attr_is_a_peripheral.attr,
&dev_attr_b_hnp_enable.attr,
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 4d25b9009ed..73a4dfba0ed 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -9,10 +9,10 @@
#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>
@@ -68,4 +68,4 @@ usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
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
index bc78c606c12..7a9111de805 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/uvc.h
@@ -28,7 +28,7 @@
struct uvc_request_data
{
- unsigned int length;
+ __s32 length;
__u8 data[60];
};
@@ -57,6 +57,7 @@ struct uvc_event
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-fh.h>
+#include <media/v4l2-device.h>
#include "uvc_queue.h"
@@ -98,8 +99,6 @@ extern unsigned int uvc_gadget_trace_param;
#define DRIVER_VERSION "0.1.0"
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0)
-#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-
#define UVC_NUM_REQUESTS 4
#define UVC_MAX_REQUEST_SIZE 64
#define UVC_MAX_EVENTS 4
@@ -147,15 +146,18 @@ enum uvc_state
struct uvc_device
{
struct video_device *vdev;
+ struct v4l2_device v4l2_dev;
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
/* Descriptors */
struct {
- const struct uvc_descriptor_header * const *control;
+ 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;
@@ -188,6 +190,7 @@ struct uvc_file_handle
* Functions
*/
+extern void uvc_function_setup_continue(struct uvc_device *uvc);
extern void uvc_endpoint_stream(struct uvc_device *dev);
extern void uvc_function_connect(struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
index d776adb2da6..1c29bc954db 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/uvc_queue.c
@@ -10,6 +10,7 @@
* (at your option) any later version.
*/
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
@@ -18,7 +19,9 @@
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
-#include <linux/atomic.h>
+
+#include <media/v4l2-common.h>
+#include <media/videobuf2-vmalloc.h>
#include "uvc.h"
@@ -28,253 +31,177 @@
* Video queues is initialized by uvc_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
- * Video buffer allocation and freeing are performed by uvc_alloc_buffers and
- * uvc_free_buffers respectively. The former acquires the video queue lock,
- * while the later must be called with the lock held (so that allocation can
- * free previously allocated buffers). Trying to free buffers that are mapped
- * to user space will return -EBUSY.
- *
- * Video buffers are managed using two queues. However, unlike most USB video
- * drivers that use an in queue and an out queue, we use a main queue to hold
- * all queued buffers (both 'empty' and 'done' buffers), and an irq queue to
- * hold empty buffers. This design (copied from video-buf) minimizes locking
- * in interrupt, as only one queue is shared between interrupt and user
- * contexts.
- *
- * Use cases
- * ---------
- *
- * Unless stated otherwise, all operations that modify the irq buffers queue
- * are protected by the irq spinlock.
- *
- * 1. The user queues the buffers, starts streaming and dequeues a buffer.
- *
- * The buffers are added to the main and irq queues. Both operations are
- * protected by the queue lock, and the later is protected by the irq
- * spinlock as well.
- *
- * The completion handler fetches a buffer from the irq queue and fills it
- * with video data. If no buffer is available (irq queue empty), the handler
- * returns immediately.
- *
- * When the buffer is full, the completion handler removes it from the irq
- * queue, marks it as ready (UVC_BUF_STATE_DONE) and wakes its wait queue.
- * At that point, any process waiting on the buffer will be woken up. If a
- * process tries to dequeue a buffer after it has been marked ready, the
- * dequeing will succeed immediately.
- *
- * 2. Buffers are queued, user is waiting on a buffer and the device gets
- * disconnected.
- *
- * When the device is disconnected, the kernel calls the completion handler
- * with an appropriate status code. The handler marks all buffers in the
- * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
- * that any process waiting on a buffer gets woken up.
- *
- * Waking up up the first buffer on the irq list is not enough, as the
- * process waiting on the buffer might restart the dequeue operation
- * immediately.
- *
+ * Video buffers are managed by videobuf2. The driver uses a mutex to protect
+ * the videobuf2 queue operations by serializing calls to videobuf2 and a
+ * spinlock to protect the IRQ queue that holds the buffers to be processed by
+ * the driver.
*/
-static void
-uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
-{
- mutex_init(&queue->mutex);
- spin_lock_init(&queue->irqlock);
- INIT_LIST_HEAD(&queue->mainqueue);
- INIT_LIST_HEAD(&queue->irqqueue);
- queue->type = type;
-}
-
-/*
- * Free the video buffers.
- *
- * This function must be called with the queue lock held.
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
*/
-static int uvc_free_buffers(struct uvc_video_queue *queue)
+
+static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- unsigned int i;
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_video *video = container_of(queue, struct uvc_video, queue);
- for (i = 0; i < queue->count; ++i) {
- if (queue->buffer[i].vma_use_count != 0)
- return -EBUSY;
- }
+ if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
+ *nbuffers = UVC_MAX_VIDEO_BUFFERS;
- if (queue->count) {
- vfree(queue->mem);
- queue->count = 0;
- }
+ *nplanes = 1;
+
+ sizes[0] = video->imagesize;
return 0;
}
-/*
- * Allocate the video buffers.
- *
- * Pages are reserved to make sure they will not be swapped, as they will be
- * filled in the URB completion handler.
- *
- * Buffers will be individually mapped, so they must all be page aligned.
- */
-static int
-uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
- unsigned int buflength)
+static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
- unsigned int bufsize = PAGE_ALIGN(buflength);
- unsigned int i;
- void *mem = NULL;
- int ret;
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
- if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
- nbuffers = UVC_MAX_VIDEO_BUFFERS;
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+ return -EINVAL;
+ }
- mutex_lock(&queue->mutex);
+ if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
+ return -ENODEV;
- if ((ret = uvc_free_buffers(queue)) < 0)
- goto done;
+ buf->state = UVC_BUF_STATE_QUEUED;
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ buf->length = vb2_plane_size(vb, 0);
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ buf->bytesused = 0;
+ else
+ buf->bytesused = vb2_get_plane_payload(vb, 0);
- /* Bail out if no buffers should be allocated. */
- if (nbuffers == 0)
- goto done;
+ return 0;
+}
- /* Decrement the number of buffers until allocation succeeds. */
- for (; nbuffers > 0; --nbuffers) {
- mem = vmalloc_32(nbuffers * bufsize);
- if (mem != NULL)
- break;
- }
+static void uvc_buffer_queue(struct vb2_buffer *vb)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+ unsigned long flags;
- if (mem == NULL) {
- ret = -ENOMEM;
- goto done;
- }
+ spin_lock_irqsave(&queue->irqlock, flags);
- for (i = 0; i < nbuffers; ++i) {
- memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
- queue->buffer[i].buf.index = i;
- queue->buffer[i].buf.m.offset = i * bufsize;
- queue->buffer[i].buf.length = buflength;
- queue->buffer[i].buf.type = queue->type;
- queue->buffer[i].buf.sequence = 0;
- queue->buffer[i].buf.field = V4L2_FIELD_NONE;
- queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
- queue->buffer[i].buf.flags = 0;
- init_waitqueue_head(&queue->buffer[i].wait);
+ if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+ list_add_tail(&buf->queue, &queue->irqqueue);
+ } else {
+ /* If the device is disconnected return the buffer to userspace
+ * directly. The next QBUF call will fail with -ENODEV.
+ */
+ buf->state = UVC_BUF_STATE_ERROR;
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
}
- queue->mem = mem;
- queue->count = nbuffers;
- queue->buf_size = bufsize;
- ret = nbuffers;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+static void uvc_wait_prepare(struct vb2_queue *vq)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
-done:
mutex_unlock(&queue->mutex);
- return ret;
}
-static void __uvc_query_buffer(struct uvc_buffer *buf,
- struct v4l2_buffer *v4l2_buf)
+static void uvc_wait_finish(struct vb2_queue *vq)
{
- memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
-
- if (buf->vma_use_count)
- v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
-
- switch (buf->state) {
- case UVC_BUF_STATE_ERROR:
- case UVC_BUF_STATE_DONE:
- v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
- break;
- case UVC_BUF_STATE_QUEUED:
- case UVC_BUF_STATE_ACTIVE:
- v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
- break;
- case UVC_BUF_STATE_IDLE:
- default:
- break;
- }
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+
+ mutex_lock(&queue->mutex);
}
-static int
-uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf)
+static struct vb2_ops uvc_queue_qops = {
+ .queue_setup = uvc_queue_setup,
+ .buf_prepare = uvc_buffer_prepare,
+ .buf_queue = uvc_buffer_queue,
+ .wait_prepare = uvc_wait_prepare,
+ .wait_finish = uvc_wait_finish,
+};
+
+static int uvc_queue_init(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type)
{
- int ret = 0;
+ int ret;
- mutex_lock(&queue->mutex);
- if (v4l2_buf->index >= queue->count) {
- ret = -EINVAL;
- goto done;
- }
+ queue->queue.type = type;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.drv_priv = queue;
+ queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
+ queue->queue.ops = &uvc_queue_qops;
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+ | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+ ret = vb2_queue_init(&queue->queue);
+ if (ret)
+ return ret;
- __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
+ mutex_init(&queue->mutex);
+ spin_lock_init(&queue->irqlock);
+ INIT_LIST_HEAD(&queue->irqqueue);
+ queue->flags = 0;
-done:
+ return 0;
+}
+
+/*
+ * Free the video buffers.
+ */
+static void uvc_free_buffers(struct uvc_video_queue *queue)
+{
+ mutex_lock(&queue->mutex);
+ vb2_queue_release(&queue->queue);
mutex_unlock(&queue->mutex);
- return ret;
}
/*
- * Queue a video buffer. Attempting to queue a buffer that has already been
- * queued will return -EINVAL.
+ * Allocate the video buffers.
*/
-static int
-uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf)
+static int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
{
- struct uvc_buffer *buf;
- unsigned long flags;
- int ret = 0;
+ int ret;
- uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
+ mutex_lock(&queue->mutex);
+ ret = vb2_reqbufs(&queue->queue, rb);
+ mutex_unlock(&queue->mutex);
- if (v4l2_buf->type != queue->type ||
- v4l2_buf->memory != V4L2_MEMORY_MMAP) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
- "and/or memory (%u).\n", v4l2_buf->type,
- v4l2_buf->memory);
- return -EINVAL;
- }
+ return ret ? ret : rb->count;
+}
- mutex_lock(&queue->mutex);
- if (v4l2_buf->index >= queue->count) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
- ret = -EINVAL;
- goto done;
- }
+static int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *buf)
+{
+ int ret;
- buf = &queue->buffer[v4l2_buf->index];
- if (buf->state != UVC_BUF_STATE_IDLE) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
- "(%u).\n", buf->state);
- ret = -EINVAL;
- goto done;
- }
+ mutex_lock(&queue->mutex);
+ ret = vb2_querybuf(&queue->queue, buf);
+ mutex_unlock(&queue->mutex);
- if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- v4l2_buf->bytesused > buf->buf.length) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
- ret = -EINVAL;
- goto done;
- }
+ return ret;
+}
- if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- buf->buf.bytesused = 0;
- else
- buf->buf.bytesused = v4l2_buf->bytesused;
+static int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *buf)
+{
+ unsigned long flags;
+ int ret;
- spin_lock_irqsave(&queue->irqlock, flags);
- if (queue->flags & UVC_QUEUE_DISCONNECTED) {
- spin_unlock_irqrestore(&queue->irqlock, flags);
- ret = -ENODEV;
+ mutex_lock(&queue->mutex);
+ ret = vb2_qbuf(&queue->queue, buf);
+ if (ret < 0)
goto done;
- }
- buf->state = UVC_BUF_STATE_QUEUED;
+ spin_lock_irqsave(&queue->irqlock, flags);
ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
queue->flags &= ~UVC_QUEUE_PAUSED;
-
- list_add_tail(&buf->stream, &queue->mainqueue);
- list_add_tail(&buf->queue, &queue->irqqueue);
spin_unlock_irqrestore(&queue->irqlock, flags);
done:
@@ -282,76 +209,19 @@ done:
return ret;
}
-static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
-{
- if (nonblocking) {
- return (buf->state != UVC_BUF_STATE_QUEUED &&
- buf->state != UVC_BUF_STATE_ACTIVE)
- ? 0 : -EAGAIN;
- }
-
- return wait_event_interruptible(buf->wait,
- buf->state != UVC_BUF_STATE_QUEUED &&
- buf->state != UVC_BUF_STATE_ACTIVE);
-}
-
/*
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
* available.
*/
-static int
-uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf,
- int nonblocking)
+static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *buf, int nonblocking)
{
- struct uvc_buffer *buf;
- int ret = 0;
-
- if (v4l2_buf->type != queue->type ||
- v4l2_buf->memory != V4L2_MEMORY_MMAP) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
- "and/or memory (%u).\n", v4l2_buf->type,
- v4l2_buf->memory);
- return -EINVAL;
- }
+ int ret;
mutex_lock(&queue->mutex);
- if (list_empty(&queue->mainqueue)) {
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
- ret = -EINVAL;
- goto done;
- }
-
- buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
- if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
- goto done;
-
- uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
- buf->buf.index, buf->state, buf->buf.bytesused);
-
- switch (buf->state) {
- case UVC_BUF_STATE_ERROR:
- uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
- "(transmission error).\n");
- ret = -EIO;
- case UVC_BUF_STATE_DONE:
- buf->state = UVC_BUF_STATE_IDLE;
- break;
-
- case UVC_BUF_STATE_IDLE:
- case UVC_BUF_STATE_QUEUED:
- case UVC_BUF_STATE_ACTIVE:
- default:
- uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
- "(driver bug?).\n", buf->state);
- ret = -EINVAL;
- goto done;
- }
-
- list_del(&buf->stream);
- __uvc_query_buffer(buf, v4l2_buf);
-
-done:
+ ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
mutex_unlock(&queue->mutex);
+
return ret;
}
@@ -361,105 +231,47 @@ done:
* This function implements video queue polling and is intended to be used by
* the device poll handler.
*/
-static unsigned int
-uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait)
+static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait)
{
- struct uvc_buffer *buf;
- unsigned int mask = 0;
+ unsigned int ret;
mutex_lock(&queue->mutex);
- if (list_empty(&queue->mainqueue))
- goto done;
-
- buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
-
- poll_wait(file, &buf->wait, wait);
- if (buf->state == UVC_BUF_STATE_DONE ||
- buf->state == UVC_BUF_STATE_ERROR)
- mask |= POLLOUT | POLLWRNORM;
-
-done:
+ ret = vb2_poll(&queue->queue, file, wait);
mutex_unlock(&queue->mutex);
- return mask;
-}
-/*
- * VMA operations.
- */
-static void uvc_vm_open(struct vm_area_struct *vma)
-{
- struct uvc_buffer *buffer = vma->vm_private_data;
- buffer->vma_use_count++;
+ return ret;
}
-static void uvc_vm_close(struct vm_area_struct *vma)
+static int uvc_queue_mmap(struct uvc_video_queue *queue,
+ struct vm_area_struct *vma)
{
- struct uvc_buffer *buffer = vma->vm_private_data;
- buffer->vma_use_count--;
-}
+ int ret;
-static struct vm_operations_struct uvc_vm_ops = {
- .open = uvc_vm_open,
- .close = uvc_vm_close,
-};
+ mutex_lock(&queue->mutex);
+ ret = vb2_mmap(&queue->queue, vma);
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+#ifndef CONFIG_MMU
/*
- * Memory-map a buffer.
+ * Get unmapped area.
*
- * This function implements video buffer memory mapping and is intended to be
- * used by the device mmap handler.
+ * NO-MMU arch need this function to make mmap() work correctly.
*/
-static int
-uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
+static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff)
{
- struct uvc_buffer *uninitialized_var(buffer);
- struct page *page;
- unsigned long addr, start, size;
- unsigned int i;
- int ret = 0;
-
- start = vma->vm_start;
- size = vma->vm_end - vma->vm_start;
+ unsigned long ret;
mutex_lock(&queue->mutex);
-
- for (i = 0; i < queue->count; ++i) {
- buffer = &queue->buffer[i];
- if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
-
- if (i == queue->count || size != queue->buf_size) {
- ret = -EINVAL;
- goto done;
- }
-
- /*
- * VM_IO marks the area as being an mmaped region for I/O to a
- * device. It also prevents the region from being core dumped.
- */
- vma->vm_flags |= VM_IO;
-
- addr = (unsigned long)queue->mem + buffer->buf.m.offset;
- while (size > 0) {
- page = vmalloc_to_page((void *)addr);
- if ((ret = vm_insert_page(vma, start, page)) < 0)
- goto done;
-
- start += PAGE_SIZE;
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- vma->vm_ops = &uvc_vm_ops;
- vma->vm_private_data = buffer;
- uvc_vm_open(vma);
-
-done:
+ ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
mutex_unlock(&queue->mutex);
return ret;
}
+#endif
/*
* Cancel the video buffers queue.
@@ -484,7 +296,7 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
queue);
list_del(&buf->queue);
buf->state = UVC_BUF_STATE_ERROR;
- wake_up(&buf->wait);
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
}
/* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_queue_buffer and the disconnection event that
@@ -516,26 +328,33 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
*/
static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
{
- unsigned int i;
+ unsigned long flags;
int ret = 0;
mutex_lock(&queue->mutex);
if (enable) {
- if (uvc_queue_streaming(queue)) {
- ret = -EBUSY;
+ ret = vb2_streamon(&queue->queue, queue->queue.type);
+ if (ret < 0)
goto done;
- }
+
queue->sequence = 0;
- queue->flags |= UVC_QUEUE_STREAMING;
queue->buf_used = 0;
} else {
- uvc_queue_cancel(queue, 0);
- INIT_LIST_HEAD(&queue->mainqueue);
+ ret = vb2_streamoff(&queue->queue, queue->queue.type);
+ if (ret < 0)
+ goto done;
- for (i = 0; i < queue->count; ++i)
- queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+ spin_lock_irqsave(&queue->irqlock, flags);
+ INIT_LIST_HEAD(&queue->irqqueue);
- queue->flags &= ~UVC_QUEUE_STREAMING;
+ /*
+ * FIXME: We need to clear the DISCONNECTED flag to ensure that
+ * applications will be able to queue buffers for the next
+ * streaming run. However, clearing it here doesn't guarantee
+ * that the device will be reconnected in the meantime.
+ */
+ queue->flags &= ~UVC_QUEUE_DISCONNECTED;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
}
done:
@@ -543,32 +362,33 @@ done:
return ret;
}
-static struct uvc_buffer *
-uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf)
+/* 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;
- unsigned long flags;
if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
- buf->buf.length != buf->buf.bytesused) {
+ buf->length != buf->bytesused) {
buf->state = UVC_BUF_STATE_QUEUED;
- buf->buf.bytesused = 0;
+ vb2_set_plane_payload(&buf->buf, 0, 0);
return buf;
}
- spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
else
nextbuf = NULL;
- spin_unlock_irqrestore(&queue->irqlock, flags);
- buf->buf.sequence = queue->sequence++;
- do_gettimeofday(&buf->buf.timestamp);
+ buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
+ buf->buf.v4l2_buf.sequence = queue->sequence++;
+ v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
+
+ vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
- wake_up(&buf->wait);
return nextbuf;
}
diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h
index 1812a8ecc5d..8e76ce982f1 100644
--- a/drivers/usb/gadget/uvc_queue.h
+++ b/drivers/usb/gadget/uvc_queue.h
@@ -6,6 +6,7 @@
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/videodev2.h>
+#include <media/videobuf2-core.h>
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
@@ -25,42 +26,35 @@ enum uvc_buffer_state {
};
struct uvc_buffer {
- unsigned long vma_use_count;
- struct list_head stream;
-
- /* Touched by interrupt handler. */
- struct v4l2_buffer buf;
+ struct vb2_buffer buf;
struct list_head queue;
- wait_queue_head_t wait;
+
enum uvc_buffer_state state;
+ void *mem;
+ unsigned int length;
+ unsigned int bytesused;
};
-#define UVC_QUEUE_STREAMING (1 << 0)
-#define UVC_QUEUE_DISCONNECTED (1 << 1)
-#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
-#define UVC_QUEUE_PAUSED (1 << 3)
+#define UVC_QUEUE_DISCONNECTED (1 << 0)
+#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1)
+#define UVC_QUEUE_PAUSED (1 << 2)
struct uvc_video_queue {
- enum v4l2_buf_type type;
+ struct vb2_queue queue;
+ struct mutex mutex; /* Protects queue */
- void *mem;
unsigned int flags;
__u32 sequence;
- unsigned int count;
- unsigned int buf_size;
unsigned int buf_used;
- struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
- struct mutex mutex; /* protects buffers and mainqueue */
- spinlock_t irqlock; /* protects irqqueue */
- struct list_head mainqueue;
+ spinlock_t irqlock; /* Protects flags and irqqueue */
struct list_head irqqueue;
};
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
- return queue->flags & UVC_QUEUE_STREAMING;
+ return vb2_is_streaming(&queue->queue);
}
#endif /* __KERNEL__ */
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
index f6e083b5019..ad48e81155e 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/uvc_v4l2.c
@@ -39,11 +39,10 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
if (data->length < 0)
return usb_ep_set_halt(cdev->gadget->ep0);
- req->length = min(uvc->event_length, data->length);
+ req->length = min_t(unsigned int, uvc->event_length, data->length);
req->zero = data->length < uvc->event_length;
- req->dma = DMA_ADDR_INVALID;
- memcpy(req->buf, data->data, data->length);
+ memcpy(req->buf, data->data, req->length);
return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
}
@@ -148,16 +147,13 @@ uvc_v4l2_release(struct file *file)
uvc_function_disconnect(uvc);
uvc_video_enable(video, 0);
- mutex_lock(&video->queue.mutex);
- if (uvc_free_buffers(&video->queue) < 0)
- printk(KERN_ERR "uvc_v4l2_release: Unable to free "
- "buffers.\n");
- mutex_unlock(&video->queue.mutex);
+ uvc_free_buffers(&video->queue);
file->private_data = NULL;
v4l2_fh_del(&handle->vfh);
v4l2_fh_exit(&handle->vfh);
kfree(handle);
+
return 0;
}
@@ -178,9 +174,9 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_capability *cap = arg;
memset(cap, 0, sizeof *cap);
- strncpy(cap->driver, "g_uvc", sizeof(cap->driver));
- strncpy(cap->card, cdev->gadget->name, sizeof(cap->card));
- strncpy(cap->bus_info, dev_name(&cdev->gadget->dev),
+ strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
+ strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
sizeof cap->bus_info);
cap->version = DRIVER_VERSION_NUMBER;
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -192,7 +188,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_format *fmt = arg;
- if (fmt->type != video->queue.type)
+ if (fmt->type != video->queue.queue.type)
return -EINVAL;
return uvc_v4l2_get_format(video, fmt);
@@ -202,7 +198,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_format *fmt = arg;
- if (fmt->type != video->queue.type)
+ if (fmt->type != video->queue.queue.type)
return -EINVAL;
return uvc_v4l2_set_format(video, fmt);
@@ -213,16 +209,13 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_requestbuffers *rb = arg;
- if (rb->type != video->queue.type ||
- rb->memory != V4L2_MEMORY_MMAP)
+ if (rb->type != video->queue.queue.type)
return -EINVAL;
- ret = uvc_alloc_buffers(&video->queue, rb->count,
- video->imagesize);
+ ret = uvc_alloc_buffers(&video->queue, rb);
if (ret < 0)
return ret;
- rb->count = ret;
ret = 0;
break;
}
@@ -231,9 +224,6 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct v4l2_buffer *buf = arg;
- if (buf->type != video->queue.type)
- return -EINVAL;
-
return uvc_query_buffer(&video->queue, buf);
}
@@ -251,24 +241,36 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
int *type = arg;
- if (*type != video->queue.type)
+ if (*type != video->queue.queue.type)
return -EINVAL;
- return uvc_video_enable(video, 1);
+ /* Enable UVC video. */
+ ret = uvc_video_enable(video, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Complete the alternate setting selection setup phase now that
+ * userspace is ready to provide video frames.
+ */
+ uvc_function_setup_continue(uvc);
+ uvc->state = UVC_STATE_STREAMING;
+
+ return 0;
}
case VIDIOC_STREAMOFF:
{
int *type = arg;
- if (*type != video->queue.type)
+ if (*type != video->queue.queue.type)
return -EINVAL;
return uvc_video_enable(video, 0);
}
/* Events */
- case VIDIOC_DQEVENT:
+ case VIDIOC_DQEVENT:
{
struct v4l2_event *event = arg;
@@ -296,7 +298,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
- return v4l2_event_subscribe(&handle->vfh, arg, 2);
+ return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
}
case VIDIOC_UNSUBSCRIBE_EVENT:
@@ -333,17 +335,21 @@ uvc_v4l2_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
- unsigned int mask = 0;
- poll_wait(file, &handle->vfh.wait, wait);
- if (v4l2_event_pending(&handle->vfh))
- mask |= POLLPRI;
+ return uvc_queue_poll(&uvc->video.queue, file, wait);
+}
- mask |= uvc_queue_poll(&uvc->video.queue, file, wait);
+#ifndef CONFIG_MMU
+static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_device *uvc = video_get_drvdata(vdev);
- return mask;
+ return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
}
+#endif
static struct v4l2_file_operations uvc_v4l2_fops = {
.owner = THIS_MODULE,
@@ -352,5 +358,8 @@ static struct v4l2_file_operations uvc_v4l2_fops = {
.ioctl = uvc_v4l2_ioctl,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+#endif
};
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
index b0e53a8ea4f..71e896d4c5a 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/uvc_video.c
@@ -32,7 +32,7 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
data[0] = 2;
data[1] = UVC_STREAM_EOH | video->fid;
- if (buf->buf.bytesused - video->queue.buf_used <= len - 2)
+ if (buf->bytesused - video->queue.buf_used <= len - 2)
data[1] |= UVC_STREAM_EOF;
return 2;
@@ -47,8 +47,8 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
void *mem;
/* Copy video data to the USB buffer. */
- mem = queue->mem + buf->buf.m.offset + queue->buf_used;
- nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
+ mem = buf->mem + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
memcpy(data, mem, nbytes);
queue->buf_used += nbytes;
@@ -82,7 +82,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
req->length = video->req_size - len;
req->zero = video->payload_size == video->max_payload_size;
- if (buf->buf.bytesused == video->queue.buf_used) {
+ if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
uvc_queue_next_buffer(&video->queue, buf);
@@ -92,7 +92,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
}
if (video->payload_size == video->max_payload_size ||
- buf->buf.bytesused == video->queue.buf_used)
+ buf->bytesused == video->queue.buf_used)
video->payload_size = 0;
}
@@ -115,7 +115,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
req->length = video->req_size - len;
- if (buf->buf.bytesused == video->queue.buf_used) {
+ if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
uvc_queue_next_buffer(&video->queue, buf);
@@ -161,6 +161,7 @@ static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = req->context;
+ struct uvc_video_queue *queue = &video->queue;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
@@ -169,13 +170,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
case 0:
break;
- case -ESHUTDOWN:
+ case -ESHUTDOWN: /* disconnect from host. */
printk(KERN_INFO "VS request cancelled.\n");
+ uvc_queue_cancel(queue, 1);
goto requeue;
default:
printk(KERN_INFO "VS request completed with status %d.\n",
req->status);
+ uvc_queue_cancel(queue, 0);
goto requeue;
}
@@ -229,13 +232,18 @@ uvc_video_free_requests(struct uvc_video *video)
static int
uvc_video_alloc_requests(struct uvc_video *video)
{
+ unsigned int req_size;
unsigned int i;
int ret = -ENOMEM;
BUG_ON(video->req_size);
+ req_size = video->ep->maxpacket
+ * max_t(unsigned int, video->ep->maxburst, 1)
+ * (video->ep->mult + 1);
+
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
- video->req_buffer[i] = kmalloc(video->ep->maxpacket, GFP_KERNEL);
+ video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
if (video->req_buffer[i] == NULL)
goto error;
@@ -245,14 +253,14 @@ uvc_video_alloc_requests(struct uvc_video *video)
video->req[i]->buf = video->req_buffer[i];
video->req[i]->length = 0;
- video->req[i]->dma = DMA_ADDR_INVALID;
video->req[i]->complete = uvc_video_complete;
video->req[i]->context = video;
list_add_tail(&video->req[i]->list, &video->req_free);
}
- video->req_size = video->ep->maxpacket;
+ video->req_size = req_size;
+
return 0;
error:
@@ -309,7 +317,8 @@ uvc_video_pump(struct uvc_video *video)
video->encode(req, video, buf);
/* Queue the USB request */
- if ((ret = usb_ep_queue(video->ep, req, GFP_KERNEL)) < 0) {
+ ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+ if (ret < 0) {
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
usb_ep_set_halt(video->ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index 668fe128f2e..8cef1e658c2 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -23,16 +23,12 @@
* 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 "uvc_queue.c"
#include "uvc_video.c"
#include "uvc_v4l2.c"
#include "f_uvc.c"
+USB_GADGET_COMPOSITE_OPTIONS();
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -47,13 +43,12 @@ static char webcam_config_label[] = "Video";
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string webcam_strings[] = {
- [STRING_MANUFACTURER_IDX].s = webcam_vendor_label,
- [STRING_PRODUCT_IDX].s = webcam_product_label,
+ [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,
{ }
};
@@ -272,7 +267,15 @@ static const struct uvc_color_matching_descriptor uvc_color_matching = {
.bMatrixCoefficients = 4,
};
-static const struct uvc_descriptor_header * const uvc_control_cls[] = {
+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,
@@ -304,6 +307,18 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
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
*/
@@ -311,8 +326,9 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
static int __init
webcam_config_bind(struct usb_configuration *c)
{
- return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
- uvc_hs_streaming_cls);
+ 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 = {
@@ -320,7 +336,7 @@ static struct usb_configuration webcam_config_driver = {
.bConfigurationValue = 1,
.iConfiguration = 0, /* dynamic */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
+ .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
static int /* __init_or_exit */
@@ -337,26 +353,22 @@ webcam_bind(struct usb_composite_dev *cdev)
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_MANUFACTURER_IDX].id = ret;
- webcam_device_descriptor.iManufacturer = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_PRODUCT_IDX].id = ret;
- webcam_device_descriptor.iProduct = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
+ ret = usb_string_ids_tab(cdev, webcam_strings);
+ if (ret < 0)
goto error;
- webcam_strings[STRING_DESCRIPTION_IDX].id = ret;
- webcam_config_driver.iConfiguration = ret;
+ 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;
@@ -369,18 +381,19 @@ error:
* Driver
*/
-static struct usb_composite_driver webcam_driver = {
+static __refdata struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
- .max_speed = USB_SPEED_HIGH,
+ .max_speed = USB_SPEED_SUPER,
+ .bind = webcam_bind,
.unbind = webcam_unbind,
};
static int __init
webcam_init(void)
{
- return usb_composite_probe(&webcam_driver, webcam_bind);
+ return usb_composite_probe(&webcam_driver);
}
static void __exit
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 31d34832907..134f354ede6 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -10,7 +10,6 @@
* (at your option) any later version.
*/
-
/*
* Gadget Zero only needs two bulk endpoints, and is an example of how you
* can write a hardware-agnostic gadget driver running inside a USB device.
@@ -42,39 +41,19 @@
#include <linux/kernel.h>
#include <linux/slab.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"
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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
@@ -84,6 +63,13 @@ module_param(buflen, uint, 0);
static bool loopdefault = 0;
module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
+static struct usb_zero_options gzero_options = {
+ .isoc_interval = GZERO_ISOC_INTERVAL,
+ .isoc_maxpacket = GZERO_ISOC_MAXPACKET,
+ .bulk_buflen = GZERO_BULK_BUFLEN,
+ .qlen = GZERO_QLEN,
+};
+
/*-------------------------------------------------------------------------*/
/* Thanks to NetChip Technologies for donating this product ID.
@@ -105,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 = {
@@ -134,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 */
};
@@ -170,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)
@@ -241,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__);
}
@@ -255,83 +217,195 @@ 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);
- sourcesink_add(cdev, autoresume != 0);
+ usb_add_config_only(cdev, &loopback_driver);
+ usb_add_config_only(cdev, &sourcesink_driver);
} else {
- sourcesink_add(cdev, autoresume != 0);
- loopback_add(cdev, autoresume != 0);
+ usb_add_config_only(cdev, &sourcesink_driver);
+ usb_add_config_only(cdev, &loopback_driver);
}
+ status = usb_add_function(&sourcesink_driver, func_ss);
+ if (status)
+ goto err_conf_flb;
- 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,
.resume = zero_resume,
@@ -342,7 +416,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&zero_driver, zero_bind);
+ return usb_composite_probe(&zero_driver);
}
module_init(init);