diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 23:08:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 23:08:32 -0700 |
commit | f549953c15deab4c54708b39af86d4edecc6cddc (patch) | |
tree | f0412f989b77cdceab34c18aa85a8a25d5942a1f /drivers/usb/gadget | |
parent | f0deb97ab13ad1f89cd0993f7339655d59788405 (diff) | |
parent | e04f5f7e423018bcec84c11af2058cdce87816f3 (diff) |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (115 commits)
EHCI: fix direction handling for interrupt data toggles
USB: serial: add IDs for WinChipHead USB->RS232 adapter
USB: OHCI: fix another regression for NVIDIA controllers
usb: gadget: m66592-udc: add pullup function
usb: gadget: m66592-udc: add function for external controller
usb: gadget: r8a66597-udc: add pullup function
usb: renesas_usbhs: support multi driver
usb: renesas_usbhs: inaccessible pipe is not an error
usb: renesas_usbhs: care buff alignment when dma handler
USB: PL2303: correctly handle baudrates above 115200
usb: r8a66597-hcd: fixup USB_PORT_STAT_C_SUSPEND shift
usb: renesas_usbhs: compile/config are rescued
usb: renesas_usbhs: fixup comment-out
usb: update email address in ohci-sh and r8a66597-hcd
usb: r8a66597-hcd: add function for external controller
EHCI: only power off port if over-current is active
USB: mon: Allow to use usbmon without debugfs
USB: EHCI: go back to using the system clock for QH unlinks
ehci: add pci quirk for Ordissimo and RM Slate 100 too
ehci: refactor pci quirk to use standard dmi_check_system method
...
Fix up trivial conflicts in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'drivers/usb/gadget')
72 files changed, 6338 insertions, 1493 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 029e288805b6..44b6b40aafb4 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -96,9 +96,6 @@ config USB_GADGET_VBUS_DRAW This value will be used except for system-specific gadget drivers that have more specific information. -config USB_GADGET_SELECTED - boolean - # # USB Peripheral Controller Support # @@ -122,10 +119,9 @@ choice # Integrated controllers # -config USB_GADGET_AT91 - boolean "Atmel AT91 USB Device Port" +config USB_AT91 + tristate "Atmel AT91 USB Device Port" depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45 - select USB_GADGET_SELECTED help Many Atmel AT91 processors (such as the AT91RM2000) have a full speed USB Device Port with support for five configurable @@ -135,27 +131,16 @@ config USB_GADGET_AT91 dynamically linked module called "at91_udc" and force all gadget drivers to also be dynamically linked. -config USB_AT91 - tristate - depends on USB_GADGET_AT91 - default USB_GADGET - -config USB_GADGET_ATMEL_USBA - boolean "Atmel USBA" +config USB_ATMEL_USBA + tristate "Atmel USBA" select USB_GADGET_DUALSPEED depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. -config USB_ATMEL_USBA - tristate - depends on USB_GADGET_ATMEL_USBA - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_FSL_USB2 - boolean "Freescale Highspeed USB DR Peripheral Controller" +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 @@ -170,26 +155,15 @@ config USB_GADGET_FSL_USB2 dynamically linked module called "fsl_usb2_udc" and force all gadget drivers to also be dynamically linked. -config USB_FSL_USB2 - tristate - depends on USB_GADGET_FSL_USB2 - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_FUSB300 - boolean "Faraday FUSB300 USB Peripheral Controller" +config USB_FUSB300 + tristate "Faraday FUSB300 USB Peripheral Controller" + depends on !PHYS_ADDR_T_64BIT select USB_GADGET_DUALSPEED help Faraday usb device controller FUSB300 driver -config USB_FUSB300 - tristate - depends on USB_GADGET_FUSB300 - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_OMAP - boolean "OMAP USB Device Controller" +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 @@ -204,14 +178,8 @@ config USB_GADGET_OMAP dynamically linked module called "omap_udc" and force all gadget drivers to also be dynamically linked. -config USB_OMAP - tristate - depends on USB_GADGET_OMAP - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_PXA25X - boolean "PXA 25x or IXP 4xx" +config USB_PXA25X + tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX select USB_OTG_UTILS help @@ -226,24 +194,18 @@ config USB_GADGET_PXA25X dynamically linked module called "pxa25x_udc" and force all gadget drivers to also be dynamically linked. -config USB_PXA25X - tristate - depends on USB_GADGET_PXA25X - default USB_GADGET - select USB_GADGET_SELECTED - # if there's only one gadget driver, using only two bulk endpoints, # don't waste memory for the other endpoints config USB_PXA25X_SMALL - depends on USB_GADGET_PXA25X + depends on USB_PXA25X bool default n if USB_ETH_RNDIS default y if USB_ZERO default y if USB_ETH default y if USB_G_SERIAL -config USB_GADGET_R8A66597 - boolean "Renesas R8A66597 USB Peripheral Controller" +config USB_R8A66597 + tristate "Renesas R8A66597 USB Peripheral Controller" select USB_GADGET_DUALSPEED help R8A66597 is a discrete USB host and peripheral controller chip that @@ -254,32 +216,22 @@ config USB_GADGET_R8A66597 dynamically linked module called "r8a66597_udc" and force all gadget drivers to also be dynamically linked. -config USB_R8A66597 - tristate - depends on USB_GADGET_R8A66597 - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_RENESAS_USBHS - boolean "Renesas USBHS" +config USB_RENESAS_USBHS_UDC + tristate 'Renesas USBHS controller' + depends on SUPERH || ARCH_SHMOBILE 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. - platform is able to configure endpoint (pipe) style + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. - Say "y" to enable the gadget specific portion of the USBHS driver. - - -config USB_RENESAS_USBHS_UDC - tristate - depends on USB_GADGET_RENESAS_USBHS - default USB_GADGET - select USB_GADGET_SELECTED + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. -config USB_GADGET_PXA27X - boolean "PXA 27x" +config USB_PXA27X + tristate "PXA 27x" depends on ARCH_PXA && (PXA27x || PXA3xx) select USB_OTG_UTILS help @@ -293,14 +245,8 @@ config USB_GADGET_PXA27X dynamically linked module called "pxa27x_udc" and force all gadget drivers to also be dynamically linked. -config USB_PXA27X - tristate - depends on USB_GADGET_PXA27X - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_S3C_HSOTG - boolean "S3C HS/OtG USB Device controller" +config USB_S3C_HSOTG + tristate "S3C HS/OtG USB Device controller" depends on S3C_DEV_USB_HSOTG select USB_GADGET_S3C_HSOTG_PIO select USB_GADGET_DUALSPEED @@ -308,14 +254,8 @@ config USB_GADGET_S3C_HSOTG The Samsung S3C64XX USB2.0 high-speed gadget controller integrated into the S3C64XX series SoC. -config USB_S3C_HSOTG - tristate - depends on USB_GADGET_S3C_HSOTG - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_IMX - boolean "Freescale IMX USB Peripheral Controller" +config USB_IMX + tristate "Freescale IMX USB Peripheral Controller" depends on ARCH_MX1 help Freescale's IMX series include an integrated full speed @@ -329,14 +269,8 @@ config USB_GADGET_IMX dynamically linked module called "imx_udc" and force all gadget drivers to also be dynamically linked. -config USB_IMX - tristate - depends on USB_GADGET_IMX - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_S3C2410 - boolean "S3C2410 USB Device Controller" +config USB_S3C2410 + tristate "S3C2410 USB Device Controller" depends on ARCH_S3C2410 help Samsung's S3C2410 is an ARM-4 processor with an integrated @@ -346,18 +280,12 @@ config USB_GADGET_S3C2410 This driver has been tested on the S3C2410, S3C2412, and S3C2440 processors. -config USB_S3C2410 - tristate - depends on USB_GADGET_S3C2410 - default USB_GADGET - select USB_GADGET_SELECTED - config USB_S3C2410_DEBUG boolean "S3C2410 udc debug messages" - depends on USB_GADGET_S3C2410 + depends on USB_S3C2410 -config USB_GADGET_S3C_HSUDC - boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller" +config USB_S3C_HSUDC + tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" depends on ARCH_S3C2410 select USB_GADGET_DUALSPEED help @@ -367,41 +295,29 @@ config USB_GADGET_S3C_HSUDC This driver has been tested on S3C2416 and S3C2450 processors. -config USB_S3C_HSUDC - tristate - depends on USB_GADGET_S3C_HSUDC - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_PXA_U2O - boolean "PXA9xx Processor USB2.0 controller" +config USB_PXA_U2O + tristate "PXA9xx Processor USB2.0 controller" + depends on ARCH_MMP select USB_GADGET_DUALSPEED help PXA9xx Processor series include a high speed USB2.0 device controller, which support high speed and full speed USB peripheral. -config USB_PXA_U2O - tristate - depends on USB_GADGET_PXA_U2O - default USB_GADGET - select USB_GADGET_SELECTED - # # Controllers available in both integrated and discrete versions # # musb builds in ../musb along with host support config USB_GADGET_MUSB_HDRC - boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)" + tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)" depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) select USB_GADGET_DUALSPEED - select USB_GADGET_SELECTED 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_GADGET_M66592 - boolean "Renesas M66592 USB Peripheral Controller" +config USB_M66592 + tristate "Renesas M66592 USB Peripheral Controller" select USB_GADGET_DUALSPEED help M66592 is a discrete USB peripheral controller chip that @@ -412,18 +328,12 @@ config USB_GADGET_M66592 dynamically linked module called "m66592_udc" and force all gadget drivers to also be dynamically linked. -config USB_M66592 - tristate - depends on USB_GADGET_M66592 - default USB_GADGET - select USB_GADGET_SELECTED - # # Controllers available only in discrete form (and all PCI controllers) # -config USB_GADGET_AMD5536UDC - boolean "AMD5536 UDC" +config USB_AMD5536UDC + tristate "AMD5536 UDC" depends on PCI select USB_GADGET_DUALSPEED help @@ -437,14 +347,8 @@ config USB_GADGET_AMD5536UDC dynamically linked module called "amd5536udc" and force all gadget drivers to also be dynamically linked. -config USB_AMD5536UDC - tristate - depends on USB_GADGET_AMD5536UDC - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_FSL_QE - boolean "Freescale QE/CPM USB Device Controller" +config USB_FSL_QE + tristate "Freescale QE/CPM USB Device Controller" depends on FSL_SOC && (QUICC_ENGINE || CPM) help Some of Freescale PowerPC processors have a Full Speed @@ -456,14 +360,8 @@ config USB_GADGET_FSL_QE Set CONFIG_USB_GADGET to "m" to build this driver as a dynamically linked module called "fsl_qe_udc". -config USB_FSL_QE - tristate - depends on USB_GADGET_FSL_QE - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_CI13XXX_PCI - boolean "MIPS USB CI13xxx PCI UDC" +config USB_CI13XXX_PCI + tristate "MIPS USB CI13xxx PCI UDC" depends on PCI select USB_GADGET_DUALSPEED help @@ -474,14 +372,31 @@ config USB_GADGET_CI13XXX_PCI dynamically linked module called "ci13xxx_udc" and force all gadget drivers to also be dynamically linked. -config USB_CI13XXX_PCI - tristate - depends on USB_GADGET_CI13XXX_PCI - default USB_GADGET - select USB_GADGET_SELECTED +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. + + It has three configurable endpoints, as well as endpoint zero + (for control transfer). + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2272" and force all + gadget drivers to also be dynamically linked. + +config USB_NET2272_DMA + boolean "Support external DMA controller" + depends on USB_NET2272 + help + The NET2272 part can optionally support an external DMA + controller, but your board has to have support in the + driver itself. -config USB_GADGET_NET2280 - boolean "NetChip 228x" + If unsure, say "N" here. The driver works fine in PIO mode. + +config USB_NET2280 + tristate "NetChip 228x" depends on PCI select USB_GADGET_DUALSPEED help @@ -496,14 +411,8 @@ config USB_GADGET_NET2280 dynamically linked module called "net2280" and force all gadget drivers to also be dynamically linked. -config USB_NET2280 - tristate - depends on USB_GADGET_NET2280 - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_GOKU - boolean "Toshiba TC86C001 'Goku-S'" +config USB_GOKU + tristate "Toshiba TC86C001 'Goku-S'" depends on PCI help The Toshiba TC86C001 is a PCI device which includes controllers @@ -516,15 +425,10 @@ config USB_GADGET_GOKU dynamically linked module called "goku_udc" and to force all gadget drivers to also be dynamically linked. -config USB_GOKU - tristate - depends on USB_GADGET_GOKU - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_LANGWELL - boolean "Intel Langwell USB Device Controller" +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 @@ -537,14 +441,8 @@ config USB_GADGET_LANGWELL dynamically linked module called "langwell_udc" and force all gadget drivers to also be dynamically linked. -config USB_LANGWELL - tristate - depends on USB_GADGET_LANGWELL - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_EG20T - boolean "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH UDC" +config USB_EG20T + tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH UDC" depends on PCI select USB_GADGET_DUALSPEED help @@ -565,14 +463,8 @@ config USB_GADGET_EG20T ML7213 is companion chip for Intel Atom E6xx series. ML7213 is completely compatible for Intel EG20T PCH. -config USB_EG20T - tristate - depends on USB_GADGET_EG20T - default USB_GADGET - select USB_GADGET_SELECTED - -config USB_GADGET_CI13XXX_MSM - boolean "MIPS USB CI13xxx for MSM" +config USB_CI13XXX_MSM + tristate "MIPS USB CI13xxx for MSM" depends on ARCH_MSM select USB_GADGET_DUALSPEED select USB_MSM_OTG @@ -588,31 +480,26 @@ config USB_GADGET_CI13XXX_MSM dynamically linked module called "ci13xxx_msm" and force all gadget drivers to also be dynamically linked. -config USB_CI13XXX_MSM - tristate - depends on USB_GADGET_CI13XXX_MSM - default USB_GADGET - select USB_GADGET_SELECTED - # # LAST -- dummy/emulated controller # -config USB_GADGET_DUMMY_HCD - boolean "Dummy HCD (DEVELOPMENT)" +config USB_DUMMY_HCD + tristate "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) select USB_GADGET_DUALSPEED + 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 side is the master; the gadget side is the slave. Gadget drivers can be high, full, or low speed; and they have access to endpoints like those from NET2280, PXA2xx, or SA1100 hardware. - + This may help in some stages of creating a driver to embed in a Linux device, since it lets you debug several parts of the gadget driver without its hardware or drivers being involved. - + Since such a gadget side driver needs to interoperate with a host side Linux-USB device driver, this may help to debug both sides of a USB protocol stack. @@ -621,12 +508,6 @@ config USB_GADGET_DUMMY_HCD dynamically linked module called "dummy_hcd" and force all gadget drivers to also be dynamically linked. -config USB_DUMMY_HCD - tristate - depends on USB_GADGET_DUMMY_HCD - default USB_GADGET - select USB_GADGET_SELECTED - # NOTE: Please keep dummy_hcd LAST so that "real hardware" appears # first and will be selected by default. @@ -637,12 +518,18 @@ config USB_GADGET_DUALSPEED bool depends on USB_GADGET +# Selected by UDC drivers that support super-speed opperation +config USB_GADGET_SUPERSPEED + bool + depends on USB_GADGET + depends on USB_GADGET_DUALSPEED + # # USB Gadget Drivers # choice tristate "USB Gadget Drivers" - depends on USB_GADGET && USB_GADGET_SELECTED + depends on USB_GADGET default USB_ETH help A Linux "Gadget Driver" talks to the USB Peripheral Controller @@ -848,7 +735,7 @@ config USB_FUNCTIONFS_GENERIC no Ethernet interface. config USB_FILE_STORAGE - tristate "File-backed Storage Gadget" + tristate "File-backed Storage Gadget (DEPRECATED)" depends on BLOCK help The File-backed Storage Gadget acts as a USB Mass Storage @@ -859,6 +746,9 @@ config USB_FILE_STORAGE 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 @@ -878,14 +768,11 @@ config USB_MASS_STORAGE device (in much the same way as the "loop" device driver), specified as a module parameter or sysfs option. - This is heavily based on File-backed Storage Gadget and in most - cases you will want to use FSG instead. This gadget is mostly - here to test the functionality of the Mass Storage Function - which may be used with composite framework. + This driver is an updated replacement for the deprecated + 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". If unsure, - consider File-backed Storage Gadget. + a dynamically linked module called "g_mass_storage". config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4fe92b18a055..9ba725af4a08 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -3,7 +3,9 @@ # ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +obj-$(CONFIG_USB_GADGET) += udc-core.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 diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 95e8138cd48f..70f2b376c86d 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1438,10 +1438,15 @@ 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); /* gadget operations */ static const struct usb_gadget_ops udc_ops = { .wakeup = udc_wakeup, .get_frame = udc_get_frame, + .start = amd5536_start, + .stop = amd5536_stop, }; /* Setups endpoint parameters, adds endpoints to linked list */ @@ -1955,7 +1960,7 @@ static int setup_ep0(struct udc *dev) } /* Called by gadget driver to register itself */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int amd5536_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct udc *dev = udc; @@ -2002,7 +2007,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); /* shutdown requests and disconnect from gadget */ static void @@ -2027,7 +2031,7 @@ __acquires(dev->lock) } /* Called by gadget driver to unregister itself */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int amd5536_stop(struct usb_gadget_driver *driver) { struct udc *dev = udc; unsigned long flags; @@ -2057,8 +2061,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /* Clear pending NAK bits */ static void udc_process_cnak_queue(struct udc *dev) @@ -3134,6 +3136,7 @@ static void udc_pci_remove(struct pci_dev *pdev) dev = pci_get_drvdata(pdev); + usb_del_gadget_udc(&udc->gadget); /* gadget driver must not be registered */ BUG_ON(dev->driver != NULL); @@ -3382,8 +3385,13 @@ 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); + 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; } diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index f4690ffcb489..98cbc06c30fd 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -985,12 +985,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 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, /* * VBUS-powered devices may also also want to support bigger @@ -1628,7 +1634,7 @@ static void at91_vbus_timer(unsigned long data) schedule_work(&udc->vbus_timer_work); } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int at91_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct at91_udc *udc = &controller; @@ -1672,9 +1678,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, DBG("bound to %s\n", driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int at91_stop(struct usb_gadget_driver *driver) { struct at91_udc *udc = &controller; unsigned long flags; @@ -1696,7 +1701,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) DBG("unbound from %s\n", driver->driver.name); return 0; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ @@ -1854,13 +1858,18 @@ static int __init at91udc_probe(struct platform_device *pdev) DBG("no VBUS detection, assuming always-on\n"); udc->vbus = 1; } + retval = usb_add_gadget_udc(dev, &udc->gadget); + if (retval) + goto fail4; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0; - +fail4: + if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled) + free_irq(udc->board.vbus_pin, udc); fail3: if (udc->board.vbus_pin > 0) gpio_free(udc->board.vbus_pin); @@ -1887,6 +1896,7 @@ static int __exit at91udc_remove(struct platform_device *pdev) DBG("remove\n"); + usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index f045c8968a6e..5b1665eb1bef 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1007,10 +1007,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 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, }; static struct usb_endpoint_descriptor usba_ep0_desc = { @@ -1789,7 +1795,7 @@ out: return IRQ_HANDLED; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int atmel_usba_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct usba_udc *udc = &the_udc; @@ -1842,9 +1848,8 @@ err_driver_bind: udc->gadget.dev.driver = NULL; return ret; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int atmel_usba_stop(struct usb_gadget_driver *driver) { struct usba_udc *udc = &the_udc; unsigned long flags; @@ -1880,7 +1885,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); static int __init usba_udc_probe(struct platform_device *pdev) { @@ -2021,12 +2025,24 @@ 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; + usba_init_debugfs(udc); for (i = 1; i < pdata->num_ep; i++) usba_ep_init_debugfs(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: @@ -2053,6 +2069,8 @@ static int __exit usba_udc_remove(struct platform_device *pdev) 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]); usba_cleanup_debugfs(udc); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 93b999e49ef3..9d89ae4765a9 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -165,6 +165,7 @@ static struct usb_composite_driver audio_driver = { .name = "g_audio", .dev = &device_desc, .strings = audio_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(audio_unbind), }; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 2720ab07ef1a..b1c1afbb8750 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -244,6 +244,7 @@ static struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(cdc_unbind), }; diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c index 139ac9419597..470981ad6f77 100644 --- a/drivers/usb/gadget/ci13xxx_msm.c +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -126,6 +126,7 @@ 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) { diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index baaf87ed7685..1265a8502ea0 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -857,7 +857,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) 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", + "%04X\t? %02X %-7.7s %4i ?\t%s\n", stamp, addr, name, status, extra); dbg_inc(&dbg_data.idx); @@ -865,7 +865,7 @@ static void dbg_print(u8 addr, const char *name, int status, const char *extra) write_unlock_irqrestore(&dbg_data.lck, flags); if (dbg_data.tty != 0) - pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", + pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n", stamp, addr, name, status, extra); } @@ -1025,15 +1025,15 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); - n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n", isr_statistics.ui); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n", isr_statistics.uei); - n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n", isr_statistics.pci); - n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", + n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n", isr_statistics.uri); - n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", + 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); @@ -1214,12 +1214,13 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); * * 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[512]; + u32 *dump; unsigned i, k, n = 0; dbg_trace("[%s] %p\n", __func__, buf); @@ -1228,8 +1229,14 @@ static ssize_t show_registers(struct device *dev, 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, sizeof(dump)/sizeof(u32)); + k = hw_register_read(dump, DUMP_ENTRIES); spin_unlock_irqrestore(udc->lock, flags); for (i = 0; i < k; i++) { @@ -1237,6 +1244,7 @@ static ssize_t show_registers(struct device *dev, "reg[0x%04X] = 0x%08X\n", i * (unsigned)sizeof(u32), dump[i]); } + kfree(dump); return n; } @@ -2515,6 +2523,9 @@ static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned 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) @@ -2524,17 +2535,19 @@ 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, }; /** - * usb_gadget_probe_driver: register a gadget driver + * ci13xxx_start: register a gadget driver * @driver: the driver being registered * @bind: the driver's bind callback * - * Check usb_gadget_probe_driver() at <linux/usb/gadget.h> for details. + * Check ci13xxx_start() at <linux/usb/gadget.h> for details. * Interrupts are enabled here. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int ci13xxx_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct ci13xxx *udc = _udc; @@ -2615,10 +2628,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, if (retval) goto done; spin_unlock_irqrestore(udc->lock, flags); - retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); + udc->ep0out.ep.desc = &ctrl_endpt_out_desc; + retval = usb_ep_enable(&udc->ep0out.ep); if (retval) return retval; - retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); + + 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); @@ -2657,14 +2673,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, spin_unlock_irqrestore(udc->lock, flags); return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); /** - * usb_gadget_unregister_driver: unregister a gadget driver + * ci13xxx_stop: unregister a gadget driver * * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int ci13xxx_stop(struct usb_gadget_driver *driver) { struct ci13xxx *udc = _udc; unsigned long i, flags; @@ -2726,7 +2741,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /****************************************************************************** * BUS block @@ -2901,12 +2915,23 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, 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 @@ -2936,6 +2961,7 @@ static void udc_remove(void) err("EINVAL"); return; } + usb_del_gadget_udc(&udc->gadget); if (udc->transceiver) { otg_set_peripheral(udc->transceiver, &udc->gadget); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 5cbb1a41c223..5ef87794fd32 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -27,7 +27,7 @@ #include <linux/utsname.h> #include <linux/usb/composite.h> - +#include <asm/unaligned.h> /* * The code in this file is utility code, used to build a gadget driver @@ -74,6 +74,130 @@ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); static char composite_manufacturer[50]; /*-------------------------------------------------------------------------*/ +/** + * next_ep_desc() - advance to the next EP descriptor + * @t: currect pointer within descriptor array + * + * Return: next EP descriptor or NULL + * + * Iterate over @t until either EP descriptor found or + * NULL (that indicates end of list) encountered + */ +static struct usb_descriptor_header** +next_ep_desc(struct usb_descriptor_header **t) +{ + for (; *t; t++) { + if ((*t)->bDescriptorType == USB_DT_ENDPOINT) + return t; + } + return NULL; +} + +/* + * for_each_ep_desc()- iterate over endpoint descriptors in the + * descriptors list + * @start: pointer within descriptor array. + * @ep_desc: endpoint descriptor to use as the loop cursor + */ +#define for_each_ep_desc(start, ep_desc) \ + for (ep_desc = next_ep_desc(start); \ + ep_desc; ep_desc = next_ep_desc(ep_desc+1)) + +/** + * config_ep_by_speed() - configures the given endpoint + * according to gadget speed. + * @g: pointer to the gadget + * @f: usb function + * @_ep: the endpoint to configure + * + * Return: error code, 0 on success + * + * This function chooses the right descriptors for a given + * endpoint according to gadget speed and saves it in the + * endpoint desc field. If the endpoint already has a descriptor + * assigned to it - overwrites it with currently corresponding + * descriptor. The endpoint maxpacket field is updated according + * to the chosen descriptor. + * Note: the supplied function should hold all the descriptors + * for supported speeds + */ +int config_ep_by_speed(struct usb_gadget *g, + struct usb_function *f, + struct usb_ep *_ep) +{ + struct usb_endpoint_descriptor *chosen_desc = NULL; + struct usb_descriptor_header **speed_desc = NULL; + + struct usb_ss_ep_comp_descriptor *comp_desc = NULL; + int want_comp_desc = 0; + + struct usb_descriptor_header **d_spd; /* cursor for speed desc */ + + if (!g || !f || !_ep) + return -EIO; + + /* select desired speed */ + switch (g->speed) { + case USB_SPEED_SUPER: + if (gadget_is_superspeed(g)) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; + } + /* else: Fall trough */ + case USB_SPEED_HIGH: + if (gadget_is_dualspeed(g)) { + speed_desc = f->hs_descriptors; + break; + } + /* else: fall through */ + default: + speed_desc = f->descriptors; + } + /* find descriptors */ + for_each_ep_desc(speed_desc, d_spd) { + chosen_desc = (struct usb_endpoint_descriptor *)*d_spd; + if (chosen_desc->bEndpointAddress == _ep->address) + goto ep_found; + } + return -EIO; + +ep_found: + /* commit results */ + _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); + _ep->desc = chosen_desc; + _ep->comp_desc = NULL; + _ep->maxburst = 0; + _ep->mult = 0; + if (!want_comp_desc) + return 0; + + /* + * Companion descriptor should follow EP descriptor + * USB 3.0 spec, #9.6.7 + */ + comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd); + if (!comp_desc || + (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP)) + return -EIO; + _ep->comp_desc = comp_desc; + if (g->speed == USB_SPEED_SUPER) { + switch (usb_endpoint_type(_ep->desc)) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + _ep->maxburst = comp_desc->bMaxBurst; + break; + case USB_ENDPOINT_XFER_ISOC: + /* mult: bits 1:0 of bmAttributes */ + _ep->mult = comp_desc->bmAttributes & 0x3; + break; + default: + /* Do nothing for control endpoints */ + break; + } + } + return 0; +} /** * usb_add_function() - add a function to a configuration @@ -123,6 +247,8 @@ int usb_add_function(struct usb_configuration *config, config->fullspeed = true; if (!config->highspeed && function->hs_descriptors) config->highspeed = true; + if (!config->superspeed && function->ss_descriptors) + config->superspeed = true; done: if (value) @@ -266,10 +392,17 @@ static int config_buf(struct usb_configuration *config, list_for_each_entry(f, &config->functions, list) { struct usb_descriptor_header **descriptors; - if (speed == USB_SPEED_HIGH) + switch (speed) { + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + break; + case USB_SPEED_HIGH: descriptors = f->hs_descriptors; - else + break; + default: descriptors = f->descriptors; + } + if (!descriptors) continue; status = usb_descriptor_fillbuf(next, len, @@ -292,9 +425,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) u8 type = w_value >> 8; enum usb_device_speed speed = USB_SPEED_UNKNOWN; - if (gadget_is_dualspeed(gadget)) { - int hs = 0; - + if (gadget->speed == USB_SPEED_SUPER) + speed = gadget->speed; + else if (gadget_is_dualspeed(gadget)) { + int hs = 0; if (gadget->speed == USB_SPEED_HIGH) hs = 1; if (type == USB_DT_OTHER_SPEED_CONFIG) @@ -308,13 +442,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) w_value &= 0xff; list_for_each_entry(c, &cdev->configs, list) { /* ignore configs that won't work at this speed */ - if (speed == USB_SPEED_HIGH) { + switch (speed) { + case USB_SPEED_SUPER: + if (!c->superspeed) + continue; + break; + case USB_SPEED_HIGH: if (!c->highspeed) continue; - } else { + break; + default: if (!c->fullspeed) continue; } + if (w_value == 0) return config_buf(c, speed, cdev->req->buf, type); w_value--; @@ -328,16 +469,22 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) struct usb_configuration *c; unsigned count = 0; int hs = 0; + int ss = 0; if (gadget_is_dualspeed(gadget)) { if (gadget->speed == USB_SPEED_HIGH) hs = 1; + if (gadget->speed == USB_SPEED_SUPER) + ss = 1; if (type == USB_DT_DEVICE_QUALIFIER) hs = !hs; } list_for_each_entry(c, &cdev->configs, list) { /* ignore configs that won't work at this speed */ - if (hs) { + if (ss) { + if (!c->superspeed) + continue; + } else if (hs) { if (!c->highspeed) continue; } else { @@ -349,6 +496,71 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) return count; } +/** + * bos_desc() - prepares the BOS descriptor. + * @cdev: pointer to usb_composite device to generate the bos + * descriptor for + * + * This function generates the BOS (Binary Device Object) + * descriptor and its device capabilities descriptors. The BOS + * descriptor should be supported by a SuperSpeed device. + */ +static int bos_desc(struct usb_composite_dev *cdev) +{ + struct usb_ext_cap_descriptor *usb_ext; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_dcd_config_params dcd_config_params; + struct usb_bos_descriptor *bos = cdev->req->buf; + + bos->bLength = USB_DT_BOS_SIZE; + bos->bDescriptorType = USB_DT_BOS; + + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); + bos->bNumDeviceCaps = 0; + + /* + * A SuperSpeed device shall include the USB2.0 extension descriptor + * and shall support LPM when operating in USB2.0 HS mode. + */ + usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); + usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; + usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT); + + /* + * The Superspeed USB Capability descriptor shall be implemented by all + * SuperSpeed devices. + */ + ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); + bos->bNumDeviceCaps++; + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* LTM is not supported yet */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | + USB_FULL_SPEED_OPERATION | + USB_HIGH_SPEED_OPERATION | + USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) + cdev->gadget->ops->get_config_params(&dcd_config_params); + else { + dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT); + } + ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; + ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; + + return le16_to_cpu(bos->wTotalLength); +} + static void device_qual(struct usb_composite_dev *cdev) { struct usb_qualifier_descriptor *qual = cdev->req->buf; @@ -361,7 +573,7 @@ static void device_qual(struct usb_composite_dev *cdev) qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; /* ASSUME same EP0 fifo size at both speeds */ - qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket; qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); qual->bRESERVED = 0; } @@ -392,28 +604,46 @@ static int set_config(struct usb_composite_dev *cdev, unsigned power = gadget_is_otg(gadget) ? 8 : 100; int tmp; - if (cdev->config) - reset_config(cdev); - if (number) { list_for_each_entry(c, &cdev->configs, list) { if (c->bConfigurationValue == number) { + /* + * We disable the FDs of the previous + * configuration only if the new configuration + * is a valid one + */ + if (cdev->config) + reset_config(cdev); result = 0; break; } } if (result < 0) goto done; - } else + } else { /* Zero configuration value - need to reset the config */ + if (cdev->config) + reset_config(cdev); result = 0; + } INFO(cdev, "%s speed config #%d: %s\n", ({ char *speed; switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + case USB_SPEED_SUPER: + speed = "super"; + break; + default: + speed = "?"; + break; } ; speed; }), number, c ? c->label : "unconfigured"); if (!c) @@ -435,10 +665,16 @@ static int set_config(struct usb_composite_dev *cdev, * function's setup callback instead of the current * configuration's setup callback. */ - if (gadget->speed == USB_SPEED_HIGH) + switch (gadget->speed) { + case USB_SPEED_SUPER: + descriptors = f->ss_descriptors; + break; + case USB_SPEED_HIGH: descriptors = f->hs_descriptors; - else + break; + default: descriptors = f->descriptors; + } for (; *descriptors; ++descriptors) { struct usb_endpoint_descriptor *ep; @@ -531,8 +767,9 @@ int usb_add_config(struct usb_composite_dev *cdev, } else { unsigned i; - DBG(cdev, "cfg %d/%p speeds:%s%s\n", + DBG(cdev, "cfg %d/%p speeds:%s%s%s\n", config->bConfigurationValue, config, + config->superspeed ? " super" : "", config->highspeed ? " high" : "", config->fullspeed ? (gadget_is_dualspeed(cdev->gadget) @@ -811,6 +1048,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; + int status = 0; u16 w_index = le16_to_cpu(ctrl->wIndex); u8 intf = w_index & 0xFF; u16 w_value = le16_to_cpu(ctrl->wValue); @@ -838,18 +1076,29 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE: cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE); + cdev->desc.bMaxPacketSize0 = + cdev->gadget->ep0->maxpacket; + if (gadget_is_superspeed(gadget)) { + if (gadget->speed >= USB_SPEED_SUPER) + cdev->desc.bcdUSB = cpu_to_le16(0x0300); + else + cdev->desc.bcdUSB = cpu_to_le16(0x0210); + } + value = min(w_length, (u16) sizeof cdev->desc); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) + if (!gadget_is_dualspeed(gadget) || + gadget->speed >= USB_SPEED_SUPER) break; device_qual(cdev); value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor)); break; case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) + if (!gadget_is_dualspeed(gadget) || + gadget->speed >= USB_SPEED_SUPER) break; /* FALLTHROUGH */ case USB_DT_CONFIG: @@ -863,6 +1112,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value >= 0) value = min(w_length, (u16) value); break; + case USB_DT_BOS: + if (gadget_is_superspeed(gadget)) { + value = bos_desc(cdev); + value = min(w_length, (u16) value); + } + break; } break; @@ -930,6 +1185,61 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); break; + + /* + * USB 3.0 additions: + * Function driver should handle get_status request. If such cb + * wasn't supplied we respond with default value = 0 + * Note: function driver should supply such cb only for the first + * interface of the function + */ + case USB_REQ_GET_STATUS: + if (!gadget_is_superspeed(gadget)) + goto unknown; + if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) + goto unknown; + value = 2; /* This is the length of the get_status reply */ + put_unaligned_le16(0, req->buf); + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + status = f->get_status ? f->get_status(f) : 0; + if (status < 0) + break; + put_unaligned_le16(status & 0x0000ffff, req->buf); + break; + /* + * Function drivers should handle SetFeature/ClearFeature + * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied + * only for the first interface of the function + */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (!gadget_is_superspeed(gadget)) + goto unknown; + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) + goto unknown; + switch (w_value) { + case USB_INTRF_FUNC_SUSPEND: + if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + value = 0; + if (f->func_suspend) + value = f->func_suspend(f, w_index >> 8); + if (value < 0) { + ERROR(cdev, + "func_suspend() returned error %d\n", + value); + value = 0; + } + break; + } + break; default: unknown: VDBG(cdev, @@ -1140,7 +1450,6 @@ static int composite_bind(struct usb_gadget *gadget) goto fail; cdev->desc = *composite->dev; - cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; /* standardized runtime overrides for device ID data */ if (idVendor) @@ -1247,7 +1556,11 @@ composite_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver composite_driver = { +#ifdef CONFIG_USB_GADGET_SUPERSPEED + .speed = USB_SPEED_SUPER, +#else .speed = USB_SPEED_HIGH, +#endif .unbind = composite_unbind, @@ -1293,6 +1606,8 @@ int usb_composite_probe(struct usb_composite_driver *driver, driver->iProduct = driver->name; composite_driver.function = (char *) driver->name; composite_driver.driver.name = driver->name; + composite_driver.speed = min((u8)composite_driver.speed, + (u8)driver->max_speed); composite = driver; composite_gadget_bind = bind; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 09084fd646ab..b2c001334876 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -165,28 +165,3 @@ usb_copy_descriptors(struct usb_descriptor_header **src) return ret; } -/** - * usb_find_endpoint - find a copy of an endpoint descriptor - * @src: original vector of descriptors - * @copy: copy of @src - * @match: endpoint descriptor found in @src - * - * This returns the copy of the @match descriptor made for @copy. Its - * intended use is to help remembering the endpoint descriptor to use - * when enabling a given endpoint. - */ -struct usb_endpoint_descriptor * -usb_find_endpoint( - struct usb_descriptor_header **src, - struct usb_descriptor_header **copy, - struct usb_endpoint_descriptor *match -) -{ - while (*src) { - if (*src == (void *) match) - return (void *)*copy; - src++; - copy++; - } - return NULL; -} diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index dbe92ee88477..8beefdd36787 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -173,7 +173,9 @@ fail_1: static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc) { - int err = usb_ep_enable(ep, desc); + int err; + ep->desc = desc; + err = usb_ep_enable(ep); ep->driver_data = dbgp.gadget; return err; } @@ -268,8 +270,8 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) dbgp.serial->in = dbgp.i_ep; dbgp.serial->out = dbgp.o_ep; - dbgp.serial->in_desc = &i_desc; - dbgp.serial->out_desc = &o_desc; + dbgp.serial->in->desc = &i_desc; + dbgp.serial->out->desc = &o_desc; if (gserial_setup(gadget, 1) < 0) { stp = 3; @@ -312,7 +314,6 @@ static int __init dbgp_bind(struct usb_gadget *gadget) dbgp.req->length = DBGP_REQ_EP0_LEN; gadget->ep0->driver_data = gadget; - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); @@ -363,6 +364,7 @@ static int dbgp_setup(struct usb_gadget *gadget, dev_dbg(&dbgp.gadget->dev, "setup: desc device\n"); len = sizeof device_desc; data = &device_desc; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; break; case USB_DT_DEBUG: dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n"); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index d3dcabc1a5fc..e755a9d267fc 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -70,6 +70,19 @@ MODULE_DESCRIPTION (DRIVER_DESC); MODULE_AUTHOR ("David Brownell"); MODULE_LICENSE ("GPL"); +struct dummy_hcd_module_parameters { + bool is_super_speed; + bool is_high_speed; +}; + +static struct dummy_hcd_module_parameters mod_data = { + .is_super_speed = false, + .is_high_speed = true, +}; +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"); /*-------------------------------------------------------------------------*/ /* gadget side driver data structres */ @@ -152,6 +165,22 @@ enum dummy_rh_state { DUMMY_RH_RUNNING }; +struct dummy_hcd { + struct dummy *dum; + enum dummy_rh_state rh_state; + struct timer_list timer; + u32 port_status; + u32 old_status; + unsigned long re_timeout; + + struct usb_device *udev; + struct list_head urbp_list; + + unsigned active:1; + unsigned old_active:1; + unsigned resuming:1; +}; + struct dummy { spinlock_t lock; @@ -167,36 +196,27 @@ struct dummy { u16 devstatus; unsigned udc_suspended:1; unsigned pullup:1; - unsigned active:1; - unsigned old_active:1; /* * MASTER/HOST side support */ - enum dummy_rh_state rh_state; - struct timer_list timer; - u32 port_status; - u32 old_status; - unsigned resuming:1; - unsigned long re_timeout; - - struct usb_device *udev; - struct list_head urbp_list; + struct dummy_hcd *hs_hcd; + struct dummy_hcd *ss_hcd; }; -static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd) +static inline struct dummy_hcd *hcd_to_dummy_hcd(struct usb_hcd *hcd) { - return (struct dummy *) (hcd->hcd_priv); + return (struct dummy_hcd *) (hcd->hcd_priv); } -static inline struct usb_hcd *dummy_to_hcd (struct dummy *dum) +static inline struct usb_hcd *dummy_hcd_to_hcd(struct dummy_hcd *dum) { return container_of((void *) dum, struct usb_hcd, hcd_priv); } -static inline struct device *dummy_dev (struct dummy *dum) +static inline struct device *dummy_dev(struct dummy_hcd *dum) { - return dummy_to_hcd(dum)->self.controller; + return dummy_hcd_to_hcd(dum)->self.controller; } static inline struct device *udc_dev (struct dummy *dum) @@ -209,9 +229,13 @@ static inline struct dummy *ep_to_dummy (struct dummy_ep *ep) return container_of (ep->gadget, struct dummy, gadget); } -static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget) +static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget) { - return container_of (gadget, struct dummy, gadget); + struct dummy *dum = container_of(gadget, struct dummy, gadget); + if (dum->gadget.speed == USB_SPEED_SUPER) + return dum->ss_hcd; + else + return dum->hs_hcd; } static inline struct dummy *gadget_dev_to_dummy (struct device *dev) @@ -219,7 +243,7 @@ static inline struct dummy *gadget_dev_to_dummy (struct device *dev) return container_of (dev, struct dummy, gadget.dev); } -static struct dummy *the_controller; +static struct dummy the_controller; /*-------------------------------------------------------------------------*/ @@ -259,61 +283,122 @@ stop_activity (struct dummy *dum) /* driver now does any non-usb quiescing necessary */ } -/* caller must hold lock */ -static void -set_link_state (struct dummy *dum) -{ - dum->active = 0; - if ((dum->port_status & USB_PORT_STAT_POWER) == 0) - dum->port_status = 0; - - /* UDC suspend must cause a disconnect */ - else if (!dum->pullup || dum->udc_suspended) { - dum->port_status &= ~(USB_PORT_STAT_CONNECTION | - USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | - USB_PORT_STAT_HIGH_SPEED | - USB_PORT_STAT_SUSPEND); - if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0) - dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); +/** + * set_link_state_by_speed() - Sets the current state of the link according to + * the hcd speed + * @dum_hcd: pointer to the dummy_hcd structure to update the link state for + * + * This function updates the port_status according to the link state and the + * speed of the hcd. + */ +static void set_link_state_by_speed(struct dummy_hcd *dum_hcd) +{ + struct dummy *dum = dum_hcd->dum; + + if (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3) { + if ((dum_hcd->port_status & USB_SS_PORT_STAT_POWER) == 0) { + dum_hcd->port_status = 0; + } else if (!dum->pullup || dum->udc_suspended) { + /* UDC suspend must cause a disconnect */ + dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE); + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) != 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + } else { + /* device is connected and not suspended */ + dum_hcd->port_status |= (USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_SPEED_5GBPS) ; + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) == 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + if ((dum_hcd->port_status & + USB_PORT_STAT_ENABLE) == 1 && + (dum_hcd->port_status & + USB_SS_PORT_LS_U0) == 1 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) + dum_hcd->active = 1; + } } else { - dum->port_status |= USB_PORT_STAT_CONNECTION; - if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0) - dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); - if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) - dum->port_status &= ~USB_PORT_STAT_SUSPEND; - else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && - dum->rh_state != DUMMY_RH_SUSPENDED) - dum->active = 1; + if ((dum_hcd->port_status & USB_PORT_STAT_POWER) == 0) { + dum_hcd->port_status = 0; + } else if (!dum->pullup || dum->udc_suspended) { + /* UDC suspend must cause a disconnect */ + dum_hcd->port_status &= ~(USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_SUSPEND); + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) != 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + } else { + dum_hcd->port_status |= USB_PORT_STAT_CONNECTION; + if ((dum_hcd->old_status & + USB_PORT_STAT_CONNECTION) == 0) + dum_hcd->port_status |= + (USB_PORT_STAT_C_CONNECTION << 16); + if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0) + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; + else if ((dum_hcd->port_status & + USB_PORT_STAT_SUSPEND) == 0 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) + dum_hcd->active = 1; + } } +} + +/* caller must hold lock */ +static void set_link_state(struct dummy_hcd *dum_hcd) +{ + struct dummy *dum = dum_hcd->dum; - if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active) - dum->resuming = 0; + dum_hcd->active = 0; + if (dum->pullup) + if ((dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 && + dum->gadget.speed != USB_SPEED_SUPER) || + (dummy_hcd_to_hcd(dum_hcd)->speed != HCD_USB3 && + dum->gadget.speed == USB_SPEED_SUPER)) + return; - if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 || - (dum->port_status & USB_PORT_STAT_RESET) != 0) { - if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 && - (dum->old_status & USB_PORT_STAT_RESET) == 0 && - dum->driver) { - stop_activity (dum); - spin_unlock (&dum->lock); - dum->driver->disconnect (&dum->gadget); - spin_lock (&dum->lock); + set_link_state_by_speed(dum_hcd); + + if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 || + dum_hcd->active) + dum_hcd->resuming = 0; + + /* if !connected or reset */ + if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 || + (dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) { + /* + * We're connected and not reset (reset occurred now), + * and driver attached - disconnect! + */ + if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 && + (dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 && + dum->driver) { + stop_activity(dum); + spin_unlock(&dum->lock); + dum->driver->disconnect(&dum->gadget); + spin_lock(&dum->lock); } - } else if (dum->active != dum->old_active) { - if (dum->old_active && dum->driver->suspend) { - spin_unlock (&dum->lock); - dum->driver->suspend (&dum->gadget); - spin_lock (&dum->lock); - } else if (!dum->old_active && dum->driver->resume) { - spin_unlock (&dum->lock); - dum->driver->resume (&dum->gadget); - spin_lock (&dum->lock); + } else if (dum_hcd->active != dum_hcd->old_active) { + if (dum_hcd->old_active && dum->driver->suspend) { + spin_unlock(&dum->lock); + dum->driver->suspend(&dum->gadget); + spin_lock(&dum->lock); + } else if (!dum_hcd->old_active && dum->driver->resume) { + spin_unlock(&dum->lock); + dum->driver->resume(&dum->gadget); + spin_lock(&dum->lock); } } - dum->old_status = dum->port_status; - dum->old_active = dum->active; + dum_hcd->old_status = dum_hcd->port_status; + dum_hcd->old_active = dum_hcd->active; } /*-------------------------------------------------------------------------*/ @@ -332,6 +417,7 @@ static int dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct dummy *dum; + struct dummy_hcd *dum_hcd; struct dummy_ep *ep; unsigned max; int retval; @@ -341,9 +427,19 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + if (!dum->driver) + return -ESHUTDOWN; + + dum_hcd = gadget_to_dummy_hcd(&dum->gadget); + if (!is_enabled(dum_hcd)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff; + + /* + * For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the + * maximum packet size. + * For SS devices the wMaxPacketSize is limited by 1024. + */ + max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; /* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints @@ -361,6 +457,10 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto done; } switch (dum->gadget.speed) { + case USB_SPEED_SUPER: + if (max == 1024) + break; + goto done; case USB_SPEED_HIGH: if (max == 512) break; @@ -379,6 +479,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; @@ -399,6 +500,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { + case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; @@ -425,10 +527,18 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", ({ char *val; switch (desc->bmAttributes & 0x03) { - case USB_ENDPOINT_XFER_BULK: val = "bulk"; break; - case USB_ENDPOINT_XFER_ISOC: val = "iso"; break; - case USB_ENDPOINT_XFER_INT: val = "intr"; break; - default: val = "ctrl"; break; + case USB_ENDPOINT_XFER_BULK: + val = "bulk"; + break; + case USB_ENDPOINT_XFER_ISOC: + val = "iso"; + break; + case USB_ENDPOINT_XFER_INT: + val = "intr"; + break; + default: + val = "ctrl"; + break; }; val; }), max); @@ -507,6 +617,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, struct dummy_ep *ep; struct dummy_request *req; struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; req = usb_request_to_dummy_request (_req); @@ -518,7 +629,8 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, return -EINVAL; dum = ep_to_dummy (ep); - if (!dum->driver || !is_enabled (dum)) + dum_hcd = gadget_to_dummy_hcd(&dum->gadget); + if (!dum->driver || !is_enabled(dum_hcd)) return -ESHUTDOWN; #if 0 @@ -662,24 +774,24 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget) static int dummy_wakeup (struct usb_gadget *_gadget) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; - dum = gadget_to_dummy (_gadget); - if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE) + dum_hcd = gadget_to_dummy_hcd(_gadget); + if (!(dum_hcd->dum->devstatus & ((1 << USB_DEVICE_B_HNP_ENABLE) | (1 << USB_DEVICE_REMOTE_WAKEUP)))) return -EINVAL; - if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0) + if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0) return -ENOLINK; - if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && - dum->rh_state != DUMMY_RH_SUSPENDED) + if ((dum_hcd->port_status & USB_PORT_STAT_SUSPEND) == 0 && + dum_hcd->rh_state != DUMMY_RH_SUSPENDED) return -EIO; /* FIXME: What if the root hub is suspended but the port isn't? */ /* hub notices our request, issues downstream resume, etc */ - dum->resuming = 1; - dum->re_timeout = jiffies + msecs_to_jiffies(20); - mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout); + dum_hcd->resuming = 1; + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(20); + mod_timer(&dummy_hcd_to_hcd(dum_hcd)->rh_timer, dum_hcd->re_timeout); return 0; } @@ -687,7 +799,7 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) { struct dummy *dum; - dum = gadget_to_dummy (_gadget); + dum = (gadget_to_dummy_hcd(_gadget))->dum; if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else @@ -695,26 +807,68 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) return 0; } +static void dummy_udc_udpate_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; + dum->ep[0].ep.maxpacket = 9; + } else { + for (i = 0; i < DUMMY_ENDPOINTS; i++) + dum->ep[i].ep.max_streams = 0; + dum->ep[0].ep.maxpacket = 64; + } +} + static int dummy_pullup (struct usb_gadget *_gadget, int value) { + struct dummy_hcd *dum_hcd; struct dummy *dum; unsigned long flags; - dum = gadget_to_dummy (_gadget); + dum = gadget_dev_to_dummy(&_gadget->dev); + + if (value && dum->driver) { + if (mod_data.is_super_speed) + dum->gadget.speed = dum->driver->speed; + else if (mod_data.is_high_speed) + dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, + dum->driver->speed); + else + dum->gadget.speed = USB_SPEED_FULL; + dummy_udc_udpate_ep0(dum); + + if (dum->gadget.speed < dum->driver->speed) + dev_dbg(udc_dev(dum), "This device can perform faster" + " if you connect it to a %s port...\n", + (dum->driver->speed == USB_SPEED_SUPER ? + "SuperSpeed" : "HighSpeed")); + } + dum_hcd = gadget_to_dummy_hcd(_gadget); + spin_lock_irqsave (&dum->lock, flags); dum->pullup = (value != 0); - set_link_state (dum); + set_link_state(dum_hcd); spin_unlock_irqrestore (&dum->lock, flags); - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } +static int dummy_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); +static int dummy_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); + static const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, .wakeup = dummy_wakeup, .set_selfpowered = dummy_set_selfpowered, .pullup = dummy_pullup, + .udc_start = dummy_udc_start, + .udc_stop = dummy_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -747,18 +901,13 @@ static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); * for each driver that registers: just add to a big root hub. */ -int -usb_gadget_probe_driver(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int dummy_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct dummy *dum = the_controller; - int retval, i; + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); + struct dummy *dum = dum_hcd->dum; - if (!dum) - return -EINVAL; - if (dum->driver) - return -EBUSY; - if (!bind || !driver->setup || driver->speed == USB_SPEED_UNKNOWN) + if (driver->speed == USB_SPEED_UNKNOWN) return -EINVAL; /* @@ -768,121 +917,77 @@ usb_gadget_probe_driver(struct usb_gadget_driver *driver, dum->devstatus = 0; - INIT_LIST_HEAD (&dum->gadget.ep_list); - for (i = 0; i < DUMMY_ENDPOINTS; i++) { - struct dummy_ep *ep = &dum->ep [i]; - - if (!ep_name [i]) - break; - ep->ep.name = ep_name [i]; - ep->ep.ops = &dummy_ep_ops; - list_add_tail (&ep->ep.ep_list, &dum->gadget.ep_list); - ep->halted = ep->wedged = ep->already_seen = - ep->setup_stage = 0; - ep->ep.maxpacket = ~0; - ep->last_io = jiffies; - ep->gadget = &dum->gadget; - ep->desc = NULL; - INIT_LIST_HEAD (&ep->queue); - } - - dum->gadget.ep0 = &dum->ep [0].ep; - dum->ep [0].ep.maxpacket = 64; - list_del_init (&dum->ep [0].ep.ep_list); - INIT_LIST_HEAD(&dum->fifo_req.queue); - - driver->driver.bus = NULL; dum->driver = driver; - dum->gadget.dev.driver = &driver->driver; dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); - retval = bind(&dum->gadget); - if (retval) { - dum->driver = NULL; - dum->gadget.dev.driver = NULL; - return retval; - } - - /* khubd will enumerate this in a while */ - spin_lock_irq (&dum->lock); - dum->pullup = 1; - set_link_state (dum); - spin_unlock_irq (&dum->lock); - - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int -usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int dummy_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct dummy *dum = the_controller; - unsigned long flags; - - if (!dum) - return -ENODEV; - if (!driver || driver != dum->driver || !driver->unbind) - return -EINVAL; + 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); - spin_lock_irqsave (&dum->lock, flags); - dum->pullup = 0; - set_link_state (dum); - spin_unlock_irqrestore (&dum->lock, flags); - - driver->unbind (&dum->gadget); - dum->gadget.dev.driver = NULL; dum->driver = NULL; - spin_lock_irqsave (&dum->lock, flags); - dum->pullup = 0; - set_link_state (dum); - spin_unlock_irqrestore (&dum->lock, flags); - - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + dummy_pullup(&dum->gadget, 0); return 0; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); #undef is_enabled -/* just declare this in any driver that really need it */ -extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); - -int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) -{ - return -ENOSYS; -} -EXPORT_SYMBOL (net2280_set_fifo_mode); - - /* The gadget structure is stored inside the hcd structure and will be * released along with it. */ static void dummy_gadget_release (struct device *dev) { - struct dummy *dum = gadget_dev_to_dummy (dev); + return; +} + +static void init_dummy_udc_hw(struct dummy *dum) +{ + int i; + + INIT_LIST_HEAD(&dum->gadget.ep_list); + for (i = 0; i < DUMMY_ENDPOINTS; i++) { + struct dummy_ep *ep = &dum->ep[i]; + + if (!ep_name[i]) + break; + ep->ep.name = ep_name[i]; + ep->ep.ops = &dummy_ep_ops; + list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list); + ep->halted = ep->wedged = ep->already_seen = + ep->setup_stage = 0; + ep->ep.maxpacket = ~0; + ep->last_io = jiffies; + ep->gadget = &dum->gadget; + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + } + + dum->gadget.ep0 = &dum->ep[0].ep; + list_del_init(&dum->ep[0].ep.ep_list); + INIT_LIST_HEAD(&dum->fifo_req.queue); - usb_put_hcd (dummy_to_hcd (dum)); +#ifdef CONFIG_USB_OTG + dum->gadget.is_otg = 1; +#endif } static int dummy_udc_probe (struct platform_device *pdev) { - struct dummy *dum = the_controller; + struct dummy *dum = &the_controller; int rc; - usb_get_hcd(dummy_to_hcd(dum)); - dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.is_dualspeed = 1; - /* maybe claim OTG support, though we won't complete HNP */ - dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); - dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; @@ -892,11 +997,22 @@ static int dummy_udc_probe (struct platform_device *pdev) 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); if (rc < 0) - device_unregister (&dum->gadget.dev); - else - platform_set_drvdata(pdev, dum); + goto err_dev; + platform_set_drvdata(pdev, dum); + return rc; + +err_dev: + usb_del_gadget_udc(&dum->gadget); +err_udc: + device_unregister(&dum->gadget.dev); return rc; } @@ -904,37 +1020,41 @@ static int dummy_udc_remove (struct platform_device *pdev) { struct dummy *dum = platform_get_drvdata (pdev); + usb_del_gadget_udc(&dum->gadget); platform_set_drvdata (pdev, NULL); device_remove_file (&dum->gadget.dev, &dev_attr_function); device_unregister (&dum->gadget.dev); return 0; } -static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state) +static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd, + int suspend) { - struct dummy *dum = platform_get_drvdata(pdev); + spin_lock_irq(&dum->lock); + dum->udc_suspended = suspend; + set_link_state(dum_hcd); + spin_unlock_irq(&dum->lock); +} - dev_dbg (&pdev->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->udc_suspended = 1; - set_link_state (dum); - spin_unlock_irq (&dum->lock); +static int dummy_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dummy *dum = platform_get_drvdata(pdev); + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget); - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + dev_dbg(&pdev->dev, "%s\n", __func__); + dummy_udc_pm(dum, dum_hcd, 1); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } -static int dummy_udc_resume (struct platform_device *pdev) +static int dummy_udc_resume(struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata(pdev); + struct dummy *dum = platform_get_drvdata(pdev); + struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(&dum->gadget); - dev_dbg (&pdev->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->udc_suspended = 0; - set_link_state (dum); - spin_unlock_irq (&dum->lock); - - usb_hcd_poll_rh_status (dummy_to_hcd (dum)); + dev_dbg(&pdev->dev, "%s\n", __func__); + dummy_udc_pm(dum, dum_hcd, 0); + usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; } @@ -968,7 +1088,7 @@ static int dummy_urb_enqueue ( struct urb *urb, gfp_t mem_flags ) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; struct urbp *urbp; unsigned long flags; int rc; @@ -981,51 +1101,51 @@ static int dummy_urb_enqueue ( return -ENOMEM; urbp->urb = urb; - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); + dum_hcd = hcd_to_dummy_hcd(hcd); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); rc = usb_hcd_link_urb_to_ep(hcd, urb); if (rc) { kfree(urbp); goto done; } - if (!dum->udev) { - dum->udev = urb->dev; - usb_get_dev (dum->udev); - } else if (unlikely (dum->udev != urb->dev)) - dev_err (dummy_dev(dum), "usb_device address has changed!\n"); + if (!dum_hcd->udev) { + dum_hcd->udev = urb->dev; + usb_get_dev(dum_hcd->udev); + } else if (unlikely(dum_hcd->udev != urb->dev)) + dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n"); - list_add_tail (&urbp->urbp_list, &dum->urbp_list); + list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); urb->hcpriv = urbp; if (usb_pipetype (urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ /* kick the scheduler, it'll do the rest */ - if (!timer_pending (&dum->timer)) - mod_timer (&dum->timer, jiffies + 1); + if (!timer_pending(&dum_hcd->timer)) + mod_timer(&dum_hcd->timer, jiffies + 1); done: - spin_unlock_irqrestore(&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; } static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; int rc; /* giveback happens automatically in timer callback, * so make sure the callback happens */ - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); + dum_hcd = hcd_to_dummy_hcd(hcd); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (!rc && dum->rh_state != DUMMY_RH_RUNNING && - !list_empty(&dum->urbp_list)) - mod_timer (&dum->timer, jiffies); + if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING && + !list_empty(&dum_hcd->urbp_list)) + mod_timer(&dum_hcd->timer, jiffies); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return rc; } @@ -1162,10 +1282,25 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) tmp *= 8 /* applies to entire frame */; limit += limit * tmp; } + if (dum->gadget.speed == USB_SPEED_SUPER) { + switch (ep->desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_ISOC: + /* Sec. 4.4.8.2 USB3.0 Spec */ + limit = 3 * 16 * 1024 * 8; + break; + case USB_ENDPOINT_XFER_INT: + /* Sec. 4.4.7.2 USB3.0 Spec */ + limit = 3 * 1024 * 8; + break; + case USB_ENDPOINT_XFER_BULK: + default: + break; + } + } return limit; } -#define is_active(dum) ((dum->port_status & \ +#define is_active(dum_hcd) ((dum_hcd->port_status & \ (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \ USB_PORT_STAT_SUSPEND)) \ == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) @@ -1174,7 +1309,8 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) { int i; - if (!is_active (dum)) + if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ? + dum->ss_hcd : dum->hs_hcd))) return NULL; if ((address & ~USB_DIR_IN) == 0) return &dum->ep [0]; @@ -1211,11 +1347,12 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) * 1 - if the request wasn't handles * error code on error */ -static int handle_control_request(struct dummy *dum, struct urb *urb, +static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, struct usb_ctrlrequest *setup, int *status) { struct dummy_ep *ep2; + struct dummy *dum = dum_hcd->dum; int ret_val = 1; unsigned w_index; unsigned w_value; @@ -1247,6 +1384,27 @@ static int handle_control_request(struct dummy *dum, struct urb *urb, case USB_DEVICE_A_ALT_HNP_SUPPORT: dum->gadget.a_alt_hnp_support = 1; break; + case USB_DEVICE_U1_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U1_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_U2_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U2_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_LTM_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_LTM_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; default: ret_val = -EOPNOTSUPP; } @@ -1273,6 +1431,27 @@ static int handle_control_request(struct dummy *dum, struct urb *urb, case USB_DEVICE_REMOTE_WAKEUP: w_value = USB_DEVICE_REMOTE_WAKEUP; break; + case USB_DEVICE_U1_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U1_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_U2_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_U2_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; + case USB_DEVICE_LTM_ENABLE: + if (dummy_hcd_to_hcd(dum_hcd)->speed == + HCD_USB3) + w_value = USB_DEV_STAT_LTM_ENABLED; + else + ret_val = -EOPNOTSUPP; + break; default: ret_val = -EOPNOTSUPP; break; @@ -1334,9 +1513,10 @@ static int handle_control_request(struct dummy *dum, struct urb *urb, /* drive both sides of the transfers; looks like irq handlers to * both drivers except the callbacks aren't in_irq(). */ -static void dummy_timer (unsigned long _dum) +static void dummy_timer(unsigned long _dum_hcd) { - struct dummy *dum = (struct dummy *) _dum; + struct dummy_hcd *dum_hcd = (struct dummy_hcd *) _dum_hcd; + struct dummy *dum = dum_hcd->dum; struct urbp *urbp, *tmp; unsigned long flags; int limit, total; @@ -1353,8 +1533,12 @@ static void dummy_timer (unsigned long _dum) case USB_SPEED_HIGH: total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/; break; + case USB_SPEED_SUPER: + /* Bus speed is 500000 bytes/ms, so use a little less */ + total = 490000; + break; default: - dev_err (dummy_dev(dum), "bogus device speed\n"); + dev_err(dummy_dev(dum_hcd), "bogus device speed\n"); return; } @@ -1363,8 +1547,8 @@ static void dummy_timer (unsigned long _dum) /* look at each urb queued by the host side driver */ spin_lock_irqsave (&dum->lock, flags); - if (!dum->udev) { - dev_err (dummy_dev(dum), + if (!dum_hcd->udev) { + dev_err(dummy_dev(dum_hcd), "timer fired with no URBs pending?\n"); spin_unlock_irqrestore (&dum->lock, flags); return; @@ -1377,7 +1561,7 @@ static void dummy_timer (unsigned long _dum) } restart: - list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) { + list_for_each_entry_safe(urbp, tmp, &dum_hcd->urbp_list, urbp_list) { struct urb *urb; struct dummy_request *req; u8 address; @@ -1388,7 +1572,7 @@ restart: urb = urbp->urb; if (urb->unlinked) goto return_urb; - else if (dum->rh_state != DUMMY_RH_RUNNING) + else if (dum_hcd->rh_state != DUMMY_RH_RUNNING) continue; type = usb_pipetype (urb->pipe); @@ -1406,7 +1590,7 @@ restart: ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ - dev_dbg (dummy_dev(dum), + dev_dbg(dummy_dev(dum_hcd), "no ep configured for urb %p\n", urb); status = -EPROTO; @@ -1422,7 +1606,7 @@ restart: } if (ep->halted && !ep->setup_stage) { /* NOTE: must not be iso! */ - dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n", + dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n", ep->ep.name, urb); status = -EPIPE; goto return_urb; @@ -1457,7 +1641,7 @@ restart: ep->setup_stage = 0; ep->halted = 0; - value = handle_control_request(dum, urb, &setup, + value = handle_control_request(dum_hcd, urb, &setup, &status); /* gadget driver handles all other requests. block @@ -1527,20 +1711,20 @@ return_urb: if (ep) ep->already_seen = ep->setup_stage = 0; - usb_hcd_unlink_urb_from_ep(dummy_to_hcd(dum), urb); + usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb); spin_unlock (&dum->lock); - usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status); + usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status); spin_lock (&dum->lock); goto restart; } - if (list_empty (&dum->urbp_list)) { - usb_put_dev (dum->udev); - dum->udev = NULL; - } else if (dum->rh_state == DUMMY_RH_RUNNING) { + if (list_empty(&dum_hcd->urbp_list)) { + usb_put_dev(dum_hcd->udev); + dum_hcd->udev = NULL; + } else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { /* want a 1 msec delay here */ - mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); + mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1)); } spin_unlock_irqrestore (&dum->lock, flags); @@ -1557,36 +1741,48 @@ return_urb: static int dummy_hub_status (struct usb_hcd *hcd, char *buf) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; unsigned long flags; int retval = 0; - dum = hcd_to_dummy (hcd); + dum_hcd = hcd_to_dummy_hcd(hcd); - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum_hcd->dum->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) goto done; - if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); - dum->port_status &= ~USB_PORT_STAT_SUSPEND; - set_link_state (dum); + if (dum_hcd->resuming && time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; + set_link_state(dum_hcd); } - if ((dum->port_status & PORT_C_MASK) != 0) { + if ((dum_hcd->port_status & PORT_C_MASK) != 0) { *buf = (1 << 1); - dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", - dum->port_status); + dev_dbg(dummy_dev(dum_hcd), "port status 0x%08x has changes\n", + dum_hcd->port_status); retval = 1; - if (dum->rh_state == DUMMY_RH_SUSPENDED) + if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED) usb_hcd_resume_root_hub (hcd); } done: - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return retval; } static inline void +ss_hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof *desc); + desc->bDescriptorType = 0x2a; + desc->bDescLength = 12; + desc->wHubCharacteristics = cpu_to_le16(0x0001); + desc->bNbrPorts = 1; + desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/ + desc->u.ss.DeviceRemovable = 0xffff; +} + +static inline void hub_descriptor (struct usb_hub_descriptor *desc) { memset (desc, 0, sizeof *desc); @@ -1606,39 +1802,64 @@ static int dummy_hub_control ( char *buf, u16 wLength ) { - struct dummy *dum; + struct dummy_hcd *dum_hcd; int retval = 0; unsigned long flags; if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; - dum = hcd_to_dummy (hcd); - spin_lock_irqsave (&dum->lock, flags); + dum_hcd = hcd_to_dummy_hcd(hcd); + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); switch (typeReq) { case ClearHubFeature: break; case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - if (dum->port_status & USB_PORT_STAT_SUSPEND) { + if (hcd->speed == HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_SUSPEND req not " + "supported for USB 3.0 roothub\n"); + goto error; + } + if (dum_hcd->port_status & USB_PORT_STAT_SUSPEND) { /* 20msec resume signaling */ - dum->resuming = 1; - dum->re_timeout = jiffies + + dum_hcd->resuming = 1; + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(20); } break; case USB_PORT_FEAT_POWER: - if (dum->port_status & USB_PORT_STAT_POWER) - dev_dbg (dummy_dev(dum), "power-off\n"); + if (hcd->speed == HCD_USB3) { + if (dum_hcd->port_status & USB_PORT_STAT_POWER) + dev_dbg(dummy_dev(dum_hcd), + "power-off\n"); + } else + if (dum_hcd->port_status & + USB_SS_PORT_STAT_POWER) + dev_dbg(dummy_dev(dum_hcd), + "power-off\n"); /* FALLS THROUGH */ default: - dum->port_status &= ~(1 << wValue); - set_link_state (dum); + dum_hcd->port_status &= ~(1 << wValue); + set_link_state(dum_hcd); } break; case GetHubDescriptor: - hub_descriptor ((struct usb_hub_descriptor *) buf); + if (hcd->speed == HCD_USB3 && + (wLength < USB_DT_SS_HUB_SIZE || + wValue != (USB_DT_SS_HUB << 8))) { + dev_dbg(dummy_dev(dum_hcd), + "Wrong hub descriptor type for " + "USB 3.0 roothub.\n"); + goto error; + } + if (hcd->speed == HCD_USB3) + ss_hub_descriptor((struct usb_hub_descriptor *) buf); + else + hub_descriptor((struct usb_hub_descriptor *) buf); break; case GetHubStatus: *(__le32 *) buf = cpu_to_le32 (0); @@ -1650,127 +1871,210 @@ static int dummy_hub_control ( /* whoever resets or resumes must GetPortStatus to * complete it!! */ - if (dum->resuming && - time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); - dum->port_status &= ~USB_PORT_STAT_SUSPEND; + if (dum_hcd->resuming && + time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND; } - if ((dum->port_status & USB_PORT_STAT_RESET) != 0 && - time_after_eq (jiffies, dum->re_timeout)) { - dum->port_status |= (USB_PORT_STAT_C_RESET << 16); - dum->port_status &= ~USB_PORT_STAT_RESET; - if (dum->pullup) { - dum->port_status |= USB_PORT_STAT_ENABLE; - /* give it the best speed we agree on */ - dum->gadget.speed = dum->driver->speed; - dum->gadget.ep0->maxpacket = 64; - switch (dum->gadget.speed) { - case USB_SPEED_HIGH: - dum->port_status |= - USB_PORT_STAT_HIGH_SPEED; - break; - case USB_SPEED_LOW: - dum->gadget.ep0->maxpacket = 8; - dum->port_status |= - USB_PORT_STAT_LOW_SPEED; - break; - default: - dum->gadget.speed = USB_SPEED_FULL; - break; + if ((dum_hcd->port_status & USB_PORT_STAT_RESET) != 0 && + time_after_eq(jiffies, dum_hcd->re_timeout)) { + dum_hcd->port_status |= (USB_PORT_STAT_C_RESET << 16); + dum_hcd->port_status &= ~USB_PORT_STAT_RESET; + if (dum_hcd->dum->pullup) { + dum_hcd->port_status |= USB_PORT_STAT_ENABLE; + + if (hcd->speed < HCD_USB3) { + switch (dum_hcd->dum->gadget.speed) { + case USB_SPEED_HIGH: + dum_hcd->port_status |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum_hcd->dum->gadget.ep0-> + maxpacket = 8; + dum_hcd->port_status |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + dum_hcd->dum->gadget.speed = + USB_SPEED_FULL; + break; + } } } } - set_link_state (dum); - ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status); - ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); + set_link_state(dum_hcd); + ((__le16 *) buf)[0] = cpu_to_le16 (dum_hcd->port_status); + ((__le16 *) buf)[1] = cpu_to_le16 (dum_hcd->port_status >> 16); break; case SetHubFeature: retval = -EPIPE; break; case SetPortFeature: switch (wValue) { + case USB_PORT_FEAT_LINK_STATE: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_LINK_STATE req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* + * Since this is dummy we don't have an actual link so + * there is nothing to do for the SET_LINK_STATE cmd + */ + break; + case USB_PORT_FEAT_U1_TIMEOUT: + case USB_PORT_FEAT_U2_TIMEOUT: + /* TODO: add suspend/resume support! */ + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_U1/2_TIMEOUT req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + break; case USB_PORT_FEAT_SUSPEND: - if (dum->active) { - dum->port_status |= USB_PORT_STAT_SUSPEND; + /* Applicable only for USB2.0 hub */ + if (hcd->speed == HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_SUSPEND req not " + "supported for USB 3.0 roothub\n"); + goto error; + } + if (dum_hcd->active) { + dum_hcd->port_status |= USB_PORT_STAT_SUSPEND; /* HNP would happen here; for now we * assume b_bus_req is always true. */ - set_link_state (dum); + set_link_state(dum_hcd); if (((1 << USB_DEVICE_B_HNP_ENABLE) - & dum->devstatus) != 0) - dev_dbg (dummy_dev(dum), + & dum_hcd->dum->devstatus) != 0) + dev_dbg(dummy_dev(dum_hcd), "no HNP yet!\n"); } break; case USB_PORT_FEAT_POWER: - dum->port_status |= USB_PORT_STAT_POWER; - set_link_state (dum); + if (hcd->speed == HCD_USB3) + dum_hcd->port_status |= USB_SS_PORT_STAT_POWER; + else + dum_hcd->port_status |= USB_PORT_STAT_POWER; + set_link_state(dum_hcd); break; + case USB_PORT_FEAT_BH_PORT_RESET: + /* Applicable only for USB3.0 hub */ + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "USB_PORT_FEAT_BH_PORT_RESET req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* FALLS THROUGH */ case USB_PORT_FEAT_RESET: /* if it's already enabled, disable */ - dum->port_status &= ~(USB_PORT_STAT_ENABLE + if (hcd->speed == HCD_USB3) { + dum_hcd->port_status = 0; + dum_hcd->port_status = + (USB_SS_PORT_STAT_POWER | + USB_PORT_STAT_CONNECTION | + USB_PORT_STAT_RESET); + } else + dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); - dum->devstatus = 0; - /* 50msec reset signaling */ - dum->re_timeout = jiffies + msecs_to_jiffies(50); + /* + * We want to reset device status. All but the + * Self powered feature + */ + dum_hcd->dum->devstatus &= + (1 << USB_DEVICE_SELF_POWERED); + /* + * FIXME USB3.0: what is the correct reset signaling + * interval? Is it still 50msec as for HS? + */ + dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLS THROUGH */ default: - if ((dum->port_status & USB_PORT_STAT_POWER) != 0) { - dum->port_status |= (1 << wValue); - set_link_state (dum); - } + if (hcd->speed == HCD_USB3) { + if ((dum_hcd->port_status & + USB_SS_PORT_STAT_POWER) != 0) { + dum_hcd->port_status |= (1 << wValue); + set_link_state(dum_hcd); + } + } else + if ((dum_hcd->port_status & + USB_PORT_STAT_POWER) != 0) { + dum_hcd->port_status |= (1 << wValue); + set_link_state(dum_hcd); + } + } + break; + case GetPortErrorCount: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "GetPortErrorCount req not " + "supported for USB 2.0 roothub\n"); + goto error; + } + /* We'll always return 0 since this is a dummy hub */ + *(__le32 *) buf = cpu_to_le32(0); + break; + case SetHubDepth: + if (hcd->speed != HCD_USB3) { + dev_dbg(dummy_dev(dum_hcd), + "SetHubDepth req not supported for " + "USB 2.0 roothub\n"); + goto error; } break; - default: - dev_dbg (dummy_dev(dum), + dev_dbg(dummy_dev(dum_hcd), "hub control req%04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); - +error: /* "protocol stall" on error */ retval = -EPIPE; } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); - if ((dum->port_status & PORT_C_MASK) != 0) + if ((dum_hcd->port_status & PORT_C_MASK) != 0) usb_hcd_poll_rh_status (hcd); return retval; } static int dummy_bus_suspend (struct usb_hcd *hcd) { - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); - dum->rh_state = DUMMY_RH_SUSPENDED; - set_link_state (dum); + spin_lock_irq(&dum_hcd->dum->lock); + dum_hcd->rh_state = DUMMY_RH_SUSPENDED; + set_link_state(dum_hcd); hcd->state = HC_STATE_SUSPENDED; - spin_unlock_irq (&dum->lock); + spin_unlock_irq(&dum_hcd->dum->lock); return 0; } static int dummy_bus_resume (struct usb_hcd *hcd) { - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); int rc = 0; dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock_irq (&dum->lock); + spin_lock_irq(&dum_hcd->dum->lock); if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { - dum->rh_state = DUMMY_RH_RUNNING; - set_link_state (dum); - if (!list_empty(&dum->urbp_list)) - mod_timer (&dum->timer, jiffies); + dum_hcd->rh_state = DUMMY_RH_RUNNING; + set_link_state(dum_hcd); + if (!list_empty(&dum_hcd->urbp_list)) + mod_timer(&dum_hcd->timer, jiffies); hcd->state = HC_STATE_RUNNING; } - spin_unlock_irq (&dum->lock); + spin_unlock_irq(&dum_hcd->dum->lock); return rc; } @@ -1786,18 +2090,37 @@ show_urb (char *buf, size_t size, struct urb *urb) urb, ({ char *s; switch (urb->dev->speed) { - case USB_SPEED_LOW: s = "ls"; break; - case USB_SPEED_FULL: s = "fs"; break; - case USB_SPEED_HIGH: s = "hs"; break; - default: s = "?"; break; + case USB_SPEED_LOW: + s = "ls"; + break; + case USB_SPEED_FULL: + s = "fs"; + break; + case USB_SPEED_HIGH: + s = "hs"; + break; + case USB_SPEED_SUPER: + s = "ss"; + break; + default: + s = "?"; + break; }; s; }), ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype (urb->pipe)) { \ - case PIPE_CONTROL: s = ""; break; \ - case PIPE_BULK: s = "-bulk"; break; \ - case PIPE_INTERRUPT: s = "-int"; break; \ - default: s = "-iso"; break; \ + case PIPE_CONTROL: \ + s = ""; \ + break; \ + case PIPE_BULK: \ + s = "-bulk"; \ + break; \ + case PIPE_INTERRUPT: \ + s = "-int"; \ + break; \ + default: \ + s = "-iso"; \ + break; \ }; s;}), urb->actual_length, urb->transfer_buffer_length); } @@ -1806,43 +2129,63 @@ static ssize_t show_urbs (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_hcd *hcd = dev_get_drvdata (dev); - struct dummy *dum = hcd_to_dummy (hcd); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); struct urbp *urbp; size_t size = 0; unsigned long flags; - spin_lock_irqsave (&dum->lock, flags); - list_for_each_entry (urbp, &dum->urbp_list, urbp_list) { + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + list_for_each_entry(urbp, &dum_hcd->urbp_list, urbp_list) { size_t temp; temp = show_urb (buf, PAGE_SIZE - size, urbp->urb); buf += temp; size += temp; } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); return size; } static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL); -static int dummy_start (struct usb_hcd *hcd) +static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - struct dummy *dum; + init_timer(&dum_hcd->timer); + dum_hcd->timer.function = dummy_timer; + dum_hcd->timer.data = (unsigned long)dum_hcd; + dum_hcd->rh_state = DUMMY_RH_RUNNING; + INIT_LIST_HEAD(&dum_hcd->urbp_list); + dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; + dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; + dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1; +#ifdef CONFIG_USB_OTG + dummy_hcd_to_hcd(dum_hcd)->self.otg_port = 1; +#endif + return 0; + + /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ + return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs); +} - dum = hcd_to_dummy (hcd); +static int dummy_start(struct usb_hcd *hcd) +{ + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); /* * MASTER side init ... we emulate a root hub that'll only ever * talk to one device (the slave side). Also appears in sysfs, * just like more familiar pci-based HCDs. */ - spin_lock_init (&dum->lock); - init_timer (&dum->timer); - dum->timer.function = dummy_timer; - dum->timer.data = (unsigned long) dum; - dum->rh_state = DUMMY_RH_RUNNING; + if (!usb_hcd_is_primary_hcd(hcd)) + return dummy_start_ss(dum_hcd); - INIT_LIST_HEAD (&dum->urbp_list); + spin_lock_init(&dum_hcd->dum->lock); + init_timer(&dum_hcd->timer); + dum_hcd->timer.function = dummy_timer; + dum_hcd->timer.data = (unsigned long)dum_hcd; + dum_hcd->rh_state = DUMMY_RH_RUNNING; + + INIT_LIST_HEAD(&dum_hcd->urbp_list); hcd->power_budget = POWER_BUDGET; hcd->state = HC_STATE_RUNNING; @@ -1853,18 +2196,17 @@ static int dummy_start (struct usb_hcd *hcd) #endif /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ - return device_create_file (dummy_dev(dum), &dev_attr_urbs); + return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs); } static void dummy_stop (struct usb_hcd *hcd) { struct dummy *dum; - dum = hcd_to_dummy (hcd); - - device_remove_file (dummy_dev(dum), &dev_attr_urbs); - usb_gadget_unregister_driver (dum->driver); - dev_info (dummy_dev(dum), "stopped\n"); + dum = (hcd_to_dummy_hcd(hcd))->dum; + device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs); + usb_gadget_unregister_driver(dum->driver); + dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n"); } /*-------------------------------------------------------------------------*/ @@ -1874,13 +2216,59 @@ static int dummy_h_get_frame (struct usb_hcd *hcd) return dummy_g_get_frame (NULL); } -static const struct hc_driver dummy_hcd = { +static int dummy_setup(struct usb_hcd *hcd) +{ + if (usb_hcd_is_primary_hcd(hcd)) { + the_controller.hs_hcd = hcd_to_dummy_hcd(hcd); + the_controller.hs_hcd->dum = &the_controller; + /* + * Mark the first roothub as being USB 2.0. + * The USB 3.0 roothub will be registered later by + * dummy_hcd_probe() + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + } else { + the_controller.ss_hcd = hcd_to_dummy_hcd(hcd); + the_controller.ss_hcd->dum = &the_controller; + hcd->speed = HCD_USB3; + hcd->self.root_hub->speed = USB_SPEED_SUPER; + } + return 0; +} + +/* Change a group of bulk endpoints to support multiple stream IDs */ +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; +} + +/* Reverts a group of bulk endpoints back to not using stream IDs. */ +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; +} + +static struct hc_driver dummy_hcd = { .description = (char *) driver_name, .product_desc = "Dummy host controller", - .hcd_priv_size = sizeof(struct dummy), + .hcd_priv_size = sizeof(struct dummy_hcd), - .flags = HCD_USB2, + .flags = HCD_USB3 | HCD_SHARED, + .reset = dummy_setup, .start = dummy_start, .stop = dummy_stop, @@ -1893,51 +2281,85 @@ static const struct hc_driver dummy_hcd = { .hub_control = dummy_hub_control, .bus_suspend = dummy_bus_suspend, .bus_resume = dummy_bus_resume, + + .alloc_streams = dummy_alloc_streams, + .free_streams = dummy_free_streams, }; static int dummy_hcd_probe(struct platform_device *pdev) { - struct usb_hcd *hcd; + struct usb_hcd *hs_hcd; + struct usb_hcd *ss_hcd; int retval; dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); - hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) + if (!mod_data.is_super_speed) + dummy_hcd.flags = HCD_USB2; + hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); + if (!hs_hcd) return -ENOMEM; - the_controller = hcd_to_dummy (hcd); - hcd->has_tt = 1; + hs_hcd->has_tt = 1; - retval = usb_add_hcd(hcd, 0, 0); + retval = usb_add_hcd(hs_hcd, 0, 0); if (retval != 0) { - usb_put_hcd (hcd); - the_controller = NULL; + usb_put_hcd(hs_hcd); + return retval; + } + + if (mod_data.is_super_speed) { + ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev, + dev_name(&pdev->dev), hs_hcd); + if (!ss_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + } + + retval = usb_add_hcd(ss_hcd, 0, 0); + if (retval) + goto put_usb3_hcd; } + return 0; + +put_usb3_hcd: + usb_put_hcd(ss_hcd); +dealloc_usb2_hcd: + usb_put_hcd(hs_hcd); + the_controller.hs_hcd = the_controller.ss_hcd = NULL; return retval; } -static int dummy_hcd_remove (struct platform_device *pdev) +static int dummy_hcd_remove(struct platform_device *pdev) { - struct usb_hcd *hcd; + struct dummy *dum; + + dum = (hcd_to_dummy_hcd(platform_get_drvdata(pdev)))->dum; + + if (dum->ss_hcd) { + usb_remove_hcd(dummy_hcd_to_hcd(dum->ss_hcd)); + usb_put_hcd(dummy_hcd_to_hcd(dum->ss_hcd)); + } + + usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); + usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); + + the_controller.hs_hcd = NULL; + the_controller.ss_hcd = NULL; - hcd = platform_get_drvdata (pdev); - usb_remove_hcd (hcd); - usb_put_hcd (hcd); - the_controller = NULL; return 0; } static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd; - struct dummy *dum; + struct dummy_hcd *dum_hcd; int rc = 0; dev_dbg (&pdev->dev, "%s\n", __func__); hcd = platform_get_drvdata (pdev); - dum = hcd_to_dummy (hcd); - if (dum->rh_state == DUMMY_RH_RUNNING) { + dum_hcd = hcd_to_dummy_hcd(hcd); + if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { dev_warn(&pdev->dev, "Root hub isn't suspended!\n"); rc = -EBUSY; } else @@ -1980,6 +2402,9 @@ static int __init init (void) 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; @@ -1997,7 +2422,8 @@ static int __init init (void) retval = platform_device_add(the_hcd_pdev); if (retval < 0) goto err_add_hcd; - if (!the_controller) { + 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. diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 9b7360ff5aa7..7a7e6b7e1fd6 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -63,13 +63,16 @@ static int ep_matches ( struct usb_gadget *gadget, struct usb_ep *ep, - struct usb_endpoint_descriptor *desc + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp ) { u8 type; const char *tmp; u16 max; + int num_req_streams = 0; + /* endpoint already claimed? */ if (NULL != ep->driver_data) return 0; @@ -129,6 +132,22 @@ ep_matches ( } /* + * Get the number of required streams from the EP companion + * descriptor and see if the EP matches it + */ + if (usb_endpoint_xfer_bulk(desc)) { + if (ep_comp) { + num_req_streams = ep_comp->bmAttributes & 0x1f; + if (num_req_streams > ep->max_streams) + return 0; + /* Update the ep_comp descriptor if needed */ + if (num_req_streams != ep->max_streams) + ep_comp->bmAttributes = ep->max_streams; + } + + } + + /* * If the protocol driver hasn't yet decided on wMaxPacketSize * and wants to know the maximum possible, provide the info. */ @@ -142,13 +161,13 @@ ep_matches ( max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); switch (type) { case USB_ENDPOINT_XFER_INT: - /* INT: limit 64 bytes full speed, 1024 high speed */ + /* INT: limit 64 bytes full speed, 1024 high/super speed */ if (!gadget->is_dualspeed && max > 64) return 0; /* FALLTHROUGH */ case USB_ENDPOINT_XFER_ISOC: - /* ISO: limit 1023 bytes full speed, 1024 high speed */ + /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ if (ep->maxpacket < max) return 0; if (!gadget->is_dualspeed && max > 1023) @@ -183,7 +202,7 @@ ep_matches ( } /* report (variable) full speed bulk maxpacket */ - if (USB_ENDPOINT_XFER_BULK == type) { + if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) { int size = ep->maxpacket; /* min() doesn't work on bitfields with gcc-3.5 */ @@ -191,6 +210,7 @@ ep_matches ( size = 64; desc->wMaxPacketSize = cpu_to_le16(size); } + ep->address = desc->bEndpointAddress; return 1; } @@ -207,38 +227,53 @@ find_ep (struct usb_gadget *gadget, const char *name) } /** - * usb_ep_autoconfig - choose an endpoint matching the descriptor + * usb_ep_autoconfig_ss() - choose an endpoint matching the ep + * descriptor and ep companion descriptor * @gadget: The device to which the endpoint must belong. * @desc: Endpoint descriptor, with endpoint direction and transfer mode - * initialized. For periodic transfers, the maximum packet - * size must also be initialized. This is modified on success. + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on + * success. + * @ep_comp: Endpoint companion descriptor, with the required + * number of streams. Will be modified when the chosen EP + * supports a different number of streams. * - * By choosing an endpoint to use with the specified descriptor, this - * routine simplifies writing gadget drivers that work with multiple - * USB device controllers. The endpoint would be passed later to - * usb_ep_enable(), along with some descriptor. + * This routine replaces the usb_ep_autoconfig when needed + * superspeed enhancments. If such enhancemnets are required, + * the FD should call usb_ep_autoconfig_ss directly and provide + * the additional ep_comp parameter. + * + * By choosing an endpoint to use with the specified descriptor, + * this routine simplifies writing gadget drivers that work with + * multiple USB device controllers. The endpoint would be + * passed later to usb_ep_enable(), along with some descriptor. * * That second descriptor won't always be the same as the first one. * For example, isochronous endpoints can be autoconfigured for high * bandwidth, and then used in several lower bandwidth altsettings. * Also, high and full speed descriptors will be different. * - * Be sure to examine and test the results of autoconfiguration on your - * hardware. This code may not make the best choices about how to use the - * USB controller, and it can't know all the restrictions that may apply. - * Some combinations of driver and hardware won't be able to autoconfigure. + * Be sure to examine and test the results of autoconfiguration + * on your hardware. This code may not make the best choices + * about how to use the USB controller, and it can't know all + * the restrictions that may apply. Some combinations of driver + * and hardware won't be able to autoconfigure. * * On success, this returns an un-claimed usb_ep, and modifies the endpoint * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value - * is initialized as if the endpoint were used at full speed. To prevent - * the endpoint from being returned by a later autoconfig call, claim it - * by assigning ep->driver_data to some non-null value. + * is initialized as if the endpoint were used at full speed and + * the bmAttribute field in the ep companion descriptor is + * updated with the assigned number of streams if it is + * different from the original value. To prevent the endpoint + * from being returned by a later autoconfig call, claim it by + * assigning ep->driver_data to some non-null value. * * On failure, this returns a null endpoint descriptor. */ -struct usb_ep *usb_ep_autoconfig ( +struct usb_ep *usb_ep_autoconfig_ss( struct usb_gadget *gadget, - struct usb_endpoint_descriptor *desc + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp ) { struct usb_ep *ep; @@ -252,23 +287,24 @@ struct usb_ep *usb_ep_autoconfig ( if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { /* ep-e, ep-f are PIO with only 64 byte fifos */ ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches (gadget, ep, desc)) + if (ep && ep_matches(gadget, ep, desc, ep_comp)) return ep; ep = find_ep (gadget, "ep-f"); - if (ep && ep_matches (gadget, ep, desc)) + if (ep && ep_matches(gadget, ep, desc, ep_comp)) return 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 = find_ep(gadget, "ep3-bulk"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) return 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 = find_ep(gadget, "ep2-bulk"); + if (ep && ep_matches(gadget, ep, desc, + ep_comp)) return ep; } @@ -287,14 +323,14 @@ struct usb_ep *usb_ep_autoconfig ( ep = find_ep(gadget, "ep2out"); } else ep = NULL; - if (ep && ep_matches (gadget, ep, desc)) + if (ep && ep_matches(gadget, ep, desc, ep_comp)) return 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)) + if (ep_matches(gadget, ep, desc, ep_comp)) return ep; } @@ -303,6 +339,46 @@ struct usb_ep *usb_ep_autoconfig ( } /** + * usb_ep_autoconfig() - choose an endpoint matching the + * descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Endpoint descriptor, with endpoint direction and transfer mode + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on success. + * + * By choosing an endpoint to use with the specified descriptor, this + * routine simplifies writing gadget drivers that work with multiple + * USB device controllers. The endpoint would be passed later to + * usb_ep_enable(), along with some descriptor. + * + * That second descriptor won't always be the same as the first one. + * For example, isochronous endpoints can be autoconfigured for high + * bandwidth, and then used in several lower bandwidth altsettings. + * Also, high and full speed descriptors will be different. + * + * Be sure to examine and test the results of autoconfiguration on your + * hardware. This code may not make the best choices about how to use the + * USB controller, and it can't know all the restrictions that may apply. + * Some combinations of driver and hardware won't be able to autoconfigure. + * + * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value + * is initialized as if the endpoint were used at full speed. To prevent + * the endpoint from being returned by a later autoconfig call, claim it + * by assigning ep->driver_data to some non-null value. + * + * On failure, this returns a null endpoint descriptor. + */ +struct usb_ep *usb_ep_autoconfig( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc +) +{ + return usb_ep_autoconfig_ss(gadget, desc, NULL); +} + + +/** * usb_ep_autoconfig_reset - reset endpoint autoconfig state * @gadget: device for which autoconfig state will be reset * diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 1690c9d68256..aafc84f33e26 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -401,6 +401,7 @@ static struct usb_composite_driver eth_driver = { .name = "g_ether", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, .unbind = __exit_p(eth_unbind), }; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index bd6226cbae86..3f8849339ade 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -39,12 +39,6 @@ * descriptors (roughly equivalent to CDC Unions) may sometimes help. */ -struct acm_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; - struct f_acm { struct gserial port; u8 ctrl_id, data_id; @@ -58,11 +52,7 @@ struct f_acm { */ spinlock_t lock; - struct acm_ep_descs fs; - struct acm_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ @@ -405,23 +395,27 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) usb_ep_disable(acm->notify); } else { VDBG(cdev, "init acm ctrl interface %d\n", intf); - acm->notify_desc = ep_choose(cdev->gadget, - acm->hs.notify, - acm->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, acm->notify)) + return -EINVAL; } - usb_ep_enable(acm->notify, acm->notify_desc); + usb_ep_enable(acm->notify); acm->notify->driver_data = acm; } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); - } else { + } + if (!acm->port.in->desc || !acm->port.out->desc) { DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); - acm->port.in_desc = ep_choose(cdev->gadget, - acm->hs.in, acm->fs.in); - acm->port.out_desc = ep_choose(cdev->gadget, - acm->hs.out, acm->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + acm->port.in) || + config_ep_by_speed(cdev->gadget, f, + acm->port.out)) { + acm->port.in->desc = NULL; + acm->port.out->desc = NULL; + return -EINVAL; + } } gserial_connect(&acm->port, acm->port_num); @@ -629,18 +623,11 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm->notify_req->complete = acm_cdc_notify_complete; acm->notify_req->context = acm; - /* copy descriptors, and track endpoint copies */ + /* copy descriptors */ f->descriptors = usb_copy_descriptors(acm_fs_function); if (!f->descriptors) goto fail; - acm->fs.in = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_in_desc); - acm->fs.out = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_out_desc); - acm->fs.notify = usb_find_endpoint(acm_fs_function, - f->descriptors, &acm_fs_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -653,15 +640,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm_hs_notify_desc.bEndpointAddress = acm_fs_notify_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ + /* copy descriptors */ f->hs_descriptors = usb_copy_descriptors(acm_hs_function); - - acm->hs.in = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_in_desc); - acm->hs.out = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_out_desc); - acm->hs.notify = usb_find_endpoint(acm_hs_function, - f->hs_descriptors, &acm_hs_notify_desc); } DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 8ee330a2ab58..02a02700b51d 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -279,7 +279,6 @@ struct f_audio { /* endpoints handle full and/or high speeds */ struct usb_ep *out_ep; - struct usb_endpoint_descriptor *out_desc; spinlock_t lock; struct f_audio_buf *copy_buf; @@ -575,7 +574,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (intf == 1) { if (alt == 1) { - usb_ep_enable(out_ep, audio->out_desc); + usb_ep_enable(out_ep); out_ep->driver_data = audio; audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); if (IS_ERR(audio->copy_buf)) @@ -677,6 +676,7 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; audio->out_ep = ep; + audio->out_ep->desc = &as_out_ep_desc; ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -776,7 +776,6 @@ int __init audio_bind_config(struct usb_configuration *c) audio->card.func.set_alt = f_audio_set_alt; audio->card.func.setup = f_audio_setup; audio->card.func.disable = f_audio_disable; - audio->out_desc = &as_out_ep_desc; control_selector_init(audio); diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 544257a89ed2..3691a0cb9465 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -46,11 +46,6 @@ * and also means that a get_alt() method is required. */ -struct ecm_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; enum ecm_notify_state { ECM_NOTIFY_NONE, /* don't notify */ @@ -64,11 +59,7 @@ struct f_ecm { char ethaddr[14]; - struct ecm_ep_descs fs; - struct ecm_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; u8 notify_state; bool is_open; @@ -86,10 +77,12 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static inline unsigned ecm_bitrate(struct usb_gadget *g) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 13 * 1024 * 8 * 1000 * 8; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else - return 19 * 64 * 1 * 1000 * 8; + return 19 * 64 * 1 * 1000 * 8; } /*-------------------------------------------------------------------------*/ @@ -219,8 +212,10 @@ static struct usb_descriptor_header *ecm_fs_function[] = { (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, (struct usb_descriptor_header *) &ecm_desc, + /* NOTE: status endpoint might need to be removed */ (struct usb_descriptor_header *) &fs_ecm_notify_desc, + /* data interface, altsettings 0 and 1 */ (struct usb_descriptor_header *) &ecm_data_nop_intf, (struct usb_descriptor_header *) &ecm_data_intf, @@ -240,6 +235,7 @@ static struct usb_endpoint_descriptor hs_ecm_notify_desc = { .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, }; + static struct usb_endpoint_descriptor hs_ecm_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -264,8 +260,10 @@ static struct usb_descriptor_header *ecm_hs_function[] = { (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, (struct usb_descriptor_header *) &ecm_desc, + /* NOTE: status endpoint might need to be removed */ (struct usb_descriptor_header *) &hs_ecm_notify_desc, + /* data interface, altsettings 0 and 1 */ (struct usb_descriptor_header *) &ecm_data_nop_intf, (struct usb_descriptor_header *) &ecm_data_intf, @@ -274,6 +272,76 @@ static struct usb_descriptor_header *ecm_hs_function[] = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_ecm_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = { + .bLength = sizeof ss_ecm_intr_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ss_ecm_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ecm_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = { + .bLength = sizeof ss_ecm_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ecm_ss_function[] = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &ecm_header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) &ecm_desc, + + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &ss_ecm_notify_desc, + (struct usb_descriptor_header *) &ss_ecm_intr_comp_desc, + + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &ss_ecm_in_desc, + (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_ecm_out_desc, + (struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string ecm_string_defs[] = { @@ -464,13 +532,13 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ecm->notify->driver_data) { VDBG(cdev, "reset ecm control %d\n", intf); usb_ep_disable(ecm->notify); - } else { + } + if (!(ecm->notify->desc)) { VDBG(cdev, "init ecm ctrl %d\n", intf); - ecm->notify_desc = ep_choose(cdev->gadget, - ecm->hs.notify, - ecm->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, ecm->notify)) + goto fail; } - usb_ep_enable(ecm->notify, ecm->notify_desc); + usb_ep_enable(ecm->notify); ecm->notify->driver_data = ecm; /* Data interface has two altsettings, 0 and 1 */ @@ -483,12 +551,17 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gether_disconnect(&ecm->port); } - if (!ecm->port.in) { + if (!ecm->port.in_ep->desc || + !ecm->port.out_ep->desc) { DBG(cdev, "init ecm\n"); - ecm->port.in = ep_choose(cdev->gadget, - ecm->hs.in, ecm->fs.in); - ecm->port.out = ep_choose(cdev->gadget, - ecm->hs.out, ecm->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + ecm->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + ecm->port.out_ep)) { + ecm->port.in_ep->desc = NULL; + ecm->port.out_ep->desc = NULL; + goto fail; + } } /* CDC Ethernet only sends data in non-default altsettings. @@ -549,7 +622,7 @@ static void ecm_disable(struct usb_function *f) if (ecm->notify->driver_data) { usb_ep_disable(ecm->notify); ecm->notify->driver_data = NULL; - ecm->notify_desc = NULL; + ecm->notify->desc = NULL; } } @@ -665,13 +738,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) if (!f->descriptors) goto fail; - ecm->fs.in = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_in_desc); - ecm->fs.out = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_out_desc); - ecm->fs.notify = usb_find_endpoint(ecm_fs_function, - f->descriptors, &fs_ecm_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -688,13 +754,20 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(ecm_hs_function); if (!f->hs_descriptors) goto fail; + } - ecm->hs.in = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_in_desc); - ecm->hs.out = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_out_desc); - ecm->hs.notify = usb_find_endpoint(ecm_hs_function, - f->hs_descriptors, &hs_ecm_notify_desc); + 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; } /* NOTE: all that is done without knowing or caring about @@ -706,6 +779,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->port.close = ecm_close; DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); @@ -714,6 +788,8 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) 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); @@ -723,9 +799,9 @@ fail: /* we might as well release our claims on endpoints */ if (ecm->notify) ecm->notify->driver_data = NULL; - if (ecm->port.out) + if (ecm->port.out_ep->desc) ecm->port.out_ep->driver_data = NULL; - if (ecm->port.in) + if (ecm->port.in_ep->desc) ecm->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -740,6 +816,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm 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); diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index b3c304290150..046c6d0e6960 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -35,17 +35,9 @@ * Ethernet link. */ -struct eem_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; -}; - struct f_eem { struct gether port; u8 ctrl_id; - - struct eem_ep_descs fs; - struct eem_ep_descs hs; }; static inline struct f_eem *func_to_eem(struct usb_function *f) @@ -123,6 +115,45 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = { + .bLength = sizeof eem_ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *eem_ss_function[] __initdata = { + /* CDC EEM control descriptors */ + (struct usb_descriptor_header *) &eem_intf, + (struct usb_descriptor_header *) &eem_ss_in_desc, + (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &eem_ss_out_desc, + (struct usb_descriptor_header *) &eem_ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string eem_string_defs[] = { @@ -176,12 +207,16 @@ static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gether_disconnect(&eem->port); } - if (!eem->port.in) { + if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) { DBG(cdev, "init eem\n"); - eem->port.in = ep_choose(cdev->gadget, - eem->hs.in, eem->fs.in); - eem->port.out = ep_choose(cdev->gadget, - eem->hs.out, eem->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + eem->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + eem->port.out_ep)) { + eem->port.in_ep->desc = NULL; + eem->port.out_ep->desc = NULL; + goto fail; + } } /* zlps should not occur because zero-length EEM packets @@ -253,11 +288,6 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) if (!f->descriptors) goto fail; - eem->fs.in = usb_find_endpoint(eem_fs_function, - f->descriptors, &eem_fs_in_desc); - eem->fs.out = usb_find_endpoint(eem_fs_function, - f->descriptors, &eem_fs_out_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -272,14 +302,22 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(eem_hs_function); if (!f->hs_descriptors) goto fail; + } + + 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->hs.in = usb_find_endpoint(eem_hs_function, - f->hs_descriptors, &eem_hs_in_desc); - eem->hs.out = usb_find_endpoint(eem_hs_function, - f->hs_descriptors, &eem_hs_out_desc); + /* copy descriptors, and track endpoint copies */ + f->ss_descriptors = usb_copy_descriptors(eem_ss_function); + if (!f->ss_descriptors) + goto fail; } DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", eem->port.in_ep->name, eem->port.out_ep->name); return 0; @@ -287,11 +325,13 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) 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) + if (eem->port.out_ep->desc) eem->port.out_ep->driver_data = NULL; - if (eem->port.in) + if (eem->port.in_ep->desc) eem->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -306,6 +346,8 @@ eem_unbind(struct usb_configuration *c, struct usb_function *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); diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 19fffccc370d..c161a9aaeb7e 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1544,7 +1544,8 @@ static int ffs_func_eps_enable(struct ffs_function *func) ds = ep->descs[ep->descs[1] ? 1 : 0]; ep->ep->driver_data = ep; - ret = usb_ep_enable(ep->ep, ds); + ep->ep->desc = ds; + ret = usb_ep_enable(ep->ep); if (likely(!ret)) { epfile->ep = ep; epfile->in = usb_endpoint_dir_in(ds); diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 598e7e2ab80c..403a48bcf560 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -59,8 +59,6 @@ struct f_hidg { struct cdev cdev; struct usb_function func; struct usb_ep *in_ep; - struct usb_endpoint_descriptor *fs_in_ep_desc; - struct usb_endpoint_descriptor *hs_in_ep_desc; }; static inline struct f_hidg *func_to_hidg(struct usb_function *f) @@ -416,7 +414,6 @@ 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); - const struct usb_endpoint_descriptor *ep_desc; int status = 0; VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); @@ -426,9 +423,13 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (hidg->in_ep->driver_data != NULL) usb_ep_disable(hidg->in_ep); - ep_desc = ep_choose(f->config->cdev->gadget, - hidg->hs_in_ep_desc, hidg->fs_in_ep_desc); - status = usb_ep_enable(hidg->in_ep, ep_desc); + status = config_ep_by_speed(f->config->cdev->gadget, f, + hidg->in_ep); + if (status) { + ERROR(cdev, "config_ep_by_speed FAILED!\n"); + goto fail; + } + status = usb_ep_enable(hidg->in_ep); if (status < 0) { ERROR(cdev, "Enable endpoint FAILED!\n"); goto fail; @@ -498,21 +499,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) if (!f->descriptors) goto fail; - hidg->fs_in_ep_desc = usb_find_endpoint(hidg_fs_descriptors, - f->descriptors, - &hidg_fs_in_ep_desc); - 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; - hidg->hs_in_ep_desc = usb_find_endpoint(hidg_hs_descriptors, - f->hs_descriptors, - &hidg_hs_in_ep_desc); - } else { - hidg->hs_in_ep_desc = NULL; } mutex_init(&hidg->lock); diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index b37960f9e753..ca660d40b11a 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -118,6 +118,49 @@ static struct usb_descriptor_header *hs_loopback_descs[] = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_loop_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_loop_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_descriptor_header *ss_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &ss_loop_source_desc, + (struct usb_descriptor_header *) &ss_loop_source_comp_desc, + (struct usb_descriptor_header *) &ss_loop_sink_desc, + (struct usb_descriptor_header *) &ss_loop_sink_comp_desc, + NULL, +}; + /* function-specific strings: */ static struct usb_string strings_loopback[] = { @@ -175,8 +218,18 @@ autoconf_fail: f->hs_descriptors = hs_loopback_descs; } + /* 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; + } + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + (gadget_is_superspeed(c->cdev->gadget) ? "super" : + (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, loop->in_ep->name, loop->out_ep->name); return 0; } @@ -250,26 +303,27 @@ static int enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) { int result = 0; - const struct usb_endpoint_descriptor *src, *sink; struct usb_ep *ep; struct usb_request *req; unsigned i; - src = ep_choose(cdev->gadget, - &hs_loop_source_desc, &fs_loop_source_desc); - sink = ep_choose(cdev->gadget, - &hs_loop_sink_desc, &fs_loop_sink_desc); - /* one endpoint writes data back IN to the host */ ep = loop->in_ep; - result = usb_ep_enable(ep, src); + result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); + if (result) + return result; + result = usb_ep_enable(ep); if (result < 0) return result; ep->driver_data = loop; /* one endpoint just reads OUT packets */ ep = loop->out_ep; - result = usb_ep_enable(ep, sink); + result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); + if (result) + goto fail0; + + result = usb_ep_enable(ep); if (result < 0) { fail0: ep = loop->in_ep; diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index efb58f9f5aa9..5b9339582007 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2324,18 +2324,6 @@ static int get_next_command(struct fsg_common *common) /*-------------------------------------------------------------------------*/ -static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, - const struct usb_endpoint_descriptor *d) -{ - int rc; - - ep->driver_data = common; - rc = usb_ep_enable(ep, d); - if (rc) - ERROR(common, "can't enable %s, result %d\n", ep->name, rc); - return rc; -} - static int alloc_request(struct fsg_common *common, struct usb_ep *ep, struct usb_request **preq) { @@ -2349,7 +2337,6 @@ static int alloc_request(struct fsg_common *common, struct usb_ep *ep, /* Reset interface setting and re-init endpoint state (toggle etc). */ static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) { - const struct usb_endpoint_descriptor *d; struct fsg_dev *fsg; int i, rc = 0; @@ -2396,20 +2383,26 @@ reset: fsg = common->fsg; /* Enable the endpoints */ - d = fsg_ep_desc(common->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); - rc = enable_endpoint(common, fsg->bulk_in, d); + rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_in); if (rc) goto reset; + fsg->bulk_in->driver_data = common; fsg->bulk_in_enabled = 1; - d = fsg_ep_desc(common->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); - rc = enable_endpoint(common, fsg->bulk_out, d); + rc = config_ep_by_speed(common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_out); if (rc) goto reset; + fsg->bulk_out->driver_data = common; fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + common->bulk_out_maxpacket = + le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 86902a60bcdb..ae69ed7e6b99 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -48,12 +48,6 @@ #define NCM_NDP_HDR_CRC 0x01000000 #define NCM_NDP_HDR_NOCRC 0x00000000 -struct ncm_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; - enum ncm_notify_state { NCM_NOTIFY_NONE, /* don't notify */ NCM_NOTIFY_CONNECT, /* issue CONNECT next */ @@ -66,11 +60,7 @@ struct f_ncm { char ethaddr[14]; - struct ncm_ep_descs fs; - struct ncm_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; u8 notify_state; bool is_open; @@ -802,13 +792,14 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ncm->notify->driver_data) { DBG(cdev, "reset ncm control %d\n", intf); usb_ep_disable(ncm->notify); - } else { + } + + if (!(ncm->notify->desc)) { DBG(cdev, "init ncm ctrl %d\n", intf); - ncm->notify_desc = ep_choose(cdev->gadget, - ncm->hs.notify, - ncm->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, ncm->notify)) + goto fail; } - usb_ep_enable(ncm->notify, ncm->notify_desc); + usb_ep_enable(ncm->notify); ncm->notify->driver_data = ncm; /* Data interface has two altsettings, 0 and 1 */ @@ -829,14 +820,17 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt == 1) { struct net_device *net; - if (!ncm->port.in) { + if (!ncm->port.in_ep->desc || + !ncm->port.out_ep->desc) { DBG(cdev, "init ncm\n"); - ncm->port.in = ep_choose(cdev->gadget, - ncm->hs.in, - ncm->fs.in); - ncm->port.out = ep_choose(cdev->gadget, - ncm->hs.out, - ncm->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + ncm->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + ncm->port.out_ep)) { + ncm->port.in_ep->desc = NULL; + ncm->port.out_ep->desc = NULL; + goto fail; + } } /* TODO */ @@ -1111,7 +1105,7 @@ static void ncm_disable(struct usb_function *f) if (ncm->notify->driver_data) { usb_ep_disable(ncm->notify); ncm->notify->driver_data = NULL; - ncm->notify_desc = NULL; + ncm->notify->desc = NULL; } } @@ -1228,13 +1222,6 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f) if (!f->descriptors) goto fail; - ncm->fs.in = usb_find_endpoint(ncm_fs_function, - f->descriptors, &fs_ncm_in_desc); - ncm->fs.out = usb_find_endpoint(ncm_fs_function, - f->descriptors, &fs_ncm_out_desc); - ncm->fs.notify = usb_find_endpoint(ncm_fs_function, - f->descriptors, &fs_ncm_notify_desc); - /* * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -1252,13 +1239,6 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(ncm_hs_function); if (!f->hs_descriptors) goto fail; - - ncm->hs.in = usb_find_endpoint(ncm_hs_function, - f->hs_descriptors, &hs_ncm_in_desc); - ncm->hs.out = usb_find_endpoint(ncm_hs_function, - f->hs_descriptors, &hs_ncm_out_desc); - ncm->hs.notify = usb_find_endpoint(ncm_hs_function, - f->hs_descriptors, &hs_ncm_notify_desc); } /* @@ -1288,9 +1268,9 @@ fail: /* we might as well release our claims on endpoints */ if (ncm->notify) ncm->notify->driver_data = NULL; - if (ncm->port.out) + if (ncm->port.out_ep->desc) ncm->port.out_ep->driver_data = NULL; - if (ncm->port.in) + if (ncm->port.in_ep->desc) ncm->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 8f8c64371475..394502abeb96 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -39,20 +39,12 @@ * ready to handle the commands. */ -struct obex_ep_descs { - struct usb_endpoint_descriptor *obex_in; - struct usb_endpoint_descriptor *obex_out; -}; - struct f_obex { struct gserial port; u8 ctrl_id; u8 data_id; u8 port_num; u8 can_activate; - - struct obex_ep_descs fs; - struct obex_ep_descs hs; }; static inline struct f_obex *func_to_obex(struct usb_function *f) @@ -227,12 +219,16 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gserial_disconnect(&obex->port); } - if (!obex->port.in_desc) { + if (!obex->port.in->desc || !obex->port.out->desc) { DBG(cdev, "init obex ttyGS%d\n", obex->port_num); - obex->port.in_desc = ep_choose(cdev->gadget, - obex->hs.obex_in, obex->fs.obex_in); - obex->port.out_desc = ep_choose(cdev->gadget, - obex->hs.obex_out, obex->fs.obex_out); + if (config_ep_by_speed(cdev->gadget, f, + obex->port.in) || + config_ep_by_speed(cdev->gadget, f, + obex->port.out)) { + obex->port.out->desc = NULL; + obex->port.in->desc = NULL; + goto fail; + } } if (alt == 1) { @@ -346,11 +342,6 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->descriptors = usb_copy_descriptors(fs_function); - obex->fs.obex_in = usb_find_endpoint(fs_function, - f->descriptors, &obex_fs_ep_in_desc); - obex->fs.obex_out = usb_find_endpoint(fs_function, - f->descriptors, &obex_fs_ep_out_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -364,11 +355,6 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(hs_function); - - obex->hs.obex_in = usb_find_endpoint(hs_function, - f->hs_descriptors, &obex_hs_ep_in_desc); - obex->hs.obex_out = usb_find_endpoint(hs_function, - f->hs_descriptors, &obex_hs_ep_out_desc); } /* Avoid letting this gadget enumerate until the userspace diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index f22fc685ddfd..8f8d3f6cd89e 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -428,17 +428,16 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) spin_lock(&port->lock); __pn_reset(f); if (alt == 1) { - struct usb_endpoint_descriptor *out, *in; int i; - out = ep_choose(gadget, - &pn_hs_sink_desc, - &pn_fs_sink_desc); - in = ep_choose(gadget, - &pn_hs_source_desc, - &pn_fs_source_desc); - usb_ep_enable(fp->out_ep, out); - usb_ep_enable(fp->in_ep, in); + if (config_ep_by_speed(gadget, f, fp->in_ep) || + config_ep_by_speed(gadget, f, fp->out_ep)) { + fp->in_ep->desc = NULL; + fp->out_ep->desc = NULL; + return -EINVAL; + } + usb_ep_enable(fp->out_ep); + usb_ep_enable(fp->in_ep); port->usb = fp; fp->out_ep->driver_data = fp; diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index fa12ec8364ef..8f3eae90919f 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -76,23 +76,13 @@ * - MS-Windows drivers sometimes emit undocumented requests. */ -struct rndis_ep_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - struct usb_endpoint_descriptor *notify; -}; - struct f_rndis { struct gether port; u8 ctrl_id, data_id; u8 ethaddr[ETH_ALEN]; int config; - struct rndis_ep_descs fs; - struct rndis_ep_descs hs; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; struct usb_request *notify_req; atomic_t notify_count; }; @@ -105,10 +95,12 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f) /* peak (theoretical) bulk transfer rate in bits-per-second */ static unsigned int bitrate(struct usb_gadget *g) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return 13 * 1024 * 8 * 1000 * 8; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return 13 * 512 * 8 * 1000 * 8; else - return 19 * 64 * 1 * 1000 * 8; + return 19 * 64 * 1 * 1000 * 8; } /*-------------------------------------------------------------------------*/ @@ -226,6 +218,7 @@ static struct usb_endpoint_descriptor fs_out_desc = { static struct usb_descriptor_header *eth_fs_function[] = { (struct usb_descriptor_header *) &rndis_iad_descriptor, + /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, @@ -233,6 +226,7 @@ static struct usb_descriptor_header *eth_fs_function[] = { (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface has no altsetting */ (struct usb_descriptor_header *) &rndis_data_intf, (struct usb_descriptor_header *) &fs_in_desc, @@ -251,6 +245,7 @@ static struct usb_endpoint_descriptor hs_notify_desc = { .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, }; + static struct usb_endpoint_descriptor hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -271,6 +266,7 @@ static struct usb_endpoint_descriptor hs_out_desc = { static struct usb_descriptor_header *eth_hs_function[] = { (struct usb_descriptor_header *) &rndis_iad_descriptor, + /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, @@ -278,6 +274,7 @@ static struct usb_descriptor_header *eth_hs_function[] = { (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface has no altsetting */ (struct usb_descriptor_header *) &rndis_data_intf, (struct usb_descriptor_header *) &hs_in_desc, @@ -285,6 +282,76 @@ static struct usb_descriptor_header *eth_hs_function[] = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { + .bLength = sizeof ss_intr_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ss_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { + .bLength = sizeof ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *eth_ss_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, + + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &ss_notify_desc, + (struct usb_descriptor_header *) &ss_intr_comp_desc, + + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &ss_in_desc, + (struct usb_descriptor_header *) &ss_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_out_desc, + (struct usb_descriptor_header *) &ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string rndis_string_defs[] = { @@ -484,13 +551,13 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (rndis->notify->driver_data) { VDBG(cdev, "reset rndis control %d\n", intf); usb_ep_disable(rndis->notify); - } else { + } + if (!rndis->notify->desc) { VDBG(cdev, "init rndis ctrl %d\n", intf); - rndis->notify_desc = ep_choose(cdev->gadget, - rndis->hs.notify, - rndis->fs.notify); + if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) + goto fail; } - usb_ep_enable(rndis->notify, rndis->notify_desc); + usb_ep_enable(rndis->notify); rndis->notify->driver_data = rndis; } else if (intf == rndis->data_id) { @@ -501,12 +568,16 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) gether_disconnect(&rndis->port); } - if (!rndis->port.in) { + if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { DBG(cdev, "init rndis\n"); - rndis->port.in = ep_choose(cdev->gadget, - rndis->hs.in, rndis->fs.in); - rndis->port.out = ep_choose(cdev->gadget, - rndis->hs.out, rndis->fs.out); + if (config_ep_by_speed(cdev->gadget, f, + rndis->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, + rndis->port.out_ep)) { + rndis->port.in_ep->desc = NULL; + rndis->port.out_ep->desc = NULL; + goto fail; + } } /* Avoid ZLPs; they can be troublesome. */ @@ -662,13 +733,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (!f->descriptors) goto fail; - rndis->fs.in = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_in_desc); - rndis->fs.out = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_out_desc); - rndis->fs.notify = usb_find_endpoint(eth_fs_function, - f->descriptors, &fs_notify_desc); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -683,16 +747,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(eth_hs_function); - if (!f->hs_descriptors) goto fail; + } - rndis->hs.in = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_in_desc); - rndis->hs.out = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_out_desc); - rndis->hs.notify = usb_find_endpoint(eth_hs_function, - f->hs_descriptors, &hs_notify_desc); + 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; } rndis->port.open = rndis_open; @@ -719,12 +789,15 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) */ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", rndis->port.in_ep->name, rndis->port.out_ep->name, rndis->notify->name); return 0; fail: + if (gadget_is_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) @@ -738,9 +811,9 @@ fail: /* we might as well release our claims on endpoints */ if (rndis->notify) rndis->notify->driver_data = NULL; - if (rndis->port.out) + if (rndis->port.out_ep->desc) rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in) + if (rndis->port.in_ep->desc) rndis->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -756,6 +829,8 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); rndis_exit(); + 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); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 490b00b01a7d..91fdf790ed20 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -27,18 +27,10 @@ * if you can arrange appropriate host side drivers. */ -struct gser_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; -}; - struct f_gser { struct gserial port; u8 data_id; u8 port_num; - - struct gser_descs fs; - struct gser_descs hs; }; static inline struct f_gser *func_to_gser(struct usb_function *f) @@ -136,12 +128,15 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (gser->port.in->driver_data) { DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); - } else { + } + if (!gser->port.in->desc || !gser->port.out->desc) { DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); - gser->port.in_desc = ep_choose(cdev->gadget, - gser->hs.in, gser->fs.in); - gser->port.out_desc = ep_choose(cdev->gadget, - gser->hs.out, gser->fs.out); + if (!config_ep_by_speed(cdev->gadget, f, gser->port.in) || + !config_ep_by_speed(cdev->gadget, f, gser->port.out)) { + gser->port.in->desc = NULL; + gser->port.out->desc = NULL; + return -EINVAL; + } } gserial_connect(&gser->port, gser->port_num); return 0; @@ -193,12 +188,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->descriptors = usb_copy_descriptors(gser_fs_function); - gser->fs.in = usb_find_endpoint(gser_fs_function, - f->descriptors, &gser_fs_in_desc); - gser->fs.out = usb_find_endpoint(gser_fs_function, - f->descriptors, &gser_fs_out_desc); - - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -211,11 +200,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(gser_hs_function); - - gser->hs.in = usb_find_endpoint(gser_hs_function, - f->hs_descriptors, &gser_hs_in_desc); - gser->hs.out = usb_find_endpoint(gser_hs_function, - f->hs_descriptors, &gser_hs_out_desc); } DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index e403a534dd55..e18b4f520951 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -131,6 +131,49 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, +}; + +static struct usb_endpoint_descriptor ss_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +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_descriptor_header *ss_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf, + (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, + NULL, +}; + /* function-specific strings: */ static struct usb_string strings_sourcesink[] = { @@ -187,8 +230,18 @@ autoconf_fail: f->hs_descriptors = hs_source_sink_descs; } + /* 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; + } + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + (gadget_is_superspeed(c->cdev->gadget) ? "super" : + (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, ss->in_ep->name, ss->out_ep->name); return 0; } @@ -343,15 +396,14 @@ static int enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) { int result = 0; - const struct usb_endpoint_descriptor *src, *sink; struct usb_ep *ep; - src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); - sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); - /* one endpoint writes (sources) zeroes IN (to the host) */ ep = ss->in_ep; - result = usb_ep_enable(ep, src); + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + return result; + result = usb_ep_enable(ep); if (result < 0) return result; ep->driver_data = ss; @@ -367,7 +419,10 @@ fail: /* one endpoint reads (sinks) anything OUT (from the host) */ ep = ss->out_ep; - result = usb_ep_enable(ep, sink); + result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); + if (result) + goto fail; + result = usb_ep_enable(ep); if (result < 0) goto fail; ep->driver_data = ss; @@ -435,6 +490,8 @@ static int sourcesink_setup(struct usb_configuration *c, u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + req->length = USB_BUFSIZ; + /* composite driver infrastructure handles everything except * the two control test requests. */ diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 8675ca415329..3dc53754ab60 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -57,18 +57,10 @@ * caring about specific product and vendor IDs. */ -struct geth_descs { - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; -}; - struct f_gether { struct gether port; char ethaddr[14]; - - struct geth_descs fs; - struct geth_descs hs; }; static inline struct f_gether *func_to_geth(struct usb_function *f) @@ -209,6 +201,46 @@ static struct usb_descriptor_header *hs_eth_function[] __initdata = { NULL, }; +/* super speed support: */ + +static struct usb_endpoint_descriptor ss_subset_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_subset_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc __initdata = { + .bLength = sizeof ss_subset_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ss_eth_function[] __initdata = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &mdlm_header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &ss_subset_in_desc, + (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, + (struct usb_descriptor_header *) &ss_subset_out_desc, + (struct usb_descriptor_header *) &ss_subset_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string geth_string_defs[] = { @@ -243,10 +275,12 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } DBG(cdev, "init + activate cdc subset\n"); - geth->port.in = ep_choose(cdev->gadget, - geth->hs.in, geth->fs.in); - geth->port.out = ep_choose(cdev->gadget, - geth->hs.out, geth->fs.out); + if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) || + config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) { + geth->port.in_ep->desc = NULL; + geth->port.out_ep->desc = NULL; + return -EINVAL; + } net = gether_connect(&geth->port); return IS_ERR(net) ? PTR_ERR(net) : 0; @@ -296,12 +330,8 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->descriptors = usb_copy_descriptors(fs_eth_function); - - geth->fs.in = usb_find_endpoint(fs_eth_function, - f->descriptors, &fs_subset_in_desc); - geth->fs.out = usb_find_endpoint(fs_eth_function, - f->descriptors, &fs_subset_out_desc); - + if (!f->descriptors) + goto fail; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -315,11 +345,20 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(hs_eth_function); + if (!f->hs_descriptors) + goto fail; + } - geth->hs.in = usb_find_endpoint(hs_eth_function, - f->hs_descriptors, &hs_subset_in_desc); - geth->hs.out = usb_find_endpoint(hs_eth_function, - f->hs_descriptors, &hs_subset_out_desc); + 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; + + /* copy descriptors, and track endpoint copies */ + f->ss_descriptors = usb_copy_descriptors(ss_eth_function); + if (!f->ss_descriptors) + goto fail; } /* NOTE: all that is done without knowing or caring about @@ -328,15 +367,21 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) */ DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", geth->port.in_ep->name, geth->port.out_ep->name); return 0; fail: + 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 (geth->port.out) + if (geth->port.out_ep->desc) geth->port.out_ep->driver_data = NULL; - if (geth->port.in) + if (geth->port.in_ep->desc) geth->port.in_ep->driver_data = NULL; ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); @@ -347,6 +392,8 @@ fail: static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { + 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); diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index be446b7e7eaa..7a8b9aa4aea5 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -262,8 +262,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_CONNECTED) return 0; - if (uvc->video.ep) - usb_ep_enable(uvc->video.ep, &uvc_streaming_ep); + if (uvc->video.ep) { + uvc->video.ep->desc = &uvc_streaming_ep; + usb_ep_enable(uvc->video.ep); + } memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; @@ -649,7 +651,7 @@ uvc_bind_config(struct usb_configuration *c, if (ret) kfree(uvc); - return 0; + return ret; error: kfree(uvc); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 0360f56221ea..639e14a2fd15 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -929,6 +929,7 @@ static int standard_setup_req(struct fsg_dev *fsg, 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; @@ -936,6 +937,11 @@ static int standard_setup_req(struct fsg_dev *fsg, VDBG(fsg, "get device qualifier\n"); if (!gadget_is_dualspeed(fsg->gadget)) 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; @@ -2713,7 +2719,8 @@ static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, int rc; ep->driver_data = fsg; - rc = usb_ep_enable(ep, d); + ep->desc = d; + rc = usb_ep_enable(ep); if (rc) ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); return rc; @@ -3416,7 +3423,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) } /* Fix up the descriptors */ - device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; device_desc.idVendor = cpu_to_le16(mod_data.vendor); device_desc.idProduct = cpu_to_le16(mod_data.product); device_desc.bcdDevice = cpu_to_le16(mod_data.release); @@ -3430,9 +3436,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) if (gadget_is_dualspeed(gadget)) { fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL; - /* Assume ep0 uses the same maxpacket value for both speeds */ - dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; - /* Assume endpoint addresses are the same for both speeds */ fsg_hs_bulk_in_desc.bEndpointAddress = fsg_fs_bulk_in_desc.bEndpointAddress; @@ -3486,6 +3489,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) } 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); diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 3a68e09309f7..3bf872e1ad39 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1927,6 +1927,10 @@ 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); + /* defined in usb_gadget.h */ static struct usb_gadget_ops qe_gadget_ops = { .get_frame = qe_get_frame, @@ -1935,6 +1939,8 @@ static struct usb_gadget_ops qe_gadget_ops = { .vbus_session = qe_vbus_session, .vbus_draw = qe_vbus_draw, .pullup = qe_pullup, + .start = fsl_qe_start, + .stop = fsl_qe_stop, }; /*------------------------------------------------------------------------- @@ -2320,7 +2326,7 @@ static irqreturn_t qe_udc_irq(int irq, void *_udc) /*------------------------------------------------------------------------- Gadget driver probe and unregister. --------------------------------------------------------------------------*/ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int fsl_qe_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int retval; @@ -2369,9 +2375,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, udc_controller->gadget.name, driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int fsl_qe_stop(struct usb_gadget_driver *driver) { struct qe_ep *loop_ep; unsigned long flags; @@ -2411,7 +2416,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /* udc structure's alloc and setup, include ep-param alloc */ static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev) @@ -2662,11 +2666,17 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev) if (ret) goto err6; + ret = usb_add_gadget_udc(&ofdev->dev, &udc_controller->gadget); + if (ret) + goto err7; + dev_info(udc_controller->dev, "%s USB controller initialized as device\n", (udc_controller->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; +err7: + device_unregister(&udc_controller->gadget.dev); err6: free_irq(udc_controller->usb_irq, udc_controller); err5: @@ -2721,6 +2731,8 @@ static int __devexit qe_udc_remove(struct platform_device *ofdev) if (!udc_controller) return -ENODEV; + usb_del_gadget_udc(&udc_controller->gadget); + udc_controller->done = &done; tasklet_disable(&udc_controller->rx_tasklet); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 3e59035e6de8..de24a4233c25 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1244,6 +1244,9 @@ 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); /* defined in gadget.h */ static struct usb_gadget_ops fsl_gadget_ops = { .get_frame = fsl_get_frame, @@ -1252,6 +1255,8 @@ static struct usb_gadget_ops fsl_gadget_ops = { .vbus_session = fsl_vbus_session, .vbus_draw = fsl_vbus_draw, .pullup = fsl_pullup, + .start = fsl_start, + .stop = fsl_stop, }; /* Set protocol stall on ep0, protocol stall will automatically be cleared @@ -1927,7 +1932,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) * Hook to gadget drivers * Called by initialization code of gadget drivers *----------------------------------------------------------------*/ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int fsl_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int retval = -ENODEV; @@ -1995,10 +2000,9 @@ out: retval); return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); /* Disconnect from gadget driver */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int fsl_stop(struct usb_gadget_driver *driver) { struct fsl_ep *loop_ep; unsigned long flags; @@ -2041,7 +2045,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*------------------------------------------------------------------------- PROC File System Support @@ -2590,9 +2593,16 @@ static int __init fsl_udc_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_unregister; } + + ret = usb_add_gadget_udc(&pdev->dev, &udc_controller->gadget); + if (ret) + goto err_del_udc; + create_proc_file(); return 0; +err_del_udc: + dma_pool_destroy(udc_controller->td_pool); err_unregister: device_unregister(&udc_controller->gadget.dev); err_free_irq: @@ -2624,6 +2634,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) if (!udc_controller) return -ENODEV; + + usb_del_gadget_udc(&udc_controller->gadget); udc_controller->done = &done; fsl_udc_clk_release(); diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 763d462454b9..24a924330c81 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -767,56 +767,6 @@ static void fusb300_rdfifo(struct fusb300_ep *ep, } while (!reg); } -/* write data to fifo */ -static void fusb300_wrfifo(struct fusb300_ep *ep, - struct fusb300_request *req) -{ - int i = 0; - u8 *tmp; - u32 data, reg; - struct fusb300 *fusb300 = ep->fusb300; - - tmp = req->req.buf; - req->req.actual = req->req.length; - - for (i = (req->req.length >> 2); i > 0; i--) { - data = *tmp | *(tmp + 1) << 8 | - *(tmp + 2) << 16 | *(tmp + 3) << 24; - - iowrite32(data, fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - tmp += 4; - } - - switch (req->req.length % 4) { - case 1: - data = *tmp; - iowrite32(data, fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - break; - case 2: - data = *tmp | *(tmp + 1) << 8; - iowrite32(data, fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - break; - case 3: - data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; - iowrite32(data, fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - break; - default: - break; - } - - do { - reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); - reg &= FUSB300_IGR1_SYNF0_EMPTY_INT; - if (i) - printk(KERN_INFO"sync fifo is not empty!\n"); - i++; - } while (!reg); -} - static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep) { u8 value; @@ -980,11 +930,6 @@ static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) } \ } while (0) -static void fusb300_ep0_complete(struct usb_ep *ep, - struct usb_request *req) -{ -} - static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) { u8 *p = (u8 *)ctrl; @@ -1029,17 +974,6 @@ static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) return ret; } -static void fusb300_set_ep_bycnt(struct fusb300_ep *ep, u32 bycnt) -{ - struct fusb300 *fusb300 = ep->fusb300; - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); - - reg &= ~FUSB300_FFR_BYCNT; - reg |= bycnt & FUSB300_FFR_BYCNT; - - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); -} - static void done(struct fusb300_ep *ep, struct fusb300_request *req, int status) { @@ -1063,8 +997,8 @@ static void done(struct fusb300_ep *ep, struct fusb300_request *req, fusb300_set_cxdone(ep->fusb300); } -void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, - struct fusb300_request *req) +static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d, + u32 len) { u32 value; u32 reg; @@ -1076,10 +1010,9 @@ void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, reg &= FUSB300_EPPRD0_H; } while (reg); - iowrite32((u32) req->req.buf, ep->fusb300->reg + - FUSB300_OFFSET_EPPRD_W1(ep->epnum)); + iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum)); - value = FUSB300_EPPRD0_BTC(req->req.length) | FUSB300_EPPRD0_H | + value = FUSB300_EPPRD0_BTC(len) | FUSB300_EPPRD0_H | FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I; iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); @@ -1116,13 +1049,12 @@ static void fusb300_set_idma(struct fusb300_ep *ep, struct fusb300_request *req) { dma_addr_t d; - u8 *tmp = NULL; d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE); if (dma_mapping_error(NULL, d)) { - kfree(req->req.buf); printk(KERN_DEBUG "dma_mapping_error\n"); + return; } dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE); @@ -1130,17 +1062,11 @@ static void fusb300_set_idma(struct fusb300_ep *ep, fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); - tmp = req->req.buf; - req->req.buf = (u8 *)d; - - fusb300_fill_idma_prdtbl(ep, req); + fusb300_fill_idma_prdtbl(ep, d, req->req.length); /* check idma is done */ fusb300_wait_idma_finished(ep); - req->req.buf = tmp; - - if (d) - dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE); + dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE); } static void in_ep_fifo_handler(struct fusb300_ep *ep) @@ -1148,14 +1074,8 @@ static void in_ep_fifo_handler(struct fusb300_ep *ep) struct fusb300_request *req = list_entry(ep->queue.next, struct fusb300_request, queue); - if (req->req.length) { -#if 0 - fusb300_set_ep_bycnt(ep, req->req.length); - fusb300_wrfifo(ep, req); -#else + if (req->req.length) fusb300_set_idma(ep, req); -#endif - } done(ep, req, 0); } @@ -1500,7 +1420,7 @@ static void init_controller(struct fusb300 *fusb300) /*------------------------------------------------------------------------*/ static struct fusb300 *the_controller; -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int fusb300_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct fusb300 *fusb300 = the_controller; @@ -1544,9 +1464,8 @@ error: return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int fusb300_udc_stop(struct usb_gadget_driver *driver) { struct fusb300 *fusb300 = the_controller; @@ -1562,7 +1481,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*--------------------------------------------------------------------------*/ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active) @@ -1572,12 +1490,15 @@ static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active) static struct usb_gadget_ops fusb300_gadget_ops = { .pullup = fusb300_udc_pullup, + .start = fusb300_udc_start, + .stop = fusb300_udc_stop, }; static int __exit fusb300_remove(struct platform_device *pdev) { struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev); + usb_del_gadget_udc(&fusb300->gadget); iounmap(fusb300->reg); free_irq(platform_get_irq(pdev, 0), fusb300); @@ -1702,9 +1623,15 @@ static int __init fusb300_probe(struct platform_device *pdev) goto clean_up3; init_controller(fusb300); + ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget); + if (ret) + goto err_add_udc; + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; +err_add_udc: + fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); clean_up3: free_irq(ires->start, fusb300); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index ebf6970a10bf..704c2800ac00 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -162,6 +162,7 @@ static struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, .strings = gfs_dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = gfs_unbind, .iProduct = DRIVER_DESC, }; diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index bcdac7c73e89..f3a83cd0ef50 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -15,150 +15,40 @@ #ifndef __GADGET_CHIPS_H #define __GADGET_CHIPS_H -#ifdef CONFIG_USB_GADGET_NET2280 -#define gadget_is_net2280(g) !strcmp("net2280", (g)->name) -#else -#define gadget_is_net2280(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_AMD5536UDC -#define gadget_is_amd5536udc(g) !strcmp("amd5536udc", (g)->name) -#else -#define gadget_is_amd5536udc(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_DUMMY_HCD -#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) -#else -#define gadget_is_dummy(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_PXA25X -#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name) -#else -#define gadget_is_pxa(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_GOKU -#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name) -#else -#define gadget_is_goku(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_OMAP -#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) -#else -#define gadget_is_omap(g) 0 -#endif - -/* various unstable versions available */ -#ifdef CONFIG_USB_GADGET_PXA27X -#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) -#else -#define gadget_is_pxa27x(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_ATMEL_USBA -#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name) -#else -#define gadget_is_atmel_usba(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_S3C2410 -#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) -#else -#define gadget_is_s3c2410(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_AT91 -#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name) -#else -#define gadget_is_at91(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_IMX -#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) -#else -#define gadget_is_imx(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_FSL_USB2 -#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name) -#else -#define gadget_is_fsl_usb2(g) 0 -#endif - -/* Mentor high speed "dual role" controller, in peripheral role */ -#ifdef CONFIG_USB_GADGET_MUSB_HDRC -#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name) -#else -#define gadget_is_musbhdrc(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_LANGWELL -#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name)) -#else -#define gadget_is_langwell(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_M66592 -#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name) -#else -#define gadget_is_m66592(g) 0 -#endif - -/* Freescale CPM/QE UDC SUPPORT */ -#ifdef CONFIG_USB_GADGET_FSL_QE -#define gadget_is_fsl_qe(g) !strcmp("fsl_qe_udc", (g)->name) -#else -#define gadget_is_fsl_qe(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_CI13XXX_PCI -#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) -#else -#define gadget_is_ci13xxx_pci(g) 0 -#endif - -// CONFIG_USB_GADGET_SX2 -// CONFIG_USB_GADGET_AU1X00 -// ... - -#ifdef CONFIG_USB_GADGET_R8A66597 -#define gadget_is_r8a66597(g) !strcmp("r8a66597_udc", (g)->name) -#else -#define gadget_is_r8a66597(g) 0 -#endif - -#ifdef CONFIG_USB_S3C_HSOTG -#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) -#else -#define gadget_is_s3c_hsotg(g) 0 -#endif - -#ifdef CONFIG_USB_S3C_HSUDC -#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) -#else -#define gadget_is_s3c_hsudc(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_EG20T -#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) -#else -#define gadget_is_pch(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_CI13XXX_MSM +/* + * NOTICE: the entries below are alphabetical and should be kept + * that way. + * + * Always be sure to add new entries to the correct position or + * accept the bashing later. + * + * 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)) -#else -#define gadget_is_ci13xxx_msm(g) 0 -#endif - -#ifdef CONFIG_USB_GADGET_RENESAS_USBHS -#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name)) -#else -#define gadget_is_renesas_usbhs(g) 0 -#endif +#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) +#define gadget_is_dummy(g) (!strcmp("dummy_udc", (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 @@ -223,6 +113,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x29; else if (gadget_is_s3c_hsudc(gadget)) return 0x30; + else if (gadget_is_net2272(gadget)) + return 0x31; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 47b86b99d449..8b9220e128a7 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -537,14 +537,16 @@ static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags) struct usb_ep *ep; unsigned i; - err = usb_ep_enable(dev->in_ep, &bulk_in_desc); + dev->in_ep->desc = &bulk_in_desc; + err = usb_ep_enable(dev->in_ep); if (err) { ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err); goto fail; } dev->in_ep->driver_data = dev; - err = usb_ep_enable(dev->out_ep, &bulk_out_desc); + dev->out_ep->desc = &bulk_out_desc; + err = usb_ep_enable(dev->out_ep); if (err) { ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err); goto fail; @@ -693,6 +695,7 @@ static int gmidi_setup(struct usb_gadget *gadget, switch (w_value >> 8) { case USB_DT_DEVICE: + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; value = min(w_length, (u16) sizeof(device_desc)); memcpy(req->buf, &device_desc, value); break; @@ -1247,8 +1250,6 @@ autoconf_fail: dev->req->complete = gmidi_setup_complete; - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - gadget->ep0->driver_data = dev; INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index bf6e11c758d5..7f87805cddc4 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -996,8 +996,14 @@ 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 const struct usb_gadget_ops goku_ops = { .get_frame = goku_get_frame, + .start = goku_start, + .stop = goku_stop, // no remote wakeup // not selfpowered }; @@ -1344,7 +1350,7 @@ static struct goku_udc *the_controller; * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int goku_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct goku_udc *dev = the_controller; @@ -1382,7 +1388,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, DBG(dev, "registered gadget driver '%s'\n", driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) @@ -1408,7 +1413,7 @@ stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) udc_enable(dev); } -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int goku_stop(struct usb_gadget_driver *driver) { struct goku_udc *dev = the_controller; unsigned long flags; @@ -1429,8 +1434,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) DBG(dev, "unregistered driver '%s'\n", driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -1730,6 +1733,8 @@ static void goku_remove(struct pci_dev *pdev) DBG(dev, "%s\n", __func__); + usb_del_gadget_udc(&dev->gadget); + BUG_ON(dev->driver); #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -1854,6 +1859,10 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err; } dev->registered = 1; + retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + if (retval) + goto err; + return 0; err: diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 2523e54097bd..9fb575034a0e 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -255,6 +255,7 @@ static struct usb_composite_driver hidg_driver = { .name = "g_hid", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(hid_unbind), }; diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index ade40066decf..692fd9b2248b 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1237,9 +1237,14 @@ irq_handler_t intr_handler(int i) ******************************************************************************* */ +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 = { @@ -1324,7 +1329,7 @@ static struct imx_udc_struct controller = { * USB gadget driver functions ******************************************************************************* */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int imx_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct imx_udc_struct *imx_usb = &controller; @@ -1368,9 +1373,8 @@ fail: imx_usb->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int imx_udc_stop(struct usb_gadget_driver *driver) { struct imx_udc_struct *imx_usb = &controller; @@ -1394,7 +1398,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /******************************************************************************* * Module functions @@ -1504,8 +1507,14 @@ static int __init imx_udc_probe(struct platform_device *pdev) imx_usb->timer.function = handle_config; imx_usb->timer.data = (unsigned long)imx_usb; - return 0; + 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); @@ -1525,6 +1534,7 @@ static int __exit imx_udc_remove(struct platform_device *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); diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index a56876aaf76c..1b240990448f 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -832,14 +832,16 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) switch (data->dev->gadget->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: - value = usb_ep_enable (ep, &data->desc); + ep->desc = &data->desc; + value = usb_ep_enable(ep); if (value == 0) data->state = STATE_EP_ENABLED; break; #ifdef CONFIG_USB_GADGET_DUALSPEED case USB_SPEED_HIGH: /* fails if caller didn't provide that descriptor... */ - value = usb_ep_enable (ep, &data->hs_desc); + ep->desc = &data->hs_desc; + value = usb_ep_enable(ep); if (value == 0) data->state = STATE_EP_ENABLED; break; @@ -1345,7 +1347,7 @@ static void make_qualifier (struct dev_data *dev) qual.bDeviceProtocol = desc->bDeviceProtocol; /* assumes ep0 uses the same value for both speeds ... */ - qual.bMaxPacketSize0 = desc->bMaxPacketSize0; + qual.bMaxPacketSize0 = dev->gadget->ep0->maxpacket; qual.bNumConfigurations = 1; qual.bRESERVED = 0; @@ -1402,7 +1404,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } dev->state = STATE_DEV_CONNECTED; - dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; INFO (dev, "connected\n"); event = next_event (dev, GADGETFS_CONNECT); @@ -1430,6 +1431,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE: value = min (w_length, (u16) sizeof *dev->dev); + dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket; req->buf = dev->dev; break; #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -1710,7 +1712,6 @@ gadgetfs_bind (struct usb_gadget *gadget) set_gadget_data (gadget, dev); dev->gadget = gadget; gadget->ep0->driver_data = dev; - dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; /* preallocate control response and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 9cee88a43a73..a06e2c27b435 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -593,8 +593,8 @@ static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req) /* ep0 */ dev_vdbg(&dev->pdev->dev, "%s-%s\n", ep->name, DIR_STRING(ep)); - dev_vdbg(&dev->pdev->dev, "ep_dqh[%d] addr: 0x%08x\n", - i, (u32)&(dev->ep_dqh[i])); + 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)); @@ -1321,7 +1321,9 @@ static int langwell_pullup(struct usb_gadget *_gadget, int is_on) return 0; } - +static int langwell_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int langwell_stop(struct usb_gadget_driver *driver); /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops langwell_ops = { @@ -1342,6 +1344,9 @@ static const struct usb_gadget_ops langwell_ops = { /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = langwell_pullup, + + .start = langwell_start, + .stop = langwell_stop, }; @@ -1852,7 +1857,7 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); * the driver might get unbound. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int langwell_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct langwell_udc *dev = the_controller; @@ -1914,11 +1919,9 @@ err_unbind: dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); - /* unregister gadget driver */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int langwell_stop(struct usb_gadget_driver *driver) { struct langwell_udc *dev = the_controller; unsigned long flags; @@ -1965,8 +1968,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -3270,7 +3271,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, /* allocate device dQH memory */ size = dev->ep_max * sizeof(struct langwell_dqh); - dev_vdbg(&dev->pdev->dev, "orig size = %d\n", size); + dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size); if (size < DQH_ALIGNMENT) size = DQH_ALIGNMENT; else if ((size % DQH_ALIGNMENT) != 0) { @@ -3285,7 +3286,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, goto error; } dev->ep_dqh_size = size; - dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %d\n", dev->ep_dqh_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); @@ -3373,6 +3374,10 @@ static int langwell_udc_probe(struct pci_dev *pdev, 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; @@ -3403,6 +3408,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) 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); @@ -3464,7 +3470,7 @@ static int langwell_udc_resume(struct pci_dev *pdev) /* allocate device dQH memory */ size = dev->ep_max * sizeof(struct langwell_dqh); - dev_vdbg(&dev->pdev->dev, "orig size = %d\n", size); + dev_vdbg(&dev->pdev->dev, "orig size = %zd\n", size); if (size < DQH_ALIGNMENT) size = DQH_ALIGNMENT; else if ((size % DQH_ALIGNMENT) != 0) { @@ -3478,7 +3484,7 @@ static int langwell_udc_resume(struct pci_dev *pdev) return -ENOMEM; } dev->ep_dqh_size = size; - dev_vdbg(&dev->pdev->dev, "ep_dqh_size = %d\n", dev->ep_dqh_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", diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 084aa080a2d5..491f825ed5c9 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006-2007 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -691,6 +691,7 @@ static void init_controller(struct m66592 *m66592) static void disable_controller(struct m66592 *m66592) { + m66592_bclr(m66592, M66592_UTST, M66592_TESTMODE); if (!m66592->pdata->on_chip) { m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); udelay(1); @@ -780,7 +781,7 @@ static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req) /* write fifo */ if (req->req.buf) { if (size > 0) - m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + m66592_write_fifo(m66592, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) m66592_bset(m66592, M66592_BVAL, ep->fifoctr); } @@ -826,7 +827,7 @@ static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req) /* write fifo */ if (req->req.buf) { - m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + m66592_write_fifo(m66592, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || ((bufsize != ep->ep.maxpacket) @@ -1048,10 +1049,30 @@ static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) { + u16 tmp; + int timeout = 3000; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - control_end(m66592, 1); + switch (le16_to_cpu(ctrl->wValue)) { + case USB_DEVICE_TEST_MODE: + control_end(m66592, 1); + /* Wait for the completion of status stage */ + do { + tmp = m66592_read(m66592, M66592_INTSTS0) & + M66592_CTSQ; + udelay(1); + } while (tmp != M66592_CS_IDST || timeout-- > 0); + + if (tmp == M66592_CS_IDST) + m66592_bset(m66592, + le16_to_cpu(ctrl->wIndex >> 8), + M66592_TESTMODE); + break; + default: + pipe_stall(m66592, 0); + break; + } break; case USB_RECIP_INTERFACE: control_end(m66592, 1); @@ -1454,7 +1475,7 @@ static struct usb_ep_ops m66592_ep_ops = { /*-------------------------------------------------------------------------*/ static struct m66592 *the_controller; -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int m66592_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct m66592 *m66592 = the_controller; @@ -1506,9 +1527,8 @@ error: return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int m66592_stop(struct usb_gadget_driver *driver) { struct m66592 *m66592 = the_controller; unsigned long flags; @@ -1533,7 +1553,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) m66592->driver = NULL; return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ static int m66592_get_frame(struct usb_gadget *_gadget) @@ -1542,14 +1561,34 @@ static int m66592_get_frame(struct usb_gadget *_gadget) return m66592_read(m66592, M66592_FRMNUM) & 0x03FF; } +static int m66592_pullup(struct usb_gadget *gadget, int is_on) +{ + struct m66592 *m66592 = gadget_to_m66592(gadget); + unsigned long flags; + + spin_lock_irqsave(&m66592->lock, flags); + if (is_on) + m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG); + else + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + spin_unlock_irqrestore(&m66592->lock, flags); + + return 0; +} + static struct usb_gadget_ops m66592_gadget_ops = { .get_frame = m66592_get_frame, + .start = m66592_start, + .stop = m66592_stop, + .pullup = m66592_pullup, }; static int __exit m66592_remove(struct platform_device *pdev) { struct m66592 *m66592 = dev_get_drvdata(&pdev->dev); + usb_del_gadget_udc(&m66592->gadget); + del_timer_sync(&m66592->timer); iounmap(m66592->reg); free_irq(platform_get_irq(pdev, 0), m66592); @@ -1691,9 +1730,16 @@ static int __init m66592_probe(struct platform_device *pdev) init_controller(m66592); + ret = usb_add_gadget_udc(&pdev->dev, &m66592->gadget); + if (ret) + goto err_add_udc; + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; +err_add_udc: + m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); + clean_up3: #ifdef CONFIG_HAVE_CLK if (m66592->pdata->on_chip) { diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index c3caf1ac73ce..7b93d579af37 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -3,7 +3,7 @@ * * Copyright (C) 2006-2007 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -561,11 +561,26 @@ static inline void m66592_write(struct m66592 *m66592, u16 val, iowrite16(val, m66592->reg + offset); } +static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, + unsigned long offset) +{ + u16 tmp; + tmp = m66592_read(m66592, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + m66592_write(m66592, tmp, offset); +} + +#define m66592_bclr(m66592, val, offset) \ + m66592_mdfy(m66592, 0, val, offset) +#define m66592_bset(m66592, val, offset) \ + m66592_mdfy(m66592, val, 0, offset) + static inline void m66592_write_fifo(struct m66592 *m66592, - unsigned long offset, + struct m66592_ep *ep, void *buf, unsigned long len) { - void __iomem *fifoaddr = m66592->reg + offset; + void __iomem *fifoaddr = m66592->reg + ep->fifoaddr; if (m66592->pdata->on_chip) { unsigned long count; @@ -591,26 +606,15 @@ static inline void m66592_write_fifo(struct m66592 *m66592, iowrite16_rep(fifoaddr, buf, len); if (odd) { unsigned char *p = buf + len*2; + if (m66592->pdata->wr0_shorted_to_wr1) + m66592_bclr(m66592, M66592_MBW_16, ep->fifosel); iowrite8(*p, fifoaddr); + if (m66592->pdata->wr0_shorted_to_wr1) + m66592_bset(m66592, M66592_MBW_16, ep->fifosel); } } } -static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, - unsigned long offset) -{ - u16 tmp; - tmp = m66592_read(m66592, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - m66592_write(m66592, tmp, offset); -} - -#define m66592_bclr(m66592, val, offset) \ - m66592_mdfy(m66592, 0, val, offset) -#define m66592_bset(m66592, val, offset) \ - m66592_mdfy(m66592, val, 0, offset) - #endif /* ifndef __M66592_UDC_H__ */ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 01822422c3e8..d3eb27427c58 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -169,6 +169,7 @@ static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, .iProduct = DRIVER_DESC, + .max_speed = USB_SPEED_HIGH, .needs_serial = 1, }; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index d9feced348e3..8c7b74717d85 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -351,6 +351,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(multi_unbind), .iProduct = DRIVER_DESC, .needs_serial = 1, diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index b1a8146b9d50..ce1ac2bcb314 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1128,6 +1128,9 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) return 0; } +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); /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops mv_ops = { @@ -1139,6 +1142,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, }; static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter) @@ -1230,7 +1235,7 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver) } } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int mv_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct mv_udc *udc = the_controller; @@ -1270,9 +1275,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int mv_udc_stop(struct usb_gadget_driver *driver) { struct mv_udc *udc = the_controller; unsigned long flags; @@ -1296,7 +1300,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); static int udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) @@ -1880,9 +1883,10 @@ static void gadget_release(struct device *_dev) static int mv_udc_remove(struct platform_device *dev) { struct mv_udc *udc = the_controller; - DECLARE_COMPLETION(done); + usb_del_gadget_udc(&udc->gadget); + udc->done = &done; /* free memory allocated in probe */ @@ -2074,11 +2078,12 @@ int mv_udc_probe(struct platform_device *dev) the_controller = udc; - goto out; + retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); + if (!retval) + return retval; error: if (udc) mv_udc_remove(udc->dev); -out: return retval; } @@ -2126,7 +2131,7 @@ static struct platform_driver udc_driver = { #endif }, }; - +MODULE_ALIAS("platform:pxa-u2o"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 99c179ad729d..62ee5087dcac 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -228,6 +228,7 @@ static struct usb_composite_driver ncm_driver = { .name = "g_ncm", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(gncm_unbind), }; diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c new file mode 100644 index 000000000000..7c7b0e120d88 --- /dev/null +++ b/drivers/usb/gadget/net2272.c @@ -0,0 +1,2752 @@ +/* + * Driver for PLX NET2272 USB device controller + * + * Copyright (C) 2005-2006 PLX Technology, Inc. + * Copyright (C) 2006-2011 Analog Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/usb.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/system.h> +#include <asm/unaligned.h> + +#include "net2272.h" + +#define DRIVER_DESC "PLX NET2272 USB Peripheral Controller" + +static const char driver_name[] = "net2272"; +static const char driver_vers[] = "2006 October 17/mainline"; +static const char driver_desc[] = DRIVER_DESC; + +static const char ep0name[] = "ep0"; +static const char * const ep_name[] = { + ep0name, + "ep-a", "ep-b", "ep-c", +}; + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) +#ifdef CONFIG_USB_GADGET_NET2272_DMA +/* + * use_dma: the NET2272 can use an external DMA controller. + * Note that since there is no generic DMA api, some functions, + * notably request_dma, start_dma, and cancel_dma will need to be + * modified for your platform's particular dma controller. + * + * If use_dma is disabled, pio will be used instead. + */ +static int use_dma = 0; +module_param(use_dma, bool, 0644); + +/* + * dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b) + * The NET2272 can only use dma for a single endpoint at a time. + * At some point this could be modified to allow either endpoint + * to take control of dma as it becomes available. + * + * Note that DMA should not be used on OUT endpoints unless it can + * be guaranteed that no short packets will arrive on an IN endpoint + * while the DMA operation is pending. Otherwise the OUT DMA will + * terminate prematurely (See NET2272 Errata 630-0213-0101) + */ +static ushort dma_ep = 1; +module_param(dma_ep, ushort, 0644); + +/* + * dma_mode: net2272 dma mode setting (see LOCCTL1 definiton): + * mode 0 == Slow DREQ mode + * mode 1 == Fast DREQ mode + * mode 2 == Burst mode + */ +static ushort dma_mode = 2; +module_param(dma_mode, ushort, 0644); +#else +#define use_dma 0 +#define dma_ep 1 +#define dma_mode 2 +#endif + +/* + * fifo_mode: net2272 buffer configuration: + * mode 0 == ep-{a,b,c} 512db each + * mode 1 == ep-a 1k, ep-{b,c} 512db + * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db + * mode 3 == ep-a 1k, ep-b disabled, ep-c 512db + */ +static ushort fifo_mode = 0; +module_param(fifo_mode, ushort, 0644); + +/* + * enable_suspend: When enabled, the driver will respond to + * USB suspend requests by powering down the NET2272. Otherwise, + * USB suspend requests will be ignored. This is acceptible for + * self-powered devices. For bus powered devices set this to 1. + */ +static ushort enable_suspend = 0; +module_param(enable_suspend, ushort, 0644); + +static void assert_out_naking(struct net2272_ep *ep, const char *where) +{ + u8 tmp; + +#ifndef DEBUG + return; +#endif + + tmp = net2272_ep_read(ep, EP_STAT0); + if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { + dev_dbg(ep->dev->dev, "%s %s %02x !NAK\n", + ep->ep.name, where, tmp); + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + } +} +#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) + +static void stop_out_naking(struct net2272_ep *ep) +{ + u8 tmp = net2272_ep_read(ep, EP_STAT0); + + if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); +} + +#define PIPEDIR(bAddress) (usb_pipein(bAddress) ? "in" : "out") + +static char *type_string(u8 bmAttributes) +{ + switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: return "bulk"; + case USB_ENDPOINT_XFER_ISOC: return "iso"; + case USB_ENDPOINT_XFER_INT: return "intr"; + default: return "control"; + } +} + +static char *buf_state_string(unsigned state) +{ + switch (state) { + case BUFF_FREE: return "free"; + case BUFF_VALID: return "valid"; + case BUFF_LCL: return "local"; + case BUFF_USB: return "usb"; + default: return "unknown"; + } +} + +static char *dma_mode_string(void) +{ + if (!use_dma) + return "PIO"; + switch (dma_mode) { + case 0: return "SLOW DREQ"; + case 1: return "FAST DREQ"; + case 2: return "BURST"; + default: return "invalid"; + } +} + +static void net2272_dequeue_all(struct net2272_ep *); +static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *); +static int net2272_fifo_status(struct usb_ep *); + +static struct usb_ep_ops net2272_ep_ops; + +/*---------------------------------------------------------------------------*/ + +static int +net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct net2272 *dev; + struct net2272_ep *ep; + u32 max; + u8 tmp; + unsigned long flags; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + + spin_lock_irqsave(&dev->lock, flags); + _ep->maxpacket = max & 0x7fff; + ep->desc = desc; + + /* net2272_ep_reset() has already been called */ + ep->stopped = 0; + ep->wedged = 0; + + /* set speed-dependent max packet */ + net2272_ep_write(ep, EP_MAXPKT0, max & 0xff); + net2272_ep_write(ep, EP_MAXPKT1, (max & 0xff00) >> 8); + + /* set type, direction, address; reset fifo counters */ + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); + tmp = usb_endpoint_type(desc); + if (usb_endpoint_xfer_bulk(desc)) { + /* catch some particularly blatant driver bugs */ + if ((dev->gadget.speed == USB_SPEED_HIGH && max != 512) || + (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ERANGE; + } + } + ep->is_iso = usb_endpoint_xfer_isoc(desc) ? 1 : 0; + tmp <<= ENDPOINT_TYPE; + tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER); + tmp |= usb_endpoint_dir_in(desc) << ENDPOINT_DIRECTION; + tmp |= (1 << ENDPOINT_ENABLE); + + /* for OUT transfers, block the rx fifo until a read is posted */ + ep->is_in = usb_endpoint_dir_in(desc); + if (!ep->is_in) + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + + net2272_ep_write(ep, EP_CFG, tmp); + + /* enable irqs */ + tmp = (1 << ep->num) | net2272_read(dev, IRQENB0); + net2272_write(dev, IRQENB0, tmp); + + tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | net2272_ep_read(ep, EP_IRQENB); + net2272_ep_write(ep, EP_IRQENB, tmp); + + tmp = desc->bEndpointAddress; + dev_dbg(dev->dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n", + _ep->name, tmp & 0x0f, PIPEDIR(tmp), + type_string(desc->bmAttributes), max, + net2272_ep_read(ep, EP_CFG)); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static void net2272_ep_reset(struct net2272_ep *ep) +{ + u8 tmp; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + ep->ep.maxpacket = ~0; + ep->ep.ops = &net2272_ep_ops; + + /* disable irqs, endpoint */ + net2272_ep_write(ep, EP_IRQENB, 0); + + /* init to our chosen defaults, notably so that we NAK OUT + * packets until the driver queues a read. + */ + tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS); + net2272_ep_write(ep, EP_RSPSET, tmp); + + tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE); + if (ep->num != 0) + tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT); + + net2272_ep_write(ep, EP_RSPCLR, tmp); + + /* scrub most status bits, and flush any fifo state */ + net2272_ep_write(ep, EP_STAT0, + (1 << DATA_IN_TOKEN_INTERRUPT) + | (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); + + net2272_ep_write(ep, EP_STAT1, + (1 << TIMEOUT) + | (1 << USB_OUT_ACK_SENT) + | (1 << USB_OUT_NAK_SENT) + | (1 << USB_IN_ACK_RCVD) + | (1 << USB_IN_NAK_SENT) + | (1 << USB_STALL_SENT) + | (1 << LOCAL_OUT_ZLP) + | (1 << BUFFER_FLUSH)); + + /* fifo size is handled seperately */ +} + +static int net2272_disable(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + net2272_dequeue_all(ep); + net2272_ep_reset(ep); + + dev_vdbg(ep->dev->dev, "disabled %s\n", _ep->name); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static struct usb_request * +net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct net2272_ep *ep; + struct net2272_request *req; + + if (!_ep) + return NULL; + ep = container_of(_ep, struct net2272_ep, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void +net2272_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2272_ep *ep; + struct net2272_request *req; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || !_req) + return; + + req = container_of(_req, struct net2272_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void +net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status) +{ + struct net2272 *dev; + unsigned stopped = ep->stopped; + + if (ep->num == 0) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + allow_status(ep); + } + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (use_dma && 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 (status && status != -ESHUTDOWN) + dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length, req->req.buf); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +static int +net2272_write_packet(struct net2272_ep *ep, u8 *buf, + struct net2272_request *req, unsigned max) +{ + u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); + u16 *bufp; + unsigned length, count; + u8 tmp; + + length = min(req->req.length - req->req.actual, max); + req->req.actual += length; + + dev_vdbg(ep->dev->dev, "write packet %s req %p max %u len %u avail %u\n", + ep->ep.name, req, max, length, + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); + + count = length; + bufp = (u16 *)buf; + + while (likely(count >= 2)) { + /* no byte-swap required; chip endian set during init */ + writew(*bufp++, ep_data); + count -= 2; + } + buf = (u8 *)bufp; + + /* write final byte by placing the NET2272 into 8-bit mode */ + if (unlikely(count)) { + tmp = net2272_read(ep->dev, LOCCTL); + net2272_write(ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH)); + writeb(*buf, ep_data); + net2272_write(ep->dev, LOCCTL, tmp); + } + return length; +} + +/* returns: 0: still running, 1: completed, negative: errno */ +static int +net2272_write_fifo(struct net2272_ep *ep, struct net2272_request *req) +{ + u8 *buf; + unsigned count, max; + int status; + + dev_vdbg(ep->dev->dev, "write_fifo %s actual %d len %d\n", + ep->ep.name, req->req.actual, req->req.length); + + /* + * Keep loading the endpoint until the final packet is loaded, + * or the endpoint buffer is full. + */ + top: + /* + * Clear interrupt status + * - Packet Transmitted interrupt will become set again when the + * host successfully takes another packet + */ + net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); + while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_FULL))) { + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* force pagesel */ + net2272_ep_read(ep, EP_STAT0); + + max = (net2272_ep_read(ep, EP_AVAIL1) << 8) | + (net2272_ep_read(ep, EP_AVAIL0)); + + if (max < ep->ep.maxpacket) + max = (net2272_ep_read(ep, EP_AVAIL1) << 8) + | (net2272_ep_read(ep, EP_AVAIL0)); + + count = net2272_write_packet(ep, buf, req, max); + /* see if we are done */ + if (req->req.length == req->req.actual) { + /* validate short or zlp packet */ + if (count < ep->ep.maxpacket) + set_fifo_bytecount(ep, 0); + net2272_done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, + queue); + status = net2272_kick_dma(ep, req); + + if (status < 0) + if ((net2272_ep_read(ep, EP_STAT0) + & (1 << BUFFER_EMPTY))) + goto top; + } + return 1; + } + net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); + } + return 0; +} + +static void +net2272_out_flush(struct net2272_ep *ep) +{ + ASSERT_OUT_NAKING(ep); + + net2272_ep_write(ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT)); + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); +} + +static int +net2272_read_packet(struct net2272_ep *ep, u8 *buf, + struct net2272_request *req, unsigned avail) +{ + u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); + unsigned is_short; + u16 *bufp; + + req->req.actual += avail; + + dev_vdbg(ep->dev->dev, "read packet %s req %p len %u avail %u\n", + ep->ep.name, req, avail, + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); + + is_short = (avail < ep->ep.maxpacket); + + if (unlikely(avail == 0)) { + /* remove any zlp from the buffer */ + (void)readw(ep_data); + return is_short; + } + + /* Ensure we get the final byte */ + if (unlikely(avail % 2)) + avail++; + bufp = (u16 *)buf; + + do { + *bufp++ = readw(ep_data); + avail -= 2; + } while (avail); + + /* + * To avoid false endpoint available race condition must read + * ep stat0 twice in the case of a short transfer + */ + if (net2272_ep_read(ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) + net2272_ep_read(ep, EP_STAT0); + + return is_short; +} + +static int +net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) +{ + u8 *buf; + unsigned is_short; + int count; + int tmp; + int cleanup = 0; + int status = -1; + + dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n", + ep->ep.name, req->req.actual, req->req.length); + + top: + do { + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + count = (net2272_ep_read(ep, EP_AVAIL1) << 8) + | net2272_ep_read(ep, EP_AVAIL0); + + net2272_ep_write(ep, EP_STAT0, + (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | + (1 << DATA_PACKET_RECEIVED_INTERRUPT)); + + tmp = req->req.length - req->req.actual; + + if (count > tmp) { + if ((tmp % ep->ep.maxpacket) != 0) { + dev_err(ep->dev->dev, + "%s out fifo %d bytes, expected %d\n", + ep->ep.name, count, tmp); + cleanup = 1; + } + count = (tmp > 0) ? tmp : 0; + } + + is_short = net2272_read_packet(ep, buf, req, count); + + /* completion */ + if (unlikely(cleanup || is_short || + ((req->req.actual == req->req.length) + && !req->req.zero))) { + + if (cleanup) { + net2272_out_flush(ep); + net2272_done(ep, req, -EOVERFLOW); + } else + net2272_done(ep, req, 0); + + /* re-initialize endpoint transfer registers + * otherwise they may result in erroneous pre-validation + * for subsequent control reads + */ + if (unlikely(ep->num == 0)) { + net2272_ep_write(ep, EP_TRANSFER2, 0); + net2272_ep_write(ep, EP_TRANSFER1, 0); + net2272_ep_write(ep, EP_TRANSFER0, 0); + } + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + status = net2272_kick_dma(ep, req); + if ((status < 0) && + !(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))) + goto top; + } + return 1; + } + } while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))); + + return 0; +} + +static void +net2272_pio_advance(struct net2272_ep *ep) +{ + struct net2272_request *req; + + if (unlikely(list_empty(&ep->queue))) + return; + + req = list_entry(ep->queue.next, struct net2272_request, queue); + (ep->is_in ? net2272_write_fifo : net2272_read_fifo)(ep, req); +} + +/* returns 0 on success, else negative errno */ +static int +net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf, + unsigned len, unsigned dir) +{ + dev_vdbg(dev->dev, "request_dma ep %d buf %08x len %d dir %d\n", + ep, buf, len, dir); + + /* The NET2272 only supports a single dma channel */ + if (dev->dma_busy) + return -EBUSY; + /* + * EP_TRANSFER (used to determine the number of bytes received + * in an OUT transfer) is 24 bits wide; don't ask for more than that. + */ + if ((dir == 1) && (len > 0x1000000)) + return -EINVAL; + + dev->dma_busy = 1; + + /* initialize platform's dma */ +#ifdef CONFIG_PCI + /* NET2272 addr, buffer addr, length, etc. */ + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + /* Setup PLX 9054 DMA mode */ + writel((1 << LOCAL_BUS_WIDTH) | + (1 << TA_READY_INPUT_ENABLE) | + (0 << LOCAL_BURST_ENABLE) | + (1 << DONE_INTERRUPT_ENABLE) | + (1 << LOCAL_ADDRESSING_MODE) | + (1 << DEMAND_MODE) | + (1 << DMA_EOT_ENABLE) | + (1 << FAST_SLOW_TERMINATE_MODE_SELECT) | + (1 << DMA_CHANNEL_INTERRUPT_SELECT), + dev->rdk1.plx9054_base_addr + DMAMODE0); + + writel(0x100000, dev->rdk1.plx9054_base_addr + DMALADR0); + writel(buf, dev->rdk1.plx9054_base_addr + DMAPADR0); + writel(len, dev->rdk1.plx9054_base_addr + DMASIZ0); + writel((dir << DIRECTION_OF_TRANSFER) | + (1 << INTERRUPT_AFTER_TERMINAL_COUNT), + dev->rdk1.plx9054_base_addr + DMADPR0); + writel((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) | + readl(dev->rdk1.plx9054_base_addr + INTCSR), + dev->rdk1.plx9054_base_addr + INTCSR); + + break; + } +#endif + + net2272_write(dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (1 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (dev->dma_eot_polarity << EOT_POLARITY) | + (dev->dma_dack_polarity << DACK_POLARITY) | + (dev->dma_dreq_polarity << DREQ_POLARITY) | + ((ep >> 1) << DMA_ENDPOINT_SELECT)); + + (void) net2272_read(dev, SCRATCH); + + return 0; +} + +static void +net2272_start_dma(struct net2272 *dev) +{ + /* start platform's dma controller */ +#ifdef CONFIG_PCI + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START), + dev->rdk1.plx9054_base_addr + DMACSR0); + break; + } +#endif +} + +/* returns 0 on success, else negative errno */ +static int +net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req) +{ + unsigned size; + u8 tmp; + + if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma) + return -EINVAL; + + /* don't use dma for odd-length transfers + * otherwise, we'd need to deal with the last byte with pio + */ + if (req->req.length & 1) + return -EINVAL; + + dev_vdbg(ep->dev->dev, "kick_dma %s req %p dma %08llx\n", + ep->ep.name, req, (unsigned long long) req->req.dma); + + net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); + + /* The NET2272 can only use DMA on one endpoint at a time */ + if (ep->dev->dma_busy) + return -EBUSY; + + /* Make sure we only DMA an even number of bytes (we'll use + * pio to complete the transfer) + */ + size = req->req.length; + size &= ~1; + + /* device-to-host transfer */ + if (ep->is_in) { + /* initialize platform's dma controller */ + if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 0)) + /* unable to obtain DMA channel; return error and use pio mode */ + return -EBUSY; + req->req.actual += size; + + /* host-to-device transfer */ + } else { + tmp = net2272_ep_read(ep, EP_STAT0); + + /* initialize platform's dma controller */ + if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 1)) + /* unable to obtain DMA channel; return error and use pio mode */ + return -EBUSY; + + if (!(tmp & (1 << BUFFER_EMPTY))) + ep->not_empty = 1; + else + ep->not_empty = 0; + + + /* allow the endpoint's buffer to fill */ + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); + + /* this transfer completed and data's already in the fifo + * return error so pio gets used. + */ + if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { + + /* deassert dreq */ + net2272_write(ep->dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (0 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (ep->dev->dma_eot_polarity << EOT_POLARITY) | + (ep->dev->dma_dack_polarity << DACK_POLARITY) | + (ep->dev->dma_dreq_polarity << DREQ_POLARITY) | + ((ep->num >> 1) << DMA_ENDPOINT_SELECT)); + + return -EBUSY; + } + } + + /* Don't use per-packet interrupts: use dma interrupts only */ + net2272_ep_write(ep, EP_IRQENB, 0); + + net2272_start_dma(ep->dev); + + return 0; +} + +static void net2272_cancel_dma(struct net2272 *dev) +{ +#ifdef CONFIG_PCI + switch (dev->dev_id) { + case PCI_DEVICE_ID_RDK1: + writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0); + writeb(1 << CHANNEL_ABORT, dev->rdk1.plx9054_base_addr + DMACSR0); + while (!(readb(dev->rdk1.plx9054_base_addr + DMACSR0) & + (1 << CHANNEL_DONE))) + continue; /* wait for dma to stabalize */ + + /* dma abort generates an interrupt */ + writeb(1 << CHANNEL_CLEAR_INTERRUPT, + dev->rdk1.plx9054_base_addr + DMACSR0); + break; + } +#endif + + dev->dma_busy = 0; +} + +/*---------------------------------------------------------------------------*/ + +static int +net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct net2272_request *req; + struct net2272_ep *ep; + struct net2272 *dev; + unsigned long flags; + int status = -1; + u8 s; + + req = container_of(_req, struct net2272_request, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue)) + return -EINVAL; + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* set up dma mapping in case the caller didn't */ + if (use_dma && ep->dma && _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; + } + + dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n", + _ep->name, _req, _req->length, _req->buf, + (unsigned long long) _req->dma, _req->zero ? "zero" : "!zero"); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + /* maybe there's no control data, just status ack */ + if (ep->num == 0 && _req->length == 0) { + net2272_done(ep, req, 0); + dev_vdbg(dev->dev, "%s status ack\n", ep->ep.name); + goto done; + } + + /* Return zlp, don't let it block subsequent packets */ + s = net2272_ep_read(ep, EP_STAT0); + if (s & (1 << BUFFER_EMPTY)) { + /* Buffer is empty check for a blocking zlp, handle it */ + if ((s & (1 << NAK_OUT_PACKETS)) && + net2272_ep_read(ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP)) { + dev_dbg(dev->dev, "WARNING: returning ZLP short packet termination!\n"); + /* + * Request is going to terminate with a short packet ... + * hope the client is ready for it! + */ + status = net2272_read_fifo(ep, req); + /* clear short packet naking */ + net2272_ep_write(ep, EP_STAT0, (1 << NAK_OUT_PACKETS)); + goto done; + } + } + + /* try dma first */ + status = net2272_kick_dma(ep, req); + + if (status < 0) { + /* dma failed (most likely in use by another endpoint) + * fallback to pio + */ + status = 0; + + if (ep->is_in) + status = net2272_write_fifo(ep, req); + else { + s = net2272_ep_read(ep, EP_STAT0); + if ((s & (1 << BUFFER_EMPTY)) == 0) + status = net2272_read_fifo(ep, req); + } + + if (unlikely(status != 0)) { + if (status > 0) + status = 0; + req = NULL; + } + } + } + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + if (likely(!list_empty(&ep->queue))) + net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); + done: + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue ALL requests */ +static void +net2272_dequeue_all(struct net2272_ep *ep) +{ + struct net2272_request *req; + + /* called with spinlock held */ + ep->stopped = 1; + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, + queue); + net2272_done(ep, req, -ESHUTDOWN); + } +} + +/* dequeue JUST ONE request */ +static int +net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2272_ep *ep; + struct net2272_request *req; + unsigned long flags; + int stopped; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0) || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + stopped = ep->stopped; + ep->stopped = 1; + + /* make sure it's still queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete */ + if (ep->queue.next == &req->queue) { + dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name); + net2272_done(ep, req, -ECONNRESET); + } + req = NULL; + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int +net2272_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) +{ + struct net2272_ep *ep; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -EINVAL; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc)) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + if (!list_empty(&ep->queue)) + ret = -EAGAIN; + else if (ep->is_in && value && net2272_fifo_status(_ep) != 0) + ret = -EAGAIN; + else { + dev_vdbg(ep->dev->dev, "%s %s %s\n", _ep->name, + value ? "set" : "clear", + wedged ? "wedge" : "halt"); + /* set/clear */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt(ep); + if (wedged) + ep->wedged = 1; + } else { + clear_halt(ep); + ep->wedged = 0; + } + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + + return ret; +} + +static int +net2272_set_halt(struct usb_ep *_ep, int value) +{ + return net2272_set_halt_and_wedge(_ep, value, 0); +} + +static int +net2272_set_wedge(struct usb_ep *_ep) +{ + if (!_ep || _ep->name == ep0name) + return -EINVAL; + return net2272_set_halt_and_wedge(_ep, 1, 1); +} + +static int +net2272_fifo_status(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + u16 avail; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return -ENODEV; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + avail = net2272_ep_read(ep, EP_AVAIL1) << 8; + avail |= net2272_ep_read(ep, EP_AVAIL0); + if (avail > ep->fifo_size) + return -EOVERFLOW; + if (ep->is_in) + avail = ep->fifo_size - avail; + return avail; +} + +static void +net2272_fifo_flush(struct usb_ep *_ep) +{ + struct net2272_ep *ep; + + ep = container_of(_ep, struct net2272_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return; + + net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); +} + +static struct usb_ep_ops net2272_ep_ops = { + .enable = net2272_enable, + .disable = net2272_disable, + + .alloc_request = net2272_alloc_request, + .free_request = net2272_free_request, + + .queue = net2272_queue, + .dequeue = net2272_dequeue, + + .set_halt = net2272_set_halt, + .set_wedge = net2272_set_wedge, + .fifo_status = net2272_fifo_status, + .fifo_flush = net2272_fifo_flush, +}; + +/*---------------------------------------------------------------------------*/ + +static int +net2272_get_frame(struct usb_gadget *_gadget) +{ + struct net2272 *dev; + unsigned long flags; + u16 ret; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + spin_lock_irqsave(&dev->lock, flags); + + ret = net2272_read(dev, FRAME1) << 8; + ret |= net2272_read(dev, FRAME0); + + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int +net2272_wakeup(struct usb_gadget *_gadget) +{ + struct net2272 *dev; + u8 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2272, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = net2272_read(dev, USBCTL0); + if (tmp & (1 << IO_WAKEUP_ENABLE)) + net2272_write(dev, USBCTL1, (1 << GENERATE_RESUME)); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int +net2272_set_selfpowered(struct usb_gadget *_gadget, int value) +{ + struct net2272 *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + + dev->is_selfpowered = value; + + return 0; +} + +static int +net2272_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct net2272 *dev; + u8 tmp; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2272, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = net2272_read(dev, USBCTL0); + dev->softconnect = (is_on != 0); + if (is_on) + tmp |= (1 << USB_DETECT_ENABLE); + else + tmp &= ~(1 << USB_DETECT_ENABLE); + net2272_write(dev, USBCTL0, tmp); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2272_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int net2272_stop(struct usb_gadget_driver *driver); + +static const struct usb_gadget_ops net2272_ops = { + .get_frame = net2272_get_frame, + .wakeup = net2272_wakeup, + .set_selfpowered = net2272_set_selfpowered, + .pullup = net2272_pullup, + .start = net2272_start, + .stop = net2272_stop, +}; + +/*---------------------------------------------------------------------------*/ + +static ssize_t +net2272_show_registers(struct device *_dev, struct device_attribute *attr, char *buf) +{ + struct net2272 *dev; + char *next; + unsigned size, t; + unsigned long flags; + u8 t1, t2; + int i; + const char *s; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + if (dev->driver) + s = dev->driver->driver.name; + else + s = "(none)"; + + /* Main Control Registers */ + t = scnprintf(next, size, "%s version %s," + "chiprev %02x, locctl %02x\n" + "irqenb0 %02x irqenb1 %02x " + "irqstat0 %02x irqstat1 %02x\n", + driver_name, driver_vers, dev->chiprev, + net2272_read(dev, LOCCTL), + net2272_read(dev, IRQENB0), + net2272_read(dev, IRQENB1), + net2272_read(dev, IRQSTAT0), + net2272_read(dev, IRQSTAT1)); + size -= t; + next += t; + + /* DMA */ + t1 = net2272_read(dev, DMAREQ); + t = scnprintf(next, size, "\ndmareq %02x: %s %s%s%s%s\n", + t1, ep_name[(t1 & 0x01) + 1], + t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "", + t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "", + t1 & (1 << DMA_REQUEST) ? "req " : "", + t1 & (1 << DMA_BUFFER_VALID) ? "valid " : ""); + size -= t; + next += t; + + /* USB Control Registers */ + t1 = net2272_read(dev, USBCTL1); + if (t1 & (1 << VBUS_PIN)) { + if (t1 & (1 << USB_HIGH_SPEED)) + s = "high speed"; + else if (dev->gadget.speed == USB_SPEED_UNKNOWN) + s = "powered"; + else + s = "full speed"; + } else + s = "not attached"; + t = scnprintf(next, size, + "usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n", + net2272_read(dev, USBCTL0), t1, + net2272_read(dev, OURADDR), s); + size -= t; + next += t; + + /* Endpoint Registers */ + for (i = 0; i < 4; ++i) { + struct net2272_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->desc) + continue; + + t1 = net2272_ep_read(ep, EP_CFG); + t2 = net2272_ep_read(ep, EP_RSPSET); + t = scnprintf(next, size, + "\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s" + "irqenb %02x\n", + ep->ep.name, t1, t2, + (t2 & (1 << ALT_NAK_OUT_PACKETS)) ? "NAK " : "", + (t2 & (1 << HIDE_STATUS_PHASE)) ? "hide " : "", + (t2 & (1 << AUTOVALIDATE)) ? "auto " : "", + (t2 & (1 << INTERRUPT_MODE)) ? "interrupt " : "", + (t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "", + (t2 & (1 << NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "", + (t2 & (1 << ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ", + (t2 & (1 << ENDPOINT_HALT)) ? "HALT " : "", + net2272_ep_read(ep, EP_IRQENB)); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tstat0 %02x stat1 %02x avail %04x " + "(ep%d%s-%s)%s\n", + net2272_ep_read(ep, EP_STAT0), + net2272_ep_read(ep, EP_STAT1), + (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0), + t1 & 0x0f, + ep->is_in ? "in" : "out", + type_string(t1 >> 5), + ep->stopped ? "*" : ""); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tep_transfer %06x\n", + ((net2272_ep_read(ep, EP_TRANSFER2) & 0xff) << 16) | + ((net2272_ep_read(ep, EP_TRANSFER1) & 0xff) << 8) | + ((net2272_ep_read(ep, EP_TRANSFER0) & 0xff))); + size -= t; + next += t; + + t1 = net2272_ep_read(ep, EP_BUFF_STATES) & 0x03; + t2 = (net2272_ep_read(ep, EP_BUFF_STATES) >> 2) & 0x03; + t = scnprintf(next, size, + "\tbuf-a %s buf-b %s\n", + buf_state_string(t1), + buf_state_string(t2)); + size -= t; + next += t; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR(registers, S_IRUGO, net2272_show_registers, NULL); + +/*---------------------------------------------------------------------------*/ + +static void +net2272_set_fifo_mode(struct net2272 *dev, int mode) +{ + u8 tmp; + + tmp = net2272_read(dev, LOCCTL) & 0x3f; + tmp |= (mode << 6); + net2272_write(dev, LOCCTL, tmp); + + INIT_LIST_HEAD(&dev->gadget.ep_list); + + /* always ep-a, ep-c ... maybe not ep-b */ + list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); + + switch (mode) { + case 0: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512; + break; + case 1: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = 1024; + dev->ep[2].fifo_size = 512; + break; + case 2: + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; + break; + case 3: + dev->ep[1].fifo_size = 1024; + break; + } + + /* ep-c is always 2 512 byte buffers */ + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + dev->ep[3].fifo_size = 512; +} + +/*---------------------------------------------------------------------------*/ + +static struct net2272 *the_controller; + +static void +net2272_usb_reset(struct net2272 *dev) +{ + dev->gadget.speed = USB_SPEED_UNKNOWN; + + net2272_cancel_dma(dev); + + net2272_write(dev, IRQENB0, 0); + net2272_write(dev, IRQENB1, 0); + + /* clear irq state */ + net2272_write(dev, IRQSTAT0, 0xff); + net2272_write(dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT)); + + net2272_write(dev, DMAREQ, + (0 << DMA_BUFFER_VALID) | + (0 << DMA_REQUEST_ENABLE) | + (1 << DMA_CONTROL_DACK) | + (dev->dma_eot_polarity << EOT_POLARITY) | + (dev->dma_dack_polarity << DACK_POLARITY) | + (dev->dma_dreq_polarity << DREQ_POLARITY) | + ((dma_ep >> 1) << DMA_ENDPOINT_SELECT)); + + net2272_cancel_dma(dev); + net2272_set_fifo_mode(dev, (fifo_mode <= 3) ? fifo_mode : 0); + + /* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping + * note that the higher level gadget drivers are expected to convert data to little endian. + * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here + */ + net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) | (1 << DATA_WIDTH)); + net2272_write(dev, LOCCTL1, (dma_mode << DMA_MODE)); +} + +static void +net2272_usb_reinit(struct net2272 *dev) +{ + int i; + + /* basic endpoint init */ + for (i = 0; i < 4; ++i) { + struct net2272_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + ep->not_empty = 0; + + if (use_dma && ep->num == dma_ep) + ep->dma = 1; + + if (i > 0 && i <= 3) + ep->fifo_size = 512; + else + ep->fifo_size = 64; + net2272_ep_reset(ep); + } + dev->ep[0].ep.maxpacket = 64; + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); +} + +static void +net2272_ep0_start(struct net2272 *dev) +{ + struct net2272_ep *ep0 = &dev->ep[0]; + + net2272_ep_write(ep0, EP_RSPSET, + (1 << NAK_OUT_PACKETS_MODE) | + (1 << ALT_NAK_OUT_PACKETS)); + net2272_ep_write(ep0, EP_RSPCLR, + (1 << HIDE_STATUS_PHASE) | + (1 << CONTROL_STATUS_PHASE_HANDSHAKE)); + net2272_write(dev, USBCTL0, + (dev->softconnect << USB_DETECT_ENABLE) | + (1 << USB_ROOT_PORT_WAKEUP_ENABLE) | + (1 << IO_WAKEUP_ENABLE)); + net2272_write(dev, IRQENB0, + (1 << SETUP_PACKET_INTERRUPT_ENABLE) | + (1 << ENDPOINT_0_INTERRUPT_ENABLE) | + (1 << DMA_DONE_INTERRUPT_ENABLE)); + net2272_write(dev, IRQENB1, + (1 << VBUS_INTERRUPT_ENABLE) | + (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) | + (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)); +} + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +static int net2272_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct net2272 *dev = the_controller; + int ret; + unsigned i; + + if (!driver || !bind || !driver->unbind || !driver->setup || + driver->speed != USB_SPEED_HIGH) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + for (i = 0; i < 4; ++i) + dev->ep[i].irqs = 0; + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + ret = bind(&dev->gadget); + if (ret) { + dev_dbg(dev->dev, "bind to driver %s --> %d\n", + driver->driver.name, ret); + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return ret; + } + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + net2272_ep0_start(dev); + + dev_dbg(dev->dev, "%s ready\n", driver->driver.name); + + return 0; +} + +static void +stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + net2272_usb_reset(dev); + for (i = 0; i < 4; ++i) + net2272_dequeue_all(&dev->ep[i]); + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + + } + net2272_usb_reinit(dev); +} + +static int net2272_stop(struct usb_gadget_driver *driver) +{ + struct net2272 *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + net2272_pullup(&dev->gadget, 0); + + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name); + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* handle ep-a/ep-b dma completions */ +static void +net2272_handle_dma(struct net2272_ep *ep) +{ + struct net2272_request *req; + unsigned len; + int status; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2272_request, queue); + else + req = NULL; + + dev_vdbg(ep->dev->dev, "handle_dma %s req %p\n", ep->ep.name, req); + + /* Ensure DREQ is de-asserted */ + net2272_write(ep->dev, DMAREQ, + (0 << DMA_BUFFER_VALID) + | (0 << DMA_REQUEST_ENABLE) + | (1 << DMA_CONTROL_DACK) + | (ep->dev->dma_eot_polarity << EOT_POLARITY) + | (ep->dev->dma_dack_polarity << DACK_POLARITY) + | (ep->dev->dma_dreq_polarity << DREQ_POLARITY) + | ((ep->dma >> 1) << DMA_ENDPOINT_SELECT)); + + ep->dev->dma_busy = 0; + + net2272_ep_write(ep, EP_IRQENB, + (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | net2272_ep_read(ep, EP_IRQENB)); + + /* device-to-host transfer completed */ + if (ep->is_in) { + /* validate a short packet or zlp if necessary */ + if ((req->req.length % ep->ep.maxpacket != 0) || + req->req.zero) + set_fifo_bytecount(ep, 0); + + net2272_done(ep, req, 0); + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + status = net2272_kick_dma(ep, req); + if (status < 0) + net2272_pio_advance(ep); + } + + /* host-to-device transfer completed */ + } else { + /* terminated with a short packet? */ + if (net2272_read(ep->dev, IRQSTAT0) & + (1 << DMA_DONE_INTERRUPT)) { + /* abort system dma */ + net2272_cancel_dma(ep->dev); + } + + /* EP_TRANSFER will contain the number of bytes + * actually received. + * NOTE: There is no overflow detection on EP_TRANSFER: + * We can't deal with transfers larger than 2^24 bytes! + */ + len = (net2272_ep_read(ep, EP_TRANSFER2) << 16) + | (net2272_ep_read(ep, EP_TRANSFER1) << 8) + | (net2272_ep_read(ep, EP_TRANSFER0)); + + if (ep->not_empty) + len += 4; + + req->req.actual += len; + + /* get any remaining data */ + net2272_pio_advance(ep); + } +} + +/*---------------------------------------------------------------------------*/ + +static void +net2272_handle_ep(struct net2272_ep *ep) +{ + struct net2272_request *req; + u8 stat0, stat1; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2272_request, queue); + else + req = NULL; + + /* ack all, and handle what we care about */ + stat0 = net2272_ep_read(ep, EP_STAT0); + stat1 = net2272_ep_read(ep, EP_STAT1); + ep->irqs++; + + dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n", + ep->ep.name, stat0, stat1, req ? &req->req : 0); + + net2272_ep_write(ep, EP_STAT0, stat0 & + ~((1 << NAK_OUT_PACKETS) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))); + net2272_ep_write(ep, EP_STAT1, stat1); + + /* data packet(s) received (in the fifo, OUT) + * direction must be validated, otherwise control read status phase + * could be interpreted as a valid packet + */ + if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT))) + net2272_pio_advance(ep); + /* data packet(s) transmitted (IN) */ + else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) + net2272_pio_advance(ep); +} + +static struct net2272_ep * +net2272_get_ep_by_addr(struct net2272 *dev, u16 wIndex) +{ + struct net2272_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return &dev->ep[0]; + + list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) + return ep; + } + return NULL; +} + +/* + * USB Test Packet: + * JKJKJKJK * 9 + * JJKKJJKK * 8 + * JJJJKKKK * 8 + * JJJJJJJKKKKKKK * 8 + * JJJJJJJK * 8 + * {JKKKKKKK * 10}, JK + */ +static const u8 net2272_test_packet[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, + 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E +}; + +static void +net2272_set_test_mode(struct net2272 *dev, int mode) +{ + int i; + + /* Disable all net2272 interrupts: + * Nothing but a power cycle should stop the test. + */ + net2272_write(dev, IRQENB0, 0x00); + net2272_write(dev, IRQENB1, 0x00); + + /* Force tranceiver to high-speed */ + net2272_write(dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED); + + net2272_write(dev, PAGESEL, 0); + net2272_write(dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT); + net2272_write(dev, EP_RSPCLR, + (1 << CONTROL_STATUS_PHASE_HANDSHAKE) + | (1 << HIDE_STATUS_PHASE)); + net2272_write(dev, EP_CFG, 1 << ENDPOINT_DIRECTION); + net2272_write(dev, EP_STAT1, 1 << BUFFER_FLUSH); + + /* wait for status phase to complete */ + while (!(net2272_read(dev, EP_STAT0) & + (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))) + ; + + /* Enable test mode */ + net2272_write(dev, USBTEST, mode); + + /* load test packet */ + if (mode == TEST_PACKET) { + /* switch to 8 bit mode */ + net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) & + ~(1 << DATA_WIDTH)); + + for (i = 0; i < sizeof(net2272_test_packet); ++i) + net2272_write(dev, EP_DATA, net2272_test_packet[i]); + + /* Validate test packet */ + net2272_write(dev, EP_TRANSFER0, 0); + } +} + +static void +net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) +{ + struct net2272_ep *ep; + u8 num, scratch; + + /* starting a control request? */ + if (unlikely(stat & (1 << SETUP_PACKET_INTERRUPT))) { + union { + u8 raw[8]; + struct usb_ctrlrequest r; + } u; + int tmp = 0; + struct net2272_request *req; + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + if (net2272_read(dev, USBCTL1) & (1 << USB_HIGH_SPEED)) + dev->gadget.speed = USB_SPEED_HIGH; + else + dev->gadget.speed = USB_SPEED_FULL; + dev_dbg(dev->dev, "%s speed\n", + (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full"); + } + + ep = &dev->ep[0]; + ep->irqs++; + + /* make sure any leftover interrupt state is cleared */ + stat &= ~(1 << ENDPOINT_0_INTERRUPT); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2272_request, queue); + net2272_done(ep, req, + (req->req.actual == req->req.length) ? 0 : -EPROTO); + } + ep->stopped = 0; + dev->protocol_stall = 0; + net2272_ep_write(ep, EP_STAT0, + (1 << DATA_IN_TOKEN_INTERRUPT) + | (1 << DATA_OUT_TOKEN_INTERRUPT) + | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) + | (1 << DATA_PACKET_RECEIVED_INTERRUPT) + | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); + net2272_ep_write(ep, EP_STAT1, + (1 << TIMEOUT) + | (1 << USB_OUT_ACK_SENT) + | (1 << USB_OUT_NAK_SENT) + | (1 << USB_IN_ACK_RCVD) + | (1 << USB_IN_NAK_SENT) + | (1 << USB_STALL_SENT) + | (1 << LOCAL_OUT_ZLP)); + + /* + * Ensure Control Read pre-validation setting is beyond maximum size + * - Control Writes can leave non-zero values in EP_TRANSFER. If + * an EP0 transfer following the Control Write is a Control Read, + * the NET2272 sees the non-zero EP_TRANSFER as an unexpected + * pre-validation count. + * - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures + * the pre-validation count cannot cause an unexpected validatation + */ + net2272_write(dev, PAGESEL, 0); + net2272_write(dev, EP_TRANSFER2, 0xff); + net2272_write(dev, EP_TRANSFER1, 0xff); + net2272_write(dev, EP_TRANSFER0, 0xff); + + u.raw[0] = net2272_read(dev, SETUP0); + u.raw[1] = net2272_read(dev, SETUP1); + u.raw[2] = net2272_read(dev, SETUP2); + u.raw[3] = net2272_read(dev, SETUP3); + u.raw[4] = net2272_read(dev, SETUP4); + u.raw[5] = net2272_read(dev, SETUP5); + u.raw[6] = net2272_read(dev, SETUP6); + u.raw[7] = net2272_read(dev, SETUP7); + /* + * If you have a big endian cpu make sure le16_to_cpus + * performs the proper byte swapping here... + */ + le16_to_cpus(&u.r.wValue); + le16_to_cpus(&u.r.wIndex); + le16_to_cpus(&u.r.wLength); + + /* ack the irq */ + net2272_write(dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT); + stat ^= (1 << SETUP_PACKET_INTERRUPT); + + /* watch control traffic at the token level, and force + * synchronization before letting the status phase happen. + */ + ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; + if (ep->is_in) { + scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) + | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) + | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); + stop_out_naking(ep); + } else + scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) + | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) + | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); + net2272_ep_write(ep, EP_IRQENB, scratch); + + if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto delegate; + switch (u.r.bRequest) { + case USB_REQ_GET_STATUS: { + struct net2272_ep *e; + u16 status = 0; + + switch (u.r.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_ENDPOINT: + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e || u.r.wLength > 2) + goto do_stall; + if (net2272_ep_read(e, EP_RSPSET) & (1 << ENDPOINT_HALT)) + status = __constant_cpu_to_le16(1); + else + status = __constant_cpu_to_le16(0); + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "%s stat %02x\n", + ep->ep.name, status); + goto next_endpoints; + case USB_RECIP_DEVICE: + if (u.r.wLength > 2) + goto do_stall; + if (dev->is_selfpowered) + status = (1 << USB_DEVICE_SELF_POWERED); + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "device stat %02x\n", status); + goto next_endpoints; + case USB_RECIP_INTERFACE: + if (u.r.wLength > 2) + goto do_stall; + + /* don't bother with a request object! */ + net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); + writew(status, net2272_reg_addr(dev, EP_DATA)); + set_fifo_bytecount(&dev->ep[0], 0); + allow_status(ep); + dev_vdbg(dev->dev, "interface status %02x\n", status); + goto next_endpoints; + } + + break; + } + case USB_REQ_CLEAR_FEATURE: { + struct net2272_ep *e; + + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT || + u.r.wLength != 0) + goto do_stall; + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e) + goto do_stall; + if (e->wedged) { + dev_vdbg(dev->dev, "%s wedged, halt not cleared\n", + ep->ep.name); + } else { + dev_vdbg(dev->dev, "%s clear halt\n", ep->ep.name); + clear_halt(e); + } + allow_status(ep); + goto next_endpoints; + } + case USB_REQ_SET_FEATURE: { + struct net2272_ep *e; + + if (u.r.bRequestType == USB_RECIP_DEVICE) { + if (u.r.wIndex != NORMAL_OPERATION) + net2272_set_test_mode(dev, (u.r.wIndex >> 8)); + allow_status(ep); + dev_vdbg(dev->dev, "test mode: %d\n", u.r.wIndex); + goto next_endpoints; + } else if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT || + u.r.wLength != 0) + goto do_stall; + e = net2272_get_ep_by_addr(dev, u.r.wIndex); + if (!e) + goto do_stall; + set_halt(e); + allow_status(ep); + dev_vdbg(dev->dev, "%s set halt\n", ep->ep.name); + goto next_endpoints; + } + case USB_REQ_SET_ADDRESS: { + net2272_write(dev, OURADDR, u.r.wValue & 0xff); + allow_status(ep); + break; + } + default: + delegate: + dev_vdbg(dev->dev, "setup %02x.%02x v%04x i%04x " + "ep_cfg %08x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, + net2272_ep_read(ep, EP_CFG)); + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &u.r); + spin_lock(&dev->lock); + } + + /* stall ep0 on error */ + if (tmp < 0) { + do_stall: + dev_vdbg(dev->dev, "req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, tmp); + dev->protocol_stall = 1; + } + /* endpoint dma irq? */ + } else if (stat & (1 << DMA_DONE_INTERRUPT)) { + net2272_cancel_dma(dev); + net2272_write(dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT); + stat &= ~(1 << DMA_DONE_INTERRUPT); + num = (net2272_read(dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT)) + ? 2 : 1; + + ep = &dev->ep[num]; + net2272_handle_dma(ep); + } + + next_endpoints: + /* endpoint data irq? */ + scratch = stat & 0x0f; + stat &= ~0x0f; + for (num = 0; scratch; num++) { + u8 t; + + /* does this endpoint's FIFO and queue need tending? */ + t = 1 << num; + if ((scratch & t) == 0) + continue; + scratch ^= t; + + ep = &dev->ep[num]; + net2272_handle_ep(ep); + } + + /* some interrupts we can just ignore */ + stat &= ~(1 << SOF_INTERRUPT); + + if (stat) + dev_dbg(dev->dev, "unhandled irqstat0 %02x\n", stat); +} + +static void +net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat) +{ + u8 tmp, mask; + + /* after disconnect there's nothing else to do! */ + tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); + mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED); + + if (stat & tmp) { + net2272_write(dev, IRQSTAT1, tmp); + if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && + ((net2272_read(dev, USBCTL1) & mask) == 0)) + || ((net2272_read(dev, USBCTL1) & (1 << VBUS_PIN)) + == 0)) + && (dev->gadget.speed != USB_SPEED_UNKNOWN)) { + dev_dbg(dev->dev, "disconnect %s\n", + dev->driver->driver.name); + stop_activity(dev, dev->driver); + net2272_ep0_start(dev); + return; + } + stat &= ~tmp; + + if (!stat) + return; + } + + tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); + if (stat & tmp) { + net2272_write(dev, IRQSTAT1, tmp); + if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + if (!enable_suspend) { + stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); + dev_dbg(dev->dev, "Suspend disabled, ignoring\n"); + } + } else { + if (dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + stat &= ~tmp; + } + + /* clear any other status/irqs */ + if (stat) + net2272_write(dev, IRQSTAT1, stat); + + /* some status we can just ignore */ + stat &= ~((1 << CONTROL_STATUS_INTERRUPT) + | (1 << SUSPEND_REQUEST_INTERRUPT) + | (1 << RESUME_INTERRUPT)); + if (!stat) + return; + else + dev_dbg(dev->dev, "unhandled irqstat1 %02x\n", stat); +} + +static irqreturn_t net2272_irq(int irq, void *_dev) +{ + struct net2272 *dev = _dev; +#if defined(PLX_PCI_RDK) || defined(PLX_PCI_RDK2) + u32 intcsr; +#endif +#if defined(PLX_PCI_RDK) + u8 dmareq; +#endif + spin_lock(&dev->lock); +#if defined(PLX_PCI_RDK) + intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); + + if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) { + writel(intcsr & ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); + net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); + intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); + writel(intcsr | (1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + } + if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) { + writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), + dev->rdk1.plx9054_base_addr + DMACSR0); + + dmareq = net2272_read(dev, DMAREQ); + if (dmareq & 0x01) + net2272_handle_dma(&dev->ep[2]); + else + net2272_handle_dma(&dev->ep[1]); + } +#endif +#if defined(PLX_PCI_RDK2) + /* see if PCI int for us by checking irqstat */ + intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT); + if (!intcsr & (1 << NET2272_PCI_IRQ)) + return IRQ_NONE; + /* check dma interrupts */ +#endif + /* Platform/devcice interrupt handler */ +#if !defined(PLX_PCI_RDK) + net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); + net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); +#endif + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +static int net2272_present(struct net2272 *dev) +{ + /* + * Quick test to see if CPU can communicate properly with the NET2272. + * Verifies connection using writes and reads to write/read and + * read-only registers. + * + * This routine is strongly recommended especially during early bring-up + * of new hardware, however for designs that do not apply Power On System + * Tests (POST) it may discarded (or perhaps minimized). + */ + unsigned int ii; + u8 val, refval; + + /* Verify NET2272 write/read SCRATCH register can write and read */ + refval = net2272_read(dev, SCRATCH); + for (ii = 0; ii < 0x100; ii += 7) { + net2272_write(dev, SCRATCH, ii); + val = net2272_read(dev, SCRATCH); + if (val != ii) { + dev_dbg(dev->dev, + "%s: write/read SCRATCH register test failed: " + "wrote:0x%2.2x, read:0x%2.2x\n", + __func__, ii, val); + return -EINVAL; + } + } + /* To be nice, we write the original SCRATCH value back: */ + net2272_write(dev, SCRATCH, refval); + + /* Verify NET2272 CHIPREV register is read-only: */ + refval = net2272_read(dev, CHIPREV_2272); + for (ii = 0; ii < 0x100; ii += 7) { + net2272_write(dev, CHIPREV_2272, ii); + val = net2272_read(dev, CHIPREV_2272); + if (val != refval) { + dev_dbg(dev->dev, + "%s: write/read CHIPREV register test failed: " + "wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n", + __func__, ii, val, refval); + return -EINVAL; + } + } + + /* + * Verify NET2272's "NET2270 legacy revision" register + * - NET2272 has two revision registers. The NET2270 legacy revision + * register should read the same value, regardless of the NET2272 + * silicon revision. The legacy register applies to NET2270 + * firmware being applied to the NET2272. + */ + val = net2272_read(dev, CHIPREV_LEGACY); + if (val != NET2270_LEGACY_REV) { + /* + * Unexpected legacy revision value + * - Perhaps the chip is a NET2270? + */ + dev_dbg(dev->dev, + "%s: WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n" + " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n", + __func__, NET2270_LEGACY_REV, val); + return -EINVAL; + } + + /* + * Verify NET2272 silicon revision + * - This revision register is appropriate for the silicon version + * of the NET2272 + */ + val = net2272_read(dev, CHIPREV_2272); + switch (val) { + case CHIPREV_NET2272_R1: + /* + * NET2272 Rev 1 has DMA related errata: + * - Newer silicon (Rev 1A or better) required + */ + dev_dbg(dev->dev, + "%s: Rev 1 detected: newer silicon recommended for DMA support\n", + __func__); + break; + case CHIPREV_NET2272_R1A: + break; + default: + /* NET2272 silicon version *may* not work with this firmware */ + dev_dbg(dev->dev, + "%s: unexpected silicon revision register value: " + " CHIPREV_2272: 0x%2.2x\n", + __func__, val); + /* + * Return Success, even though the chip rev is not an expected value + * - Older, pre-built firmware can attempt to operate on newer silicon + * - Often, new silicon is perfectly compatible + */ + } + + /* Success: NET2272 checks out OK */ + return 0; +} + +static void +net2272_gadget_release(struct device *_dev) +{ + struct net2272 *dev = dev_get_drvdata(_dev); + kfree(dev); +} + +/*---------------------------------------------------------------------------*/ + +static void __devexit +net2272_remove(struct net2272 *dev) +{ + usb_del_gadget_udc(&dev->gadget); + + /* start with the driver above us */ + if (dev->driver) { + /* should have been done already by driver model core */ + dev_warn(dev->dev, "pci remove, driver '%s' is still registered\n", + dev->driver->driver.name); + usb_gadget_unregister_driver(dev->driver); + } + + free_irq(dev->irq, dev); + iounmap(dev->base_addr); + + device_unregister(&dev->gadget.dev); + device_remove_file(dev->dev, &dev_attr_registers); + + dev_info(dev->dev, "unbind\n"); + the_controller = NULL; +} + +static struct net2272 * __devinit +net2272_probe_init(struct device *dev, unsigned int irq) +{ + struct net2272 *ret; + + if (the_controller) { + dev_warn(dev, "ignoring\n"); + return ERR_PTR(-EBUSY); + } + + if (!irq) { + dev_dbg(dev, "No IRQ!\n"); + return ERR_PTR(-ENODEV); + } + + /* alloc, and start init */ + ret = kzalloc(sizeof(*ret), GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&ret->lock); + ret->irq = irq; + ret->dev = dev; + ret->gadget.ops = &net2272_ops; + ret->gadget.is_dualspeed = 1; + + /* 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 +net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) +{ + int ret; + + /* See if there... */ + if (net2272_present(dev)) { + dev_warn(dev->dev, "2272 not found!\n"); + ret = -ENODEV; + goto err; + } + + net2272_usb_reset(dev); + net2272_usb_reinit(dev); + + ret = request_irq(dev->irq, net2272_irq, irqflags, driver_name, dev); + if (ret) { + dev_err(dev->dev, "request interrupt %i failed\n", dev->irq); + goto err; + } + + dev->chiprev = net2272_read(dev, CHIPREV_2272); + + /* done */ + dev_info(dev->dev, "%s\n", driver_desc); + dev_info(dev->dev, "irq %i, mem %p, chip rev %04x, dma %s\n", + dev->irq, dev->base_addr, dev->chiprev, + dma_mode_string()); + dev_info(dev->dev, "version: %s\n", driver_vers); + + the_controller = dev; + + 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; + + ret = usb_add_gadget_udc(dev->dev, &dev->gadget); + if (ret) + goto err_add_udc; + + return 0; + +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: + return ret; +} + +#ifdef CONFIG_PCI + +/* + * wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us + */ + +static int __devinit +net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev) +{ + unsigned long resource, len, tmp; + void __iomem *mem_mapped_addr[4]; + int ret, i; + + /* + * BAR 0 holds PLX 9054 config registers + * BAR 1 is i/o memory; unused here + * BAR 2 holds EPLD config registers + * BAR 3 holds NET2272 registers + */ + + /* Find and map all address spaces */ + for (i = 0; i < 4; ++i) { + if (i == 1) + continue; /* BAR1 unused */ + + resource = pci_resource_start(pdev, i); + len = pci_resource_len(pdev, i); + + if (!request_mem_region(resource, len, driver_name)) { + dev_dbg(dev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err; + } + + mem_mapped_addr[i] = ioremap_nocache(resource, len); + if (mem_mapped_addr[i] == NULL) { + release_mem_region(resource, len); + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err; + } + } + + dev->rdk1.plx9054_base_addr = mem_mapped_addr[0]; + dev->rdk1.epld_base_addr = mem_mapped_addr[2]; + dev->base_addr = mem_mapped_addr[3]; + + /* Set PLX 9054 bus width (16 bits) */ + tmp = readl(dev->rdk1.plx9054_base_addr + LBRD1); + writel((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT, + dev->rdk1.plx9054_base_addr + LBRD1); + + /* Enable PLX 9054 Interrupts */ + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) | + (1 << PCI_INTERRUPT_ENABLE) | + (1 << LOCAL_INTERRUPT_INPUT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + + writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), + dev->rdk1.plx9054_base_addr + DMACSR0); + + /* reset */ + writeb((1 << EPLD_DMA_ENABLE) | + (1 << DMA_CTL_DACK) | + (1 << DMA_TIMEOUT_ENABLE) | + (1 << USER) | + (0 << MPX_MODE) | + (1 << BUSWIDTH) | + (1 << NET2272_RESET), + dev->base_addr + EPLD_IO_CONTROL_REGISTER); + + mb(); + writeb(readb(dev->base_addr + EPLD_IO_CONTROL_REGISTER) & + ~(1 << NET2272_RESET), + dev->base_addr + EPLD_IO_CONTROL_REGISTER); + udelay(200); + + return 0; + + err: + while (--i >= 0) { + iounmap(mem_mapped_addr[i]); + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } + + return ret; +} + +static int __devinit +net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev) +{ + unsigned long resource, len; + void __iomem *mem_mapped_addr[2]; + int ret, i; + + /* + * BAR 0 holds FGPA config registers + * BAR 1 holds NET2272 registers + */ + + /* Find and map all address spaces, bar2-3 unused in rdk 2 */ + for (i = 0; i < 2; ++i) { + resource = pci_resource_start(pdev, i); + len = pci_resource_len(pdev, i); + + if (!request_mem_region(resource, len, driver_name)) { + dev_dbg(dev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err; + } + + mem_mapped_addr[i] = ioremap_nocache(resource, len); + if (mem_mapped_addr[i] == NULL) { + release_mem_region(resource, len); + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err; + } + } + + dev->rdk2.fpga_base_addr = mem_mapped_addr[0]; + dev->base_addr = mem_mapped_addr[1]; + + mb(); + /* Set 2272 bus width (16 bits) and reset */ + writel((1 << CHIP_RESET), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); + udelay(200); + writel((1 << BUS_WIDTH), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); + /* Print fpga version number */ + dev_info(dev->dev, "RDK2 FPGA version %08x\n", + readl(dev->rdk2.fpga_base_addr + RDK2_FPGAREV)); + /* Enable FPGA Interrupts */ + writel((1 << NET2272_PCI_IRQ), dev->rdk2.fpga_base_addr + RDK2_IRQENB); + + return 0; + + err: + while (--i >= 0) { + iounmap(mem_mapped_addr[i]); + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } + + return ret; +} + +static int __devinit +net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net2272 *dev; + int ret; + + dev = net2272_probe_init(&pdev->dev, pdev->irq); + if (IS_ERR(dev)) + return PTR_ERR(dev); + dev->dev_id = pdev->device; + + if (pci_enable_device(pdev) < 0) { + ret = -ENODEV; + goto err_free; + } + + pci_set_master(pdev); + + switch (pdev->device) { + case PCI_DEVICE_ID_RDK1: ret = net2272_rdk1_probe(pdev, dev); break; + case PCI_DEVICE_ID_RDK2: ret = net2272_rdk2_probe(pdev, dev); break; + default: BUG(); + } + if (ret) + goto err_pci; + + ret = net2272_probe_fin(dev, 0); + if (ret) + goto err_pci; + + pci_set_drvdata(pdev, dev); + + return 0; + + err_pci: + pci_disable_device(pdev); + err_free: + kfree(dev); + + return ret; +} + +static void __devexit +net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev) +{ + int i; + + /* disable PLX 9054 interrupts */ + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & + ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + + /* clean up resources allocated during probe() */ + iounmap(dev->rdk1.plx9054_base_addr); + iounmap(dev->rdk1.epld_base_addr); + + for (i = 0; i < 4; ++i) { + if (i == 1) + continue; /* BAR1 unused */ + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + } +} + +static void __devexit +net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev) +{ + int i; + + /* disable fpga interrupts + writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & + ~(1 << PCI_INTERRUPT_ENABLE), + dev->rdk1.plx9054_base_addr + INTCSR); + */ + + /* clean up resources allocated during probe() */ + iounmap(dev->rdk2.fpga_base_addr); + + for (i = 0; i < 2; ++i) + release_mem_region(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); +} + +static void __devexit +net2272_pci_remove(struct pci_dev *pdev) +{ + struct net2272 *dev = pci_get_drvdata(pdev); + + net2272_remove(dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_RDK1: net2272_rdk1_remove(pdev, dev); break; + case PCI_DEVICE_ID_RDK2: net2272_rdk2_remove(pdev, dev); break; + default: BUG(); + } + + pci_disable_device(pdev); + + kfree(dev); +} + +/* Table of matching PCI IDs */ +static struct pci_device_id __devinitdata pci_ids[] = { + { /* RDK 1 card */ + .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), + .class_mask = 0, + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_RDK1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { /* RDK 2 card */ + .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), + .class_mask = 0, + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_RDK2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver net2272_pci_driver = { + .name = driver_name, + .id_table = pci_ids, + + .probe = net2272_pci_probe, + .remove = __devexit_p(net2272_pci_remove), +}; + +static int net2272_pci_register(void) +{ + return pci_register_driver(&net2272_pci_driver); +} + +static void net2272_pci_unregister(void) +{ + pci_unregister_driver(&net2272_pci_driver); +} + +#else +static inline int net2272_pci_register(void) { return 0; } +static inline void net2272_pci_unregister(void) { } +#endif + +/*---------------------------------------------------------------------------*/ + +static int __devinit +net2272_plat_probe(struct platform_device *pdev) +{ + struct net2272 *dev; + int ret; + unsigned int irqflags; + resource_size_t base, len; + struct resource *iomem, *iomem_bus, *irq_res; + + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem_bus = platform_get_resource(pdev, IORESOURCE_BUS, 0); + if (!irq_res || !iomem) { + dev_err(&pdev->dev, "must provide irq/base addr"); + return -EINVAL; + } + + dev = net2272_probe_init(&pdev->dev, irq_res->start); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + irqflags = 0; + if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) + irqflags |= IRQF_TRIGGER_RISING; + if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) + irqflags |= IRQF_TRIGGER_FALLING; + if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) + irqflags |= IRQF_TRIGGER_HIGH; + if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) + irqflags |= IRQF_TRIGGER_LOW; + + base = iomem->start; + len = resource_size(iomem); + if (iomem_bus) + dev->base_shift = iomem_bus->start; + + if (!request_mem_region(base, len, driver_name)) { + dev_dbg(dev->dev, "get request memory region!\n"); + ret = -EBUSY; + goto err; + } + dev->base_addr = ioremap_nocache(base, len); + if (!dev->base_addr) { + dev_dbg(dev->dev, "can't map memory\n"); + ret = -EFAULT; + goto err_req; + } + + ret = net2272_probe_fin(dev, IRQF_TRIGGER_LOW); + if (ret) + goto err_io; + + platform_set_drvdata(pdev, dev); + dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", + (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); + + the_controller = dev; + + return 0; + + err_io: + iounmap(dev->base_addr); + err_req: + release_mem_region(base, len); + err: + return ret; +} + +static int __devexit +net2272_plat_remove(struct platform_device *pdev) +{ + struct net2272 *dev = platform_get_drvdata(pdev); + + net2272_remove(dev); + + release_mem_region(pdev->resource[0].start, + resource_size(&pdev->resource[0])); + + kfree(dev); + + return 0; +} + +static struct platform_driver net2272_plat_driver = { + .probe = net2272_plat_probe, + .remove = __devexit_p(net2272_plat_remove), + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, + /* FIXME .suspend, .resume */ +}; +MODULE_ALIAS("platform:net2272"); + +static int __init net2272_init(void) +{ + int ret; + + ret = net2272_pci_register(); + if (ret) + return ret; + ret = platform_driver_register(&net2272_plat_driver); + if (ret) + goto err_pci; + return ret; + +err_pci: + net2272_pci_unregister(); + return ret; +} +module_init(net2272_init); + +static void __exit net2272_cleanup(void) +{ + net2272_pci_unregister(); + platform_driver_unregister(&net2272_plat_driver); +} +module_exit(net2272_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("PLX Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/net2272.h new file mode 100644 index 000000000000..e59505789359 --- /dev/null +++ b/drivers/usb/gadget/net2272.h @@ -0,0 +1,601 @@ +/* + * PLX NET2272 high/full speed USB device controller + * + * Copyright (C) 2005-2006 PLX Technology, Inc. + * Copyright (C) 2006-2011 Analog Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __NET2272_H__ +#define __NET2272_H__ + +/* Main Registers */ +#define REGADDRPTR 0x00 +#define REGDATA 0x01 +#define IRQSTAT0 0x02 +#define ENDPOINT_0_INTERRUPT 0 +#define ENDPOINT_A_INTERRUPT 1 +#define ENDPOINT_B_INTERRUPT 2 +#define ENDPOINT_C_INTERRUPT 3 +#define VIRTUALIZED_ENDPOINT_INTERRUPT 4 +#define SETUP_PACKET_INTERRUPT 5 +#define DMA_DONE_INTERRUPT 6 +#define SOF_INTERRUPT 7 +#define IRQSTAT1 0x03 +#define CONTROL_STATUS_INTERRUPT 1 +#define VBUS_INTERRUPT 2 +#define SUSPEND_REQUEST_INTERRUPT 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4 +#define RESUME_INTERRUPT 5 +#define ROOT_PORT_RESET_INTERRUPT 6 +#define RESET_STATUS 7 +#define PAGESEL 0x04 +#define DMAREQ 0x1c +#define DMA_ENDPOINT_SELECT 0 +#define DREQ_POLARITY 1 +#define DACK_POLARITY 2 +#define EOT_POLARITY 3 +#define DMA_CONTROL_DACK 4 +#define DMA_REQUEST_ENABLE 5 +#define DMA_REQUEST 6 +#define DMA_BUFFER_VALID 7 +#define SCRATCH 0x1d +#define IRQENB0 0x20 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4 +#define SETUP_PACKET_INTERRUPT_ENABLE 5 +#define DMA_DONE_INTERRUPT_ENABLE 6 +#define SOF_INTERRUPT_ENABLE 7 +#define IRQENB1 0x21 +#define VBUS_INTERRUPT_ENABLE 2 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4 +#define RESUME_INTERRUPT_ENABLE 5 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6 +#define LOCCTL 0x22 +#define DATA_WIDTH 0 +#define LOCAL_CLOCK_OUTPUT 1 +#define LOCAL_CLOCK_OUTPUT_OFF 0 +#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1 +#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2 +#define LOCAL_CLOCK_OUTPUT_15MHZ 3 +#define LOCAL_CLOCK_OUTPUT_30MHZ 4 +#define LOCAL_CLOCK_OUTPUT_60MHZ 5 +#define DMA_SPLIT_BUS_MODE 4 +#define BYTE_SWAP 5 +#define BUFFER_CONFIGURATION 6 +#define BUFFER_CONFIGURATION_EPA512_EPB512 0 +#define BUFFER_CONFIGURATION_EPA1024_EPB512 1 +#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2 +#define BUFFER_CONFIGURATION_EPA1024DB 3 +#define CHIPREV_LEGACY 0x23 +#define NET2270_LEGACY_REV 0x40 +#define LOCCTL1 0x24 +#define DMA_MODE 0 +#define SLOW_DREQ 0 +#define FAST_DREQ 1 +#define BURST_MODE 2 +#define DMA_DACK_ENABLE 2 +#define CHIPREV_2272 0x25 +#define CHIPREV_NET2272_R1 0x10 +#define CHIPREV_NET2272_R1A 0x11 +/* USB Registers */ +#define USBCTL0 0x18 +#define IO_WAKEUP_ENABLE 1 +#define USB_DETECT_ENABLE 3 +#define USB_ROOT_PORT_WAKEUP_ENABLE 5 +#define USBCTL1 0x19 +#define VBUS_PIN 0 +#define USB_FULL_SPEED 1 +#define USB_HIGH_SPEED 2 +#define GENERATE_RESUME 3 +#define VIRTUAL_ENDPOINT_ENABLE 4 +#define FRAME0 0x1a +#define FRAME1 0x1b +#define OURADDR 0x30 +#define FORCE_IMMEDIATE 7 +#define USBDIAG 0x31 +#define FORCE_TRANSMIT_CRC_ERROR 0 +#define PREVENT_TRANSMIT_BIT_STUFF 1 +#define FORCE_RECEIVE_ERROR 2 +#define FAST_TIMES 4 +#define USBTEST 0x32 +#define TEST_MODE_SELECT 0 +#define NORMAL_OPERATION 0 +#define TEST_J 1 +#define TEST_K 2 +#define TEST_SE0_NAK 3 +#define TEST_PACKET 4 +#define TEST_FORCE_ENABLE 5 +#define XCVRDIAG 0x33 +#define FORCE_FULL_SPEED 2 +#define FORCE_HIGH_SPEED 3 +#define OPMODE 4 +#define NORMAL_OPERATION 0 +#define NON_DRIVING 1 +#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2 +#define LINESTATE 6 +#define SE0_STATE 0 +#define J_STATE 1 +#define K_STATE 2 +#define SE1_STATE 3 +#define VIRTOUT0 0x34 +#define VIRTOUT1 0x35 +#define VIRTIN0 0x36 +#define VIRTIN1 0x37 +#define SETUP0 0x40 +#define SETUP1 0x41 +#define SETUP2 0x42 +#define SETUP3 0x43 +#define SETUP4 0x44 +#define SETUP5 0x45 +#define SETUP6 0x46 +#define SETUP7 0x47 +/* Endpoint Registers (Paged via PAGESEL) */ +#define EP_DATA 0x05 +#define EP_STAT0 0x06 +#define DATA_IN_TOKEN_INTERRUPT 0 +#define DATA_OUT_TOKEN_INTERRUPT 1 +#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 +#define DATA_PACKET_RECEIVED_INTERRUPT 3 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4 +#define NAK_OUT_PACKETS 5 +#define BUFFER_EMPTY 6 +#define BUFFER_FULL 7 +#define EP_STAT1 0x07 +#define TIMEOUT 0 +#define USB_OUT_ACK_SENT 1 +#define USB_OUT_NAK_SENT 2 +#define USB_IN_ACK_RCVD 3 +#define USB_IN_NAK_SENT 4 +#define USB_STALL_SENT 5 +#define LOCAL_OUT_ZLP 6 +#define BUFFER_FLUSH 7 +#define EP_TRANSFER0 0x08 +#define EP_TRANSFER1 0x09 +#define EP_TRANSFER2 0x0a +#define EP_IRQENB 0x0b +#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 +#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1 +#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 +#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4 +#define EP_AVAIL0 0x0c +#define EP_AVAIL1 0x0d +#define EP_RSPCLR 0x0e +#define EP_RSPSET 0x0f +#define ENDPOINT_HALT 0 +#define ENDPOINT_TOGGLE 1 +#define NAK_OUT_PACKETS_MODE 2 +#define CONTROL_STATUS_PHASE_HANDSHAKE 3 +#define INTERRUPT_MODE 4 +#define AUTOVALIDATE 5 +#define HIDE_STATUS_PHASE 6 +#define ALT_NAK_OUT_PACKETS 7 +#define EP_MAXPKT0 0x28 +#define EP_MAXPKT1 0x29 +#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3 +#define NONE_ADDITIONAL_TRANSACTION 0 +#define ONE_ADDITIONAL_TRANSACTION 1 +#define TWO_ADDITIONAL_TRANSACTION 2 +#define EP_CFG 0x2a +#define ENDPOINT_NUMBER 0 +#define ENDPOINT_DIRECTION 4 +#define ENDPOINT_TYPE 5 +#define ENDPOINT_ENABLE 7 +#define EP_HBW 0x2b +#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0 +#define DATA0_PID 0 +#define DATA1_PID 1 +#define DATA2_PID 2 +#define MDATA_PID 3 +#define EP_BUFF_STATES 0x2c +#define BUFFER_A_STATE 0 +#define BUFFER_B_STATE 2 +#define BUFF_FREE 0 +#define BUFF_VALID 1 +#define BUFF_LCL 2 +#define BUFF_USB 3 + +/*---------------------------------------------------------------------------*/ + +#define PCI_DEVICE_ID_RDK1 0x9054 + +/* PCI-RDK EPLD Registers */ +#define RDK_EPLD_IO_REGISTER1 0x00000000 +#define RDK_EPLD_USB_RESET 0 +#define RDK_EPLD_USB_POWERDOWN 1 +#define RDK_EPLD_USB_WAKEUP 2 +#define RDK_EPLD_USB_EOT 3 +#define RDK_EPLD_DPPULL 4 +#define RDK_EPLD_IO_REGISTER2 0x00000004 +#define RDK_EPLD_BUSWIDTH 0 +#define RDK_EPLD_USER 2 +#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 +#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 +#define RDK_EPLD_STATUS_REGISTER 0x00000008 +#define RDK_EPLD_USB_LRESET 0 +#define RDK_EPLD_REVISION_REGISTER 0x0000000c + +/* PCI-RDK PLX 9054 Registers */ +#define INTCSR 0x68 +#define PCI_INTERRUPT_ENABLE 8 +#define LOCAL_INTERRUPT_INPUT_ENABLE 11 +#define LOCAL_INPUT_INTERRUPT_ACTIVE 15 +#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18 +#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19 +#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21 +#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22 +#define CNTRL 0x6C +#define RELOAD_CONFIGURATION_REGISTERS 29 +#define PCI_ADAPTER_SOFTWARE_RESET 30 +#define DMAMODE0 0x80 +#define LOCAL_BUS_WIDTH 0 +#define INTERNAL_WAIT_STATES 2 +#define TA_READY_INPUT_ENABLE 6 +#define LOCAL_BURST_ENABLE 8 +#define SCATTER_GATHER_MODE 9 +#define DONE_INTERRUPT_ENABLE 10 +#define LOCAL_ADDRESSING_MODE 11 +#define DEMAND_MODE 12 +#define DMA_EOT_ENABLE 14 +#define FAST_SLOW_TERMINATE_MODE_SELECT 15 +#define DMA_CHANNEL_INTERRUPT_SELECT 17 +#define DMAPADR0 0x84 +#define DMALADR0 0x88 +#define DMASIZ0 0x8c +#define DMADPR0 0x90 +#define DESCRIPTOR_LOCATION 0 +#define END_OF_CHAIN 1 +#define INTERRUPT_AFTER_TERMINAL_COUNT 2 +#define DIRECTION_OF_TRANSFER 3 +#define DMACSR0 0xa8 +#define CHANNEL_ENABLE 0 +#define CHANNEL_START 1 +#define CHANNEL_ABORT 2 +#define CHANNEL_CLEAR_INTERRUPT 3 +#define CHANNEL_DONE 4 +#define DMATHR 0xb0 +#define LBRD1 0xf8 +#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0 +#define W8_BIT 0 +#define W16_BIT 1 + +/* Special OR'ing of INTCSR bits */ +#define LOCAL_INTERRUPT_TEST \ + ((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_INTERRUPT_INPUT_ENABLE)) + +#define DMA_CHANNEL_0_TEST \ + ((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE)) + +#define DMA_CHANNEL_1_TEST \ + ((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \ + (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE)) + +/* EPLD Registers */ +#define RDK_EPLD_IO_REGISTER1 0x00000000 +#define RDK_EPLD_USB_RESET 0 +#define RDK_EPLD_USB_POWERDOWN 1 +#define RDK_EPLD_USB_WAKEUP 2 +#define RDK_EPLD_USB_EOT 3 +#define RDK_EPLD_DPPULL 4 +#define RDK_EPLD_IO_REGISTER2 0x00000004 +#define RDK_EPLD_BUSWIDTH 0 +#define RDK_EPLD_USER 2 +#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 +#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 +#define RDK_EPLD_STATUS_REGISTER 0x00000008 +#define RDK_EPLD_USB_LRESET 0 +#define RDK_EPLD_REVISION_REGISTER 0x0000000c + +#define EPLD_IO_CONTROL_REGISTER 0x400 +#define NET2272_RESET 0 +#define BUSWIDTH 1 +#define MPX_MODE 3 +#define USER 4 +#define DMA_TIMEOUT_ENABLE 5 +#define DMA_CTL_DACK 6 +#define EPLD_DMA_ENABLE 7 +#define EPLD_DMA_CONTROL_REGISTER 0x800 +#define SPLIT_DMA_MODE 0 +#define SPLIT_DMA_DIRECTION 1 +#define SPLIT_DMA_ENABLE 2 +#define SPLIT_DMA_INTERRUPT_ENABLE 3 +#define SPLIT_DMA_INTERRUPT 4 +#define EPLD_DMA_MODE 5 +#define EPLD_DMA_CONTROLLER_ENABLE 7 +#define SPLIT_DMA_ADDRESS_LOW 0xc00 +#define SPLIT_DMA_ADDRESS_HIGH 0x1000 +#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400 +#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800 +#define EPLD_REVISION_REGISTER 0x1c00 +#define SPLIT_DMA_RAM 0x4000 +#define DMA_RAM_SIZE 0x1000 + +/*---------------------------------------------------------------------------*/ + +#define PCI_DEVICE_ID_RDK2 0x3272 + +/* PCI-RDK version 2 registers */ + +/* Main Control Registers */ + +#define RDK2_IRQENB 0x00 +#define RDK2_IRQSTAT 0x04 +#define PB7 23 +#define PB6 22 +#define PB5 21 +#define PB4 20 +#define PB3 19 +#define PB2 18 +#define PB1 17 +#define PB0 16 +#define GP3 23 +#define GP2 23 +#define GP1 23 +#define GP0 23 +#define DMA_RETRY_ABORT 6 +#define DMA_PAUSE_DONE 5 +#define DMA_ABORT_DONE 4 +#define DMA_OUT_FIFO_TRANSFER_DONE 3 +#define DMA_LOCAL_DONE 2 +#define DMA_PCI_DONE 1 +#define NET2272_PCI_IRQ 0 + +#define RDK2_LOCCTLRDK 0x08 +#define CHIP_RESET 3 +#define SPLIT_DMA 2 +#define MULTIPLEX_MODE 1 +#define BUS_WIDTH 0 + +#define RDK2_GPIOCTL 0x10 +#define GP3_OUT_ENABLE 7 +#define GP2_OUT_ENABLE 6 +#define GP1_OUT_ENABLE 5 +#define GP0_OUT_ENABLE 4 +#define GP3_DATA 3 +#define GP2_DATA 2 +#define GP1_DATA 1 +#define GP0_DATA 0 + +#define RDK2_LEDSW 0x14 +#define LED3 27 +#define LED2 26 +#define LED1 25 +#define LED0 24 +#define PBUTTON 16 +#define DIPSW 0 + +#define RDK2_DIAG 0x18 +#define RDK2_FAST_TIMES 2 +#define FORCE_PCI_SERR 1 +#define FORCE_PCI_INT 0 +#define RDK2_FPGAREV 0x1C + +/* Dma Control registers */ +#define RDK2_DMACTL 0x80 +#define ADDR_HOLD 24 +#define RETRY_COUNT 16 /* 23:16 */ +#define FIFO_THRESHOLD 11 /* 15:11 */ +#define MEM_WRITE_INVALIDATE 10 +#define READ_MULTIPLE 9 +#define READ_LINE 8 +#define RDK2_DMA_MODE 6 /* 7:6 */ +#define CONTROL_DACK 5 +#define EOT_ENABLE 4 +#define EOT_POLARITY 3 +#define DACK_POLARITY 2 +#define DREQ_POLARITY 1 +#define DMA_ENABLE 0 + +#define RDK2_DMASTAT 0x84 +#define GATHER_COUNT 12 /* 14:12 */ +#define FIFO_COUNT 6 /* 11:6 */ +#define FIFO_FLUSH 5 +#define FIFO_TRANSFER 4 +#define PAUSE_DONE 3 +#define ABORT_DONE 2 +#define DMA_ABORT 1 +#define DMA_START 0 + +#define RDK2_DMAPCICOUNT 0x88 +#define DMA_DIRECTION 31 +#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */ + +#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */ + +#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */ + +/*---------------------------------------------------------------------------*/ + +#define REG_INDEXED_THRESHOLD (1 << 5) + +/* DRIVER DATA STRUCTURES and UTILITIES */ +struct net2272_ep { + struct usb_ep ep; + struct net2272 *dev; + unsigned long irqs; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num:8, + fifo_size:12, + stopped:1, + wedged:1, + is_in:1, + is_iso:1, + dma:1, + not_empty:1; +}; + +struct net2272 { + /* each device provides one gadget, several endpoints */ + struct usb_gadget gadget; + struct device *dev; + unsigned short dev_id; + + spinlock_t lock; + struct net2272_ep ep[4]; + struct usb_gadget_driver *driver; + unsigned protocol_stall:1, + softconnect:1, + is_selfpowered:1, + wakeup:1, + dma_eot_polarity:1, + dma_dack_polarity:1, + dma_dreq_polarity:1, + dma_busy:1; + u16 chiprev; + u8 pagesel; + + unsigned int irq; + unsigned short fifo_mode; + + unsigned int base_shift; + u16 __iomem *base_addr; + union { +#ifdef CONFIG_PCI + struct { + void __iomem *plx9054_base_addr; + void __iomem *epld_base_addr; + } rdk1; + struct { + /* Bar0, Bar1 is base_addr both mem-mapped */ + void __iomem *fpga_base_addr; + } rdk2; +#endif + }; +}; + +static void __iomem * +net2272_reg_addr(struct net2272 *dev, unsigned int reg) +{ + return dev->base_addr + (reg << dev->base_shift); +} + +static void +net2272_write(struct net2272 *dev, unsigned int reg, u8 value) +{ + if (reg >= REG_INDEXED_THRESHOLD) { + /* + * Indexed register; use REGADDRPTR/REGDATA + * - Save and restore REGADDRPTR. This prevents REGADDRPTR from + * changes between other code sections, but it is time consuming. + * - Performance tips: either do not save and restore REGADDRPTR (if it + * is safe) or do save/restore operations only in critical sections. + u8 tmp = readb(dev->base_addr + REGADDRPTR); + */ + writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); + writeb(value, net2272_reg_addr(dev, REGDATA)); + /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ + } else + writeb(value, net2272_reg_addr(dev, reg)); +} + +static u8 +net2272_read(struct net2272 *dev, unsigned int reg) +{ + u8 ret; + + if (reg >= REG_INDEXED_THRESHOLD) { + /* + * Indexed register; use REGADDRPTR/REGDATA + * - Save and restore REGADDRPTR. This prevents REGADDRPTR from + * changes between other code sections, but it is time consuming. + * - Performance tips: either do not save and restore REGADDRPTR (if it + * is safe) or do save/restore operations only in critical sections. + u8 tmp = readb(dev->base_addr + REGADDRPTR); + */ + writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); + ret = readb(net2272_reg_addr(dev, REGDATA)); + /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ + } else + ret = readb(net2272_reg_addr(dev, reg)); + + return ret; +} + +static void +net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value) +{ + struct net2272 *dev = ep->dev; + + if (dev->pagesel != ep->num) { + net2272_write(dev, PAGESEL, ep->num); + dev->pagesel = ep->num; + } + net2272_write(dev, reg, value); +} + +static u8 +net2272_ep_read(struct net2272_ep *ep, unsigned int reg) +{ + struct net2272 *dev = ep->dev; + + if (dev->pagesel != ep->num) { + net2272_write(dev, PAGESEL, ep->num); + dev->pagesel = ep->num; + } + return net2272_read(dev, reg); +} + +static void allow_status(struct net2272_ep *ep) +{ + /* ep0 only */ + net2272_ep_write(ep, EP_RSPCLR, + (1 << CONTROL_STATUS_PHASE_HANDSHAKE) | + (1 << ALT_NAK_OUT_PACKETS) | + (1 << NAK_OUT_PACKETS_MODE)); + ep->stopped = 1; +} + +static void set_halt(struct net2272_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE); + net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT); +} + +static void clear_halt(struct net2272_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + net2272_ep_write(ep, EP_RSPCLR, + (1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE)); +} + +/* count (<= 4) bytes in the next fifo write will be valid */ +static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count) +{ + /* net2272_ep_write will truncate to u8 for us */ + net2272_ep_write(ep, EP_TRANSFER2, count >> 16); + net2272_ep_write(ep, EP_TRANSFER1, count >> 8); + net2272_ep_write(ep, EP_TRANSFER0, count); +} + +struct net2272_request { + struct usb_request req; + struct list_head queue; + unsigned mapped:1, + valid:1; +}; + +#endif diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 476d88e1ae97..3dd40b4e675c 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1410,11 +1410,17 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) return 0; } +static int net2280_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int net2280_stop(struct usb_gadget_driver *driver); + static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, + .start = net2280_start, + .stop = net2280_stop, }; /*-------------------------------------------------------------------------*/ @@ -1738,62 +1744,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode) list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); } -/* just declare this in any driver that really need it */ -extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); - -/** - * net2280_set_fifo_mode - change allocation of fifo buffers - * @gadget: access to the net2280 device that will be updated - * @mode: 0 for default, four 1kB buffers (ep-a through ep-d); - * 1 for two 2kB buffers (ep-a and ep-b only); - * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c). - * - * returns zero on success, else negative errno. when this succeeds, - * the contents of gadget->ep_list may have changed. - * - * you may only call this function when endpoints a-d are all disabled. - * use it whenever extra hardware buffering can help performance, such - * as before enabling "high bandwidth" interrupt endpoints that use - * maxpacket bigger than 512 (when double buffering would otherwise - * be unavailable). - */ -int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) -{ - int i; - struct net2280 *dev; - int status = 0; - unsigned long flags; - - if (!gadget) - return -ENODEV; - dev = container_of (gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - - for (i = 1; i <= 4; i++) - if (dev->ep [i].desc) { - status = -EINVAL; - break; - } - if (mode < 0 || mode > 2) - status = -EINVAL; - if (status == 0) - set_fifo_mode (dev, mode); - spin_unlock_irqrestore (&dev->lock, flags); - - if (status == 0) { - if (mode == 1) - DEBUG (dev, "fifo: ep-a 2K, ep-b 2K\n"); - else if (mode == 2) - DEBUG (dev, "fifo: ep-a 2K, ep-b 1K, ep-c 1K\n"); - /* else all are 1K */ - } - return status; -} -EXPORT_SYMBOL (net2280_set_fifo_mode); - -/*-------------------------------------------------------------------------*/ - /* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second @@ -1930,7 +1880,7 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int net2280_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct net2280 *dev = the_controller; @@ -1994,7 +1944,6 @@ err_unbind: dev->driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) @@ -2022,7 +1971,7 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) usb_reinit (dev); } -int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int net2280_stop(struct usb_gadget_driver *driver) { struct net2280 *dev = the_controller; unsigned long flags; @@ -2049,8 +1998,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); return 0; } -EXPORT_SYMBOL (usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -2732,6 +2679,8 @@ static void net2280_remove (struct pci_dev *pdev) { struct net2280 *dev = pci_get_drvdata (pdev); + usb_del_gadget_udc(&dev->gadget); + BUG_ON(dev->driver); /* then clean up the resources we allocated during probe() */ @@ -2916,6 +2865,9 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) retval = device_create_file (&pdev->dev, &dev_attr_registers); if (retval) goto done; + retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + if (retval) + goto done; return 0; done: diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 55ca63ad3506..c7fb7723c014 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -241,6 +241,7 @@ static struct usb_composite_driver nokia_driver = { .name = "g_nokia", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, .unbind = __exit_p(nokia_unbind), }; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 82fd24935332..740c7daed279 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1375,6 +1375,10 @@ 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 struct usb_gadget_ops omap_gadget_ops = { .get_frame = omap_get_frame, .wakeup = omap_wakeup, @@ -1382,6 +1386,8 @@ static struct usb_gadget_ops omap_gadget_ops = { .vbus_session = omap_vbus_session, .vbus_draw = omap_vbus_draw, .pullup = omap_pullup, + .start = omap_udc_start, + .stop = omap_udc_stop, }; /*-------------------------------------------------------------------------*/ @@ -2102,7 +2108,7 @@ static inline int machine_without_vbus_sense(void) ); } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int omap_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { int status = -ENODEV; @@ -2186,9 +2192,8 @@ done: omap_udc_enable_clock(0); return status; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +static int omap_udc_stop(struct usb_gadget_driver *driver) { unsigned long flags; int status = -ENODEV; @@ -2222,8 +2227,6 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) DBG("unregistered driver '%s'\n", driver->driver.name); return status; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -2991,9 +2994,16 @@ known: create_proc_file(); status = device_add(&udc->gadget.dev); + if (status) + goto cleanup4; + + status = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (!status) return status; /* If fail, fall through */ +cleanup4: + remove_proc_file(); + #ifdef USE_ISO cleanup3: free_irq(pdev->resource[2].start, udc); @@ -3029,6 +3039,8 @@ static int __exit omap_udc_remove(struct platform_device *pdev) if (!udc) return -ENODEV; + + usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 68dbcc3e4cc2..f96615ab6b77 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -1176,6 +1176,9 @@ 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 const struct usb_gadget_ops pch_udc_ops = { .get_frame = pch_udc_pcd_get_frame, .wakeup = pch_udc_pcd_wakeup, @@ -1183,6 +1186,8 @@ static const struct usb_gadget_ops pch_udc_ops = { .pullup = pch_udc_pcd_pullup, .vbus_session = pch_udc_pcd_vbus_session, .vbus_draw = pch_udc_pcd_vbus_draw, + .start = pch_udc_start, + .stop = pch_udc_stop, }; /** @@ -2690,7 +2695,7 @@ static int init_dma_pools(struct pch_udc_dev *dev) return 0; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int pch_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct pch_udc_dev *dev = pch_udc; @@ -2733,9 +2738,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, dev->connected = 1; return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int pch_udc_stop(struct usb_gadget_driver *driver) { struct pch_udc_dev *dev = pch_udc; @@ -2761,7 +2765,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) pch_udc_set_disconnect(dev); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); static void pch_udc_shutdown(struct pci_dev *pdev) { @@ -2778,6 +2781,8 @@ static void pch_udc_remove(struct pci_dev *pdev) { struct pch_udc_dev *dev = pci_get_drvdata(pdev); + usb_del_gadget_udc(&dev->gadget); + /* gadget driver must not be registered */ if (dev->driver) dev_err(&pdev->dev, @@ -2953,6 +2958,9 @@ static int pch_udc_probe(struct pci_dev *pdev, /* 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); + if (retval) + goto finished; return 0; finished: diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 978e6a101bf2..a341dde6f9c3 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -89,8 +89,7 @@ struct printer_dev { u8 config; s8 interface; struct usb_ep *in_ep, *out_ep; - const struct usb_endpoint_descriptor - *in, *out; + struct list_head rx_reqs; /* List of free RX structs */ struct list_head rx_reqs_active; /* List of Active RX xfers */ struct list_head rx_buffers; /* List of completed xfers */ @@ -898,19 +897,20 @@ set_printer_interface(struct printer_dev *dev) { int result = 0; - dev->in = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); + dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc); dev->in_ep->driver_data = dev; - dev->out = ep_desc(dev->gadget, &hs_ep_out_desc, &fs_ep_out_desc); + dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc, + &fs_ep_out_desc); dev->out_ep->driver_data = dev; - result = usb_ep_enable(dev->in_ep, dev->in); + result = usb_ep_enable(dev->in_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; } - result = usb_ep_enable(dev->out_ep, dev->out); + result = usb_ep_enable(dev->out_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; @@ -921,8 +921,8 @@ done: if (result != 0) { (void) usb_ep_disable(dev->in_ep); (void) usb_ep_disable(dev->out_ep); - dev->in = NULL; - dev->out = NULL; + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; } /* caller is responsible for cleanup on error */ @@ -936,12 +936,14 @@ static void printer_reset_interface(struct printer_dev *dev) DBG(dev, "%s\n", __func__); - if (dev->in) + if (dev->in_ep->desc) usb_ep_disable(dev->in_ep); - if (dev->out) + if (dev->out_ep->desc) usb_ep_disable(dev->out_ep); + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; dev->interface = -1; } @@ -1107,9 +1109,9 @@ static void printer_soft_reset(struct printer_dev *dev) list_add(&req->list, &dev->tx_reqs); } - if (usb_ep_enable(dev->in_ep, dev->in)) + if (usb_ep_enable(dev->in_ep)) DBG(dev, "Failed to enable USB in_ep\n"); - if (usb_ep_enable(dev->out_ep, dev->out)) + if (usb_ep_enable(dev->out_ep)) DBG(dev, "Failed to enable USB out_ep\n"); wake_up_interruptible(&dev->rx_wait); @@ -1149,6 +1151,8 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) 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; @@ -1156,6 +1160,12 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_DEVICE_QUALIFIER: if (!gadget->is_dualspeed) 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); @@ -1451,15 +1461,11 @@ autoconf_fail: out_ep->driver_data = out_ep; /* claim */ #ifdef CONFIG_USB_GADGET_DUALSPEED - /* assumes ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ + /* 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 */ - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered(gadget); if (gadget->is_otg) { diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 774545494cf2..e4e59b4de25d 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1011,12 +1011,18 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } +static int pxa25x_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int pxa25x_stop(struct usb_gadget_driver *driver); + static const struct usb_gadget_ops pxa25x_udc_ops = { .get_frame = pxa25x_udc_get_frame, .wakeup = pxa25x_udc_wakeup, .vbus_session = pxa25x_udc_vbus_session, .pullup = pxa25x_udc_pullup, .vbus_draw = pxa25x_udc_vbus_draw, + .start = pxa25x_start, + .stop = pxa25x_stop, }; /*-------------------------------------------------------------------------*/ @@ -1263,7 +1269,7 @@ static void udc_enable (struct pxa25x_udc *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int pxa25x_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct pxa25x_udc *dev = the_controller; @@ -1322,7 +1328,6 @@ fail: bind_fail: return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); static void stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) @@ -1351,7 +1356,7 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) udc_reinit(dev); } -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int pxa25x_stop(struct usb_gadget_driver *driver) { struct pxa25x_udc *dev = the_controller; @@ -1379,8 +1384,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) dump_state(dev); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - /*-------------------------------------------------------------------------*/ @@ -2231,8 +2234,11 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) #endif create_debug_files(dev); - return 0; + retval = usb_add_gadget_udc(&pdev->dev, &dev->gadget); + if (!retval) + return retval; + remove_debug_files(dev); #ifdef CONFIG_ARCH_LUBBOCK lubbock_fail0: free_irq(LUBBOCK_USB_DISC_IRQ, dev); @@ -2261,6 +2267,7 @@ static int __exit 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; diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 57607696735c..85b68c75dc9d 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1680,12 +1680,18 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) return -EOPNOTSUPP; } +static int pxa27x_udc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int pxa27x_udc_stop(struct usb_gadget_driver *driver); + static const struct usb_gadget_ops pxa_udc_ops = { .get_frame = pxa_udc_get_frame, .wakeup = pxa_udc_wakeup, .pullup = pxa_udc_pullup, .vbus_session = pxa_udc_vbus_session, .vbus_draw = pxa_udc_vbus_draw, + .start = pxa27x_udc_start, + .stop = pxa27x_udc_stop, }; /** @@ -1791,7 +1797,7 @@ static void udc_enable(struct pxa_udc *udc) } /** - * usb_gadget_probe_driver - Register gadget driver + * pxa27x_start - Register gadget driver * @driver: gadget driver * @bind: bind function * @@ -1805,7 +1811,7 @@ static void udc_enable(struct pxa_udc *udc) * * Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int pxa27x_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct pxa_udc *udc = the_controller; @@ -1860,8 +1866,6 @@ add_fail: udc->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); - /** * stop_activity - Stops udc endpoints @@ -1888,12 +1892,12 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) } /** - * usb_gadget_unregister_driver - Unregister the gadget driver + * pxa27x_udc_stop - Unregister the gadget driver * @driver: gadget driver * * Returns 0 if no error, -ENODEV, -EINVAL otherwise */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int pxa27x_udc_stop(struct usb_gadget_driver *driver) { struct pxa_udc *udc = the_controller; @@ -1917,7 +1921,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return otg_set_peripheral(udc->transceiver, NULL); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /** * handle_ep0_ctrl_req - handle control endpoint control request @@ -2516,9 +2519,14 @@ static int __init pxa_udc_probe(struct platform_device *pdev) driver_name, IRQ_USB, retval); goto err_irq; } + retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (retval) + goto err_add_udc; pxa_init_debugfs(udc); return 0; +err_add_udc: + free_irq(udc->irq, udc); err_irq: iounmap(udc->regs); err_map: @@ -2537,6 +2545,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) struct pxa_udc *udc = platform_get_drvdata(_dev); int gpio = udc->mach->gpio_pullup; + usb_del_gadget_udc(&udc->gadget); usb_gadget_unregister_driver(udc->driver); free_irq(udc->irq, udc); pxa_cleanup_debugfs(udc); diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 6dcc1f68fa60..50991e5bd5e8 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -3,7 +3,7 @@ * * Copyright (C) 2006-2009 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -576,7 +576,11 @@ static void init_controller(struct r8a66597 *r8a66597) u16 endian = r8a66597->pdata->endian ? BIGEND : 0; if (r8a66597->pdata->on_chip) { - r8a66597_bset(r8a66597, 0x04, SYSCFG1); + if (r8a66597->pdata->buswait) + r8a66597_write(r8a66597, r8a66597->pdata->buswait, + SYSCFG1); + else + r8a66597_write(r8a66597, 0x0f, SYSCFG1); r8a66597_bset(r8a66597, HSE, SYSCFG0); r8a66597_bclr(r8a66597, USBE, SYSCFG0); @@ -618,6 +622,7 @@ static void disable_controller(struct r8a66597 *r8a66597) { if (r8a66597->pdata->on_chip) { r8a66597_bset(r8a66597, SCKE, SYSCFG0); + r8a66597_bclr(r8a66597, UTST, TESTMODE); /* disable interrupts */ r8a66597_write(r8a66597, 0, INTENB0); @@ -635,6 +640,7 @@ static void disable_controller(struct r8a66597 *r8a66597) r8a66597_bclr(r8a66597, SCKE, SYSCFG0); } else { + r8a66597_bclr(r8a66597, UTST, TESTMODE); r8a66597_bclr(r8a66597, SCKE, SYSCFG0); udelay(1); r8a66597_bclr(r8a66597, PLLC, SYSCFG0); @@ -999,10 +1005,29 @@ static void clear_feature(struct r8a66597 *r8a66597, static void set_feature(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) { + u16 tmp; + int timeout = 3000; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - control_end(r8a66597, 1); + switch (le16_to_cpu(ctrl->wValue)) { + case USB_DEVICE_TEST_MODE: + control_end(r8a66597, 1); + /* Wait for the completion of status stage */ + do { + tmp = r8a66597_read(r8a66597, INTSTS0) & CTSQ; + udelay(1); + } while (tmp != CS_IDST || timeout-- > 0); + + if (tmp == CS_IDST) + r8a66597_bset(r8a66597, + le16_to_cpu(ctrl->wIndex >> 8), + TESTMODE); + break; + default: + pipe_stall(r8a66597, 0); + break; + } break; case USB_RECIP_INTERFACE: control_end(r8a66597, 1); @@ -1410,7 +1435,7 @@ static struct usb_ep_ops r8a66597_ep_ops = { /*-------------------------------------------------------------------------*/ static struct r8a66597 *the_controller; -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int r8a66597_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct r8a66597 *r8a66597 = the_controller; @@ -1444,6 +1469,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, goto error; } + init_controller(r8a66597); r8a66597_bset(r8a66597, VBSE, INTENB0); if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) { r8a66597_start_xclock(r8a66597); @@ -1462,9 +1488,8 @@ error: return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int r8a66597_stop(struct usb_gadget_driver *driver) { struct r8a66597 *r8a66597 = the_controller; unsigned long flags; @@ -1475,20 +1500,16 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) spin_lock_irqsave(&r8a66597->lock, flags); if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN) r8a66597_usb_disconnect(r8a66597); - spin_unlock_irqrestore(&r8a66597->lock, flags); - r8a66597_bclr(r8a66597, VBSE, INTENB0); + disable_controller(r8a66597); + spin_unlock_irqrestore(&r8a66597->lock, flags); driver->unbind(&r8a66597->gadget); - init_controller(r8a66597); - disable_controller(r8a66597); - device_del(&r8a66597->gadget.dev); r8a66597->driver = NULL; return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ static int r8a66597_get_frame(struct usb_gadget *_gadget) @@ -1497,14 +1518,33 @@ static int r8a66597_get_frame(struct usb_gadget *_gadget) return r8a66597_read(r8a66597, FRMNUM) & 0x03FF; } +static int r8a66597_pullup(struct usb_gadget *gadget, int is_on) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + if (is_on) + r8a66597_bset(r8a66597, DPRPU, SYSCFG0); + else + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + spin_unlock_irqrestore(&r8a66597->lock, flags); + + return 0; +} + static struct usb_gadget_ops r8a66597_gadget_ops = { .get_frame = r8a66597_get_frame, + .start = r8a66597_start, + .stop = r8a66597_stop, + .pullup = r8a66597_pullup, }; static int __exit r8a66597_remove(struct platform_device *pdev) { struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); + usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); iounmap(r8a66597->reg); free_irq(platform_get_irq(pdev, 0), r8a66597); @@ -1645,11 +1685,15 @@ static int __init r8a66597_probe(struct platform_device *pdev) goto clean_up3; r8a66597->ep0_req->complete = nop_completion; - init_controller(r8a66597); + ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget); + if (ret) + goto err_add_udc; dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); return 0; +err_add_udc: + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); clean_up3: free_irq(irq, r8a66597); clean_up2: @@ -1679,6 +1723,7 @@ static struct platform_driver r8a66597_driver = { .name = (char *) udc_name, }, }; +MODULE_ALIAS("platform:r8a66597_udc"); static int __init r8a66597_udc_init(void) { diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 5fc22e09a0f1..503f766c23a7 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -3,7 +3,7 @@ * * Copyright (C) 2007-2009 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * Author : Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 0dfee282878a..8bdee67ce09a 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2574,7 +2574,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) return 0; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int s3c_hsotg_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct s3c_hsotg *hsotg = our_hsotg; @@ -2745,9 +2745,8 @@ err: hsotg->gadget.dev.driver = NULL; return ret; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int s3c_hsotg_stop(struct usb_gadget_driver *driver) { struct s3c_hsotg *hsotg = our_hsotg; int ep; @@ -2775,7 +2774,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) { @@ -2784,6 +2782,8 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) static struct usb_gadget_ops s3c_hsotg_gadget_ops = { .get_frame = s3c_hsotg_gadget_getframe, + .start = s3c_hsotg_start, + .stop = s3c_hsotg_stop, }; /** @@ -3403,6 +3403,10 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) 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); @@ -3410,6 +3414,11 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) 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); @@ -3427,6 +3436,8 @@ 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); diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index d5e3e1e58626..3fa717c5f4bc 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1133,7 +1133,7 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) return IRQ_HANDLED; } -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int s3c_hsudc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct s3c_hsudc *hsudc = the_controller; @@ -1181,9 +1181,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, return 0; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int s3c_hsudc_stop(struct usb_gadget_driver *driver) { struct s3c_hsudc *hsudc = the_controller; unsigned long flags; @@ -1210,7 +1209,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) driver->driver.name); return 0; } -EXPORT_SYMBOL(usb_gadget_unregister_driver); static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc) { @@ -1224,6 +1222,8 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) static struct usb_gadget_ops s3c_hsudc_gadget_ops = { .get_frame = s3c_hsudc_gadget_getframe, + .start = s3c_hsudc_start, + .stop = s3c_hsudc_stop, }; static int s3c_hsudc_probe(struct platform_device *pdev) @@ -1311,7 +1311,15 @@ static int s3c_hsudc_probe(struct platform_device *pdev) disable_irq(hsudc->irq); local_irq_enable(); + + ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); + if (ret) + goto err_add_udc; + return 0; +err_add_udc: + clk_disable(hsudc->uclk); + clk_put(hsudc->uclk); err_clk: free_irq(hsudc->irq, hsudc); err_irq: @@ -1333,6 +1341,7 @@ static struct platform_driver s3c_hsudc_driver = { }, .probe = s3c_hsudc_probe, }; +MODULE_ALIAS("platform:s3c-hsudc"); static int __init s3c_hsudc_modinit(void) { diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 100f2635cf0a..85c1b0d66293 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1552,6 +1552,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 const struct usb_gadget_ops s3c2410_ops = { .get_frame = s3c2410_udc_get_frame, .wakeup = s3c2410_udc_wakeup, @@ -1559,6 +1563,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, }; static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd) @@ -1567,7 +1573,7 @@ static void s3c2410_udc_command(enum s3c2410_udc_cmd_e cmd) return; if (udc_info->udc_command) { - udc_info->udc_command(S3C2410_UDC_P_DISABLE); + udc_info->udc_command(cmd); } else if (gpio_is_valid(udc_info->pullup_pin)) { int value; @@ -1672,10 +1678,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev) s3c2410_udc_command(S3C2410_UDC_P_ENABLE); } -/* - * usb_gadget_probe_driver - */ -int usb_gadget_probe_driver(struct usb_gadget_driver *driver, +static int s3c2410_udc_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct s3c2410_udc *udc = the_controller; @@ -1730,12 +1733,8 @@ register_error: udc->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_probe_driver); -/* - * usb_gadget_unregister_driver - */ -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +static int s3c2410_udc_stop(struct usb_gadget_driver *driver) { struct s3c2410_udc *udc = the_controller; @@ -1955,6 +1954,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev) goto err_vbus_irq; } + retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (retval) + goto err_add_udc; + if (s3c2410_udc_debugfs_root) { udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root, @@ -1967,6 +1970,10 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return 0; +err_add_udc: + if (udc_info && !udc_info->udc_command && + gpio_is_valid(udc_info->pullup_pin)) + gpio_free(udc_info->pullup_pin); err_vbus_irq: if (udc_info && udc_info->vbus_pin > 0) free_irq(gpio_to_irq(udc_info->vbus_pin), udc); @@ -1992,6 +1999,8 @@ static int s3c2410_udc_remove(struct platform_device *pdev) unsigned int irq; dev_dbg(&pdev->dev, "%s()\n", __func__); + + usb_del_gadget_udc(&udc->gadget); if (udc->driver) return -EBUSY; @@ -2048,26 +2057,22 @@ static int s3c2410_udc_resume(struct platform_device *pdev) #define s3c2410_udc_resume NULL #endif -static struct platform_driver udc_driver_2410 = { - .driver = { - .name = "s3c2410-usbgadget", - .owner = THIS_MODULE, - }, - .probe = s3c2410_udc_probe, - .remove = s3c2410_udc_remove, - .suspend = s3c2410_udc_suspend, - .resume = s3c2410_udc_resume, +static const struct platform_device_id s3c_udc_ids[] = { + { "s3c2410-usbgadget", }, + { "s3c2440-usbgadget", }, }; +MODULE_DEVICE_TABLE(platform, s3c_udc_ids); -static struct platform_driver udc_driver_2440 = { +static struct platform_driver udc_driver_24x0 = { .driver = { - .name = "s3c2440-usbgadget", + .name = "s3c24x0-usbgadget", .owner = THIS_MODULE, }, .probe = s3c2410_udc_probe, .remove = s3c2410_udc_remove, .suspend = s3c2410_udc_suspend, .resume = s3c2410_udc_resume, + .id_table = s3c_udc_ids, }; static int __init udc_init(void) @@ -2083,11 +2088,7 @@ static int __init udc_init(void) s3c2410_udc_debugfs_root = NULL; } - retval = platform_driver_register(&udc_driver_2410); - if (retval) - goto err; - - retval = platform_driver_register(&udc_driver_2440); + retval = platform_driver_register(&udc_driver_24x0); if (retval) goto err; @@ -2100,13 +2101,10 @@ err: static void __exit udc_exit(void) { - platform_driver_unregister(&udc_driver_2410); - platform_driver_unregister(&udc_driver_2440); + platform_driver_unregister(&udc_driver_24x0); debugfs_remove(s3c2410_udc_debugfs_root); } -EXPORT_SYMBOL(usb_gadget_unregister_driver); - module_init(udc_init); module_exit(udc_exit); @@ -2114,5 +2112,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c2410-usbgadget"); -MODULE_ALIAS("platform:s3c2440-usbgadget"); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 1ac57a973aa9..ed1b816e58d8 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -242,6 +242,7 @@ static struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, }; static int __init init(void) diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 1fa4f705b0b4..d3dd227a2bfc 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -494,7 +494,7 @@ static struct usb_descriptor_header *fsg_hs_function[] = { }; /* Maxpacket and other transfer characteristics vary by speed. */ -static struct usb_endpoint_descriptor * +static __maybe_unused struct usb_endpoint_descriptor * fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, struct usb_endpoint_descriptor *hs) { diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 2ac1d2147325..dfed4c1d96c0 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -97,16 +97,17 @@ struct eth_dev { static unsigned qmult = 5; module_param(qmult, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); +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 highspeed */ +/* for dual-speed hardware, use deeper queues at high/super speed */ static inline int qlen(struct usb_gadget *gadget) { - if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) + if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH || + gadget->speed == USB_SPEED_SUPER)) return qmult * DEFAULT_QLEN; else return DEFAULT_QLEN; @@ -598,9 +599,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle highspeed IRQ rate back slightly */ + /* throttle high/super speed IRQ rate back slightly */ if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH || + dev->gadget->speed == USB_SPEED_SUPER) ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) : 0; @@ -693,8 +695,8 @@ static int eth_stop(struct net_device *net) usb_ep_disable(link->out_ep); if (netif_carrier_ok(net)) { DBG(dev, "host still using in/out endpoints\n"); - usb_ep_enable(link->in_ep, link->in); - usb_ep_enable(link->out_ep, link->out); + usb_ep_enable(link->in_ep); + usb_ep_enable(link->out_ep); } } spin_unlock_irqrestore(&dev->lock, flags); @@ -871,7 +873,7 @@ struct net_device *gether_connect(struct gether *link) return ERR_PTR(-EINVAL); link->in_ep->driver_data = dev; - result = usb_ep_enable(link->in_ep, link->in); + result = usb_ep_enable(link->in_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", link->in_ep->name, result); @@ -879,7 +881,7 @@ struct net_device *gether_connect(struct gether *link) } link->out_ep->driver_data = dev; - result = usb_ep_enable(link->out_ep, link->out); + result = usb_ep_enable(link->out_ep); if (result != 0) { DBG(dev, "enable %s --> %d\n", link->out_ep->name, result); @@ -969,7 +971,7 @@ void gether_disconnect(struct gether *link) } spin_unlock(&dev->req_lock); link->in_ep->driver_data = NULL; - link->in = NULL; + link->in_ep->desc = NULL; usb_ep_disable(link->out_ep); spin_lock(&dev->req_lock); @@ -984,7 +986,7 @@ void gether_disconnect(struct gether *link) } spin_unlock(&dev->req_lock); link->out_ep->driver_data = NULL; - link->out = NULL; + link->out_ep->desc = NULL; /* finish forgetting about this USB link episode */ dev->header_len = 0; diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index b56e1e7d423c..c966440ddd70 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -52,10 +52,6 @@ struct gether { struct usb_ep *in_ep; struct usb_ep *out_ep; - /* descriptors match device speed at gether_connect() time */ - struct usb_endpoint_descriptor *in; - struct usb_endpoint_descriptor *out; - bool is_zlp_ok; u16 cdc_filter; diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 40f7716b31fc..a8aa46962d81 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1247,12 +1247,12 @@ int gserial_connect(struct gserial *gser, u8 port_num) port = ports[port_num].port; /* activate the endpoints */ - status = usb_ep_enable(gser->in, gser->in_desc); + status = usb_ep_enable(gser->in); if (status < 0) return status; gser->in->driver_data = port; - status = usb_ep_enable(gser->out, gser->out_desc); + status = usb_ep_enable(gser->out); if (status < 0) goto fail_out; gser->out->driver_data = port; diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index 300f0ed9475d..9b0fe6450fbf 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -35,8 +35,6 @@ struct gserial { struct usb_ep *in; struct usb_ep *out; - struct usb_endpoint_descriptor *in_desc; - struct usb_endpoint_descriptor *out_desc; /* REVISIT avoid this CDC-ACM support harder ... */ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c new file mode 100644 index 000000000000..05ba47214361 --- /dev/null +++ b/drivers/usb/gadget/udc-core.c @@ -0,0 +1,484 @@ +/** + * udc.c - Core UDC Framework + * + * Copyright (C) 2010 Texas Instruments + * Author: Felipe Balbi <balbi@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/err.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/** + * struct usb_udc - describes one usb device controller + * @driver - the gadget driver pointer. For use by the class code + * @dev - the child device to the actual controller + * @gadget - the gadget. For use by the class code + * @list - for use by the udc class driver + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; +}; + +static struct class *udc_class; +static LIST_HEAD(udc_list); +static DEFINE_MUTEX(udc_lock); + +/* ------------------------------------------------------------------------- */ + +/** + * 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 *)) +{ + return gadget->ops->start(driver, bind); +} + +/** + * usb_gadget_udc_start - tells usb device controller to start up + * @gadget: The gadget we want to get started + * @driver: The driver we want to bind to @gadget + * + * This call is issued by the UDC Class driver when it's about + * to register a gadget driver to the device controller, before + * calling gadget driver's bind() method. + * + * It allows the controller to be powered off until strictly + * necessary to have it powered on. + * + * Returns zero on success, else negative errno. + */ +static inline int usb_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + return gadget->ops->udc_start(gadget, driver); +} + +/** + * usb_gadget_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 + * + * This call is issued by the UDC Class driver after calling + * gadget driver's unbind() method. + * + * The details are implementation specific, but it can go as + * far as powering off UDC completely and disable its data + * line pullups. + */ +static inline void usb_gadget_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + gadget->ops->udc_stop(gadget, driver); +} + +/** + * usb_udc_release - release the usb_udc struct + * @dev: the dev member within usb_udc + * + * This is called by driver's core in order to free memory once the last + * reference is released. + */ +static void usb_udc_release(struct device *dev) +{ + struct usb_udc *udc; + + udc = container_of(dev, struct usb_udc, dev); + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(udc); +} + +static const struct attribute_group *usb_udc_attr_groups[]; +/** + * 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) +{ + struct usb_udc *udc; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto err1; + + device_initialize(&udc->dev); + udc->dev.release = usb_udc_release; + udc->dev.class = udc_class; + udc->dev.groups = usb_udc_attr_groups; + udc->dev.parent = parent; + ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); + if (ret) + goto err2; + + udc->gadget = gadget; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + + ret = device_add(&udc->dev); + if (ret) + goto err3; + + mutex_unlock(&udc_lock); + + return 0; +err3: + list_del(&udc->list); + mutex_unlock(&udc_lock); + +err2: + put_device(&udc->dev); + +err1: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); + +static int udc_is_newstyle(struct usb_udc *udc) +{ + if (udc->gadget->ops->udc_start && udc->gadget->ops->udc_stop) + return 1; + return 0; +} + + +static void usb_gadget_remove_driver(struct usb_udc *udc) +{ + dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", + udc->gadget->name); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + + if (udc_is_newstyle(udc)) { + usb_gadget_disconnect(udc->gadget); + udc->driver->unbind(udc->gadget); + usb_gadget_udc_stop(udc->gadget, udc->driver); + + } else { + usb_gadget_stop(udc->gadget, udc->driver); + } + + udc->driver = NULL; + udc->dev.driver = NULL; +} + +/** + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. + * + * This, will call usb_gadget_unregister_driver() if + * the @udc is still busy. + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + struct usb_udc *udc = NULL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; + + dev_err(gadget->dev.parent, "gadget not registered.\n"); + mutex_unlock(&udc_lock); + + return; + +found: + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + list_del(&udc->list); + mutex_unlock(&udc_lock); + + if (udc->driver) + usb_gadget_remove_driver(udc); + + kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + device_unregister(&udc->dev); +} +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct usb_udc *udc = NULL; + int ret; + + if (!driver || !bind || !driver->setup) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + /* For now we take the first one */ + if (!udc->driver) + goto found; + } + + pr_debug("couldn't find an available UDC\n"); + mutex_unlock(&udc_lock); + return -ENODEV; + +found: + 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; + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + if (!driver || !driver->unbind) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->driver == driver) { + usb_gadget_remove_driver(udc); + ret = 0; + break; + } + + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +/* ------------------------------------------------------------------------- */ + +static ssize_t usb_udc_srp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "1")) + usb_gadget_wakeup(udc->gadget); + + return n; +} +static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); + +static ssize_t usb_udc_softconn_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = dev_get_drvdata(dev); + + if (sysfs_streq(buf, "connect")) { + usb_gadget_connect(udc->gadget); + } else if (sysfs_streq(buf, "disconnect")) { + usb_gadget_disconnect(udc->gadget); + } else { + dev_err(dev, "unsupported command '%s'\n", buf); + return -EINVAL; + } + + return n; +} +static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); + +static ssize_t usb_udc_speed_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; + + switch (gadget->speed) { + case USB_SPEED_LOW: + return snprintf(buf, PAGE_SIZE, "low-speed\n"); + case USB_SPEED_FULL: + return snprintf(buf, PAGE_SIZE, "full-speed\n"); + case USB_SPEED_HIGH: + return snprintf(buf, PAGE_SIZE, "high-speed\n"); + case USB_SPEED_WIRELESS: + return snprintf(buf, PAGE_SIZE, "wireless\n"); + case USB_SPEED_SUPER: + return snprintf(buf, PAGE_SIZE, "super-speed\n"); + case USB_SPEED_UNKNOWN: /* FALLTHROUGH */ + default: + return snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + } +} +static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL); + +#define USB_UDC_ATTR(name) \ +ssize_t usb_udc_##name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ + struct usb_gadget *gadget = udc->gadget; \ + \ + return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ +} \ +static DEVICE_ATTR(name, S_IRUSR, usb_udc_##name##_show, NULL) + +static USB_UDC_ATTR(is_dualspeed); +static USB_UDC_ATTR(is_otg); +static USB_UDC_ATTR(is_a_peripheral); +static USB_UDC_ATTR(b_hnp_enable); +static USB_UDC_ATTR(a_hnp_support); +static USB_UDC_ATTR(a_alt_hnp_support); + +static struct attribute *usb_udc_attrs[] = { + &dev_attr_srp.attr, + &dev_attr_soft_connect.attr, + &dev_attr_speed.attr, + + &dev_attr_is_dualspeed.attr, + &dev_attr_is_otg.attr, + &dev_attr_is_a_peripheral.attr, + &dev_attr_b_hnp_enable.attr, + &dev_attr_a_hnp_support.attr, + &dev_attr_a_alt_hnp_support.attr, + NULL, +}; + +static const struct attribute_group usb_udc_attr_group = { + .attrs = usb_udc_attrs, +}; + +static const struct attribute_group *usb_udc_attr_groups[] = { + &usb_udc_attr_group, + NULL, +}; + +static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + int ret; + + ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_NAME\n"); + return ret; + } + + if (udc->driver) { + ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", + udc->driver->function); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); + return ret; + } + } + + return 0; +} + +static int __init usb_udc_init(void) +{ + udc_class = class_create(THIS_MODULE, "udc"); + if (IS_ERR(udc_class)) { + pr_err("failed to create udc class --> %ld\n", + PTR_ERR(udc_class)); + return PTR_ERR(udc_class); + } + + udc_class->dev_uevent = usb_udc_uevent; + return 0; +} +subsys_initcall(usb_udc_init); + +static void __exit usb_udc_exit(void) +{ + class_destroy(udc_class); +} +module_exit(usb_udc_exit); + +MODULE_DESCRIPTION("UDC Framework"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index a5a0fdb808c7..df6882de50bf 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -373,6 +373,7 @@ static struct usb_composite_driver webcam_driver = { .name = "g_webcam", .dev = &webcam_device_descriptor, .strings = webcam_device_strings, + .max_speed = USB_SPEED_HIGH, .unbind = webcam_unbind, }; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 6d16db9d9d2d..00e2fd2d4791 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -340,6 +340,7 @@ static struct usb_composite_driver zero_driver = { .name = "zero", .dev = &device_desc, .strings = dev_strings, + .max_speed = USB_SPEED_SUPER, .unbind = zero_unbind, .suspend = zero_suspend, .resume = zero_resume, |