diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 3 | ||||
-rw-r--r-- | drivers/usb/chipidea/debug.c | 4 | ||||
-rw-r--r-- | drivers/usb/class/usbtmc.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 104 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 2 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 21 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 14 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 51 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 13 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-omap.c | 172 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 7 | ||||
-rw-r--r-- | drivers/usb/dwc3/host.c | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 824 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 101 | ||||
-rw-r--r-- | drivers/usb/gadget/composite.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/configfs.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/function/Makefile | 34 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_acm.c (renamed from drivers/usb/gadget/f_acm.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_ecm.c (renamed from drivers/usb/gadget/f_ecm.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_eem.c (renamed from drivers/usb/gadget/f_eem.c) | 22 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_fs.c (renamed from drivers/usb/gadget/f_fs.c) | 352 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_hid.c (renamed from drivers/usb/gadget/f_hid.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_loopback.c (renamed from drivers/usb/gadget/f_loopback.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_mass_storage.c (renamed from drivers/usb/gadget/f_mass_storage.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_mass_storage.h (renamed from drivers/usb/gadget/f_mass_storage.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_midi.c (renamed from drivers/usb/gadget/f_midi.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_ncm.c (renamed from drivers/usb/gadget/f_ncm.c) | 480 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_obex.c (renamed from drivers/usb/gadget/f_obex.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_phonet.c (renamed from drivers/usb/gadget/f_phonet.c) | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_rndis.c (renamed from drivers/usb/gadget/f_rndis.c) | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_serial.c (renamed from drivers/usb/gadget/f_serial.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_sourcesink.c (renamed from drivers/usb/gadget/f_sourcesink.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_subset.c (renamed from drivers/usb/gadget/f_subset.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_uac1.c (renamed from drivers/usb/gadget/f_uac1.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_uac2.c (renamed from drivers/usb/gadget/f_uac2.c) | 24 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_uvc.c (renamed from drivers/usb/gadget/f_uvc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_uvc.h (renamed from drivers/usb/gadget/f_uvc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/g_zero.h (renamed from drivers/usb/gadget/g_zero.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/ndis.h (renamed from drivers/usb/gadget/ndis.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/rndis.c (renamed from drivers/usb/gadget/rndis.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/rndis.h (renamed from drivers/usb/gadget/rndis.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/storage_common.c (renamed from drivers/usb/gadget/storage_common.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/storage_common.h (renamed from drivers/usb/gadget/storage_common.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_ecm.h (renamed from drivers/usb/gadget/u_ecm.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_eem.h (renamed from drivers/usb/gadget/u_eem.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_ether.c (renamed from drivers/usb/gadget/u_ether.c) | 22 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_ether.h (renamed from drivers/usb/gadget/u_ether.h) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_ether_configfs.h (renamed from drivers/usb/gadget/u_ether_configfs.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_fs.h (renamed from drivers/usb/gadget/u_fs.h) | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_gether.h (renamed from drivers/usb/gadget/u_gether.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_ncm.h (renamed from drivers/usb/gadget/u_ncm.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_phonet.h (renamed from drivers/usb/gadget/u_phonet.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_rndis.h (renamed from drivers/usb/gadget/u_rndis.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_serial.c (renamed from drivers/usb/gadget/u_serial.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_serial.h (renamed from drivers/usb/gadget/u_serial.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_uac1.c (renamed from drivers/usb/gadget/u_uac1.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_uac1.h (renamed from drivers/usb/gadget/u_uac1.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/uvc.h (renamed from drivers/usb/gadget/uvc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/uvc_queue.c (renamed from drivers/usb/gadget/uvc_queue.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/uvc_queue.h (renamed from drivers/usb/gadget/uvc_queue.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/uvc_v4l2.c (renamed from drivers/usb/gadget/uvc_v4l2.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/uvc_video.c (renamed from drivers/usb/gadget/uvc_video.c) | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/Kconfig | 475 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/Makefile | 44 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/acm_ms.c (renamed from drivers/usb/gadget/acm_ms.c) | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/audio.c (renamed from drivers/usb/gadget/audio.c) | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/cdc2.c (renamed from drivers/usb/gadget/cdc2.c) | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/dbgp.c (renamed from drivers/usb/gadget/dbgp.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/ether.c (renamed from drivers/usb/gadget/ether.c) | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/g_ffs.c (renamed from drivers/usb/gadget/g_ffs.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/gmidi.c (renamed from drivers/usb/gadget/gmidi.c) | 13 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/hid.c (renamed from drivers/usb/gadget/hid.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/inode.c (renamed from drivers/usb/gadget/inode.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/mass_storage.c (renamed from drivers/usb/gadget/mass_storage.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/multi.c (renamed from drivers/usb/gadget/multi.c) | 13 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/ncm.c (renamed from drivers/usb/gadget/ncm.c) | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/nokia.c (renamed from drivers/usb/gadget/nokia.c) | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/printer.c (renamed from drivers/usb/gadget/printer.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/serial.c (renamed from drivers/usb/gadget/serial.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/tcm_usb_gadget.c (renamed from drivers/usb/gadget/tcm_usb_gadget.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/tcm_usb_gadget.h (renamed from drivers/usb/gadget/tcm_usb_gadget.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/webcam.c (renamed from drivers/usb/gadget/webcam.c) | 15 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/zero.c (renamed from drivers/usb/gadget/zero.c) | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/net2280.c | 2905 | ||||
-rw-r--r-- | drivers/usb/gadget/net2280.h | 308 | ||||
-rw-r--r-- | drivers/usb/gadget/u_os_desc.h | 59 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/Kconfig | 386 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/Makefile | 31 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/amd5536udc.c (renamed from drivers/usb/gadget/amd5536udc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/amd5536udc.h (renamed from drivers/usb/gadget/amd5536udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/at91_udc.c (renamed from drivers/usb/gadget/at91_udc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/at91_udc.h (renamed from drivers/usb/gadget/at91_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.c (renamed from drivers/usb/gadget/atmel_usba_udc.c) | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.h (renamed from drivers/usb/gadget/atmel_usba_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/bcm63xx_udc.c (renamed from drivers/usb/gadget/bcm63xx_udc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/dummy_hcd.c (renamed from drivers/usb/gadget/dummy_hcd.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fotg210-udc.c (renamed from drivers/usb/gadget/fotg210-udc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fotg210.h (renamed from drivers/usb/gadget/fotg210.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fsl_mxc_udc.c (renamed from drivers/usb/gadget/fsl_mxc_udc.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fsl_qe_udc.c (renamed from drivers/usb/gadget/fsl_qe_udc.c) | 19 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fsl_qe_udc.h (renamed from drivers/usb/gadget/fsl_qe_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_udc_core.c) | 19 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fsl_usb2_udc.h (renamed from drivers/usb/gadget/fsl_usb2_udc.h) | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fusb300_udc.c (renamed from drivers/usb/gadget/fusb300_udc.c) | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/fusb300_udc.h (renamed from drivers/usb/gadget/fusb300_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/gadget_chips.h (renamed from drivers/usb/gadget/gadget_chips.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/goku_udc.c (renamed from drivers/usb/gadget/goku_udc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/goku_udc.h (renamed from drivers/usb/gadget/goku_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/gr_udc.c (renamed from drivers/usb/gadget/gr_udc.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/gr_udc.h (renamed from drivers/usb/gadget/gr_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/lpc32xx_udc.c (renamed from drivers/usb/gadget/lpc32xx_udc.c) | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/m66592-udc.c (renamed from drivers/usb/gadget/m66592-udc.c) | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/m66592-udc.h (renamed from drivers/usb/gadget/m66592-udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/mv_u3d.h (renamed from drivers/usb/gadget/mv_u3d.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/mv_u3d_core.c (renamed from drivers/usb/gadget/mv_u3d_core.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/mv_udc.h (renamed from drivers/usb/gadget/mv_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/mv_udc_core.c (renamed from drivers/usb/gadget/mv_udc_core.c) | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2272.c (renamed from drivers/usb/gadget/net2272.c) | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2272.h (renamed from drivers/usb/gadget/net2272.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2280.c | 3827 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/net2280.h | 403 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/omap_udc.c (renamed from drivers/usb/gadget/omap_udc.c) | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/omap_udc.h (renamed from drivers/usb/gadget/omap_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pch_udc.c (renamed from drivers/usb/gadget/pch_udc.c) | 22 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pxa25x_udc.c (renamed from drivers/usb/gadget/pxa25x_udc.c) | 75 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pxa25x_udc.h (renamed from drivers/usb/gadget/pxa25x_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pxa27x_udc.c (renamed from drivers/usb/gadget/pxa27x_udc.c) | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/pxa27x_udc.h (renamed from drivers/usb/gadget/pxa27x_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/r8a66597-udc.c (renamed from drivers/usb/gadget/r8a66597-udc.c) | 92 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/r8a66597-udc.h (renamed from drivers/usb/gadget/r8a66597-udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/s3c-hsudc.c (renamed from drivers/usb/gadget/s3c-hsudc.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/s3c2410_udc.c (renamed from drivers/usb/gadget/s3c2410_udc.c) | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/s3c2410_udc.h (renamed from drivers/usb/gadget/s3c2410_udc.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/udc/udc-core.c (renamed from drivers/usb/gadget/udc-core.c) | 0 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-exynos.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mem.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-msm.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 25 | ||||
-rw-r--r-- | drivers/usb/host/ehci-spear.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 67 | ||||
-rw-r--r-- | drivers/usb/host/fhci-dbg.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/fotg210-hcd.c | 48 | ||||
-rw-r--r-- | drivers/usb/host/max3421-hcd.c | 48 | ||||
-rw-r--r-- | drivers/usb/host/ohci-dbg.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/ohci-exynos.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 381 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 11 | ||||
-rw-r--r-- | drivers/usb/host/ohci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-q.c | 262 | ||||
-rw-r--r-- | drivers/usb/host/ohci-spear.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 23 | ||||
-rw-r--r-- | drivers/usb/host/oxu210hp-hcd.c | 48 | ||||
-rw-r--r-- | drivers/usb/host/uhci-grlib.c | 31 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/uhci-platform.c | 22 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 17 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 52 | ||||
-rw-r--r-- | drivers/usb/host/xhci-rcar.c | 148 | ||||
-rw-r--r-- | drivers/usb/host/xhci-rcar.h | 27 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 108 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 163 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 2 | ||||
-rw-r--r-- | drivers/usb/misc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/misc/lvstest.c | 460 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.c | 1 | ||||
-rw-r--r-- | drivers/usb/misc/usb3503.c | 37 | ||||
-rw-r--r-- | drivers/usb/musb/blackfin.c | 20 | ||||
-rw-r--r-- | drivers/usb/musb/davinci.c | 20 | ||||
-rw-r--r-- | drivers/usb/musb/jz4740.c | 3 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 41 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 12 | ||||
-rw-r--r-- | drivers/usb/musb/musb_cppi41.c | 70 | ||||
-rw-r--r-- | drivers/usb/musb/musb_dma.h | 1 | ||||
-rw-r--r-- | drivers/usb/musb/musb_dsps.c | 104 | ||||
-rw-r--r-- | drivers/usb/musb/musb_host.c | 19 | ||||
-rw-r--r-- | drivers/usb/musb/musb_regs.h | 7 | ||||
-rw-r--r-- | drivers/usb/musb/tusb6010.c | 16 | ||||
-rw-r--r-- | drivers/usb/musb/ux500.c | 28 | ||||
-rw-r--r-- | drivers/usb/musb/ux500_dma.c | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-am335x.c | 12 | ||||
-rw-r--r-- | drivers/usb/phy/phy-gpio-vbus-usb.c | 49 | ||||
-rw-r--r-- | drivers/usb/phy/phy-msm-usb.c | 16 | ||||
-rw-r--r-- | drivers/usb/phy/phy-samsung-usb.h | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-tegra-usb.c | 11 | ||||
-rw-r--r-- | drivers/usb/phy/phy.c | 5 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/common.c | 66 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/common.h | 2 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod_gadget.c | 2 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.c | 11 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.h | 1 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/rcar2.c | 77 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/rcar2.h | 4 | ||||
-rw-r--r-- | drivers/usb/serial/cp210x.c | 3 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 87 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio_ids.h | 20 | ||||
-rw-r--r-- | drivers/usb/serial/kl5kusb105.c | 30 | ||||
-rw-r--r-- | drivers/usb/serial/mos7840.c | 5 | ||||
-rw-r--r-- | drivers/usb/serial/option.c | 31 | ||||
-rw-r--r-- | drivers/usb/serial/pl2303.c | 1 | ||||
-rw-r--r-- | drivers/usb/serial/pl2303.h | 1 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 37 | ||||
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 7 | ||||
-rw-r--r-- | drivers/usb/serial/zte_ev.c | 20 | ||||
-rw-r--r-- | drivers/usb/storage/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/storage/ene_ub6250.c | 4 | ||||
-rw-r--r-- | drivers/usb/storage/sddr09.c | 4 | ||||
-rw-r--r-- | drivers/usb/storage/uas-detect.h | 40 | ||||
-rw-r--r-- | drivers/usb/storage/uas.c | 2 | ||||
-rw-r--r-- | drivers/usb/storage/unusual_devs.h | 6 | ||||
-rw-r--r-- | drivers/usb/storage/usb.c | 10 | ||||
-rw-r--r-- | drivers/usb/usbip/Kconfig | 41 | ||||
-rw-r--r-- | drivers/usb/usbip/Makefile | 10 | ||||
-rw-r--r-- | drivers/usb/usbip/README | 7 | ||||
-rw-r--r-- | drivers/usb/usbip/stub.h | 113 | ||||
-rw-r--r-- | drivers/usb/usbip/stub_dev.c | 498 | ||||
-rw-r--r-- | drivers/usb/usbip/stub_main.c | 335 | ||||
-rw-r--r-- | drivers/usb/usbip/stub_rx.c | 594 | ||||
-rw-r--r-- | drivers/usb/usbip/stub_tx.c | 398 | ||||
-rw-r--r-- | drivers/usb/usbip/usbip_common.c | 776 | ||||
-rw-r--r-- | drivers/usb/usbip/usbip_common.h | 335 | ||||
-rw-r--r-- | drivers/usb/usbip/usbip_event.c | 128 | ||||
-rw-r--r-- | drivers/usb/usbip/usbip_protocol.txt | 358 | ||||
-rw-r--r-- | drivers/usb/usbip/vhci.h | 129 | ||||
-rw-r--r-- | drivers/usb/usbip/vhci_hcd.c | 1171 | ||||
-rw-r--r-- | drivers/usb/usbip/vhci_rx.c | 268 | ||||
-rw-r--r-- | drivers/usb/usbip/vhci_sysfs.c | 252 | ||||
-rw-r--r-- | drivers/usb/usbip/vhci_tx.c | 224 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wa-xfer.c | 3 |
244 files changed, 14019 insertions, 5773 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e0cad4418085..cf1b19bca306 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -92,6 +92,8 @@ source "drivers/usb/storage/Kconfig" source "drivers/usb/image/Kconfig" +source "drivers/usb/usbip/Kconfig" + endif source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 3cba892b83a2..d7be71778059 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -60,3 +60,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_COMMON) += common/ + +obj-$(CONFIG_USBIP_CORE) += usbip/ diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 2e58f8dfd311..65444b02bd68 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -133,6 +133,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { ret = PTR_ERR(data->phy); + /* Return -EINVAL if no usbphy is available */ + if (ret == -ENODEV) + ret = -EINVAL; goto err_clk; } diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 7cccab6ff308..795d6538d630 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = { .release = single_release, }; -int ci_otg_show(struct seq_file *s, void *unused) +static int ci_otg_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; struct otg_fsm *fsm; @@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = { .release = single_release, }; -int ci_registers_show(struct seq_file *s, void *unused) +static int ci_registers_show(struct seq_file *s, void *unused) { struct ci_hdrc *ci = s->private; u32 tmp_reg; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 103a6e9ee49d..ec978408a2ee 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) u8 *buffer; int rv; int n; - int actual; + int actual = 0; int max_size; dev = &data->intf->dev; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 1ab4df1de2da..b2a540b43f97 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; + + /* + * Adjust bInterval for quirked devices. + * This quirk fixes bIntervals reported in + * linear microframes. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval), i, j); + i = j = n; + } break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 257876ea03a1..0b59731c3021 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u = (is_in ? URB_DIR_IN : URB_DIR_OUT); if (uurb->flags & USBDEVFS_URB_ISO_ASAP) u |= URB_ISO_ASAP; - if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK) + if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) u |= URB_SHORT_NOT_OK; if (uurb->flags & USBDEVFS_URB_NO_FSBR) u |= URB_NO_FSBR; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4aeb10034de7..9bffd26cea05 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev) */ lpm_disable_error = usb_unlocked_disable_lpm(udev); - /* Terminate all URBs for this interface unless the driver - * supports "soft" unbinding. + /* + * Terminate all URBs for this interface unless the driver + * supports "soft" unbinding and the device is still present. */ - if (!driver->soft_unbind) + if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED) usb_disable_interface(udev, intf, false); driver->disconnect(intf); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 82044b5d6113..efc953119ce2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && hcd->driver->shutdown) { hcd->driver->shutdown(hcd); + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) + free_irq(hcd->irq, hcd); pci_disable_device(dev); } } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index bec31e2efb88..487abcfcccd8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); } @@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev, struct usb_bus *usb_bus = rh_usb_dev->bus; struct usb_hcd *usb_hcd; - if (usb_bus == NULL) /* FIXME: not sure if this case is possible */ - return -ENODEV; usb_hcd = bus_to_hcd(usb_bus); result = sscanf(buf, "%u\n", &val); if (result == 1) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0e950ad8cb25..46f5161c7891 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1728,8 +1728,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) * - Change autosuspend delay of hub can avoid unnecessary auto * suspend timer for hub, also may decrease power consumption * of USB bus. + * + * - If user has indicated to prevent autosuspend by passing + * usbcore.autosuspend = -1 then keep autosuspend disabled. */ - pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#ifdef CONFIG_PM_RUNTIME + if (hdev->dev.power.autosuspend_delay >= 0) + pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#endif /* * Hubs have proper suspend/resume support, except for root hubs @@ -2107,8 +2113,8 @@ void usb_disconnect(struct usb_device **pdev) { struct usb_port *port_dev = NULL; struct usb_device *udev = *pdev; - struct usb_hub *hub; - int port1; + struct usb_hub *hub = NULL; + int port1 = 1; /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. @@ -2606,13 +2612,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ -static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) +static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1, + u16 portstatus) { - return hub_is_superspeed(hub->hdev) && - (((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_INACTIVE) || - ((portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_COMP_MOD)) ; + u16 link_state; + + if (!hub_is_superspeed(hub->hdev)) + return false; + + if (test_bit(port1, hub->warm_reset_bits)) + return true; + + link_state = portstatus & USB_PORT_STAT_LINK_STATE; + return link_state == USB_SS_PORT_LS_SS_INACTIVE + || link_state == USB_SS_PORT_LS_COMP_MOD; } static int hub_port_wait_reset(struct usb_hub *hub, int port1, @@ -2649,7 +2662,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portstatus & USB_PORT_STAT_RESET)) return -EBUSY; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) return -ENOTCONN; /* Device went away? */ @@ -2749,9 +2762,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, if (status < 0) goto done; - if (hub_port_warm_reset_required(hub, portstatus)) + if (hub_port_warm_reset_required(hub, port1, portstatus)) warm = true; } + clear_bit(port1, hub->warm_reset_bits); /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { @@ -2788,7 +2802,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, &portstatus, &portchange) < 0) goto done; - if (!hub_port_warm_reset_required(hub, portstatus)) + if (!hub_port_warm_reset_required(hub, port1, + portstatus)) goto done; /* @@ -2875,8 +2890,13 @@ static int check_port_resume_type(struct usb_device *udev, { struct usb_port *port_dev = hub->ports[port1 - 1]; + /* Is a warm reset needed to recover the connection? */ + if (status == 0 && udev->reset_resume + && hub_port_warm_reset_required(hub, port1, portstatus)) { + /* pass */; + } /* Is the device still present? */ - if (status || port_is_suspended(hub, portstatus) || + else if (status || port_is_suspended(hub, portstatus) || !port_is_power_on(hub, portstatus) || !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) @@ -3264,6 +3284,43 @@ static int finish_port_resume(struct usb_device *udev) } /* + * There are some SS USB devices which take longer time for link training. + * XHCI specs 4.19.4 says that when Link training is successful, port + * sets CSC bit to 1. So if SW reads port status before successful link + * training, then it will not find device to be present. + * USB Analyzer log with such buggy devices show that in some cases + * device switch on the RX termination after long delay of host enabling + * the VBUS. In few other cases it has been seen that device fails to + * negotiate link training in first attempt. It has been + * reported till now that few devices take as long as 2000 ms to train + * the link after host enabling its VBUS and termination. Following + * routine implements a 2000 ms timeout for link training. If in a case + * link trains before timeout, loop will exit earlier. + * + * FIXME: If a device was connected before suspend, but was removed + * while system was asleep, then the loop in the following routine will + * only exit at timeout. + * + * This routine should only be called when persist is enabled for a SS + * device. + */ +static int wait_for_ss_port_enable(struct usb_device *udev, + struct usb_hub *hub, int *port1, + u16 *portchange, u16 *portstatus) +{ + int status = 0, delay_ms = 0; + + while (delay_ms < 2000) { + if (status || *portstatus & USB_PORT_STAT_CONNECTION) + break; + msleep(20); + delay_ms += 20; + status = hub_port_status(hub, *port1, portstatus, portchange); + } + return status; +} + +/* * usb_port_resume - re-activate a suspended usb device's upstream port * @udev: device to re-activate, not a root hub * Context: must be able to sleep; device not locked; pm locks held @@ -3359,6 +3416,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) } } + if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) + status = wait_for_ss_port_enable(udev, hub, &port1, &portchange, + &portstatus); + status = check_port_resume_type(udev, hub, port1, status, portchange, portstatus); if (status == 0) @@ -3879,7 +3940,8 @@ int usb_disable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return 0; hcd = bus_to_hcd(udev->bus); @@ -3935,7 +3997,8 @@ void usb_enable_lpm(struct usb_device *udev) if (!udev || !udev->parent || udev->speed != USB_SPEED_SUPER || - !udev->lpm_capable) + !udev->lpm_capable || + udev->state < USB_STATE_DEFAULT) return; udev->lpm_disable_count--; @@ -4550,6 +4613,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, struct usb_hcd *hcd = bus_to_hcd(hdev->bus); struct usb_port *port_dev = hub->ports[port1 - 1]; struct usb_device *udev = port_dev->child; + static int unreliable_port = -1; /* Disconnect any existing devices under this port */ if (udev) { @@ -4570,10 +4634,12 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce_be_stable(hub, port1); if (status < 0) { - if (status != -ENODEV && printk_ratelimit()) - dev_err(&port_dev->dev, - "connect-debounce failed\n"); + if (status != -ENODEV && + port1 != unreliable_port && + printk_ratelimit()) + dev_err(&port_dev->dev, "connect-debounce failed\n"); portstatus &= ~USB_PORT_STAT_CONNECTION; + unreliable_port = port1; } else { portstatus = status; } @@ -4889,7 +4955,7 @@ static void port_event(struct usb_hub *hub, int port1) * Warm reset a USB3 protocol port if it's in * SS.Inactive state. */ - if (hub_port_warm_reset_required(hub, portstatus)) { + if (hub_port_warm_reset_required(hub, port1, portstatus)) { dev_dbg(&port_dev->dev, "do warm reset\n"); if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) || udev->state == USB_STATE_NOTATTACHED) { diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 326308e53961..c77d8778af4b 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -52,6 +52,8 @@ struct usb_hub { unsigned long power_bits[1]; /* ports that are powered */ unsigned long child_usage_bits[1]; /* ports powered on for children */ + unsigned long warm_reset_bits[1]; /* ports requesting warm + reset recovery */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index fe1b6d0967e3..cd3f9dc24a06 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev) msleep(hub_power_on_good_delay(hub)); if (udev && !retval) { /* - * Attempt to wait for usb hub port to be reconnected in order - * to make the resume procedure successful. The device may have - * disconnected while the port was powered off, so ignore the - * return status. + * Our preference is to simply wait for the port to reconnect, + * as that is the lowest latency method to restart the port. + * However, there are cases where toggling port power results in + * the host port and the device port getting out of sync causing + * a link training live lock. Upon timeout, flag the port as + * needing warm reset recovery (to be performed later by + * usb_port_resume() as requested via usb_wakeup_notification()) */ - retval = hub_port_debounce_be_connected(hub, port1); - if (retval < 0) - dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", - retval); - retval = 0; + if (hub_port_debounce_be_connected(hub, port1) < 0) { + dev_dbg(&port_dev->dev, "reconnect timeout\n"); + if (hub_is_superspeed(hdev)) + set_bit(port1, hub->warm_reset_bits); + } /* Force the child awake to revalidate after the power loss. */ if (!test_and_set_bit(port1, hub->child_usage_bits)) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 739ee8e8bdfd..bae636e2a1a3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* SKYMEDI USB_DRIVE */ { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Razer - Razer Blade Keyboard */ + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, @@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, + /* USB3503 */ + { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME }, + { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 991386ceb4ec..c9e8ee81b6b7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) URB_FREE_BUFFER); switch (xfertype) { case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4d1144990d4c..2dd2362198d2 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, } return dev; } +EXPORT_SYMBOL_GPL(usb_alloc_dev); /** * usb_get_dev - increments the reference count of the usb device structure diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f3c56a2fed5b..7c9618e916e2 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,6 +1,4 @@ /** - * linux/drivers/usb/gadget/s3c-hsotg.c - * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); * * Set stall for ep0 as response for setup request. */ -static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { +static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) +{ struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; u32 reg; u32 ctrl; @@ -1902,7 +1901,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) { u32 dsts = readl(hsotg->regs + DSTS); - int ep0_mps = 0, ep_mps; + int ep0_mps = 0, ep_mps = 8; /* * This should signal the finish of the enumeration phase @@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - if(hsotg->dedicated_fifos) + if (hsotg->dedicated_fifos) if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) s3c_hsotg_txfifo_flush(hsotg, ep->index); } @@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev) int i; hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); - if (!hsotg) { - dev_err(dev, "cannot get memory\n"); + if (!hsotg) return -ENOMEM; - } /* * Attempt to find a generic PHY, then look for an old style @@ -3512,7 +3509,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep), GFP_KERNEL); if (!eps) { - dev_err(dev, "cannot get memory\n"); ret = -ENOMEM; goto err_supplies; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 261c3b428220..785510a0a0c3 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE help Say Y here to enable verbose debugging messages on DWC3 Driver. +config DWC3_HOST_USB3_LPM_ENABLE + bool "Enable USB3 LPM Capability" + depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y + default n + help + Select this when you want to enable USB3 LPM with dwc3 xhci host. + endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index eb69eb9f06c8..b769c1faaf03 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc) } dwc->revision = reg; + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + /* issue device SoftReset too */ timeout = jiffies + msecs_to_jiffies(500); dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); @@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev) return -ENODEV; } + dwc->xhci_resources[0].start = res->start; + dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + + DWC3_XHCI_REGS_END; + dwc->xhci_resources[0].flags = res->flags; + dwc->xhci_resources[0].name = res->name; + + res->start += DWC3_GLOBALS_REGS_START; + + /* + * Request memory region but exclude xHCI regs, + * since it will be requested by the xhci-plat driver. + */ + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + /* + * restore res->start back to its original value so that, + * in case the probe is deferred, we don't end up getting error in + * request the memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; + if (node) { dwc->maximum_speed = of_usb_get_maximum_speed(node); @@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) return ret; - dwc->xhci_resources[0].start = res->start; - dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + - DWC3_XHCI_REGS_END; - dwc->xhci_resources[0].flags = res->flags; - dwc->xhci_resources[0].name = res->name; - - res->start += DWC3_GLOBALS_REGS_START; - - /* - * Request memory region but exclude xHCI regs, - * since it will be requested by the xhci-plat driver. - */ - regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); - spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); - dwc->regs = regs; - dwc->regs_size = resource_size(res); - dev->dma_mask = dev->parent->dma_mask; dev->dma_parms = dev->parent->dma_parms; dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 57332e3768e4..48fb264065db 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -191,6 +191,19 @@ #define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) #define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + /* Global HWPARAMS4 Register */ #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) #define DWC3_MAX_HIBER_SCRATCHBUFS 15 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 07a736acd0f2..9dcfbe7cd5f5 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -77,10 +77,6 @@ #define USBOTGSS_DEV_EBC_EN 0x0110 #define USBOTGSS_DEBUG_OFFSET 0x0600 -/* REVISION REGISTER */ -#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7) -#define USBOTGSS_REVISION_XMAJOR1 1 -#define USBOTGSS_REVISION_XMAJOR2 2 /* SYSCONFIG REGISTER */ #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) @@ -129,7 +125,6 @@ struct dwc3_omap { u32 irq_eoi_offset; u32 debug_offset; u32 irq0_offset; - u32 revision; u32 dma_status:1; @@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static void dwc3_omap_map_offset(struct dwc3_omap *omap) +{ + struct device_node *node = omap->dev->of_node; + + /* + * Differentiate between OMAP5 and AM437x. + * + * For OMAP5(ES2.0) and AM437x wrapper revision is same, even + * though there are changes in wrapper register offsets. + * + * Using dt compatible to differentiate AM437x. + */ + if (of_device_is_compatible(node, "ti,am437x-dwc3")) { + omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; + omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; + omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; + omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; + omap->debug_offset = USBOTGSS_DEBUG_OFFSET; + } +} + +static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap) +{ + u32 reg; + struct device_node *node = omap->dev->of_node; + int utmi_mode = 0; + + reg = dwc3_omap_read_utmi_status(omap); + + of_property_read_u32(node, "utmi-mode", &utmi_mode); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode); + } + + dwc3_omap_write_utmi_status(omap, reg); +} + +static int dwc3_omap_extcon_register(struct dwc3_omap *omap) +{ + int ret; + struct device_node *node = omap->dev->of_node; + struct extcon_dev *edev; + + if (of_property_read_bool(node, "extcon")) { + edev = extcon_get_edev_by_phandle(omap->dev, 0); + if (IS_ERR(edev)) { + dev_vdbg(omap->dev, "couldn't get extcon device\n"); + return -EPROBE_DEFER; + } + + omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; + ret = extcon_register_interest(&omap->extcon_vbus_dev, + edev->name, "USB", + &omap->vbus_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB\n"); + + omap->id_nb.notifier_call = dwc3_omap_id_notifier; + ret = extcon_register_interest(&omap->extcon_id_dev, + edev->name, "USB-HOST", + &omap->id_nb); + if (ret < 0) + dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n"); + + if (extcon_get_cable_state(edev, "USB") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + if (extcon_get_cable_state(edev, "USB-HOST") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + } + + return 0; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; - struct extcon_dev *edev; struct regulator *vbus_reg = NULL; int ret; int irq; - int utmi_mode = 0; - int x_major; - u32 reg; void __iomem *base; @@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err0; } - reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION); - omap->revision = reg; - x_major = USBOTGSS_REVISION_XMAJOR(reg); - - /* Differentiate between OMAP5 and AM437x */ - switch (x_major) { - case USBOTGSS_REVISION_XMAJOR1: - case USBOTGSS_REVISION_XMAJOR2: - omap->irq_eoi_offset = 0; - omap->irq0_offset = 0; - omap->irqmisc_offset = 0; - omap->utmi_otg_offset = 0; - omap->debug_offset = 0; - break; - default: - /* Default to the latest revision */ - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - break; - } - - /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are - * changes in wrapper registers, Using dt compatible for aegis - */ - - if (of_device_is_compatible(node, "ti,am437x-dwc3")) { - omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET; - omap->irq0_offset = USBOTGSS_IRQ0_OFFSET; - omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET; - omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET; - omap->debug_offset = USBOTGSS_DEBUG_OFFSET; - } - - reg = dwc3_omap_read_utmi_status(omap); - - of_property_read_u32(node, "utmi-mode", &utmi_mode); - - switch (utmi_mode) { - case DWC3_OMAP_UTMI_MODE_SW: - reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - case DWC3_OMAP_UTMI_MODE_HW: - reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - default: - dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode); - } - - dwc3_omap_write_utmi_status(omap, reg); + dwc3_omap_map_offset(omap); + dwc3_omap_set_utmi_mode(omap); /* check the DMA Status */ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); @@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); - if (of_property_read_bool(node, "extcon")) { - edev = extcon_get_edev_by_phandle(dev, 0); - if (IS_ERR(edev)) { - dev_vdbg(dev, "couldn't get extcon device\n"); - ret = -EPROBE_DEFER; - goto err2; - } - - omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; - ret = extcon_register_interest(&omap->extcon_vbus_dev, - edev->name, "USB", &omap->vbus_nb); - if (ret < 0) - dev_vdbg(dev, "failed to register notifier for USB\n"); - omap->id_nb.notifier_call = dwc3_omap_id_notifier; - ret = extcon_register_interest(&omap->extcon_id_dev, edev->name, - "USB-HOST", &omap->id_nb); - if (ret < 0) - dev_vdbg(dev, - "failed to register notifier for USB-HOST\n"); - - if (extcon_get_cable_state(edev, "USB") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); - if (extcon_get_cable_state(edev, "USB-HOST") == true) - dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); - } + ret = dwc3_omap_extcon_register(omap); + if (ret < 0) + goto err2; ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index dab7927d1009..349cacc577d8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, } static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, - struct dwc3_ep *dep, const struct dwc3_event_depevt *event, - int start_new) + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { unsigned status = 0; int clean_busy; @@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; } - dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERINPROGRESS: if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { @@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; } - dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + dwc3_endpoint_transfer_complete(dwc, dep, event); break; case DWC3_DEPEVT_XFERNOTREADY: if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 32db328cc769..dcb8ca084598 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -16,12 +16,14 @@ */ #include <linux/platform_device.h> +#include <linux/usb/xhci_pdriver.h> #include "core.h" int dwc3_host_init(struct dwc3 *dwc) { struct platform_device *xhci; + struct usb_xhci_pdata pdata; int ret; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); @@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc) goto err1; } + memset(&pdata, 0, sizeof(pdata)); + +#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE + pdata.usb3_lpm_capable = 1; +#endif + + ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); + if (ret) { + dev_err(dwc->dev, "couldn't add platform data to xHCI device\n"); + goto err1; + } + ret = platform_device_add(xhci); if (ret) { dev_err(dwc->dev, "failed to register xHCI device\n"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ba18e9c110cc..5c822afb6d70 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS a module parameter as well. If unsure, say 2. -# -# USB Peripheral Controller Support -# -# The order here is alphabetical, except that integrated controllers go -# before discrete ones so they will be the initial/default value: -# - integrated/SOC controllers first -# - licensed IP used in both SOC and discrete versions -# - discrete ones (including all PCI-only controllers) -# - debug/dummy gadget+hcd is last. -# -menu "USB Peripheral Controller" - -# -# Integrated controllers -# - -config USB_AT91 - tristate "Atmel AT91 USB Device Port" - depends on ARCH_AT91 - help - Many Atmel AT91 processors (such as the AT91RM2000) have a - full speed USB Device Port with support for five configurable - endpoints (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "at91_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_LPC32XX - tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX && I2C - select USB_ISP1301 - help - This option selects the USB device controller in the LPC32xx SoC. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "lpc32xx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_ATMEL_USBA - tristate "Atmel USBA" - depends on AVR32 || ARCH_AT91 - help - USBA is the integrated high-speed USB Device controller on - the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. - -config USB_BCM63XX_UDC - tristate "Broadcom BCM63xx Peripheral Controller" - depends on BCM63XX - help - Many Broadcom BCM63xx chipsets (such as the BCM6328) have a - high speed USB Device Port with support for four fixed endpoints - (plus endpoint zero). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "bcm63xx_udc". - -config USB_FSL_USB2 - tristate "Freescale Highspeed USB DR Peripheral Controller" - depends on FSL_SOC || ARCH_MXC - select USB_FSL_MPH_DR_OF if OF - help - Some of Freescale PowerPC and i.MX processors have a High Speed - Dual-Role(DR) USB controller, which supports device mode. - - The number of programmable endpoints is different through - SOC revisions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fsl_usb2_udc" and force - all gadget drivers to also be dynamically linked. - -config USB_FUSB300 - tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT && HAS_DMA - help - Faraday usb device controller FUSB300 driver - -config USB_FOTG210_UDC - depends on HAS_DMA - tristate "Faraday FOTG210 USB Peripheral Controller" - help - Faraday USB2.0 OTG controller which can be configured as - high speed or full speed USB device. This driver supppors - Bulk Transfer so far. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "fotg210_udc". - -config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA - help - Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB - VHDL IP core library. - -config USB_OMAP - tristate "OMAP USB Device Controller" - depends on ARCH_OMAP1 - depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3) - help - Many Texas Instruments OMAP processors have flexible full - speed USB device controllers, with support for up to 30 - endpoints (plus endpoint zero). This driver supports the - controller in the OMAP 1611, and should work with controllers - in other OMAP processors too, given minor tweaks. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "omap_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA25X - tristate "PXA 25x or IXP 4xx" - depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX - help - Intel's PXA 25x series XScale ARM-5TE processors include - an integrated full speed USB 1.1 device controller. The - controller in the IXP 4xx series is register-compatible. - - It has fifteen fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa25x_udc" and force all - gadget drivers to also be dynamically linked. - -# 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_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_R8A66597 - tristate "Renesas R8A66597 USB Peripheral Controller" - depends on HAS_DMA - help - R8A66597 is a discrete USB host and peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has nine configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "r8a66597_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_RENESAS_USBHS_UDC - tristate 'Renesas USBHS controller' - depends on USB_RENESAS_USBHS - help - Renesas USBHS is a discrete USB host and peripheral controller chip - that supports both full and high speed USB 2.0 data transfers. - It has nine or more configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "renesas_usbhs" and force all - gadget drivers to also be dynamically linked. - -config USB_PXA27X - tristate "PXA 27x" - help - Intel's PXA 27x series XScale ARM v5TE processors include - an integrated full speed USB 1.1 device controller. - - It has up to 23 endpoints, as well as endpoint zero (for - control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "pxa27x_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_S3C2410 - tristate "S3C2410 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2410 is an ARM-4 processor with an integrated - full speed USB 1.1 device controller. It has 4 configurable - endpoints, as well as endpoint zero (for control transfers). - - This driver has been tested on the S3C2410, S3C2412, and - S3C2440 processors. - -config USB_S3C2410_DEBUG - boolean "S3C2410 udc debug messages" - depends on USB_S3C2410 - -config USB_S3C_HSUDC - tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" - depends on ARCH_S3C24XX - help - Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC - integrated with dual speed USB 2.0 device controller. It has - 8 endpoints, as well as endpoint zero. - - This driver has been tested on S3C2416 and S3C2450 processors. - -config USB_MV_UDC - tristate "Marvell USB2.0 Device Controller" - depends on HAS_DMA - help - Marvell Socs (including PXA and MMP series) include a high speed - USB2.0 OTG controller, which can be configured as high speed or - full speed USB peripheral. - -config USB_MV_U3D - depends on HAS_DMA - tristate "MARVELL PXA2128 USB 3.0 controller" - help - MARVELL PXA2128 Processor series include a super speed USB3.0 device - controller, which support super speed USB peripheral. - -# -# Controllers available in both integrated and discrete versions -# - -config USB_M66592 - tristate "Renesas M66592 USB Peripheral Controller" - help - M66592 is a discrete USB peripheral controller chip that - supports both full and high speed USB 2.0 data transfers. - It has seven configurable endpoints, and endpoint zero. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "m66592_udc" and force all - gadget drivers to also be dynamically linked. - -# -# Controllers available only in discrete form (and all PCI controllers) -# - -config USB_AMD5536UDC - tristate "AMD5536 UDC" - depends on PCI - help - The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. - It is a USB Highspeed DMA capable USB device controller. Beside ep0 - it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). - The UDC port supports OTG operation, and may be used as a host port - if it's not being used to implement peripheral or OTG roles. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "amd5536udc" and force all - gadget drivers to also be dynamically linked. - -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 - QE/CPM2 USB controller, which support device mode with 4 - programmable endpoints. This driver supports the - controller in the MPC8360 and MPC8272, and should work with - controllers having QE or CPM2, given minor tweaks. - - Set CONFIG_USB_GADGET to "m" to build this driver as a - dynamically linked module called "fsl_qe_udc". - -config USB_NET2272 - tristate "PLX NET2272" - 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 && HAS_DMA - help - The NET2272 part can optionally support an external DMA - controller, but your board has to have support in the - driver itself. - - If unsure, say "N" here. The driver works fine in PIO mode. - -config USB_NET2280 - tristate "NetChip 228x" - depends on PCI - help - NetChip 2280 / 2282 is a PCI based USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - - It has six configurable endpoints, as well as endpoint zero - (for control transfers) and several endpoints with dedicated - functions. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2280" and force all - gadget drivers to also be dynamically linked. - -config USB_GOKU - tristate "Toshiba TC86C001 'Goku-S'" - depends on PCI - help - The Toshiba TC86C001 is a PCI device which includes controllers - for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). - - The device controller has three configurable (bulk or interrupt) - endpoints, plus endpoint zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "goku_udc" and to force all - gadget drivers to also be dynamically linked. - -config USB_EG20T - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" - depends on PCI - help - This is a USB device driver for EG20T PCH. - EG20T PCH is the platform controller hub that is used in Intel's - general embedded platform. EG20T PCH has USB device interface. - Using this interface, it is able to access system devices connected - to USB device. - This driver enables USB device function. - USB device is a USB peripheral controller which - supports both full and high speed USB 2.0 data transfers. - This driver supports both control transfer and bulk transfer modes. - This driver dose not support interrupt transfer or isochronous - transfer modes. - - This driver also can be used for LAPIS Semiconductor's ML7213 which is - for IVI(In-Vehicle Infotainment) use. - ML7831 is for general purpose use. - ML7213/ML7831 is companion chip for Intel Atom E6xx series. - ML7213/ML7831 is completely compatible for Intel EG20T PCH. - -# -# LAST -- dummy/emulated controller -# - -config USB_DUMMY_HCD - tristate "Dummy HCD (DEVELOPMENT)" - depends on USB=y || (USB=m && USB_GADGET=m) - 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. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "dummy_hcd" and force all - gadget drivers to also be dynamically linked. - -# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears -# first and will be selected by default. - -endmenu +source "drivers/usb/gadget/udc/Kconfig" # # USB Gadget Drivers @@ -714,466 +353,7 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. -config USB_ZERO - tristate "Gadget Zero (DEVELOPMENT)" - select USB_LIBCOMPOSITE - select USB_F_SS_LB - help - Gadget Zero is a two-configuration device. It either sinks and - sources bulk data; or it loops back a configurable number of - transfers. It also implements control requests, for "chapter 9" - conformance. The driver needs only two bulk-capable endpoints, so - it can work on top of most device-side usb controllers. It's - useful for testing, and is also a working example showing how - USB "gadget drivers" can be written. - - Make this be the first driver you try using on top of any new - USB peripheral controller driver. Then you can use host-side - test software, like the "usbtest" driver, to put your hardware - and its driver through a basic set of functional tests. - - Gadget Zero also works with the host-side "usb-skeleton" driver, - and with many kinds of host-side test software. You may need - to tweak product and vendor IDs before host software knows about - this device, and arrange to select an appropriate configuration. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_zero". - -config USB_ZERO_HNPTEST - boolean "HNP Test Device" - depends on USB_ZERO && USB_OTG - help - You can configure this device to enumerate using the device - identifiers of the USB-OTG test device. That means that when - this gadget connects to another OTG device, with this one using - the "B-Peripheral" role, that device will use HNP to let this - one serve as the USB host instead (in the "B-Host" role). - -config USB_AUDIO - tristate "Audio Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_PCM - help - This Gadget Audio driver is compatible with USB Audio Class - specification 2.0. It implements 1 AudioControl interface, - 1 AudioStreaming Interface each for USB-OUT and USB-IN. - Number of channels, sample rate and sample size can be - specified as module parameters. - This driver doesn't expect any real Audio codec to be present - on the device - the audio streams are simply sinked to and - sourced from a virtual ALSA sound card created. The user-space - application may choose to do whatever it wants with the data - received from the USB Host and choose to provide whatever it - wants as audio data to the USB Host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_audio". - -config GADGET_UAC1 - bool "UAC 1.0 (Legacy)" - depends on USB_AUDIO - help - If you instead want older UAC Spec-1.0 driver that also has audio - paths hardwired to the Audio codec chip on-board and doesn't work - without one. - -config USB_ETH - tristate "Ethernet Gadget (with CDC Ethernet support)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - select CRC32 - help - This driver implements Ethernet style communication, in one of - several ways: - - - The "Communication Device Class" (CDC) Ethernet Control Model. - That protocol is often avoided with pure Ethernet adapters, in - favor of simpler vendor-specific hardware, but is widely - supported by firmware for smart network devices. - - - On hardware can't implement that protocol, a simple CDC subset - is used, placing fewer demands on USB. - - - CDC Ethernet Emulation Model (EEM) is a newer standard that has - a simpler interface that can be used by more USB hardware. - - RNDIS support is an additional option, more demanding than than - subset. - - Within the USB device, this gadget driver exposes a network device - "usbX", where X depends on what other networking devices you have. - Treat it like a two-node Ethernet link: host, and gadget. - - The Linux-USB host-side "usbnet" driver interoperates with this - driver, so that deep I/O queues can be supported. On 2.4 kernels, - use "CDCEther" instead, if you're using the CDC option. That CDC - mode should also interoperate with standard CDC Ethernet class - drivers on other host operating systems. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ether". - -config USB_ETH_RNDIS - bool "RNDIS support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_RNDIS - default y - help - Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, - and Microsoft provides redistributable binary RNDIS drivers for - older versions of Windows. - - If you say "y" here, the Ethernet gadget driver will try to provide - a second device configuration, supporting RNDIS to talk to such - Microsoft USB hosts. - - To make MS-Windows work with this, use Documentation/usb/linux.inf - as the "driver info file". For versions of MS-Windows older than - XP, you'll need to download drivers from Microsoft's website; a URL - is given in comments found in that info file. - -config USB_ETH_EEM - bool "Ethernet Emulation Model (EEM) support" - depends on USB_ETH - select USB_LIBCOMPOSITE - select USB_F_EEM - default n - help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". - -config USB_G_NCM - tristate "Network Control Model (NCM) support" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_ETHER - select USB_F_NCM - select CRC32 - help - This driver implements USB CDC NCM subclass standard. NCM is - an advanced protocol for Ethernet encapsulation, allows grouping - of several ethernet frames into one USB transfer and different - alignment possibilities. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_ncm". - -config USB_GADGETFS - tristate "Gadget Filesystem" - help - This driver provides a filesystem based API that lets user mode - programs implement a single-configuration USB device, including - endpoint I/O and control requests that don't relate to enumeration. - All endpoints, transfer speeds, and transfer types supported by - the hardware are available, through read() and write() calls. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "gadgetfs". - -config USB_FUNCTIONFS - tristate "Function Filesystem" - select USB_LIBCOMPOSITE - select USB_F_FS - select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) - help - The Function Filesystem (FunctionFS) lets one create USB - composite functions in user space in the same way GadgetFS - lets one create USB gadgets in user space. This allows creation - of composite gadgets such that some of the functions are - implemented in kernel space (for instance Ethernet, serial or - mass storage) and other are implemented in user space. - - If you say "y" or "m" here you will be able what kind of - configurations the gadget will provide. - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_ffs". - -config USB_FUNCTIONFS_ETH - bool "Include configuration with CDC ECM (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_ECM - select USB_F_SUBSET - help - Include a configuration with CDC ECM function (Ethernet) and the - Function Filesystem. - -config USB_FUNCTIONFS_RNDIS - bool "Include configuration with RNDIS (Ethernet)" - depends on USB_FUNCTIONFS && NET - select USB_U_ETHER - select USB_F_RNDIS - help - Include a configuration with RNDIS function (Ethernet) and the Filesystem. - -config USB_FUNCTIONFS_GENERIC - bool "Include 'pure' configuration" - depends on USB_FUNCTIONFS - help - Include a configuration with the Function Filesystem alone with - no Ethernet interface. - -config USB_MASS_STORAGE - tristate "Mass Storage Gadget" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_F_MASS_STORAGE - help - The Mass Storage Gadget acts as a USB Mass Storage disk drive. - As its storage repository it can use a regular file or a block - device (in much the same way as the "loop" device driver), - specified as a module parameter or sysfs option. - - This driver is a replacement for now removed File-backed - Storage Gadget (g_file_storage). - - Say "y" to link the driver statically, or "m" to build - a dynamically linked module called "g_mass_storage". - -config USB_GADGET_TARGET - tristate "USB Gadget Target Fabric Module" - depends on TARGET_CORE - select USB_LIBCOMPOSITE - help - This fabric is an USB gadget. Two USB protocols are supported that is - BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is - advertised on alternative interface 0 (primary) and UAS is on - alternative interface 1. Both protocols can work on USB2.0 and USB3.0. - UAS utilizes the USB 3.0 feature called streams support. - -config USB_G_SERIAL - tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" - depends on TTY - select USB_U_SERIAL - select USB_F_ACM - select USB_F_SERIAL - select USB_F_OBEX - select USB_LIBCOMPOSITE - help - The Serial Gadget talks to the Linux-USB generic serial driver. - This driver supports a CDC-ACM module option, which can be used - to interoperate with MS-Windows hosts or with the Linux-USB - "cdc-acm" driver. - - This driver also supports a CDC-OBEX option. You will need a - user space OBEX server talking to /dev/ttyGS*, since the kernel - itself doesn't implement the OBEX protocol. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_serial". - - For more information, see Documentation/usb/gadget_serial.txt - which includes instructions and a "driver info file" needed to - make MS-Windows work with CDC ACM. - -config USB_MIDI_GADGET - tristate "MIDI Gadget" - depends on SND - select USB_LIBCOMPOSITE - select SND_RAWMIDI - help - The MIDI Gadget acts as a USB Audio device, with one MIDI - input and one MIDI output. These MIDI jacks appear as - a sound "card" in the ALSA sound system. Other MIDI - connections can then be made on the gadget system, using - ALSA's aconnect utility etc. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_midi". - -config USB_G_PRINTER - tristate "Printer Gadget" - select USB_LIBCOMPOSITE - help - The Printer Gadget channels data between the USB host and a - userspace program driving the print engine. The user space - program reads and writes the device file /dev/g_printer to - receive or send printer data. It can use ioctl calls to - the device file to get or set printer status. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_printer". - - For more information, see Documentation/usb/gadget_printer.txt - which includes sample code for accessing the device file. - -if TTY - -config USB_CDC_COMPOSITE - tristate "CDC Composite Device (Ethernet and ACM)" - depends on NET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_ECM - help - This driver provides two functions in one configuration: - a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. - - This driver requires four bulk and two interrupt endpoints, - plus the ability to handle altsettings. Not all peripheral - controllers are that capable. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module. - -config USB_G_NOKIA - tristate "Nokia composite gadget" - depends on PHONET - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_OBEX - select USB_F_PHONET - select USB_F_ECM - help - The Nokia composite gadget provides support for acm, obex - and phonet in only one composite gadget driver. - - It's only really useful for N900 hardware. If you're building - a kernel for N900, say Y or M here. If unsure, say N. - -config USB_G_ACM_MS - tristate "CDC Composite Device (ACM and mass storage)" - depends on BLOCK - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_F_ACM - select USB_F_MASS_STORAGE - help - This driver provides two functions in one configuration: - a mass storage, and a CDC ACM (serial port) link. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_acm_ms". - -config USB_G_MULTI - tristate "Multifunction Composite Gadget" - depends on BLOCK && NET - select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS - select USB_LIBCOMPOSITE - select USB_U_SERIAL - select USB_U_ETHER - select USB_F_ACM - select USB_F_MASS_STORAGE - help - The Multifunction Composite Gadget provides Ethernet (RNDIS - and/or CDC Ethernet), mass storage and ACM serial link - interfaces. - - You will be asked to choose which of the two configurations is - to be available in the gadget. At least one configuration must - be chosen to make the gadget usable. Selecting more than one - configuration will prevent Windows from automatically detecting - the gadget as a composite gadget, so an INF file will be needed to - use the gadget. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_multi". - -config USB_G_MULTI_RNDIS - bool "RNDIS + CDC Serial + Storage configuration" - depends on USB_G_MULTI - select USB_F_RNDIS - default y - help - This option enables a configuration with RNDIS, CDC Serial and - Mass Storage functions available in the Multifunction Composite - Gadget. This is the configuration dedicated for Windows since RNDIS - is Microsoft's protocol. - - If unsure, say "y". - -config USB_G_MULTI_CDC - bool "CDC Ethernet + CDC Serial + Storage configuration" - depends on USB_G_MULTI - default n - select USB_F_ECM - help - This option enables a configuration with CDC Ethernet (ECM), CDC - Serial and Mass Storage functions available in the Multifunction - Composite Gadget. - - If unsure, say "y". - -endif # TTY - -config USB_G_HID - tristate "HID Gadget" - select USB_LIBCOMPOSITE - help - The HID gadget driver provides generic emulation of USB - Human Interface Devices (HID). - - For more information, see Documentation/usb/gadget_hid.txt which - includes sample code for accessing the device files. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_hid". - -# Standalone / single function gadgets -config USB_G_DBGP - tristate "EHCI Debug Device Gadget" - depends on TTY - select USB_LIBCOMPOSITE - help - This gadget emulates an EHCI Debug device. This is useful when you want - to interact with an EHCI Debug Port. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_dbgp". - -if USB_G_DBGP -choice - prompt "EHCI Debug Device mode" - default USB_G_DBGP_SERIAL - -config USB_G_DBGP_PRINTK - depends on USB_G_DBGP - bool "printk" - help - Directly printk() received data. No interaction. - -config USB_G_DBGP_SERIAL - depends on USB_G_DBGP - select USB_U_SERIAL - bool "serial" - help - Userland can interact using /dev/ttyGSxxx. -endchoice -endif - -# put drivers that need isochronous transfer support (for audio -# or video class gadget drivers), or specific hardware, here. -config USB_G_WEBCAM - tristate "USB Webcam Gadget" - depends on VIDEO_DEV - select USB_LIBCOMPOSITE - select VIDEOBUF2_VMALLOC - help - The Webcam Gadget acts as a composite USB Audio and Video Class - device. It provides a userspace API to process UVC control requests - and stream video data to the host. - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "g_webcam". +source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 49514ea60a98..9add915d41f7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,105 +1,12 @@ # # USB peripheral controller drivers # -ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG +subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG +ccflags-y += -Idrivers/usb/gadget/udc -obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o -obj-$(CONFIG_USB_NET2272) += net2272.o -obj-$(CONFIG_USB_NET2280) += net2280.o -obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o -obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o -obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o -obj-$(CONFIG_USB_GOKU) += goku_udc.o -obj-$(CONFIG_USB_OMAP) += omap_udc.o -obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o -obj-$(CONFIG_USB_AT91) += at91_udc.o -obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o -obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o -obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o -fsl_usb2_udc-y := fsl_udc_core.o -fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o -obj-$(CONFIG_USB_M66592) += m66592-udc.o -obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o -obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o -obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o -obj-$(CONFIG_USB_EG20T) += pch_udc.o -obj-$(CONFIG_USB_MV_UDC) += mv_udc.o -mv_udc-y := mv_udc_core.o -obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o -obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o -obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o -obj-$(CONFIG_USB_GR_UDC) += gr_udc.o -# USB Functions -usb_f_acm-y := f_acm.o -obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o -usb_f_ss_lb-y := f_loopback.o f_sourcesink.o -obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o -obj-$(CONFIG_USB_U_SERIAL) += u_serial.o -usb_f_serial-y := f_serial.o -obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o -usb_f_obex-y := f_obex.o -obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o -obj-$(CONFIG_USB_U_ETHER) += u_ether.o -usb_f_ncm-y := f_ncm.o -obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o -usb_f_ecm-y := f_ecm.o -obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o -usb_f_phonet-y := f_phonet.o -obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o -usb_f_eem-y := f_eem.o -obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o -usb_f_ecm_subset-y := f_subset.o -obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o -usb_f_rndis-y := f_rndis.o rndis.o -obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o -usb_f_mass_storage-y := f_mass_storage.o storage_common.o -obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o -usb_f_fs-y := f_fs.o -obj-$(CONFIG_USB_F_FS) += usb_f_fs.o - -# -# USB gadget drivers -# -g_zero-y := zero.o -g_audio-y := audio.o -g_ether-y := ether.o -g_serial-y := serial.o -g_midi-y := gmidi.o -gadgetfs-y := inode.o -g_mass_storage-y := mass_storage.o -g_printer-y := printer.o -g_cdc-y := cdc2.o -g_multi-y := multi.o -g_hid-y := hid.o -g_dbgp-y := dbgp.o -g_nokia-y := nokia.o -g_webcam-y := webcam.o -g_ncm-y := ncm.o -g_acm_ms-y := acm_ms.o -g_tcm_usb_gadget-y := tcm_usb_gadget.o - -obj-$(CONFIG_USB_ZERO) += g_zero.o -obj-$(CONFIG_USB_AUDIO) += g_audio.o -obj-$(CONFIG_USB_ETH) += g_ether.o -obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o -obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o -obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o -obj-$(CONFIG_USB_G_SERIAL) += g_serial.o -obj-$(CONFIG_USB_G_PRINTER) += g_printer.o -obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o -obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o -obj-$(CONFIG_USB_G_HID) += g_hid.o -obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o -obj-$(CONFIG_USB_G_MULTI) += g_multi.o -obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o -obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o -obj-$(CONFIG_USB_G_NCM) += g_ncm.o -obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o -obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f80151932053..6935a822ce2b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) } if (cdev->req) { kfree(cdev->req->buf); + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } cdev->next_string_id = 0; diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 97142146eead..811c2c7cc269 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, if (page[len - 1] == '\n' || page[len - 1] == '\0') --len; - new_data = kzalloc(len, GFP_KERNEL); + new_data = kmemdup(page, len, GFP_KERNEL); if (!new_data) return -ENOMEM; - memcpy(new_data, page, len); - if (desc->opts_mutex) mutex_lock(desc->opts_mutex); kfree(ext_prop->data); diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile new file mode 100644 index 000000000000..83ae1065149d --- /dev/null +++ b/drivers/usb/gadget/function/Makefile @@ -0,0 +1,34 @@ +# +# USB peripheral controller drivers +# + +ccflags-y := -Idrivers/usb/gadget/ +ccflags-y += -Idrivers/usb/gadget/udc/ + +# USB Functions +usb_f_acm-y := f_acm.o +obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o +usb_f_ss_lb-y := f_loopback.o f_sourcesink.o +obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL) += u_serial.o +usb_f_serial-y := f_serial.o +obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o +usb_f_obex-y := f_obex.o +obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o +obj-$(CONFIG_USB_U_ETHER) += u_ether.o +usb_f_ncm-y := f_ncm.o +obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o +usb_f_ecm-y := f_ecm.o +obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o +usb_f_phonet-y := f_phonet.o +obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o +usb_f_eem-y := f_eem.o +obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o +usb_f_ecm_subset-y := f_subset.o +obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o +usb_f_rndis-y := f_rndis.o rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o +usb_f_mass_storage-y := f_mass_storage.o storage_common.o +obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c index ab1065afbbd0..ab1065afbbd0 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 798760fa7e70..798760fa7e70 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/function/f_eem.c index d61c11d765d0..4d8b236ea608 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) int padlen = 0; u16 len = skb->len; - if (!skb_cloned(skb)) { - int headroom = skb_headroom(skb); - int tailroom = skb_tailroom(skb); + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); - /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, - * stick two bytes of zero-length EEM packet on the end. - */ - if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) - padlen += 2; + /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, + * stick two bytes of zero-length EEM packet on the end. + */ + if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) + padlen += 2; - if ((tailroom >= (ETH_FCS_LEN + padlen)) && - (headroom >= EEM_HLEN)) - goto done; - } + if ((tailroom >= (ETH_FCS_LEN + padlen)) && + (headroom >= EEM_HLEN) && !skb_cloned(skb)) + goto done; skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); dev_kfree_skb_any(skb); diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8598c27c7d43..dc30adf15a01 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -34,6 +34,7 @@ #include "u_fs.h" #include "u_f.h" +#include "u_os_desc.h" #include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ @@ -1646,13 +1647,22 @@ enum ffs_entity_type { FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT }; +enum ffs_os_desc_type { + FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP +}; + typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, u8 *valuep, struct usb_descriptor_header *desc, void *priv); -static int __must_check ffs_do_desc(char *data, unsigned len, - ffs_entity_callback entity, void *priv) +typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv); + +static int __must_check ffs_do_single_desc(char *data, unsigned len, + ffs_entity_callback entity, + void *priv) { struct usb_descriptor_header *_ds = (void *)data; u8 length; @@ -1804,7 +1814,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len, if (!data) return _len - len; - ret = ffs_do_desc(data, len, entity, priv); + ret = ffs_do_single_desc(data, len, entity, priv); if (unlikely(ret < 0)) { pr_debug("%s returns %d\n", __func__, ret); return ret; @@ -1857,11 +1867,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, return 0; } +static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type, + struct usb_os_desc_header *desc) +{ + u16 bcd_version = le16_to_cpu(desc->bcdVersion); + u16 w_index = le16_to_cpu(desc->wIndex); + + if (bcd_version != 1) { + pr_vdebug("unsupported os descriptors version: %d", + bcd_version); + return -EINVAL; + } + switch (w_index) { + case 0x4: + *next_type = FFS_OS_DESC_EXT_COMPAT; + break; + case 0x5: + *next_type = FFS_OS_DESC_EXT_PROP; + break; + default: + pr_vdebug("unsupported os descriptor type: %d", w_index); + return -EINVAL; + } + + return sizeof(*desc); +} + +/* + * Process all extended compatibility/extended property descriptors + * of a feature descriptor + */ +static int __must_check ffs_do_single_os_desc(char *data, unsigned len, + enum ffs_os_desc_type type, + u16 feature_count, + ffs_os_desc_callback entity, + void *priv, + struct usb_os_desc_header *h) +{ + int ret; + const unsigned _len = len; + + ENTER(); + + /* loop over all ext compat/ext prop descriptors */ + while (feature_count--) { + ret = entity(type, h, data, len, priv); + if (unlikely(ret < 0)) { + pr_debug("bad OS descriptor, type: %d\n", type); + return ret; + } + data += ret; + len -= ret; + } + return _len - len; +} + +/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */ +static int __must_check ffs_do_os_descs(unsigned count, + char *data, unsigned len, + ffs_os_desc_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (num = 0; num < count; ++num) { + int ret; + enum ffs_os_desc_type type; + u16 feature_count; + struct usb_os_desc_header *desc = (void *)data; + + if (len < sizeof(*desc)) + return -EINVAL; + + /* + * Record "descriptor" entity. + * Process dwLength, bcdVersion, wIndex, get b/wCount. + * Move the data pointer to the beginning of extended + * compatibilities proper or extended properties proper + * portions of the data + */ + if (le32_to_cpu(desc->dwLength) > len) + return -EINVAL; + + ret = __ffs_do_os_desc_header(&type, desc); + if (unlikely(ret < 0)) { + pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", + num, ret); + return ret; + } + /* + * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??" + */ + feature_count = le16_to_cpu(desc->wCount); + if (type == FFS_OS_DESC_EXT_COMPAT && + (feature_count > 255 || desc->Reserved)) + return -EINVAL; + len -= ret; + data += ret; + + /* + * Process all function/property descriptors + * of this Feature Descriptor + */ + ret = ffs_do_single_os_desc(data, len, type, + feature_count, entity, priv, desc); + if (unlikely(ret < 0)) { + pr_debug("%s returns %d\n", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + } + return _len - len; +} + +/** + * Validate contents of the buffer from userspace related to OS descriptors. + */ +static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_data *ffs = priv; + u8 length; + + ENTER(); + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *d = data; + int i; + + if (len < sizeof(*d) || + d->bFirstInterfaceNumber >= ffs->interfaces_count || + d->Reserved1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) + if (d->Reserved2[i]) + return -EINVAL; + + length = sizeof(struct usb_ext_compat_desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *d = data; + u32 type, pdl; + u16 pnl; + + if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) + return -EINVAL; + length = le32_to_cpu(d->dwSize); + type = le32_to_cpu(d->dwPropertyDataType); + if (type < USB_EXT_PROP_UNICODE || + type > USB_EXT_PROP_UNICODE_MULTI) { + pr_vdebug("unsupported os descriptor property type: %d", + type); + return -EINVAL; + } + pnl = le16_to_cpu(d->wPropertyNameLength); + pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + if (length != 14 + pnl + pdl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", + length, pnl, pdl, type); + return -EINVAL; + } + ++ffs->ms_os_descs_ext_prop_count; + /* property name reported to the host as "WCHAR"s */ + ffs->ms_os_descs_ext_prop_name_len += pnl * 2; + ffs->ms_os_descs_ext_prop_data_len += pdl; + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + return -EINVAL; + } + return length; +} + static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { char *data = _data, *raw_descs; - unsigned counts[3], flags; + unsigned os_descs_count = 0, counts[3], flags; int ret = -EINVAL, i; ENTER(); @@ -1879,7 +2069,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, flags = get_unaligned_le32(data + 8); if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | - FUNCTIONFS_HAS_SS_DESC)) { + FUNCTIONFS_HAS_SS_DESC | + FUNCTIONFS_HAS_MS_OS_DESC)) { ret = -ENOSYS; goto error; } @@ -1902,6 +2093,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, len -= 4; } } + if (flags & (1 << i)) { + os_descs_count = get_unaligned_le32(data); + data += 4; + len -= 4; + }; /* Read descriptors */ raw_descs = data; @@ -1915,6 +2111,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, data += ret; len -= ret; } + if (os_descs_count) { + ret = ffs_do_os_descs(os_descs_count, data, len, + __ffs_data_do_os_desc, ffs); + if (ret < 0) + goto error; + data += ret; + len -= ret; + } if (raw_descs == data || len) { ret = -EINVAL; @@ -1927,6 +2131,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ffs->fs_descs_count = counts[0]; ffs->hs_descs_count = counts[1]; ffs->ss_descs_count = counts[2]; + ffs->ms_os_descs_count = os_descs_count; return 0; @@ -2268,6 +2473,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } +static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_function *func = priv; + u8 length = 0; + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *desc = data; + struct usb_os_desc_table *t; + + t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; + t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; + memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, + ARRAY_SIZE(desc->CompatibleID) + + ARRAY_SIZE(desc->SubCompatibleID)); + length = sizeof(*desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *desc = data; + struct usb_os_desc_table *t; + struct usb_os_desc_ext_prop *ext_prop; + char *ext_prop_name; + char *ext_prop_data; + + t = &func->function.os_desc_table[h->interface]; + t->if_id = func->interfaces_nums[h->interface]; + + ext_prop = func->ffs->ms_os_descs_ext_prop_avail; + func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop); + + ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); + ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); + ext_prop->data_len = le32_to_cpu(*(u32 *) + usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); + length = ext_prop->name_len + ext_prop->data_len + 14; + + ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail; + func->ffs->ms_os_descs_ext_prop_name_avail += + ext_prop->name_len; + + ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail; + func->ffs->ms_os_descs_ext_prop_data_avail += + ext_prop->data_len; + memcpy(ext_prop_data, + usb_ext_prop_data_ptr(data, ext_prop->name_len), + ext_prop->data_len); + /* unicode data reported to the host as "WCHAR"s */ + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + case USB_EXT_PROP_UNICODE_MULTI: + ext_prop->data_len *= 2; + break; + } + ext_prop->data = ext_prop_data; + + memcpy(ext_prop_name, usb_ext_prop_name_ptr(data), + ext_prop->name_len); + /* property name reported to the host as "WCHAR"s */ + ext_prop->name_len *= 2; + ext_prop->name = ext_prop_name; + + t->os_desc->ext_prop_len += + ext_prop->name_len + ext_prop->data_len + 14; + ++t->os_desc->ext_prop_count; + list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop); + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + } + + return length; +} + static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, struct usb_configuration *c) { @@ -2329,7 +2613,7 @@ static int _ffs_func_bind(struct usb_configuration *c, const int super = gadget_is_superspeed(func->gadget) && func->ffs->ss_descs_count; - int fs_len, hs_len, ret; + int fs_len, hs_len, ss_len, ret, i; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2341,6 +2625,18 @@ static int _ffs_func_bind(struct usb_configuration *c, vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); + vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, char[16], ext_compat, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc, os_desc, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop, + ffs->ms_os_descs_ext_prop_count); + vla_item_with_sz(d, char, ext_prop_name, + ffs->ms_os_descs_ext_prop_name_len); + vla_item_with_sz(d, char, ext_prop_data, + ffs->ms_os_descs_ext_prop_data_len); vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; @@ -2351,12 +2647,16 @@ static int _ffs_func_bind(struct usb_configuration *c, return -ENOTSUPP; /* Allocate a single chunk, less management later on */ - vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); if (unlikely(!vlabuf)) return -ENOMEM; - /* Zero */ - memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); + ffs->ms_os_descs_ext_prop_name_avail = + vla_ptr(vlabuf, d, ext_prop_name); + ffs->ms_os_descs_ext_prop_data_avail = + vla_ptr(vlabuf, d, ext_prop_data); + /* Copy descriptors */ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, ffs->raw_descs_length); @@ -2410,12 +2710,16 @@ static int _ffs_func_bind(struct usb_configuration *c, if (likely(super)) { func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); - ret = ffs_do_descs(ffs->ss_descs_count, + ss_len = ffs_do_descs(ffs->ss_descs_count, vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, d_raw_descs__sz - fs_len - hs_len, __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + if (unlikely(ss_len < 0)) { + ret = ss_len; goto error; + } + } else { + ss_len = 0; } /* @@ -2431,6 +2735,28 @@ static int _ffs_func_bind(struct usb_configuration *c, if (unlikely(ret < 0)) goto error; + func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); + if (c->cdev->use_os_string) + for (i = 0; i < ffs->interfaces_count; ++i) { + struct usb_os_desc *desc; + + desc = func->function.os_desc_table[i].os_desc = + vla_ptr(vlabuf, d, os_desc) + + i * sizeof(struct usb_os_desc); + desc->ext_compat_id = + vla_ptr(vlabuf, d, ext_compat) + i * 16; + INIT_LIST_HEAD(&desc->ext_prop); + } + ret = ffs_do_os_descs(ffs->ms_os_descs_count, + vla_ptr(vlabuf, d, raw_descs) + + fs_len + hs_len + ss_len, + d_raw_descs__sz - fs_len - hs_len - ss_len, + __ffs_func_bind_do_os_desc, func); + if (unlikely(ret < 0)) + goto error; + func->function.os_desc_n = + c->cdev->use_os_string ? ffs->interfaces_count : 0; + /* And we're done */ ffs_event_add(ffs, FUNCTIONFS_BIND); return 0; @@ -2901,12 +3227,12 @@ static void *ffs_acquire_dev(const char *dev_name) ffs_dev = _ffs_find_dev(dev_name); if (!ffs_dev) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else if (ffs_dev->mounted) ffs_dev = ERR_PTR(-EBUSY); else if (ffs_dev->ffs_acquire_dev_callback && ffs_dev->ffs_acquire_dev_callback(ffs_dev)) - ffs_dev = ERR_PTR(-ENODEV); + ffs_dev = ERR_PTR(-ENOENT); else ffs_dev->mounted = true; diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a95290a1289f..a95290a1289f 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 4557cd03f0b1..4557cd03f0b1 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b96393908860..b96393908860 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index b4866fcef30b..b4866fcef30b 100644 --- a/drivers/usb/gadget/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..807b31c0edc3 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index a9499fd30792..bcdc882cd415 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -68,6 +68,18 @@ struct f_ncm { * callback and ethernet open/close */ spinlock_t lock; + + struct net_device *netdev; + + /* For multi-frame NDP TX */ + struct sk_buff *skb_tx_data; + struct sk_buff *skb_tx_ndp; + u16 ndp_dgram_count; + bool timer_force_tx; + struct tasklet_struct tx_tasklet; + struct hrtimer task_timer; + + bool timer_stopping; }; static inline struct f_ncm *func_to_ncm(struct usb_function *f) @@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g) * If the host can group frames, allow it to do that, 16K is selected, * because it's used by default by the current linux host driver */ -#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE +#define NTB_DEFAULT_IN_SIZE 16384 #define NTB_OUT_SIZE 16384 -/* - * skbs of size less than that will not be aligned - * to NCM's dwNtbInMaxSize to save bus bandwidth +/* Allocation for storing the NDP, 32 should suffice for a + * 16k packet. This allows a maximum of 32 * 507 Byte packets to + * be transmitted in a single 16kB skb, though when sending full size + * packets this limit will be plenty. + * Smaller packets are not likely to be trying to maximize the + * throughput and will be mstly sending smaller infrequent frames. */ +#define TX_MAX_NUM_DPE 32 -#define MAX_TX_NONFIXED (512 * 3) +/* Delay for the transmit to wait before sending an unfilled NTB frame. */ +#define TX_TIMEOUT_NSECS 300000 #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ USB_CDC_NCM_NTB32_SUPPORTED) @@ -355,14 +372,15 @@ struct ndp_parser_opts { u32 ndp_sign; unsigned nth_size; unsigned ndp_size; + unsigned dpe_size; unsigned ndplen_align; /* sizes in u16 units */ unsigned dgram_item_len; /* index or length */ unsigned block_length; - unsigned fp_index; + unsigned ndp_index; unsigned reserved1; unsigned reserved2; - unsigned next_fp_index; + unsigned next_ndp_index; }; #define INIT_NDP16_OPTS { \ @@ -370,13 +388,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \ .ndplen_align = 4, \ .dgram_item_len = 1, \ .block_length = 1, \ - .fp_index = 1, \ + .ndp_index = 1, \ .reserved1 = 0, \ .reserved2 = 0, \ - .next_fp_index = 1, \ + .next_ndp_index = 1, \ } @@ -385,13 +404,14 @@ struct ndp_parser_opts { .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ + .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \ .ndplen_align = 8, \ .dgram_item_len = 2, \ .block_length = 2, \ - .fp_index = 2, \ + .ndp_index = 2, \ .reserved1 = 1, \ .reserved2 = 2, \ - .next_fp_index = 2, \ + .next_ndp_index = 2, \ } static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; @@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (ncm->port.in_ep->driver_data) { DBG(cdev, "reset ncm\n"); + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); ncm_reset_values(ncm); } @@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) net = gether_connect(&ncm->port); if (IS_ERR(net)) return PTR_ERR(net); + ncm->netdev = net; + ncm->timer_stopping = false; } spin_lock(&ncm->lock); @@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf) return ncm->port.in_ep->driver_data ? 1 : 0; } +static struct sk_buff *package_for_tx(struct f_ncm *ncm) +{ + __le16 *ntb_iter; + struct sk_buff *skb2 = NULL; + unsigned ndp_pad; + unsigned ndp_index; + unsigned new_len; + + const struct ndp_parser_opts *opts = ncm->parser_opts; + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; + + /* Stop the timer */ + hrtimer_try_to_cancel(&ncm->task_timer); + + ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) - + ncm->skb_tx_data->len; + ndp_index = ncm->skb_tx_data->len + ndp_pad; + new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len; + + /* Set the final BlockLength and wNdpIndex */ + ntb_iter = (void *) ncm->skb_tx_data->data; + /* Increment pointer to BlockLength */ + ntb_iter += 2 + 1 + 1; + put_ncm(&ntb_iter, opts->block_length, new_len); + put_ncm(&ntb_iter, opts->ndp_index, ndp_index); + + /* Set the final NDP wLength */ + new_len = opts->ndp_size + + (ncm->ndp_dgram_count * dgram_idx_len); + ncm->ndp_dgram_count = 0; + /* Increment from start to wLength */ + ntb_iter = (void *) ncm->skb_tx_ndp->data; + ntb_iter += 2; + put_unaligned_le16(new_len, ntb_iter); + + /* Merge the skbs */ + swap(skb2, ncm->skb_tx_data); + if (ncm->skb_tx_data) { + dev_kfree_skb_any(ncm->skb_tx_data); + ncm->skb_tx_data = NULL; + } + + /* Insert NDP alignment. */ + ntb_iter = (void *) skb_put(skb2, ndp_pad); + memset(ntb_iter, 0, ndp_pad); + + /* Copy NTB across. */ + ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len); + memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len); + dev_kfree_skb_any(ncm->skb_tx_ndp); + ncm->skb_tx_ndp = NULL; + + /* Insert zero'd datagram. */ + ntb_iter = (void *) skb_put(skb2, dgram_idx_len); + memset(ntb_iter, 0, dgram_idx_len); + + return skb2; +} + static struct sk_buff *ncm_wrap_ntb(struct gether *port, struct sk_buff *skb) { struct f_ncm *ncm = func_to_ncm(&port->func); - struct sk_buff *skb2; + struct sk_buff *skb2 = NULL; int ncb_len = 0; - __le16 *tmp; - int div; - int rem; - int pad; - int ndp_align; - int ndp_pad; + __le16 *ntb_data; + __le16 *ntb_ndp; + int dgram_pad; + unsigned max_size = ncm->port.fixed_in_len; const struct ndp_parser_opts *opts = ncm->parser_opts; - unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; - - div = le16_to_cpu(ntb_parameters.wNdpInDivisor); - rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); - ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); + const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); + const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); + const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; - ncb_len += opts->nth_size; - ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len; - ncb_len += ndp_pad; - ncb_len += opts->ndp_size; - ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */ - ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */ - pad = ALIGN(ncb_len, div) + rem - ncb_len; - ncb_len += pad; - - if (ncb_len + skb->len + crc_len > max_size) { - dev_kfree_skb_any(skb); + if (!skb && !ncm->skb_tx_data) return NULL; - } - skb2 = skb_copy_expand(skb, ncb_len, - max_size - skb->len - ncb_len - crc_len, - GFP_ATOMIC); - dev_kfree_skb_any(skb); - if (!skb2) - return NULL; + if (skb) { + /* Add the CRC if required up front */ + if (ncm->is_crc) { + uint32_t crc; + __le16 *crc_pos; + + crc = ~crc32_le(~0, + skb->data, + skb->len); + crc_pos = (void *) skb_put(skb, sizeof(uint32_t)); + put_unaligned_le32(crc, crc_pos); + } - skb = skb2; + /* If the new skb is too big for the current NCM NTB then + * set the current stored skb to be sent now and clear it + * ready for new data. + * NOTE: Assume maximum align for speed of calculation. + */ + if (ncm->skb_tx_data + && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE + || (ncm->skb_tx_data->len + + div + rem + skb->len + + ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len)) + > max_size)) { + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; + } - tmp = (void *) skb_push(skb, ncb_len); - memset(tmp, 0, ncb_len); + if (!ncm->skb_tx_data) { + ncb_len = opts->nth_size; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; - put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */ - tmp += 2; - /* wHeaderLength */ - put_unaligned_le16(opts->nth_size, tmp++); - tmp++; /* skip wSequence */ - put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */ - /* (d)wFpIndex */ - /* the first pointer is right after the NTH + align */ - put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad); + /* Create a new skb for the NTH and datagrams. */ + ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC); + if (!ncm->skb_tx_data) + goto err; - tmp = (void *)tmp + ndp_pad; + ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len); + memset(ntb_data, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(opts->nth_sign, ntb_data); + ntb_data += 2; + /* wHeaderLength */ + put_unaligned_le16(opts->nth_size, ntb_data++); + + /* Allocate an skb for storing the NDP, + * TX_MAX_NUM_DPE should easily suffice for a + * 16k packet. + */ + ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size + + opts->dpe_size + * TX_MAX_NUM_DPE), + GFP_ATOMIC); + if (!ncm->skb_tx_ndp) + goto err; + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, + opts->ndp_size); + memset(ntb_ndp, 0, ncb_len); + /* dwSignature */ + put_unaligned_le32(ncm->ndp_sign, ntb_ndp); + ntb_ndp += 2; - /* NDP */ - put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ - tmp += 2; - /* wLength */ - put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); + /* There is always a zeroed entry */ + ncm->ndp_dgram_count = 1; - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; + /* Note: we skip opts->next_ndp_index */ + } - if (ncm->is_crc) { - uint32_t crc; + /* Delay the timer. */ + hrtimer_start(&ncm->task_timer, + ktime_set(0, TX_TIMEOUT_NSECS), + HRTIMER_MODE_REL); + + /* Add the datagram position entries */ + ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len); + memset(ntb_ndp, 0, dgram_idx_len); + + ncb_len = ncm->skb_tx_data->len; + dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; + ncb_len += dgram_pad; + + /* (d)wDatagramIndex */ + put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len); + /* (d)wDatagramLength */ + put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len); + ncm->ndp_dgram_count++; + + /* Add the new data to the skb */ + ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad); + memset(ntb_data, 0, dgram_pad); + ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len); + memcpy(ntb_data, skb->data, skb->len); + dev_kfree_skb_any(skb); + skb = NULL; - crc = ~crc32_le(~0, - skb->data + ncb_len, - skb->len - ncb_len); - put_unaligned_le32(crc, skb->data + skb->len); - skb_put(skb, crc_len); + } else if (ncm->skb_tx_data && ncm->timer_force_tx) { + /* If the tx was requested because of a timeout then send */ + skb2 = package_for_tx(ncm); + if (!skb2) + goto err; } - /* (d)wDatagramIndex[0] */ - put_ncm(&tmp, opts->dgram_item_len, ncb_len); - /* (d)wDatagramLength[0] */ - put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len); - /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */ + return skb2; - if (skb->len > MAX_TX_NONFIXED) - memset(skb_put(skb, max_size - skb->len), - 0, max_size - skb->len); +err: + ncm->netdev->stats.tx_dropped++; + + if (skb) + dev_kfree_skb_any(skb); + if (ncm->skb_tx_data) + dev_kfree_skb_any(ncm->skb_tx_data); + if (ncm->skb_tx_ndp) + dev_kfree_skb_any(ncm->skb_tx_ndp); - return skb; + return NULL; +} + +/* + * This transmits the NTB if there are frames waiting. + */ +static void ncm_tx_tasklet(unsigned long data) +{ + struct f_ncm *ncm = (void *)data; + + if (ncm->timer_stopping) + return; + + /* Only send if data is available. */ + if (ncm->skb_tx_data) { + ncm->timer_force_tx = true; + ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev); + ncm->timer_force_tx = false; + } +} + +/* + * The transmit should only be run if no skb data has been sent + * for a certain duration. + */ +static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) +{ + struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); + tasklet_schedule(&ncm->tx_tasklet); + return HRTIMER_NORESTART; } static int ncm_unwrap_ntb(struct gether *port, @@ -963,6 +1124,7 @@ static int ncm_unwrap_ntb(struct gether *port, struct f_ncm *ncm = func_to_ncm(&port->func); __le16 *tmp = (void *) skb->data; unsigned index, index2; + int ndp_index; unsigned dg_len, dg_len2; unsigned ndp_len; struct sk_buff *skb2; @@ -995,91 +1157,101 @@ static int ncm_unwrap_ntb(struct gether *port, goto err; } - index = get_ncm(&tmp, opts->fp_index); - /* NCM 3.2 */ - if (((index % 4) != 0) && (index < opts->nth_size)) { - INFO(port->func.config->cdev, "Bad index: %x\n", - index); - goto err; - } - - /* walk through NDP */ - tmp = ((void *)skb->data) + index; - if (get_unaligned_le32(tmp) != ncm->ndp_sign) { - INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); - goto err; - } - tmp += 2; - - ndp_len = get_unaligned_le16(tmp++); - /* - * NCM 3.3.1 - * entry is 2 items - * item size is 16/32 bits, opts->dgram_item_len * 2 bytes - * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry - */ - if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2)) - || (ndp_len % opts->ndplen_align != 0)) { - INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len); - goto err; - } - tmp += opts->reserved1; - tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ - tmp += opts->reserved2; - - ndp_len -= opts->ndp_size; - index2 = get_ncm(&tmp, opts->dgram_item_len); - dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - dgram_counter = 0; + ndp_index = get_ncm(&tmp, opts->ndp_index); + /* Run through all the NDP's in the NTB */ do { - index = index2; - dg_len = dg_len2; - if (dg_len < 14 + crc_len) { /* ethernet header + crc */ - INFO(port->func.config->cdev, "Bad dgram length: %x\n", - dg_len); + /* NCM 3.2 */ + if (((ndp_index % 4) != 0) && + (ndp_index < opts->nth_size)) { + INFO(port->func.config->cdev, "Bad index: %#X\n", + ndp_index); goto err; } - if (ncm->is_crc) { - uint32_t crc, crc2; - - crc = get_unaligned_le32(skb->data + - index + dg_len - crc_len); - crc2 = ~crc32_le(~0, - skb->data + index, - dg_len - crc_len); - if (crc != crc2) { - INFO(port->func.config->cdev, "Bad CRC\n"); - goto err; - } + + /* walk through NDP */ + tmp = (void *)(skb->data + ndp_index); + if (get_unaligned_le32(tmp) != ncm->ndp_sign) { + INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); + goto err; } + tmp += 2; + ndp_len = get_unaligned_le16(tmp++); + /* + * NCM 3.3.1 + * entry is 2 items + * item size is 16/32 bits, opts->dgram_item_len * 2 bytes + * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry + * Each entry is a dgram index and a dgram length. + */ + if ((ndp_len < opts->ndp_size + + 2 * 2 * (opts->dgram_item_len * 2)) + || (ndp_len % opts->ndplen_align != 0)) { + INFO(port->func.config->cdev, "Bad NDP length: %#X\n", + ndp_len); + goto err; + } + tmp += opts->reserved1; + /* Check for another NDP (d)wNextNdpIndex */ + ndp_index = get_ncm(&tmp, opts->next_ndp_index); + tmp += opts->reserved2; + + ndp_len -= opts->ndp_size; index2 = get_ncm(&tmp, opts->dgram_item_len); dg_len2 = get_ncm(&tmp, opts->dgram_item_len); + dgram_counter = 0; + + do { + index = index2; + dg_len = dg_len2; + if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */ + INFO(port->func.config->cdev, + "Bad dgram length: %#X\n", dg_len); + goto err; + } + if (ncm->is_crc) { + uint32_t crc, crc2; + + crc = get_unaligned_le32(skb->data + + index + dg_len - + crc_len); + crc2 = ~crc32_le(~0, + skb->data + index, + dg_len - crc_len); + if (crc != crc2) { + INFO(port->func.config->cdev, + "Bad CRC\n"); + goto err; + } + } + + index2 = get_ncm(&tmp, opts->dgram_item_len); + dg_len2 = get_ncm(&tmp, opts->dgram_item_len); - if (index2 == 0 || dg_len2 == 0) { - skb2 = skb; - } else { - skb2 = skb_clone(skb, GFP_ATOMIC); + /* + * Copy the data into a new skb. + * This ensures the truesize is correct + */ + skb2 = netdev_alloc_skb_ip_align(ncm->netdev, + dg_len - crc_len); if (skb2 == NULL) goto err; - } + memcpy(skb_put(skb2, dg_len - crc_len), + skb->data + index, dg_len - crc_len); - if (!skb_pull(skb2, index)) { - ret = -EOVERFLOW; - goto err; - } + skb_queue_tail(list, skb2); - skb_trim(skb2, dg_len - crc_len); - skb_queue_tail(list, skb2); + ndp_len -= 2 * (opts->dgram_item_len * 2); - ndp_len -= 2 * (opts->dgram_item_len * 2); + dgram_counter++; - dgram_counter++; + if (index2 == 0 || dg_len2 == 0) + break; + } while (ndp_len > 2 * (opts->dgram_item_len * 2)); + } while (ndp_index); - if (index2 == 0 || dg_len2 == 0) - break; - } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */ + dev_kfree_skb_any(skb); VDBG(port->func.config->cdev, "Parsed NTB with %d frames\n", dgram_counter); @@ -1097,8 +1269,11 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); - if (ncm->port.in_ep->driver_data) + if (ncm->port.in_ep->driver_data) { + ncm->timer_stopping = true; + ncm->netdev = NULL; gether_disconnect(&ncm->port); + } if (ncm->notify->driver_data) { usb_ep_disable(ncm->notify); @@ -1267,6 +1442,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->port.open = ncm_open; ncm->port.close = ncm_close; + tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm); + hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ncm->task_timer.function = ncm_tx_timeout; + DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ncm->port.in_ep->name, ncm->port.out_ep->name, @@ -1380,6 +1559,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ncm unbind\n"); + hrtimer_cancel(&ncm->task_timer); + tasklet_kill(&ncm->tx_tasklet); + + ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ncm->notify_req->buf); @@ -1416,6 +1599,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) ncm->port.ioport = netdev_priv(opts->net); mutex_unlock(&opts->lock); ncm->port.is_fixed = true; + ncm->port.supports_multi_frame = true; ncm->port.func.name = "cdc_network"; /* descriptors are per-instance copies */ diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/function/f_obex.c index aebae1853bce..aebae1853bce 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index f2b781773eed..b9cfc1571d71 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -721,7 +721,8 @@ struct net_device *gphonet_setup_default(void) struct phonet_port *port; /* Create net device */ - dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); + dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN, + pn_net_setup); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 9c41e9515b8e..ddb09dc6d1f2 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; + if (cdev->use_os_string) + f->os_desc_table[0].if_id = + rndis_iad_descriptor.bFirstInterface; + status = usb_interface_id(c, f); if (status < 0) goto fail; diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 9ecbcbf36a45..9ecbcbf36a45 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index d3cd52db78fe..d3cd52db78fe 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 1ea8baf33333..1ea8baf33333 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 2b4c82d84bfc..2b4c82d84bfc 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6261db4a9910..3ed89ecc8d6d 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -348,14 +348,34 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { spin_lock_init(&uac2->p_prm.lock); runtime->hw.rate_min = p_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */ + switch (p_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(p_chmask); runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize / runtime->hw.periods_min; } else { spin_lock_init(&uac2->c_prm.lock); runtime->hw.rate_min = c_srate; - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */ + switch (c_ssize) { + case 3: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } runtime->hw.channels_min = num_channels(c_chmask); runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize / runtime->hw.periods_min; diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e2a1f50bd93c..e2a1f50bd93c 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index ec52752f7326..ec52752f7326 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 15f180904f8a..15f180904f8a 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/function/ndis.h index a19f72dec0cd..a19f72dec0cd 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/function/ndis.h diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324f6977..95d2324f6977 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/function/rndis.c diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4c3775..0f4abb4c3775 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/function/rndis.h diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 648f9e489b39..648f9e489b39 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h index 70c891469f57..70c891469f57 100644 --- a/drivers/usb/gadget/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 262cc03cc2c0..262cc03cc2c0 100644 --- a/drivers/usb/gadget/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/function/u_eem.h index e3ae97874c4f..e3ae97874c4f 100644 --- a/drivers/usb/gadget/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 97b027724ee7..6e6f87656e7b 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *net) { struct eth_dev *dev = netdev_priv(net); - int length = skb->len; + int length = 0; int retval; struct usb_request *req = NULL; unsigned long flags; @@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } spin_unlock_irqrestore(&dev->lock, flags); - if (!in) { + if (skb && !in) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /* apply outgoing CDC or RNDIS filters */ - if (!is_promisc(cdc_filter)) { + if (skb && !is_promisc(cdc_filter)) { u8 *dest = skb->data; if (is_multicast_ether_addr(dest)) { @@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (dev->port_usb) skb = dev->wrap(dev->port_usb, skb); spin_unlock_irqrestore(&dev->lock, flags); - if (!skb) + if (!skb) { + /* Multi frame CDC protocols may store the frame for + * later which is not a dropped frame. + */ + if (dev->port_usb->supports_multi_frame) + goto multiframe; goto drop; - - length = skb->len; + } } + + length = skb->len; req->buf = skb->data; req->context = skb; req->complete = tx_complete; @@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; +multiframe: spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); @@ -1120,10 +1127,7 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); - netif_tx_lock(dev->net); netif_stop_queue(dev->net); - netif_tx_unlock(dev->net); - netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 0f0290acea7e..334b38947916 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -18,6 +18,7 @@ #include <linux/if_ether.h> #include <linux/usb/composite.h> #include <linux/usb/cdc.h> +#include <linux/netdevice.h> #include "gadget_chips.h" @@ -74,6 +75,7 @@ struct gether { bool is_fixed; u32 fixed_out_len; u32 fixed_in_len; + bool supports_multi_frame; struct sk_buff *(*wrap)(struct gether *port, struct sk_buff *skb); int (*unwrap)(struct gether *port, diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index bcbd30146cfd..bcbd30146cfd 100644 --- a/drivers/usb/gadget/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/function/u_fs.h index bf0ba375d459..63d6e71569c1 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -216,6 +216,13 @@ struct ffs_data { unsigned fs_descs_count; unsigned hs_descs_count; unsigned ss_descs_count; + unsigned ms_os_descs_count; + unsigned ms_os_descs_ext_prop_count; + unsigned ms_os_descs_ext_prop_name_len; + unsigned ms_os_descs_ext_prop_data_len; + void *ms_os_descs_ext_prop_avail; + void *ms_os_descs_ext_prop_name_avail; + void *ms_os_descs_ext_prop_data_avail; unsigned short strings_count; unsigned short interfaces_count; diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/function/u_gether.h index d4078426ba5d..d4078426ba5d 100644 --- a/drivers/usb/gadget/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index ce0f3a78ca13..ce0f3a78ca13 100644 --- a/drivers/usb/gadget/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h index 98ced18779ea..98ced18779ea 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/function/u_phonet.h diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a297..e902aa42a297 100644 --- a/drivers/usb/gadget/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c index ad0aca812002..ad0aca812002 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h index c20210c0babd..c20210c0babd 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 7a55fea43430..7a55fea43430 100644 --- a/drivers/usb/gadget/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 18c2e729faf6..18c2e729faf6 100644 --- a/drivers/usb/gadget/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/function/uvc.h index 7a9111de8054..7a9111de8054 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/function/uvc.h diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1c29bc954db9..1c29bc954db9 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 8e76ce982f1e..8e76ce982f1e 100644 --- a/drivers/usb/gadget/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ad48e81155e2..ad48e81155e2 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 71e896d4c5ae..a5eb9a3fbb7a 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -195,6 +195,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) printk(KERN_INFO "Failed to queue request (%d).\n", ret); usb_ep_set_halt(ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvc_queue_cancel(queue, 0); goto requeue; } spin_unlock_irqrestore(&video->queue.irqlock, flags); @@ -281,6 +282,7 @@ error: static int uvc_video_pump(struct uvc_video *video) { + struct uvc_video_queue *queue = &video->queue; struct usb_request *req; struct uvc_buffer *buf; unsigned long flags; @@ -322,6 +324,7 @@ uvc_video_pump(struct uvc_video *video) printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvc_queue_cancel(queue, 0); break; } spin_unlock_irqrestore(&video->queue.irqlock, flags); diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig new file mode 100644 index 000000000000..aa376f006333 --- /dev/null +++ b/drivers/usb/gadget/legacy/Kconfig @@ -0,0 +1,475 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +config USB_ZERO + tristate "Gadget Zero (DEVELOPMENT)" + select USB_LIBCOMPOSITE + select USB_F_SS_LB + help + Gadget Zero is a two-configuration device. It either sinks and + sources bulk data; or it loops back a configurable number of + transfers. It also implements control requests, for "chapter 9" + conformance. The driver needs only two bulk-capable endpoints, so + it can work on top of most device-side usb controllers. It's + useful for testing, and is also a working example showing how + USB "gadget drivers" can be written. + + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + + Gadget Zero also works with the host-side "usb-skeleton" driver, + and with many kinds of host-side test software. You may need + to tweak product and vendor IDs before host software knows about + this device, and arrange to select an appropriate configuration. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_zero". + +config USB_ZERO_HNPTEST + boolean "HNP Test Device" + depends on USB_ZERO && USB_OTG + help + You can configure this device to enumerate using the device + identifiers of the USB-OTG test device. That means that when + this gadget connects to another OTG device, with this one using + the "B-Peripheral" role, that device will use HNP to let this + one serve as the USB host instead (in the "B-Host" role). + +config USB_AUDIO + tristate "Audio Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_PCM + help + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, + 1 AudioStreaming Interface each for USB-OUT and USB-IN. + Number of channels, sample rate and sample size can be + specified as module parameters. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to and + sourced from a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host and choose to provide whatever it + wants as audio data to the USB Host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_audio". + +config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work + without one. + +config USB_ETH + tristate "Ethernet Gadget (with CDC Ethernet support)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + select CRC32 + help + This driver implements Ethernet style communication, in one of + several ways: + + - The "Communication Device Class" (CDC) Ethernet Control Model. + That protocol is often avoided with pure Ethernet adapters, in + favor of simpler vendor-specific hardware, but is widely + supported by firmware for smart network devices. + + - On hardware can't implement that protocol, a simple CDC subset + is used, placing fewer demands on USB. + + - CDC Ethernet Emulation Model (EEM) is a newer standard that has + a simpler interface that can be used by more USB hardware. + + RNDIS support is an additional option, more demanding than than + subset. + + Within the USB device, this gadget driver exposes a network device + "usbX", where X depends on what other networking devices you have. + Treat it like a two-node Ethernet link: host, and gadget. + + The Linux-USB host-side "usbnet" driver interoperates with this + driver, so that deep I/O queues can be supported. On 2.4 kernels, + use "CDCEther" instead, if you're using the CDC option. That CDC + mode should also interoperate with standard CDC Ethernet class + drivers on other host operating systems. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ether". + +config USB_ETH_RNDIS + bool "RNDIS support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_RNDIS + default y + help + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + If you say "y" here, the Ethernet gadget driver will try to provide + a second device configuration, supporting RNDIS to talk to such + Microsoft USB hosts. + + To make MS-Windows work with this, use Documentation/usb/linux.inf + as the "driver info file". For versions of MS-Windows older than + XP, you'll need to download drivers from Microsoft's website; a URL + is given in comments found in that info file. + +config USB_ETH_EEM + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH + select USB_LIBCOMPOSITE + select USB_F_EEM + default n + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". + +config USB_G_NCM + tristate "Network Control Model (NCM) support" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_ETHER + select USB_F_NCM + select CRC32 + help + This driver implements USB CDC NCM subclass standard. NCM is + an advanced protocol for Ethernet encapsulation, allows grouping + of several ethernet frames into one USB transfer and different + alignment possibilities. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ncm". + +config USB_GADGETFS + tristate "Gadget Filesystem" + help + This driver provides a filesystem based API that lets user mode + programs implement a single-configuration USB device, including + endpoint I/O and control requests that don't relate to enumeration. + All endpoints, transfer speeds, and transfer types supported by + the hardware are available, through read() and write() calls. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "gadgetfs". + +config USB_FUNCTIONFS + tristate "Function Filesystem" + select USB_LIBCOMPOSITE + select USB_F_FS + select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) + help + The Function Filesystem (FunctionFS) lets one create USB + composite functions in user space in the same way GadgetFS + lets one create USB gadgets in user space. This allows creation + of composite gadgets such that some of the functions are + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + + If you say "y" or "m" here you will be able what kind of + configurations the gadget will provide. + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_ffs". + +config USB_FUNCTIONFS_ETH + bool "Include configuration with CDC ECM (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_ECM + select USB_F_SUBSET + help + Include a configuration with CDC ECM function (Ethernet) and the + Function Filesystem. + +config USB_FUNCTIONFS_RNDIS + bool "Include configuration with RNDIS (Ethernet)" + depends on USB_FUNCTIONFS && NET + select USB_U_ETHER + select USB_F_RNDIS + help + Include a configuration with RNDIS function (Ethernet) and the Filesystem. + +config USB_FUNCTIONFS_GENERIC + bool "Include 'pure' configuration" + depends on USB_FUNCTIONFS + help + Include a configuration with the Function Filesystem alone with + no Ethernet interface. + +config USB_MASS_STORAGE + tristate "Mass Storage Gadget" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_F_MASS_STORAGE + help + The Mass Storage Gadget acts as a USB Mass Storage disk drive. + As its storage repository it can use a regular file or a block + device (in much the same way as the "loop" device driver), + specified as a module parameter or sysfs option. + + This driver is a replacement for now removed File-backed + Storage Gadget (g_file_storage). + + Say "y" to link the driver statically, or "m" to build + a dynamically linked module called "g_mass_storage". + +config USB_GADGET_TARGET + tristate "USB Gadget Target Fabric Module" + depends on TARGET_CORE + select USB_LIBCOMPOSITE + help + This fabric is an USB gadget. Two USB protocols are supported that is + BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is + advertised on alternative interface 0 (primary) and UAS is on + alternative interface 1. Both protocols can work on USB2.0 and USB3.0. + UAS utilizes the USB 3.0 feature called streams support. + +config USB_G_SERIAL + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + depends on TTY + select USB_U_SERIAL + select USB_F_ACM + select USB_F_SERIAL + select USB_F_OBEX + select USB_LIBCOMPOSITE + help + The Serial Gadget talks to the Linux-USB generic serial driver. + This driver supports a CDC-ACM module option, which can be used + to interoperate with MS-Windows hosts or with the Linux-USB + "cdc-acm" driver. + + This driver also supports a CDC-OBEX option. You will need a + user space OBEX server talking to /dev/ttyGS*, since the kernel + itself doesn't implement the OBEX protocol. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_serial". + + For more information, see Documentation/usb/gadget_serial.txt + which includes instructions and a "driver info file" needed to + make MS-Windows work with CDC ACM. + +config USB_MIDI_GADGET + tristate "MIDI Gadget" + depends on SND + select USB_LIBCOMPOSITE + select SND_RAWMIDI + help + The MIDI Gadget acts as a USB Audio device, with one MIDI + input and one MIDI output. These MIDI jacks appear as + a sound "card" in the ALSA sound system. Other MIDI + connections can then be made on the gadget system, using + ALSA's aconnect utility etc. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_midi". + +config USB_G_PRINTER + tristate "Printer Gadget" + select USB_LIBCOMPOSITE + help + The Printer Gadget channels data between the USB host and a + userspace program driving the print engine. The user space + program reads and writes the device file /dev/g_printer to + receive or send printer data. It can use ioctl calls to + the device file to get or set printer status. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_printer". + + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + +if TTY + +config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_ECM + help + This driver provides two functions in one configuration: + a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. + + This driver requires four bulk and two interrupt endpoints, + plus the ability to handle altsettings. Not all peripheral + controllers are that capable. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + +config USB_G_NOKIA + tristate "Nokia composite gadget" + depends on PHONET + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_OBEX + select USB_F_PHONET + select USB_F_ECM + help + The Nokia composite gadget provides support for acm, obex + and phonet in only one composite gadget driver. + + It's only really useful for N900 hardware. If you're building + a kernel for N900, say Y or M here. If unsure, say N. + +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_F_ACM + select USB_F_MASS_STORAGE + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + +config USB_G_MULTI + tristate "Multifunction Composite Gadget" + depends on BLOCK && NET + select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS + select USB_LIBCOMPOSITE + select USB_U_SERIAL + select USB_U_ETHER + select USB_F_ACM + select USB_F_MASS_STORAGE + help + The Multifunction Composite Gadget provides Ethernet (RNDIS + and/or CDC Ethernet), mass storage and ACM serial link + interfaces. + + You will be asked to choose which of the two configurations is + to be available in the gadget. At least one configuration must + be chosen to make the gadget usable. Selecting more than one + configuration will prevent Windows from automatically detecting + the gadget as a composite gadget, so an INF file will be needed to + use the gadget. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_multi". + +config USB_G_MULTI_RNDIS + bool "RNDIS + CDC Serial + Storage configuration" + depends on USB_G_MULTI + select USB_F_RNDIS + default y + help + This option enables a configuration with RNDIS, CDC Serial and + Mass Storage functions available in the Multifunction Composite + Gadget. This is the configuration dedicated for Windows since RNDIS + is Microsoft's protocol. + + If unsure, say "y". + +config USB_G_MULTI_CDC + bool "CDC Ethernet + CDC Serial + Storage configuration" + depends on USB_G_MULTI + default n + select USB_F_ECM + help + This option enables a configuration with CDC Ethernet (ECM), CDC + Serial and Mass Storage functions available in the Multifunction + Composite Gadget. + + If unsure, say "y". + +endif # TTY + +config USB_G_HID + tristate "HID Gadget" + select USB_LIBCOMPOSITE + help + The HID gadget driver provides generic emulation of USB + Human Interface Devices (HID). + + For more information, see Documentation/usb/gadget_hid.txt which + includes sample code for accessing the device files. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_hid". + +# Standalone / single function gadgets +config USB_G_DBGP + tristate "EHCI Debug Device Gadget" + depends on TTY + select USB_LIBCOMPOSITE + help + This gadget emulates an EHCI Debug device. This is useful when you want + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + +if USB_G_DBGP +choice + prompt "EHCI Debug Device mode" + default USB_G_DBGP_SERIAL + +config USB_G_DBGP_PRINTK + depends on USB_G_DBGP + bool "printk" + help + Directly printk() received data. No interaction. + +config USB_G_DBGP_SERIAL + depends on USB_G_DBGP + select USB_U_SERIAL + bool "serial" + help + Userland can interact using /dev/ttyGSxxx. +endchoice +endif + +# put drivers that need isochronous transfer support (for audio +# or video class gadget drivers), or specific hardware, here. +config USB_G_WEBCAM + tristate "USB Webcam Gadget" + depends on VIDEO_DEV + select USB_LIBCOMPOSITE + select VIDEOBUF2_VMALLOC + help + The Webcam Gadget acts as a composite USB Audio and Video Class + device. It provides a userspace API to process UVC control requests + and stream video data to the host. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_webcam". diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile new file mode 100644 index 000000000000..edba2d1ee0f3 --- /dev/null +++ b/drivers/usb/gadget/legacy/Makefile @@ -0,0 +1,44 @@ +# +# USB gadget drivers +# + +ccflags-y := -Idrivers/usb/gadget/ +ccflags-y += -Idrivers/usb/gadget/udc/ +ccflags-y += -Idrivers/usb/gadget/function/ + +g_zero-y := zero.o +g_audio-y := audio.o +g_ether-y := ether.o +g_serial-y := serial.o +g_midi-y := gmidi.o +gadgetfs-y := inode.o +g_mass_storage-y := mass_storage.o +g_printer-y := printer.o +g_cdc-y := cdc2.o +g_multi-y := multi.o +g_hid-y := hid.o +g_dbgp-y := dbgp.o +g_nokia-y := nokia.o +g_webcam-y := webcam.o +g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o +g_tcm_usb_gadget-y := tcm_usb_gadget.o + +obj-$(CONFIG_USB_ZERO) += g_zero.o +obj-$(CONFIG_USB_AUDIO) += g_audio.o +obj-$(CONFIG_USB_ETH) += g_ether.o +obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o +obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o +obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o +obj-$(CONFIG_USB_G_SERIAL) += g_serial.o +obj-$(CONFIG_USB_G_PRINTER) += g_printer.o +obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_G_HID) += g_hid.o +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o +obj-$(CONFIG_USB_G_MULTI) += g_multi.o +obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o +obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o +obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index a252444cc0a7..c30b7b572465 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -267,18 +267,8 @@ static __refdata struct usb_composite_driver acm_ms_driver = { .unbind = __exit_p(acm_ms_unbind), }; +module_usb_composite_driver(acm_ms_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); MODULE_LICENSE("GPL v2"); - -static int __init init(void) -{ - return usb_composite_probe(&acm_ms_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&acm_ms_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/legacy/audio.c index 231b0efe8fdc..6eb695e5e43a 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -172,17 +172,7 @@ static __refdata struct usb_composite_driver audio_driver = { .unbind = __exit_p(audio_unbind), }; -static int __init init(void) -{ - return usb_composite_probe(&audio_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&audio_driver); -} -module_exit(cleanup); +module_usb_composite_driver(audio_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index e126b6b248e6..2e85d9473478 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -231,18 +231,8 @@ static __refdata struct usb_composite_driver cdc_driver = { .unbind = __exit_p(cdc_unbind), }; +module_usb_composite_driver(cdc_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&cdc_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&cdc_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 986fc511a2ed..225e385a6160 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -222,10 +222,12 @@ static void dbgp_unbind(struct usb_gadget *gadget) { #ifdef CONFIG_USB_G_DBGP_SERIAL kfree(dbgp.serial); + dbgp.serial = NULL; #endif if (dbgp.req) { kfree(dbgp.req->buf); usb_ep_free_request(gadget->ep0, dbgp.req); + dbgp.req = NULL; } gadget->ep0->driver_data = NULL; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/legacy/ether.c index c1c113ef950c..c5fdc61cdc4a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/legacy/ether.c @@ -475,18 +475,8 @@ static __refdata struct usb_composite_driver eth_driver = { .unbind = __exit_p(eth_unbind), }; +module_usb_composite_driver(eth_driver); + MODULE_DESCRIPTION(PREFIX DRIVER_DESC); MODULE_AUTHOR("David Brownell, Benedikt Spanger"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(ð_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(ð_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index fe12e6a27448..06acfa55864a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -276,7 +276,7 @@ module_exit(gfs_exit); static void *functionfs_acquire_dev(struct ffs_dev *dev) { if (!try_module_get(THIS_MODULE)) - return ERR_PTR(-ENODEV); + return ERR_PTR(-ENOENT); return 0; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index e879e2c9f461..3d696b86ff76 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -163,15 +163,4 @@ static __refdata struct usb_composite_driver midi_driver = { .unbind = __exit_p(midi_unbind), }; -static int __init midi_init(void) -{ - return usb_composite_probe(&midi_driver); -} -module_init(midi_init); - -static void __exit midi_cleanup(void) -{ - usb_composite_unregister(&midi_driver); -} -module_exit(midi_cleanup); - +module_usb_composite_driver(midi_driver); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/legacy/hid.c index 778613eb37af..778613eb37af 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/legacy/hid.c diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/legacy/inode.c index 2e4ce7704908..e96077b8bf79 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -440,7 +440,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) value = -ENOMEM; kbuf = memdup_user(buf, len); - if (!kbuf) { + if (IS_ERR(kbuf)) { value = PTR_ERR(kbuf); goto free1; } diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index 8e27a8c96444..8e27a8c96444 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c index 940f6cde8e89..39d27bb343b4 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -507,15 +507,4 @@ static __refdata struct usb_composite_driver multi_driver = { .needs_serial = 1, }; - -static int __init multi_init(void) -{ - return usb_composite_probe(&multi_driver); -} -module_init(multi_init); - -static void __exit multi_exit(void) -{ - usb_composite_unregister(&multi_driver); -} -module_exit(multi_exit); +module_usb_composite_driver(multi_driver); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 81956feca1bd..e90e23db2acb 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -204,18 +204,8 @@ static __refdata struct usb_composite_driver ncm_driver = { .unbind = __exit_p(gncm_unbind), }; +module_usb_composite_driver(ncm_driver); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Yauheni Kaliuta"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&ncm_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&ncm_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/legacy/nokia.c index 3ab386167519..9b8fd701648c 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -347,14 +347,4 @@ static __refdata struct usb_composite_driver nokia_driver = { .unbind = __exit_p(nokia_unbind), }; -static int __init nokia_init(void) -{ - return usb_composite_probe(&nokia_driver); -} -module_init(nokia_init); - -static void __exit nokia_cleanup(void) -{ - usb_composite_unregister(&nokia_driver); -} -module_exit(nokia_cleanup); +module_usb_composite_driver(nokia_driver); diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/legacy/printer.c index 6474081dcbaf..6474081dcbaf 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/legacy/printer.c diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c index 1f5f978d35d5..1f5f978d35d5 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/legacy/serial.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 6cdb7a534f23..6cdb7a534f23 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h index 8289219925b8..8289219925b8 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.h +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 8cef1e658c29..a11d8e420bfe 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -390,20 +390,7 @@ static __refdata struct usb_composite_driver webcam_driver = { .unbind = webcam_unbind, }; -static int __init -webcam_init(void) -{ - return usb_composite_probe(&webcam_driver); -} - -static void __exit -webcam_cleanup(void) -{ - usb_composite_unregister(&webcam_driver); -} - -module_init(webcam_init); -module_exit(webcam_cleanup); +module_usb_composite_driver(webcam_driver); MODULE_AUTHOR("Laurent Pinchart"); MODULE_DESCRIPTION("Webcam Video Gadget"); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/legacy/zero.c index 134f354ede62..c3d496828b74 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -411,17 +411,7 @@ static __refdata struct usb_composite_driver zero_driver = { .resume = zero_resume, }; +module_usb_composite_driver(zero_driver); + MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - -static int __init init(void) -{ - return usb_composite_probe(&zero_driver); -} -module_init(init); - -static void __exit cleanup(void) -{ - usb_composite_unregister(&zero_driver); -} -module_exit(cleanup); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c deleted file mode 100644 index 300b3a71383b..000000000000 --- a/drivers/usb/gadget/net2280.c +++ /dev/null @@ -1,2905 +0,0 @@ -/* - * Driver for the PLX NET2280 USB device controller. - * Specs and errata are available from <http://www.plxtech.com>. - * - * PLX Technology Inc. (formerly NetChip Technology) supported the - * development of this driver. - * - * - * CODE STATUS HIGHLIGHTS - * - * This driver should work well with most "gadget" drivers, including - * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers - * as well as Gadget Zero and Gadgetfs. - * - * DMA is enabled by default. Drivers using transfer queues might use - * DMA chaining to remove IRQ latencies between transfers. (Except when - * short OUT transfers happen.) Drivers can use the req->no_interrupt - * hint to completely eliminate some IRQs, if a later IRQ is guaranteed - * and DMA chaining is enabled. - * - * Note that almost all the errata workarounds here are only needed for - * rev1 chips. Rev1a silicon (0110) fixes almost all of them. - */ - -/* - * Copyright (C) 2003 David Brownell - * Copyright (C) 2003-2005 PLX Technology, Inc. - * - * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility - * with 2282 chip - * - * 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. - */ - -#undef DEBUG /* messages on error and most fault paths */ -#undef VERBOSE /* extra debug messages (success too) */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/prefetch.h> - -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/unaligned.h> - - -#define DRIVER_DESC "PLX NET228x USB Peripheral Controller" -#define DRIVER_VERSION "2005 Sept 27" - -#define EP_DONTUSE 13 /* nonzero */ - -#define USE_RDK_LEDS /* GPIO pins control three LEDs */ - - -static const char driver_name [] = "net2280"; -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", "ep-d", - "ep-e", "ep-f", -}; - -/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) - * use_dma_chaining -- dma descriptor queueing gives even more irq reduction - * - * The net2280 DMA engines are not tightly integrated with their FIFOs; - * not all cases are (yet) handled well in this driver or the silicon. - * Some gadget drivers work better with the dma support here than others. - * These two parameters let you use PIO or more aggressive DMA. - */ -static bool use_dma = 1; -static bool use_dma_chaining = 0; - -/* "modprobe net2280 use_dma=n" etc */ -module_param (use_dma, bool, S_IRUGO); -module_param (use_dma_chaining, bool, S_IRUGO); - - -/* mode 0 == ep-{a,b,c,d} 1K fifo each - * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable - * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable - */ -static ushort fifo_mode = 0; - -/* "modprobe net2280 fifo_mode=1" etc */ -module_param (fifo_mode, ushort, 0644); - -/* enable_suspend -- When enabled, the driver will respond to - * USB suspend requests by powering down the NET2280. Otherwise, - * USB suspend requests will be ignored. This is acceptable for - * self-powered devices - */ -static bool enable_suspend = 0; - -/* "modprobe net2280 enable_suspend=1" etc */ -module_param (enable_suspend, bool, S_IRUGO); - -/* force full-speed operation */ -static bool full_speed; -module_param(full_speed, bool, 0444); -MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); - -#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") - -#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) -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"; - } - return "control"; -} -#endif - -#include "net2280.h" - -#define valid_bit cpu_to_le32 (1 << VALID_BIT) -#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE) - -/*-------------------------------------------------------------------------*/ - -static int -net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -{ - struct net2280 *dev; - struct net2280_ep *ep; - u32 max, tmp; - unsigned long flags; - - ep = container_of (_ep, struct net2280_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; - - /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) - return -EDOM; - - /* sanity check ep-e/ep-f since their fifos are small */ - max = usb_endpoint_maxp (desc) & 0x1fff; - if (ep->num > 4 && max > 64) - return -ERANGE; - - spin_lock_irqsave (&dev->lock, flags); - _ep->maxpacket = max & 0x7ff; - ep->desc = desc; - - /* ep_reset() has already been called */ - ep->stopped = 0; - ep->wedged = 0; - ep->out_overflow = 0; - - /* set speed-dependent max packet; may kick in high bandwidth */ - set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); - - /* FIFO lines can't go to different packets. PIO is ok, so - * use it instead of troublesome (non-bulk) multi-packet DMA. - */ - if (ep->dma && (max % 4) != 0 && use_dma_chaining) { - DEBUG (ep->dev, "%s, no dma for maxpacket %d\n", - ep->ep.name, ep->ep.maxpacket); - ep->dma = NULL; - } - - /* set type, direction, address; reset fifo counters */ - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { - /* erratum 0105 workaround prevents hs NYET */ - if (dev->chiprev == 0100 - && dev->gadget.speed == USB_SPEED_HIGH - && !(desc->bEndpointAddress & USB_DIR_IN)) - writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE), - &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { - /* 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 = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; - tmp <<= ENDPOINT_TYPE; - tmp |= desc->bEndpointAddress; - tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ - tmp |= 1 << ENDPOINT_ENABLE; - wmb (); - - /* for OUT transfers, block the rx fifo until a read is posted */ - ep->is_in = (tmp & USB_DIR_IN) != 0; - if (!ep->is_in) - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - else if (dev->pdev->device != 0x2280) { - /* Added for 2282, Don't use nak packets on an in endpoint, - * this was ignored on 2280 - */ - writel ((1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } - - writel (tmp, &ep->regs->ep_cfg); - - /* enable irqs */ - if (!ep->dma) { /* pio, per-packet */ - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - - tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); - if (dev->pdev->device == 0x2280) - tmp |= readl (&ep->regs->ep_irqenb); - writel (tmp, &ep->regs->ep_irqenb); - } else { /* dma, per-request */ - tmp = (1 << (8 + ep->num)); /* completion */ - tmp |= readl (&dev->regs->pciirqenb1); - writel (tmp, &dev->regs->pciirqenb1); - - /* for short OUT transfers, dma completions can't - * advance the queue; do it pio-style, by hand. - * NOTE erratum 0112 workaround #2 - */ - if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { - tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); - writel (tmp, &ep->regs->ep_irqenb); - - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); - } - } - - tmp = desc->bEndpointAddress; - DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n", - _ep->name, tmp & 0x0f, DIR_STRING (tmp), - type_string (desc->bmAttributes), - ep->dma ? "dma" : "pio", max); - - /* pci writes may still be posted */ - spin_unlock_irqrestore (&dev->lock, flags); - return 0; -} - -static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) -{ - u32 result; - - do { - result = readl (ptr); - if (result == ~(u32)0) /* "device unplugged" */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay (1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; -} - -static const struct usb_ep_ops net2280_ep_ops; - -static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) -{ - u32 tmp; - - ep->desc = NULL; - INIT_LIST_HEAD (&ep->queue); - - usb_ep_set_maxpacket_limit(&ep->ep, ~0); - ep->ep.ops = &net2280_ep_ops; - - /* disable the dma, irqs, endpoint... */ - if (ep->dma) { - writel (0, &ep->dma->dmactl); - writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_TRANSACTION_DONE_INTERRUPT) - | (1 << DMA_ABORT) - , &ep->dma->dmastat); - - tmp = readl (®s->pciirqenb0); - tmp &= ~(1 << ep->num); - writel (tmp, ®s->pciirqenb0); - } else { - tmp = readl (®s->pciirqenb1); - tmp &= ~(1 << (8 + ep->num)); /* completion */ - writel (tmp, ®s->pciirqenb1); - } - writel (0, &ep->regs->ep_irqenb); - - /* init to our chosen defaults, notably so that we NAK OUT - * packets until the driver queues a read (+note erratum 0112) - */ - if (!ep->is_in || ep->dev->pdev->device == 0x2280) { - tmp = (1 << SET_NAK_OUT_PACKETS_MODE) - | (1 << SET_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } else { - /* added for 2282 */ - tmp = (1 << CLEAR_NAK_OUT_PACKETS_MODE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE); - } - - if (ep->num != 0) { - tmp |= (1 << CLEAR_ENDPOINT_TOGGLE) - | (1 << CLEAR_ENDPOINT_HALT); - } - writel (tmp, &ep->regs->ep_rsp); - - /* scrub most status bits, and flush any fifo state */ - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << FIFO_FLUSH) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - - /* fifo size is handled separately */ -} - -static void nuke (struct net2280_ep *); - -static int net2280_disable (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - unsigned long flags; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - nuke (ep); - ep_reset (ep->dev->regs, ep); - - VDEBUG (ep->dev, "disabled %s %s\n", - ep->dma ? "dma" : "pio", _ep->name); - - /* synch memory views with the device */ - (void) readl (&ep->regs->ep_cfg); - - if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) - ep->dma = &ep->dev->dma [ep->num - 1]; - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct usb_request * -net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - if (!_ep) - return NULL; - ep = container_of (_ep, struct net2280_ep, ep); - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD (&req->queue); - - /* this dma descriptor may be swapped with the previous dummy */ - if (ep->dma) { - struct net2280_dma *td; - - td = pci_pool_alloc (ep->dev->requests, gfp_flags, - &req->td_dma); - if (!td) { - kfree (req); - return NULL; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - req->td = td; - } - return &req->req; -} - -static void -net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || !_req) - return; - - req = container_of (_req, struct net2280_request, req); - WARN_ON (!list_empty (&req->queue)); - if (req->td) - pci_pool_free (ep->dev->requests, req->td, req->td_dma); - kfree (req); -} - -/*-------------------------------------------------------------------------*/ - -/* load a packet into the fifo we use for usb IN transfers. - * works for all endpoints. - * - * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo - * at a time, but this code is simpler because it knows it only writes - * one packet. ep-a..ep-d should use dma instead. - */ -static void -write_fifo (struct net2280_ep *ep, struct usb_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf; - u32 tmp; - unsigned count, total; - - /* INVARIANT: fifo is currently empty. (testable) */ - - if (req) { - buf = req->buf + req->actual; - prefetch (buf); - total = req->length - req->actual; - } else { - total = 0; - buf = NULL; - } - - /* write just one packet at a time */ - count = ep->ep.maxpacket; - if (count > total) /* min() cannot be used on a bitfield */ - count = total; - - VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", - ep->ep.name, count, - (count != ep->ep.maxpacket) ? " (short)" : "", - req); - while (count >= 4) { - /* NOTE be careful if you try to align these. fifo lines - * should normally be full (4 bytes) and successive partial - * lines are ok only in certain cases. - */ - tmp = get_unaligned ((u32 *)buf); - cpu_to_le32s (&tmp); - writel (tmp, ®s->ep_data); - buf += 4; - count -= 4; - } - - /* last fifo entry is "short" unless we wrote a full packet. - * also explicitly validate last word in (periodic) transfers - * when maxpacket is not a multiple of 4 bytes. - */ - if (count || total < ep->ep.maxpacket) { - tmp = count ? get_unaligned ((u32 *)buf) : count; - cpu_to_le32s (&tmp); - set_fifo_bytecount (ep, count & 0x03); - writel (tmp, ®s->ep_data); - } - - /* pci writes may still be posted */ -} - -/* work around erratum 0106: PCI and USB race over the OUT fifo. - * caller guarantees chiprev 0100, out endpoint is NAKing, and - * there's no real data in the fifo. - * - * NOTE: also used in cases where that erratum doesn't apply: - * where the host wrote "too much" data to us. - */ -static void out_flush (struct net2280_ep *ep) -{ - u32 __iomem *statp; - u32 tmp; - - ASSERT_OUT_NAKING (ep); - - statp = &ep->regs->ep_stat; - writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - , statp); - writel ((1 << FIFO_FLUSH), statp); - mb (); - tmp = readl (statp); - if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - /* high speed did bulk NYET; fifo isn't filling */ - && ep->dev->gadget.speed == USB_SPEED_FULL) { - unsigned usec; - - usec = 50; /* 64 byte bulk/interrupt */ - handshake (statp, (1 << USB_OUT_PING_NAK_SENT), - (1 << USB_OUT_PING_NAK_SENT), usec); - /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ - } -} - -/* unload packet(s) from the fifo we use for usb OUT transfers. - * returns true iff the request completed, because of short packet - * or the request buffer having filled with full packets. - * - * for ep-a..ep-d this will read multiple packets out when they - * have been accepted. - */ -static int -read_fifo (struct net2280_ep *ep, struct net2280_request *req) -{ - struct net2280_ep_regs __iomem *regs = ep->regs; - u8 *buf = req->req.buf + req->req.actual; - unsigned count, tmp, is_short; - unsigned cleanup = 0, prevent = 0; - - /* erratum 0106 ... packets coming in during fifo reads might - * be incompletely rejected. not all cases have workarounds. - */ - if (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed == USB_SPEED_FULL) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS))) - cleanup = 1; - else if ((tmp & (1 << FIFO_FULL))) { - start_out_naking (ep); - prevent = 1; - } - /* else: hope we don't see the problem */ - } - - /* never overflow the rx buffer. the fifo reads packets until - * it sees a short one; we might not be ready for them all. - */ - prefetchw (buf); - count = readl (®s->ep_avail); - if (unlikely (count == 0)) { - udelay (1); - tmp = readl (&ep->regs->ep_stat); - count = readl (®s->ep_avail); - /* handled that data already? */ - if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0) - return 0; - } - - tmp = req->req.length - req->req.actual; - if (count > tmp) { - /* as with DMA, data overflow gets flushed */ - if ((tmp % ep->ep.maxpacket) != 0) { - ERROR (ep->dev, - "%s out fifo %d bytes, expected %d\n", - ep->ep.name, count, tmp); - req->req.status = -EOVERFLOW; - cleanup = 1; - /* NAK_OUT_PACKETS will be set, so flushing is safe; - * the next read will start with the next packet - */ - } /* else it's a ZLP, no worries */ - count = tmp; - } - req->req.actual += count; - - is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); - - VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", - ep->ep.name, count, is_short ? " (short)" : "", - cleanup ? " flush" : "", prevent ? " nak" : "", - req, req->req.actual, req->req.length); - - while (count >= 4) { - tmp = readl (®s->ep_data); - cpu_to_le32s (&tmp); - put_unaligned (tmp, (u32 *)buf); - buf += 4; - count -= 4; - } - if (count) { - tmp = readl (®s->ep_data); - /* LE conversion is implicit here: */ - do { - *buf++ = (u8) tmp; - tmp >>= 8; - } while (--count); - } - if (cleanup) - out_flush (ep); - if (prevent) { - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - (void) readl (&ep->regs->ep_rsp); - } - - return is_short || ((req->req.actual == req->req.length) - && !req->req.zero); -} - -/* fill out dma descriptor to match a given request */ -static void -fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *td = req->td; - u32 dmacount = req->req.length; - - /* don't let DMA continue after a short OUT packet, - * so overruns can't affect the next transfer. - * in case of overruns on max-size packets, we can't - * stop the fifo from filling but we can flush it. - */ - if (ep->is_in) - dmacount |= (1 << DMA_DIRECTION); - if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) - || ep->dev->pdev->device != 0x2280) - dmacount |= (1 << END_OF_CHAIN); - - req->valid = valid; - if (valid) - dmacount |= (1 << VALID_BIT); - if (likely(!req->req.no_interrupt || !use_dma_chaining)) - dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); - - /* td->dmadesc = previously set by caller */ - td->dmaaddr = cpu_to_le32 (req->req.dma); - - /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ - wmb (); - td->dmacount = cpu_to_le32(dmacount); -} - -static const u32 dmactl_default = - (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) - | (1 << DMA_CLEAR_COUNT_ENABLE) - /* erratum 0116 workaround part 1 (use POLLING) */ - | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) - | (1 << DMA_VALID_BIT_POLLING_ENABLE) - | (1 << DMA_VALID_BIT_ENABLE) - | (1 << DMA_SCATTER_GATHER_ENABLE) - /* erratum 0116 workaround part 2 (no AUTOSTART) */ - | (1 << DMA_ENABLE); - -static inline void spin_stop_dma (struct net2280_dma_regs __iomem *dma) -{ - handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50); -} - -static inline void stop_dma (struct net2280_dma_regs __iomem *dma) -{ - writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl); - spin_stop_dma (dma); -} - -static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) -{ - struct net2280_dma_regs __iomem *dma = ep->dma; - unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION); - - if (ep->dev->pdev->device != 0x2280) - tmp |= (1 << END_OF_CHAIN); - - writel (tmp, &dma->dmacount); - writel (readl (&dma->dmastat), &dma->dmastat); - - writel (td_dma, &dma->dmadesc); - writel (dmactl, &dma->dmactl); - - /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ - (void) readl (&ep->dev->pci->pcimstctl); - - writel ((1 << DMA_START), &dma->dmastat); - - if (!ep->is_in) - stop_out_naking (ep); -} - -static void start_dma (struct net2280_ep *ep, struct net2280_request *req) -{ - u32 tmp; - struct net2280_dma_regs __iomem *dma = ep->dma; - - /* FIXME can't use DMA for ZLPs */ - - /* on this path we "know" there's no dma active (yet) */ - WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); - writel (0, &ep->dma->dmactl); - - /* previous OUT packet might have been short */ - if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) - & (1 << NAK_OUT_PACKETS)) != 0) { - writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), - &ep->regs->ep_stat); - - tmp = readl (&ep->regs->ep_avail); - if (tmp) { - writel (readl (&dma->dmastat), &dma->dmastat); - - /* transfer all/some fifo data */ - writel (req->req.dma, &dma->dmaaddr); - tmp = min (tmp, req->req.length); - - /* dma irq, faking scatterlist status */ - req->td->dmacount = cpu_to_le32 (req->req.length - tmp); - writel ((1 << DMA_DONE_INTERRUPT_ENABLE) - | tmp, &dma->dmacount); - req->td->dmadesc = 0; - req->valid = 1; - - writel ((1 << DMA_ENABLE), &dma->dmactl); - writel ((1 << DMA_START), &dma->dmastat); - return; - } - } - - tmp = dmactl_default; - - /* force packet boundaries between dma requests, but prevent the - * controller from automagically writing a last "short" packet - * (zero length) unless the driver explicitly said to do that. - */ - if (ep->is_in) { - if (likely ((req->req.length % ep->ep.maxpacket) != 0 - || req->req.zero)) { - tmp |= (1 << DMA_FIFO_VALIDATE); - ep->in_fifo_validate = 1; - } else - ep->in_fifo_validate = 0; - } - - /* init req->td, pointing to the current dummy */ - req->td->dmadesc = cpu_to_le32 (ep->td_dma); - fill_dma_desc (ep, req, 1); - - if (!use_dma_chaining) - req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN); - - start_queue (ep, tmp, req->td_dma); -} - -static inline void -queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid) -{ - struct net2280_dma *end; - dma_addr_t tmp; - - /* swap new dummy for old, link; fill and maybe activate */ - end = ep->dummy; - ep->dummy = req->td; - req->td = end; - - tmp = ep->td_dma; - ep->td_dma = req->td_dma; - req->td_dma = tmp; - - end->dmadesc = cpu_to_le32 (ep->td_dma); - - fill_dma_desc (ep, req, valid); -} - -static void -done (struct net2280_ep *ep, struct net2280_request *req, int status) -{ - struct net2280 *dev; - unsigned stopped = ep->stopped; - - list_del_init (&req->queue); - - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - dev = ep->dev; - if (ep->dma) - usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); - - if (status && status != -ESHUTDOWN) - VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - spin_unlock (&dev->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dev->lock); - ep->stopped = stopped; -} - -/*-------------------------------------------------------------------------*/ - -static int -net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct net2280_request *req; - struct net2280_ep *ep; - struct net2280 *dev; - unsigned long flags; - - /* we always require a cpu-view buffer, so that we can - * always use pio (as fallback or whatever). - */ - req = container_of (_req, struct net2280_request, req); - if (!_req || !_req->complete || !_req->buf - || !list_empty (&req->queue)) - return -EINVAL; - if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) - return -EDOM; - ep = container_of (_ep, struct net2280_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; - - /* FIXME implement PIO fallback for ZLPs with DMA */ - if (ep->dma && _req->length == 0) - return -EOPNOTSUPP; - - /* set up dma mapping in case the caller didn't */ - if (ep->dma) { - int ret; - - ret = usb_gadget_map_request(&dev->gadget, _req, - ep->is_in); - if (ret) - return ret; - } - -#if 0 - VDEBUG (dev, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); -#endif - - spin_lock_irqsave (&dev->lock, flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - if (list_empty (&ep->queue) && !ep->stopped) { - /* use DMA if the endpoint supports it, else pio */ - if (ep->dma) - start_dma (ep, req); - else { - /* maybe there's no control data, just status ack */ - if (ep->num == 0 && _req->length == 0) { - allow_status (ep); - done (ep, req, 0); - VDEBUG (dev, "%s status ack\n", ep->ep.name); - goto done; - } - - /* PIO ... stuff the fifo, or unblock it. */ - if (ep->is_in) - write_fifo (ep, _req); - else if (list_empty (&ep->queue)) { - u32 s; - - /* OUT FIFO might have packet(s) buffered */ - s = readl (&ep->regs->ep_stat); - if ((s & (1 << FIFO_EMPTY)) == 0) { - /* note: _req->short_not_ok is - * ignored here since PIO _always_ - * stops queue advance here, and - * _req->status doesn't change for - * short reads (only _req->actual) - */ - if (read_fifo (ep, req)) { - done (ep, req, 0); - if (ep->num == 0) - allow_status (ep); - /* don't queue it */ - req = NULL; - } else - s = readl (&ep->regs->ep_stat); - } - - /* don't NAK, let the fifo fill */ - if (req && (s & (1 << NAK_OUT_PACKETS))) - writel ((1 << CLEAR_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } - } - - } else if (ep->dma) { - int valid = 1; - - if (ep->is_in) { - int expect; - - /* preventing magic zlps is per-engine state, not - * per-transfer; irq logic must recover hiccups. - */ - expect = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (expect != ep->in_fifo_validate) - valid = 0; - } - queue_dma (ep, req, valid); - - } /* else the irq handler advances the queue. */ - - ep->responded = 1; - if (req) - list_add_tail (&req->queue, &ep->queue); -done: - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static inline void -dma_done ( - struct net2280_ep *ep, - struct net2280_request *req, - u32 dmacount, - int status -) -{ - req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); - done (ep, req, status); -} - -static void restart_dma (struct net2280_ep *ep); - -static void scan_dma_completions (struct net2280_ep *ep) -{ - /* only look at descriptors that were "naturally" retired, - * so fifo and list head state won't matter - */ - while (!list_empty (&ep->queue)) { - struct net2280_request *req; - u32 tmp; - - req = list_entry (ep->queue.next, - struct net2280_request, queue); - if (!req->valid) - break; - rmb (); - tmp = le32_to_cpup (&req->td->dmacount); - if ((tmp & (1 << VALID_BIT)) != 0) - break; - - /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" - * cases where DMA must be aborted; this code handles - * all non-abort DMA completions. - */ - if (unlikely (req->td->dmadesc == 0)) { - /* paranoia */ - tmp = readl (&ep->dma->dmacount); - if (tmp & DMA_BYTE_COUNT_MASK) - break; - /* single transfer mode */ - dma_done (ep, req, tmp, 0); - break; - } else if (!ep->is_in - && (req->req.length % ep->ep.maxpacket) != 0) { - tmp = readl (&ep->regs->ep_stat); - - /* AVOID TROUBLE HERE by not issuing short reads from - * your gadget driver. That helps avoids errata 0121, - * 0122, and 0124; not all cases trigger the warning. - */ - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARNING (ep->dev, "%s lost packet sync!\n", - ep->ep.name); - req->req.status = -EOVERFLOW; - } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { - /* fifo gets flushed later */ - ep->out_overflow = 1; - DEBUG (ep->dev, "%s dma, discard %d len %d\n", - ep->ep.name, tmp, - req->req.length); - req->req.status = -EOVERFLOW; - } - } - dma_done (ep, req, tmp, 0); - } -} - -static void restart_dma (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 dmactl = dmactl_default; - - if (ep->stopped) - return; - req = list_entry (ep->queue.next, struct net2280_request, queue); - - if (!use_dma_chaining) { - start_dma (ep, req); - return; - } - - /* the 2280 will be processing the queue unless queue hiccups after - * the previous transfer: - * IN: wanted automagic zlp, head doesn't (or vice versa) - * DMA_FIFO_VALIDATE doesn't init from dma descriptors. - * OUT: was "usb-short", we must restart. - */ - if (ep->is_in && !req->valid) { - struct net2280_request *entry, *prev = NULL; - int reqmode, done = 0; - - DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); - ep->in_fifo_validate = likely (req->req.zero - || (req->req.length % ep->ep.maxpacket) != 0); - if (ep->in_fifo_validate) - dmactl |= (1 << DMA_FIFO_VALIDATE); - list_for_each_entry (entry, &ep->queue, queue) { - __le32 dmacount; - - if (entry == req) - continue; - dmacount = entry->td->dmacount; - if (!done) { - reqmode = likely (entry->req.zero - || (entry->req.length - % ep->ep.maxpacket) != 0); - if (reqmode == ep->in_fifo_validate) { - entry->valid = 1; - dmacount |= valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - continue; - } else { - /* force a hiccup */ - prev->td->dmacount |= dma_done_ie; - done = 1; - } - } - - /* walk the rest of the queue so unlinks behave */ - entry->valid = 0; - dmacount &= ~valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - } - } - - writel (0, &ep->dma->dmactl); - start_queue (ep, dmactl, req->td_dma); -} - -static void abort_dma (struct net2280_ep *ep) -{ - /* abort the current transfer */ - if (likely (!list_empty (&ep->queue))) { - /* FIXME work around errata 0121, 0122, 0124 */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - } else - stop_dma (ep->dma); - scan_dma_completions (ep); -} - -/* dequeue ALL requests */ -static void nuke (struct net2280_ep *ep) -{ - struct net2280_request *req; - - /* called with spinlock held */ - ep->stopped = 1; - if (ep->dma) - abort_dma (ep); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, - queue); - done (ep, req, -ESHUTDOWN); - } -} - -/* dequeue JUST ONE request */ -static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2280_ep *ep; - struct net2280_request *req; - unsigned long flags; - u32 dmactl; - int stopped; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - stopped = ep->stopped; - - /* quiesce dma while we patch the queue */ - dmactl = 0; - ep->stopped = 1; - if (ep->dma) { - dmactl = readl (&ep->dma->dmactl); - /* WARNING erratum 0127 may kick in ... */ - stop_dma (ep->dma); - scan_dma_completions (ep); - } - - /* 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) { - if (ep->dma) { - DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name); - _req->status = -ECONNRESET; - abort_dma (ep); - if (likely (ep->queue.next == &req->queue)) { - // NOTE: misreports single-transfer mode - req->td->dmacount = 0; /* invalidate */ - dma_done (ep, req, - readl (&ep->dma->dmacount), - -ECONNRESET); - } - } else { - DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name); - done (ep, req, -ECONNRESET); - } - req = NULL; - - /* patch up hardware chaining data */ - } else if (ep->dma && use_dma_chaining) { - if (req->queue.prev == ep->queue.next) { - writel (le32_to_cpu (req->td->dmadesc), - &ep->dma->dmadesc); - if (req->td->dmacount & dma_done_ie) - writel (readl (&ep->dma->dmacount) - | le32_to_cpu(dma_done_ie), - &ep->dma->dmacount); - } else { - struct net2280_request *prev; - - prev = list_entry (req->queue.prev, - struct net2280_request, queue); - prev->td->dmadesc = req->td->dmadesc; - if (req->td->dmacount & dma_done_ie) - prev->td->dmacount |= dma_done_ie; - } - } - - if (req) - done (ep, req, -ECONNRESET); - ep->stopped = stopped; - - if (ep->dma) { - /* turn off dma on inactive queues */ - if (list_empty (&ep->queue)) - stop_dma (ep->dma); - else if (!ep->stopped) { - /* resume current request, or start new one */ - if (req) - writel (dmactl, &ep->dma->dmactl); - else - start_dma (ep, list_entry (ep->queue.next, - struct net2280_request, queue)); - } - } - - spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int net2280_fifo_status (struct usb_ep *_ep); - -static int -net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) -{ - struct net2280_ep *ep; - unsigned long flags; - int retval = 0; - - ep = container_of (_ep, struct net2280_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 */ && (ep->desc->bmAttributes & 0x03) - == USB_ENDPOINT_XFER_ISOC) - return -EINVAL; - - spin_lock_irqsave (&ep->dev->lock, flags); - if (!list_empty (&ep->queue)) - retval = -EAGAIN; - else if (ep->is_in && value && net2280_fifo_status (_ep) != 0) - retval = -EAGAIN; - else { - VDEBUG (ep->dev, "%s %s %s\n", _ep->name, - value ? "set" : "clear", - wedged ? "wedge" : "halt"); - /* set/clear, then synch memory views with the device */ - 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; - } - (void) readl (&ep->regs->ep_rsp); - } - spin_unlock_irqrestore (&ep->dev->lock, flags); - - return retval; -} - -static int -net2280_set_halt(struct usb_ep *_ep, int value) -{ - return net2280_set_halt_and_wedge(_ep, value, 0); -} - -static int -net2280_set_wedge(struct usb_ep *_ep) -{ - if (!_ep || _ep->name == ep0name) - return -EINVAL; - return net2280_set_halt_and_wedge(_ep, 1, 1); -} - -static int -net2280_fifo_status (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - u32 avail; - - ep = container_of (_ep, struct net2280_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 = readl (&ep->regs->ep_avail) & ((1 << 12) - 1); - if (avail > ep->fifo_size) - return -EOVERFLOW; - if (ep->is_in) - avail = ep->fifo_size - avail; - return avail; -} - -static void -net2280_fifo_flush (struct usb_ep *_ep) -{ - struct net2280_ep *ep; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return; - - writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); - (void) readl (&ep->regs->ep_rsp); -} - -static const struct usb_ep_ops net2280_ep_ops = { - .enable = net2280_enable, - .disable = net2280_disable, - - .alloc_request = net2280_alloc_request, - .free_request = net2280_free_request, - - .queue = net2280_queue, - .dequeue = net2280_dequeue, - - .set_halt = net2280_set_halt, - .set_wedge = net2280_set_wedge, - .fifo_status = net2280_fifo_status, - .fifo_flush = net2280_fifo_flush, -}; - -/*-------------------------------------------------------------------------*/ - -static int net2280_get_frame (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - unsigned long flags; - u16 retval; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - spin_lock_irqsave (&dev->lock, flags); - retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff; - spin_unlock_irqrestore (&dev->lock, flags); - return retval; -} - -static int net2280_wakeup (struct usb_gadget *_gadget) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE)) - writel (1 << GENERATE_RESUME, &dev->usb->usbstat); - spin_unlock_irqrestore (&dev->lock, flags); - - /* pci writes may still be posted */ - return 0; -} - -static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - if (value) - tmp |= (1 << SELF_POWERED_STATUS); - else - tmp &= ~(1 << SELF_POWERED_STATUS); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_pullup(struct usb_gadget *_gadget, int is_on) -{ - struct net2280 *dev; - u32 tmp; - unsigned long flags; - - if (!_gadget) - return -ENODEV; - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - tmp = readl (&dev->usb->usbctl); - dev->softconnect = (is_on != 0); - if (is_on) - tmp |= (1 << USB_DETECT_ENABLE); - else - tmp &= ~(1 << USB_DETECT_ENABLE); - writel (tmp, &dev->usb->usbctl); - spin_unlock_irqrestore (&dev->lock, flags); - - return 0; -} - -static int net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); - -static const struct usb_gadget_ops net2280_ops = { - .get_frame = net2280_get_frame, - .wakeup = net2280_wakeup, - .set_selfpowered = net2280_set_selfpowered, - .pullup = net2280_pullup, - .udc_start = net2280_start, - .udc_stop = net2280_stop, -}; - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - -/* FIXME move these into procfs, and use seq_file. - * Sysfs _still_ doesn't behave for arbitrarily sized files, - * and also doesn't help products using this with 2.4 kernels. - */ - -/* "function" sysfs attribute */ -static ssize_t function_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - if (!dev->driver - || !dev->driver->function - || strlen (dev->driver->function) > PAGE_SIZE) - return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -} -static DEVICE_ATTR_RO(function); - -static ssize_t registers_show(struct device *_dev, - struct device_attribute *attr, char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size, t; - unsigned long flags; - int i; - u32 t1, t2; - 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 " DRIVER_VERSION - ", chiprev %04x, dma %s\n\n" - "devinit %03x fifoctl %08x gadget '%s'\n" - "pci irqenb0 %02x irqenb1 %08x " - "irqstat0 %04x irqstat1 %08x\n", - driver_name, dev->chiprev, - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled", - readl (&dev->regs->devinit), - readl (&dev->regs->fifoctl), - s, - readl (&dev->regs->pciirqenb0), - readl (&dev->regs->pciirqenb1), - readl (&dev->regs->irqstat0), - readl (&dev->regs->irqstat1)); - size -= t; - next += t; - - /* USB Control Registers */ - t1 = readl (&dev->usb->usbctl); - t2 = readl (&dev->usb->usbstat); - if (t1 & (1 << VBUS_PIN)) { - if (t2 & (1 << HIGH_SPEED)) - s = "high speed"; - else if (dev->gadget.speed == USB_SPEED_UNKNOWN) - s = "powered"; - else - s = "full speed"; - /* full speed bit (6) not working?? */ - } else - s = "not attached"; - t = scnprintf (next, size, - "stdrsp %08x usbctl %08x usbstat %08x " - "addr 0x%02x (%s)\n", - readl (&dev->usb->stdrsp), t1, t2, - readl (&dev->usb->ouraddr), s); - size -= t; - next += t; - - /* PCI Master Control Registers */ - - /* DMA Control Registers */ - - /* Configurable EP Control Registers */ - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->desc) - continue; - - t1 = readl (&ep->regs->ep_cfg); - t2 = readl (&ep->regs->ep_rsp) & 0xff; - t = scnprintf (next, size, - "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" - "irqenb %02x\n", - ep->ep.name, t1, t2, - (t2 & (1 << CLEAR_NAK_OUT_PACKETS)) - ? "NAK " : "", - (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE)) - ? "hide " : "", - (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR)) - ? "CRC " : "", - (t2 & (1 << CLEAR_INTERRUPT_MODE)) - ? "interrupt " : "", - (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) - ? "status " : "", - (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE)) - ? "NAKmode " : "", - (t2 & (1 << CLEAR_ENDPOINT_TOGGLE)) - ? "DATA1 " : "DATA0 ", - (t2 & (1 << CLEAR_ENDPOINT_HALT)) - ? "HALT " : "", - readl (&ep->regs->ep_irqenb)); - size -= t; - next += t; - - t = scnprintf (next, size, - "\tstat %08x avail %04x " - "(ep%d%s-%s)%s\n", - readl (&ep->regs->ep_stat), - readl (&ep->regs->ep_avail), - t1 & 0x0f, DIR_STRING (t1), - type_string (t1 >> 8), - ep->stopped ? "*" : ""); - size -= t; - next += t; - - if (!ep->dma) - continue; - - t = scnprintf (next, size, - " dma\tctl %08x stat %08x count %08x\n" - "\taddr %08x desc %08x\n", - readl (&ep->dma->dmactl), - readl (&ep->dma->dmastat), - readl (&ep->dma->dmacount), - readl (&ep->dma->dmaaddr), - readl (&ep->dma->dmadesc)); - size -= t; - next += t; - - } - - /* Indexed Registers */ - // none yet - - /* Statistics */ - t = scnprintf (next, size, "\nirqs: "); - size -= t; - next += t; - for (i = 0; i < 7; i++) { - struct net2280_ep *ep; - - ep = &dev->ep [i]; - if (i && !ep->irqs) - continue; - t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs); - size -= t; - next += t; - - } - t = scnprintf (next, size, "\n"); - size -= t; - next += t; - - spin_unlock_irqrestore (&dev->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(registers); - -static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, - char *buf) -{ - struct net2280 *dev; - char *next; - unsigned size; - unsigned long flags; - int i; - - dev = dev_get_drvdata (_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave (&dev->lock, flags); - - for (i = 0; i < 7; i++) { - struct net2280_ep *ep = &dev->ep [i]; - struct net2280_request *req; - int t; - - if (i != 0) { - const struct usb_endpoint_descriptor *d; - - d = ep->desc; - if (!d) - continue; - t = d->bEndpointAddress; - t = scnprintf (next, size, - "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", - ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, - (t & USB_DIR_IN) ? "in" : "out", - ({ char *val; - switch (d->bmAttributes & 0x03) { - case USB_ENDPOINT_XFER_BULK: - val = "bulk"; break; - case USB_ENDPOINT_XFER_INT: - val = "intr"; break; - default: - val = "iso"; break; - } val; }), - usb_endpoint_maxp (d) & 0x1fff, - ep->dma ? "dma" : "pio", ep->fifo_size - ); - } else /* ep0 should only have one transfer queued */ - t = scnprintf (next, size, "ep0 max 64 pio %s\n", - ep->is_in ? "in" : "out"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (list_empty (&ep->queue)) { - t = scnprintf (next, size, "\t(nothing queued)\n"); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - continue; - } - list_for_each_entry (req, &ep->queue, queue) { - if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc)) - t = scnprintf (next, size, - "\treq %p len %d/%d " - "buf %p (dmacount %08x)\n", - &req->req, req->req.actual, - req->req.length, req->req.buf, - readl (&ep->dma->dmacount)); - else - t = scnprintf (next, size, - "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - - if (ep->dma) { - struct net2280_dma *td; - - td = req->td; - t = scnprintf (next, size, "\t td %08x " - " count %08x buf %08x desc %08x\n", - (u32) req->td_dma, - le32_to_cpu (td->dmacount), - le32_to_cpu (td->dmaaddr), - le32_to_cpu (td->dmadesc)); - if (t <= 0 || t > size) - goto done; - size -= t; - next += t; - } - } - } - -done: - spin_unlock_irqrestore (&dev->lock, flags); - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(queues); - - -#else - -#define device_create_file(a,b) (0) -#define device_remove_file(a,b) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -/* another driver-specific mode might be a request type doing dma - * to/from another device fifo instead of to/from memory. - */ - -static void set_fifo_mode (struct net2280 *dev, int mode) -{ - /* keeping high bits preserves BAR2 */ - writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); - - /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ - INIT_LIST_HEAD (&dev->gadget.ep_list); - list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list); - switch (mode) { - case 0: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024; - break; - case 1: - dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048; - break; - case 2: - list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); - dev->ep [1].fifo_size = 2048; - dev->ep [2].fifo_size = 1024; - break; - } - /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ - list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list); - list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); -} - -/* keeping it simple: - * - one bus driver, initted first; - * - one function driver, initted second - * - * most of the work to support multiple net2280 controllers would - * be to associate this gadget driver (yes?) with all of them, or - * perhaps to bind specific drivers to specific devices. - */ - -static void usb_reset (struct net2280 *dev) -{ - u32 tmp; - - dev->gadget.speed = USB_SPEED_UNKNOWN; - (void) readl (&dev->usb->usbctl); - - net2280_led_init (dev); - - /* disable automatic responses, and irqs */ - writel (0, &dev->usb->stdrsp); - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* clear old dma and irq state */ - for (tmp = 0; tmp < 4; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp + 1]; - - if (ep->dma) - abort_dma (ep); - } - writel (~0, &dev->regs->irqstat0), - writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), - - /* reset, and enable pci */ - tmp = readl (&dev->regs->devinit) - | (1 << PCI_ENABLE) - | (1 << FIFO_SOFT_RESET) - | (1 << USB_SOFT_RESET) - | (1 << M8051_RESET); - writel (tmp, &dev->regs->devinit); - - /* standard fifo and endpoint allocations */ - set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0); -} - -static void usb_reinit (struct net2280 *dev) -{ - u32 tmp; - int init_dma; - - /* use_dma changes are ignored till next device re-init */ - init_dma = use_dma; - - /* basic endpoint init */ - for (tmp = 0; tmp < 7; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp]; - - ep->ep.name = ep_name [tmp]; - ep->dev = dev; - ep->num = tmp; - - if (tmp > 0 && tmp <= 4) { - ep->fifo_size = 1024; - if (init_dma) - ep->dma = &dev->dma [tmp - 1]; - } else - ep->fifo_size = 64; - ep->regs = &dev->epregs [tmp]; - ep_reset (dev->regs, ep); - } - usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); - usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64); - - dev->gadget.ep0 = &dev->ep [0].ep; - dev->ep [0].stopped = 0; - INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); - - /* we want to prevent lowlevel/insecure access from the USB host, - * but erratum 0119 means this enable bit is ignored - */ - for (tmp = 0; tmp < 5; tmp++) - writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg); -} - -static void ep0_start (struct net2280 *dev) -{ - writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - , &dev->epregs [0].ep_rsp); - - /* - * hardware optionally handles a bunch of standard requests - * that the API hides from drivers anyway. have it do so. - * endpoint status/features are handled in software, to - * help pass tests for some dubious behavior. - */ - writel ( (1 << SET_TEST_MODE) - | (1 << SET_ADDRESS) - | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) - | (1 << GET_DEVICE_STATUS) - | (1 << GET_INTERFACE_STATUS) - , &dev->usb->stdrsp); - writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE) - | (1 << SELF_POWERED_USB_DEVICE) - | (1 << REMOTE_WAKEUP_SUPPORT) - | (dev->softconnect << USB_DETECT_ENABLE) - | (1 << SELF_POWERED_STATUS) - , &dev->usb->usbctl); - - /* enable irqs so we can see ep0 and general operation */ - writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE) - | (1 << ENDPOINT_0_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb0); - writel ( (1 << PCI_INTERRUPT_ENABLE) - | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) - | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE) - | (1 << VBUS_INTERRUPT_ENABLE) - | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) - | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) - , &dev->regs->pciirqenb1); - - /* don't leave any writes posted */ - (void) readl (&dev->usb->usbctl); -} - -/* 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 net2280_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - int retval; - unsigned i; - - /* insist on high speed support from the driver, since - * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) - * "must not be used in normal operation" - */ - if (!driver || driver->max_speed < USB_SPEED_HIGH - || !driver->setup) - return -EINVAL; - - dev = container_of (_gadget, struct net2280, gadget); - - for (i = 0; i < 7; i++) - dev->ep [i].irqs = 0; - - /* hook up the driver ... */ - dev->softconnect = 1; - driver->driver.bus = NULL; - dev->driver = driver; - - retval = device_create_file (&dev->pdev->dev, &dev_attr_function); - if (retval) goto err_unbind; - retval = device_create_file (&dev->pdev->dev, &dev_attr_queues); - if (retval) goto err_func; - - /* Enable force-full-speed testing mode, if desired */ - if (full_speed) - writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag); - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - */ - net2280_led_active (dev, 1); - ep0_start (dev); - - DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", - driver->driver.name, - readl (&dev->usb->usbctl), - readl (&dev->usb->stdrsp)); - - /* pci writes may still be posted */ - return 0; - -err_func: - device_remove_file (&dev->pdev->dev, &dev_attr_function); -err_unbind: - dev->driver = NULL; - return retval; -} - -static void -stop_activity (struct net2280 *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. - */ - usb_reset (dev); - for (i = 0; i < 7; i++) - nuke (&dev->ep [i]); - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - - usb_reinit (dev); -} - -static int net2280_stop(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2280 *dev; - unsigned long flags; - - dev = container_of (_gadget, struct net2280, gadget); - - spin_lock_irqsave (&dev->lock, flags); - stop_activity (dev, driver); - spin_unlock_irqrestore (&dev->lock, flags); - - dev->driver = NULL; - - net2280_led_active (dev, 0); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); - - device_remove_file (&dev->pdev->dev, &dev_attr_function); - device_remove_file (&dev->pdev->dev, &dev_attr_queues); - - DEBUG(dev, "unregistered driver '%s'\n", - driver ? driver->driver.name : ""); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. - * also works for dma-capable endpoints, in pio mode or just - * to manually advance the queue after short OUT transfers. - */ -static void handle_ep_small (struct net2280_ep *ep) -{ - struct net2280_request *req; - u32 t; - /* 0 error, 1 mid-data, 2 done */ - int mode = 1; - - if (!list_empty (&ep->queue)) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - - /* ack all, and handle what we care about */ - t = readl (&ep->regs->ep_stat); - ep->irqs++; -#if 0 - VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n", - ep->ep.name, t, req ? &req->req : 0); -#endif - if (!ep->is_in || ep->dev->pdev->device == 0x2280) - writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat); - else - /* Added for 2282 */ - writel (t, &ep->regs->ep_stat); - - /* for ep0, monitor token irqs to catch data stage length errors - * and to synchronize on status. - * - * also, to defer reporting of protocol stalls ... here's where - * data or status first appears, handling stalls here should never - * cause trouble on the host side.. - * - * control requests could be slightly faster without token synch for - * status, but status can jam up that way. - */ - if (unlikely (ep->num == 0)) { - if (ep->is_in) { - /* status; stop NAKing */ - if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - if (!req) - allow_status (ep); - mode = 2; - /* reply to extra IN data tokens with a zlp */ - } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - mode = 2; - } else if (ep->responded && - !req && !ep->stopped) - write_fifo (ep, NULL); - } - } else { - /* status; stop NAKing */ - if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt (ep); - } - mode = 2; - /* an extra OUT token is an error */ - } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) - && req - && req->req.actual == req->req.length) - || (ep->responded && !req)) { - ep->dev->protocol_stall = 1; - set_halt (ep); - ep->stopped = 1; - if (req) - done (ep, req, -EOVERFLOW); - req = NULL; - } - } - } - - if (unlikely (!req)) - return; - - /* manual DMA queue advance after short OUT */ - if (likely (ep->dma)) { - if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - u32 count; - int stopped = ep->stopped; - - /* TRANSFERRED works around OUT_DONE erratum 0112. - * we expect (N <= maxpacket) bytes; host wrote M. - * iff (M < N) we won't ever see a DMA interrupt. - */ - ep->stopped = 1; - for (count = 0; ; t = readl (&ep->regs->ep_stat)) { - - /* any preceding dma transfers must finish. - * dma handles (M >= N), may empty the queue - */ - scan_dma_completions (ep); - if (unlikely (list_empty (&ep->queue) - || ep->out_overflow)) { - req = NULL; - break; - } - req = list_entry (ep->queue.next, - struct net2280_request, queue); - - /* here either (M < N), a "real" short rx; - * or (M == N) and the queue didn't empty - */ - if (likely (t & (1 << FIFO_EMPTY))) { - count = readl (&ep->dma->dmacount); - count &= DMA_BYTE_COUNT_MASK; - if (readl (&ep->dma->dmadesc) - != req->td_dma) - req = NULL; - break; - } - udelay(1); - } - - /* stop DMA, leave ep NAKing */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma (ep->dma); - - if (likely (req)) { - req->td->dmacount = 0; - t = readl (&ep->regs->ep_avail); - dma_done (ep, req, count, - (ep->out_overflow || t) - ? -EOVERFLOW : 0); - } - - /* also flush to prevent erratum 0106 trouble */ - if (unlikely (ep->out_overflow - || (ep->dev->chiprev == 0x0100 - && ep->dev->gadget.speed - == USB_SPEED_FULL))) { - out_flush (ep); - ep->out_overflow = 0; - } - - /* (re)start dma if needed, stop NAKing */ - ep->stopped = stopped; - if (!list_empty (&ep->queue)) - restart_dma (ep); - } else - DEBUG (ep->dev, "%s dma ep_stat %08x ??\n", - ep->ep.name, t); - return; - - /* data packet(s) received (in the fifo, OUT) */ - } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) { - if (read_fifo (ep, req) && ep->num != 0) - mode = 2; - - /* data packet(s) transmitted (IN) */ - } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) { - unsigned len; - - len = req->req.length - req->req.actual; - if (len > ep->ep.maxpacket) - len = ep->ep.maxpacket; - req->req.actual += len; - - /* if we wrote it all, we're usually done */ - if (req->req.actual == req->req.length) { - if (ep->num == 0) { - /* send zlps until the status stage */ - } else if (!req->req.zero || len != ep->ep.maxpacket) - mode = 2; - } - - /* there was nothing to do ... */ - } else if (mode == 1) - return; - - /* done */ - if (mode == 2) { - /* stream endpoints often resubmit/unlink in completion */ - done (ep, req, 0); - - /* maybe advance queue to next request */ - if (ep->num == 0) { - /* NOTE: net2280 could let gadget driver start the - * status stage later. since not all controllers let - * them control that, the api doesn't (yet) allow it. - */ - if (!ep->stopped) - allow_status (ep); - req = NULL; - } else { - if (!list_empty (&ep->queue) && !ep->stopped) - req = list_entry (ep->queue.next, - struct net2280_request, queue); - else - req = NULL; - if (req && !ep->is_in) - stop_out_naking (ep); - } - } - - /* is there a buffer for the next packet? - * for best streaming performance, make sure there is one. - */ - if (req && !ep->stopped) { - - /* load IN fifo with next packet (may be zlp) */ - if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) - write_fifo (ep, &req->req); - } -} - -static struct net2280_ep * -get_ep_by_addr (struct net2280 *dev, u16 wIndex) -{ - struct net2280_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; -} - -static void handle_stat0_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 num, scratch; - - /* most of these don't need individual acks */ - stat &= ~(1 << INTA_ASSERTED); - if (!stat) - return; - // DEBUG (dev, "irqstat0 %04x\n", stat); - - /* starting a control request? */ - if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) { - union { - u32 raw [2]; - struct usb_ctrlrequest r; - } u; - int tmp; - struct net2280_request *req; - - if (dev->gadget.speed == USB_SPEED_UNKNOWN) { - if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) - dev->gadget.speed = USB_SPEED_HIGH; - else - dev->gadget.speed = USB_SPEED_FULL; - net2280_led_speed (dev, dev->gadget.speed); - DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); - } - - ep = &dev->ep [0]; - ep->irqs++; - - /* make sure any leftover request state is cleared */ - stat &= ~(1 << ENDPOINT_0_INTERRUPT); - while (!list_empty (&ep->queue)) { - req = list_entry (ep->queue.next, - struct net2280_request, queue); - done (ep, req, (req->req.actual == req->req.length) - ? 0 : -EPROTO); - } - ep->stopped = 0; - dev->protocol_stall = 0; - - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - u.raw [0] = readl (&dev->usb->setup0123); - u.raw [1] = readl (&dev->usb->setup4567); - - cpu_to_le32s (&u.raw [0]); - cpu_to_le32s (&u.raw [1]); - - tmp = 0; - -#define w_value le16_to_cpu(u.r.wValue) -#define w_index le16_to_cpu(u.r.wIndex) -#define w_length le16_to_cpu(u.r.wLength) - - /* ack the irq */ - writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); - stat ^= (1 << SETUP_PACKET_INTERRUPT); - - /* watch control traffic at the token level, and force - * synchronization before letting the status stage happen. - * FIXME ignore tokens we'll NAK, until driver responds. - * that'll mean a lot less irqs for some drivers. - */ - ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; - if (ep->is_in) { - scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - stop_out_naking (ep); - } else - scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT); - writel (scratch, &dev->epregs [0].ep_irqenb); - - /* we made the hardware handle most lowlevel requests; - * everything else goes uplevel to the gadget code. - */ - ep->responded = 1; - switch (u.r.bRequest) { - case USB_REQ_GET_STATUS: { - struct net2280_ep *e; - __le32 status; - - /* hw handles device and interface status */ - if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) - goto delegate; - if ((e = get_ep_by_addr (dev, w_index)) == NULL - || w_length > 2) - goto do_stall; - - if (readl (&e->regs->ep_rsp) - & (1 << SET_ENDPOINT_HALT)) - status = cpu_to_le32 (1); - else - status = cpu_to_le32 (0); - - /* don't bother with a request object! */ - writel (0, &dev->epregs [0].ep_irqenb); - set_fifo_bytecount (ep, w_length); - writel ((__force u32)status, &dev->epregs [0].ep_data); - allow_status (ep); - VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); - goto next_endpoints; - } - break; - case USB_REQ_CLEAR_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->wedged) { - VDEBUG(dev, "%s wedged, halt not cleared\n", - ep->ep.name); - } else { - VDEBUG(dev, "%s clear halt\n", ep->ep.name); - clear_halt(e); - } - allow_status (ep); - goto next_endpoints; - } - break; - case USB_REQ_SET_FEATURE: { - struct net2280_ep *e; - - /* hw handles device features */ - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (w_value != USB_ENDPOINT_HALT - || w_length != 0) - goto do_stall; - if ((e = get_ep_by_addr (dev, w_index)) == NULL) - goto do_stall; - if (e->ep.name == ep0name) - goto do_stall; - set_halt (e); - allow_status (ep); - VDEBUG (dev, "%s set halt\n", ep->ep.name); - goto next_endpoints; - } - break; - default: -delegate: - VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x " - "ep_cfg %08x\n", - u.r.bRequestType, u.r.bRequest, - w_value, w_index, w_length, - readl (&ep->regs->ep_cfg)); - ep->responded = 0; - 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: - VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n", - u.r.bRequestType, u.r.bRequest, tmp); - dev->protocol_stall = 1; - } - - /* some in/out token irq should follow; maybe stall then. - * driver must queue a request (even zlp) or halt ep0 - * before the host times out. - */ - } - -#undef w_value -#undef w_index -#undef w_length - -next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = 1 << num; - if ((scratch & t) == 0) - continue; - scratch ^= t; - - ep = &dev->ep [num]; - handle_ep_small (ep); - } - - if (stat) - DEBUG (dev, "unhandled irqstat0 %08x\n", stat); -} - -#define DMA_INTERRUPTS ( \ - (1 << DMA_D_INTERRUPT) \ - | (1 << DMA_C_INTERRUPT) \ - | (1 << DMA_B_INTERRUPT) \ - | (1 << DMA_A_INTERRUPT)) -#define PCI_ERROR_INTERRUPTS ( \ - (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \ - | (1 << PCI_RETRY_ABORT_INTERRUPT)) - -static void handle_stat1_irqs (struct net2280 *dev, u32 stat) -{ - struct net2280_ep *ep; - u32 tmp, num, mask, scratch; - - /* after disconnect there's nothing else to do! */ - tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); - mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); - - /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. - * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and - * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT - * only indicates a change in the reset state). - */ - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) - && ((readl (&dev->usb->usbstat) & mask) - == 0)) - || ((readl (&dev->usb->usbctl) - & (1 << VBUS_PIN)) == 0) - ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { - DEBUG (dev, "disconnect %s\n", - dev->driver->driver.name); - stop_activity (dev, dev->driver); - ep0_start (dev); - return; - } - stat &= ~tmp; - - /* vBUS can bounce ... one of many reasons to ignore the - * notion of hotplug events on bus connect/disconnect! - */ - if (!stat) - return; - } - - /* NOTE: chip stays in PCI D0 state for now, but it could - * enter D1 to save more power - */ - tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); - if (stat & tmp) { - writel (tmp, &dev->regs->irqstat1); - if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { - if (dev->driver->suspend) - dev->driver->suspend (&dev->gadget); - if (!enable_suspend) - stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); - } else { - if (dev->driver->resume) - dev->driver->resume (&dev->gadget); - /* at high speed, note erratum 0133 */ - } - stat &= ~tmp; - } - - /* clear any other status/irqs */ - if (stat) - writel (stat, &dev->regs->irqstat1); - - /* some status we can just ignore */ - if (dev->pdev->device == 0x2280) - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << SUSPEND_REQUEST_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_INTERRUPT)); - else - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << RESUME_INTERRUPT) - | (1 << SOF_DOWN_INTERRUPT) - | (1 << SOF_INTERRUPT)); - - if (!stat) - return; - // DEBUG (dev, "irqstat1 %08x\n", stat); - - /* DMA status, for ep-{a,b,c,d} */ - scratch = stat & DMA_INTERRUPTS; - stat &= ~DMA_INTERRUPTS; - scratch >>= 9; - for (num = 0; scratch; num++) { - struct net2280_dma_regs __iomem *dma; - - tmp = 1 << num; - if ((tmp & scratch) == 0) - continue; - scratch ^= tmp; - - ep = &dev->ep [num + 1]; - dma = ep->dma; - - if (!dma) - continue; - - /* clear ep's dma status */ - tmp = readl (&dma->dmastat); - writel (tmp, &dma->dmastat); - - /* chaining should stop on abort, short OUT from fifo, - * or (stat0 codepath) short OUT transfer. - */ - if (!use_dma_chaining) { - if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) - == 0) { - DEBUG (ep->dev, "%s no xact done? %08x\n", - ep->ep.name, tmp); - continue; - } - stop_dma (ep->dma); - } - - /* OUT transfers terminate when the data from the - * host is in our memory. Process whatever's done. - * On this path, we know transfer's last packet wasn't - * less than req->length. NAK_OUT_PACKETS may be set, - * or the FIFO may already be holding new packets. - * - * IN transfers can linger in the FIFO for a very - * long time ... we ignore that for now, accounting - * precisely (like PIO does) needs per-packet irqs - */ - scan_dma_completions (ep); - - /* disable dma on inactive queues; else maybe restart */ - if (list_empty (&ep->queue)) { - if (use_dma_chaining) - stop_dma (ep->dma); - } else { - tmp = readl (&dma->dmactl); - if (!use_dma_chaining - || (tmp & (1 << DMA_ENABLE)) == 0) - restart_dma (ep); - else if (ep->is_in && use_dma_chaining) { - struct net2280_request *req; - __le32 dmacount; - - /* the descriptor at the head of the chain - * may still have VALID_BIT clear; that's - * used to trigger changing DMA_FIFO_VALIDATE - * (affects automagic zlp writes). - */ - req = list_entry (ep->queue.next, - struct net2280_request, queue); - dmacount = req->td->dmacount; - dmacount &= cpu_to_le32 ( - (1 << VALID_BIT) - | DMA_BYTE_COUNT_MASK); - if (dmacount && (dmacount & valid_bit) == 0) - restart_dma (ep); - } - } - ep->irqs++; - } - - /* NOTE: there are other PCI errors we might usefully notice. - * if they appear very often, here's where to try recovering. - */ - if (stat & PCI_ERROR_INTERRUPTS) { - ERROR (dev, "pci dma error; stat %08x\n", stat); - stat &= ~PCI_ERROR_INTERRUPTS; - /* these are fatal errors, but "maybe" they won't - * happen again ... - */ - stop_activity (dev, dev->driver); - ep0_start (dev); - stat = 0; - } - - if (stat) - DEBUG (dev, "unhandled irqstat1 %08x\n", stat); -} - -static irqreturn_t net2280_irq (int irq, void *_dev) -{ - struct net2280 *dev = _dev; - - /* shared interrupt, not ours */ - if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) - return IRQ_NONE; - - spin_lock (&dev->lock); - - /* handle disconnect, dma, and more */ - handle_stat1_irqs (dev, readl (&dev->regs->irqstat1)); - - /* control requests and PIO */ - handle_stat0_irqs (dev, readl (&dev->regs->irqstat0)); - - spin_unlock (&dev->lock); - - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -static void gadget_release (struct device *_dev) -{ - struct net2280 *dev = dev_get_drvdata (_dev); - - kfree (dev); -} - -/* tear down the binding between this driver and the pci device */ - -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() */ - net2280_led_shutdown (dev); - if (dev->requests) { - int i; - for (i = 1; i < 5; i++) { - if (!dev->ep [i].dummy) - continue; - pci_pool_free (dev->requests, dev->ep [i].dummy, - dev->ep [i].td_dma); - } - pci_pool_destroy (dev->requests); - } - if (dev->got_irq) - free_irq (pdev->irq, dev); - if (dev->regs) - iounmap (dev->regs); - if (dev->region) - release_mem_region (pci_resource_start (pdev, 0), - pci_resource_len (pdev, 0)); - if (dev->enabled) - pci_disable_device (pdev); - device_remove_file (&pdev->dev, &dev_attr_registers); - - INFO (dev, "unbind\n"); -} - -/* wrap this driver around the specified device, but - * don't respond over USB until a gadget driver binds to us. - */ - -static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net2280 *dev; - unsigned long resource, len; - void __iomem *base = NULL; - int retval, i; - - /* alloc, and start init */ - dev = kzalloc (sizeof *dev, GFP_KERNEL); - if (dev == NULL){ - retval = -ENOMEM; - goto done; - } - - pci_set_drvdata (pdev, dev); - spin_lock_init (&dev->lock); - dev->pdev = pdev; - dev->gadget.ops = &net2280_ops; - dev->gadget.max_speed = USB_SPEED_HIGH; - - /* the "gadget" abstracts/virtualizes the controller */ - dev->gadget.name = driver_name; - - /* now all the pci goodies ... */ - if (pci_enable_device (pdev) < 0) { - retval = -ENODEV; - goto done; - } - dev->enabled = 1; - - /* BAR 0 holds all the registers - * BAR 1 is 8051 memory; unused here (note erratum 0103) - * BAR 2 is fifo memory; unused here - */ - resource = pci_resource_start (pdev, 0); - len = pci_resource_len (pdev, 0); - if (!request_mem_region (resource, len, driver_name)) { - DEBUG (dev, "controller already in use\n"); - retval = -EBUSY; - goto done; - } - dev->region = 1; - - /* FIXME provide firmware download interface to put - * 8051 code into the chip, e.g. to turn on PCI PM. - */ - - base = ioremap_nocache (resource, len); - if (base == NULL) { - DEBUG (dev, "can't map memory\n"); - retval = -EFAULT; - goto done; - } - dev->regs = (struct net2280_regs __iomem *) base; - dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); - dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); - dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); - dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); - dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); - - /* put into initial config, link up all endpoints */ - writel (0, &dev->usb->usbctl); - usb_reset (dev); - usb_reinit (dev); - - /* irq setup after old hardware is cleaned up */ - if (!pdev->irq) { - ERROR (dev, "No IRQ. Check PCI setup!\n"); - retval = -ENODEV; - goto done; - } - - if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev) - != 0) { - ERROR (dev, "request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto done; - } - dev->got_irq = 1; - - /* DMA setup */ - /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ - dev->requests = pci_pool_create ("requests", pdev, - sizeof (struct net2280_dma), - 0 /* no alignment requirements */, - 0 /* or page-crossing issues */); - if (!dev->requests) { - DEBUG (dev, "can't get request pool\n"); - retval = -ENOMEM; - goto done; - } - for (i = 1; i < 5; i++) { - struct net2280_dma *td; - - td = pci_pool_alloc (dev->requests, GFP_KERNEL, - &dev->ep [i].td_dma); - if (!td) { - DEBUG (dev, "can't get dummy %d\n", i); - retval = -ENOMEM; - goto done; - } - td->dmacount = 0; /* not VALID */ - td->dmadesc = td->dmaaddr; - dev->ep [i].dummy = td; - } - - /* enable lower-overhead pci memory bursts during DMA */ - writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) - // 256 write retries may not be enough... - // | (1 << PCI_RETRY_ABORT_ENABLE) - | (1 << DMA_READ_MULTIPLE_ENABLE) - | (1 << DMA_READ_LINE_ENABLE) - , &dev->pci->pcimstctl); - /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ - pci_set_master (pdev); - pci_try_set_mwi (pdev); - - /* ... also flushes any posted pci writes */ - dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; - - /* done */ - INFO (dev, "%s\n", driver_desc); - INFO (dev, "irq %d, pci mem %p, chip rev %04x\n", - pdev->irq, base, dev->chiprev); - INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled"); - retval = device_create_file (&pdev->dev, &dev_attr_registers); - if (retval) goto done; - - retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, - gadget_release); - if (retval) - goto done; - return 0; - -done: - if (dev) - net2280_remove (pdev); - return retval; -} - -/* make sure the board is quiescent; otherwise it will continue - * generating IRQs across the upcoming reboot. - */ - -static void net2280_shutdown (struct pci_dev *pdev) -{ - struct net2280 *dev = pci_get_drvdata (pdev); - - /* disable IRQs */ - writel (0, &dev->regs->pciirqenb0); - writel (0, &dev->regs->pciirqenb1); - - /* disable the pullup so the host will think we're gone */ - writel (0, &dev->usb->usbctl); - - /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); -} - - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id pci_ids [] = { { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2280, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x17cc, - .device = 0x2282, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - -}, { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE (pci, pci_ids); - -/* pci driver glue; this is a "new style" PCI driver module */ -static struct pci_driver net2280_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = net2280_probe, - .remove = net2280_remove, - .shutdown = net2280_shutdown, - - /* FIXME add power management support */ -}; - -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); -MODULE_LICENSE ("GPL"); - -static int __init init (void) -{ - if (!use_dma) - use_dma_chaining = 0; - return pci_register_driver (&net2280_pci_driver); -} -module_init (init); - -static void __exit cleanup (void) -{ - pci_unregister_driver (&net2280_pci_driver); -} -module_exit (cleanup); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h deleted file mode 100644 index a844be0d683a..000000000000 --- a/drivers/usb/gadget/net2280.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * NetChip 2280 high/full speed USB device controller. - * Unlike many such controllers, this one talks PCI. - */ - -/* - * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) - * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/usb/net2280.h> - -/*-------------------------------------------------------------------------*/ - -#ifdef __KERNEL__ - -/* indexed registers [11.10] are accessed indirectly - * caller must own the device lock. - */ - -static inline u32 -get_idx_reg (struct net2280_regs __iomem *regs, u32 index) -{ - writel (index, ®s->idxaddr); - /* NOTE: synchs device/cpu memory views */ - return readl (®s->idxdata); -} - -static inline void -set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value) -{ - writel (index, ®s->idxaddr); - writel (value, ®s->idxdata); - /* posted, may not be visible yet */ -} - -#endif /* __KERNEL__ */ - - -#define REG_DIAG 0x0 -#define RETRY_COUNTER 16 -#define FORCE_PCI_SERR 11 -#define FORCE_PCI_INTERRUPT 10 -#define FORCE_USB_INTERRUPT 9 -#define FORCE_CPU_INTERRUPT 8 -#define ILLEGAL_BYTE_ENABLES 5 -#define FAST_TIMES 4 -#define FORCE_RECEIVE_ERROR 2 -#define FORCE_TRANSMIT_CRC_ERROR 0 -#define REG_FRAME 0x02 /* from last sof */ -#define REG_CHIPREV 0x03 /* in bcd */ -#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ - -#define CHIPREV_1 0x0100 -#define CHIPREV_1A 0x0110 - -#ifdef __KERNEL__ - -/* ep a-f highspeed and fullspeed maxpacket, addresses - * computed from ep->num - */ -#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \ - (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1)) - -/*-------------------------------------------------------------------------*/ - -/* [8.3] for scatter/gather i/o - * use struct net2280_dma_regs bitfields - */ -struct net2280_dma { - __le32 dmacount; - __le32 dmaaddr; /* the buffer */ - __le32 dmadesc; /* next dma descriptor */ - __le32 _reserved; -} __attribute__ ((aligned (16))); - -/*-------------------------------------------------------------------------*/ - -/* DRIVER DATA STRUCTURES and UTILITIES */ - -struct net2280_ep { - struct usb_ep ep; - struct net2280_ep_regs __iomem *regs; - struct net2280_dma_regs __iomem *dma; - struct net2280_dma *dummy; - dma_addr_t td_dma; /* of dummy */ - struct net2280 *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, - in_fifo_validate : 1, - out_overflow : 1, - stopped : 1, - wedged : 1, - is_in : 1, - is_iso : 1, - responded : 1; -}; - -static inline void allow_status (struct net2280_ep *ep) -{ - /* ep0 only */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - | (1 << CLEAR_NAK_OUT_PACKETS) - | (1 << CLEAR_NAK_OUT_PACKETS_MODE) - , &ep->regs->ep_rsp); - ep->stopped = 1; -} - -/* count (<= 4) bytes in the next fifo write will be valid */ -static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) -{ - writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); -} - -struct net2280_request { - struct usb_request req; - struct net2280_dma *td; - dma_addr_t td_dma; - struct list_head queue; - unsigned mapped : 1, - valid : 1; -}; - -struct net2280 { - /* each pci device provides one gadget, several endpoints */ - struct usb_gadget gadget; - spinlock_t lock; - struct net2280_ep ep [7]; - struct usb_gadget_driver *driver; - unsigned enabled : 1, - protocol_stall : 1, - softconnect : 1, - got_irq : 1, - region : 1; - u16 chiprev; - - /* pci state used to access those endpoints */ - struct pci_dev *pdev; - struct net2280_regs __iomem *regs; - struct net2280_usb_regs __iomem *usb; - struct net2280_pci_regs __iomem *pci; - struct net2280_dma_regs __iomem *dma; - struct net2280_dep_regs __iomem *dep; - struct net2280_ep_regs __iomem *epregs; - - struct pci_pool *requests; - // statistics... -}; - -static inline void set_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) - /* set NAK_OUT for erratum 0114 */ - | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) - | (1 << SET_ENDPOINT_HALT) - , &ep->regs->ep_rsp); -} - -static inline void clear_halt (struct net2280_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - writel ( (1 << CLEAR_ENDPOINT_HALT) - | (1 << CLEAR_ENDPOINT_TOGGLE) - /* unless the gadget driver left a short packet in the - * fifo, this reverses the erratum 0114 workaround. - */ - | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS) - , &ep->regs->ep_rsp); -} - -#ifdef USE_RDK_LEDS - -static inline void net2280_led_init (struct net2280 *dev) -{ - /* LED3 (green) is on during USB activity. note erratum 0113. */ - writel ((1 << GPIO3_LED_SELECT) - | (1 << GPIO3_OUTPUT_ENABLE) - | (1 << GPIO2_OUTPUT_ENABLE) - | (1 << GPIO1_OUTPUT_ENABLE) - | (1 << GPIO0_OUTPUT_ENABLE) - , &dev->regs->gpioctl); -} - -/* indicate speed with bi-color LED 0/1 */ -static inline -void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed) -{ - u32 val = readl (&dev->regs->gpioctl); - switch (speed) { - case USB_SPEED_HIGH: /* green */ - val &= ~(1 << GPIO0_DATA); - val |= (1 << GPIO1_DATA); - break; - case USB_SPEED_FULL: /* red */ - val &= ~(1 << GPIO1_DATA); - val |= (1 << GPIO0_DATA); - break; - default: /* (off/black) */ - val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA)); - break; - } - writel (val, &dev->regs->gpioctl); -} - -/* indicate power with LED 2 */ -static inline void net2280_led_active (struct net2280 *dev, int is_active) -{ - u32 val = readl (&dev->regs->gpioctl); - - // FIXME this LED never seems to turn on. - if (is_active) - val |= GPIO2_DATA; - else - val &= ~GPIO2_DATA; - writel (val, &dev->regs->gpioctl); -} -static inline void net2280_led_shutdown (struct net2280 *dev) -{ - /* turn off all four GPIO*_DATA bits */ - writel (readl (&dev->regs->gpioctl) & ~0x0f, - &dev->regs->gpioctl); -} - -#else - -#define net2280_led_init(dev) do { } while (0) -#define net2280_led_speed(dev, speed) do { } while (0) -#define net2280_led_shutdown(dev) do { } while (0) - -#endif - -/*-------------------------------------------------------------------------*/ - -#define xprintk(dev,level,fmt,args...) \ - printk(level "%s %s: " fmt , driver_name , \ - pci_name(dev->pdev) , ## args) - -#ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ - xprintk(dev , KERN_DEBUG , fmt , ## args) -#else -#define DEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE -#define VDEBUG DEBUG -#else -#define VDEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* VERBOSE */ - -#define ERROR(dev,fmt,args...) \ - xprintk(dev , KERN_ERR , fmt , ## args) -#define WARNING(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) -#define INFO(dev,fmt,args...) \ - xprintk(dev , KERN_INFO , fmt , ## args) - -/*-------------------------------------------------------------------------*/ - -static inline void start_out_naking (struct net2280_ep *ep) -{ - /* NOTE: hardware races lurk here, and PING protocol issues */ - writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); - /* synch with device */ - readl (&ep->regs->ep_rsp); -} - -#ifdef DEBUG -static inline void assert_out_naking (struct net2280_ep *ep, const char *where) -{ - u32 tmp = readl (&ep->regs->ep_stat); - - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - DEBUG (ep->dev, "%s %s %08x !NAK\n", - ep->ep.name, where, tmp); - writel ((1 << SET_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } -} -#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__func__) -#else -#define ASSERT_OUT_NAKING(ep) do {} while (0) -#endif - -static inline void stop_out_naking (struct net2280_ep *ep) -{ - u32 tmp; - - tmp = readl (&ep->regs->ep_stat); - if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -} - -#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h index ea5cf8c2da28..947b7ddff691 100644 --- a/drivers/usb/gadget/u_os_desc.h +++ b/drivers/usb/gadget/u_os_desc.h @@ -35,27 +35,63 @@ #define USB_EXT_PROP_UNICODE_LINK 6 #define USB_EXT_PROP_UNICODE_MULTI 7 +static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset) +{ + return buf + offset; +} + +static inline u8 *usb_ext_prop_size_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE); +} + +static inline u8 *usb_ext_prop_type_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE); +} + +static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH); +} + +static inline u8 *usb_ext_prop_name_ptr(u8 *buf) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME); +} + +static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, + USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off); +} + +static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off) +{ + return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off); +} + static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) { - put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]); + put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf)); } static inline void usb_ext_prop_put_type(u8 *buf, int type) { - put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]); + put_unaligned_le32(type, usb_ext_prop_type_ptr(buf)); } static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) { int result; - put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]); + put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf)); result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2); + (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2); if (result < 0) return result; - put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]); + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]); return pnl; } @@ -63,26 +99,23 @@ static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, int data_len) { - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len); + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); + memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len); } static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, int data_len) { int result; - put_unaligned_le32(data_len, - &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); - + put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl)); result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, - (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], + (wchar_t *) usb_ext_prop_data_ptr(buf, pnl), data_len - 2); if (result < 0) return result; put_unaligned_le16(0, - &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]); + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]); return data_len; } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig new file mode 100644 index 000000000000..34ebaa68504c --- /dev/null +++ b/drivers/usb/gadget/udc/Kconfig @@ -0,0 +1,386 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +# +# USB Peripheral Controller Support +# +# The order here is alphabetical, except that integrated controllers go +# before discrete ones so they will be the initial/default value: +# - integrated/SOC controllers first +# - licensed IP used in both SOC and discrete versions +# - discrete ones (including all PCI-only controllers) +# - debug/dummy gadget+hcd is last. +# +menu "USB Peripheral Controller" + +# +# Integrated controllers +# + +config USB_AT91 + tristate "Atmel AT91 USB Device Port" + depends on ARCH_AT91 + help + Many Atmel AT91 processors (such as the AT91RM2000) have a + full speed USB Device Port with support for five configurable + endpoints (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "at91_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_LPC32XX + tristate "LPC32XX USB Peripheral Controller" + depends on ARCH_LPC32XX && I2C + select USB_ISP1301 + help + This option selects the USB device controller in the LPC32xx SoC. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "lpc32xx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_ATMEL_USBA + tristate "Atmel USBA" + depends on AVR32 || ARCH_AT91 + help + USBA is the integrated high-speed USB Device controller on + the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. + +config USB_BCM63XX_UDC + tristate "Broadcom BCM63xx Peripheral Controller" + depends on BCM63XX + help + Many Broadcom BCM63xx chipsets (such as the BCM6328) have a + high speed USB Device Port with support for four fixed endpoints + (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "bcm63xx_udc". + +config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" + depends on FSL_SOC || ARCH_MXC + select USB_FSL_MPH_DR_OF if OF + help + Some of Freescale PowerPC and i.MX processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. + + The number of programmable endpoints is different through + SOC revisions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fsl_usb2_udc" and force + all gadget drivers to also be dynamically linked. + +config USB_FUSB300 + tristate "Faraday FUSB300 USB Peripheral Controller" + depends on !PHYS_ADDR_T_64BIT && HAS_DMA + help + Faraday usb device controller FUSB300 driver + +config USB_FOTG210_UDC + depends on HAS_DMA + tristate "Faraday FOTG210 USB Peripheral Controller" + help + Faraday USB2.0 OTG controller which can be configured as + high speed or full speed USB device. This driver supppors + Bulk Transfer so far. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fotg210_udc". + +config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + VHDL IP core library. + +config USB_OMAP + tristate "OMAP USB Device Controller" + depends on ARCH_OMAP1 + depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3) + help + Many Texas Instruments OMAP processors have flexible full + speed USB device controllers, with support for up to 30 + endpoints (plus endpoint zero). This driver supports the + controller in the OMAP 1611, and should work with controllers + in other OMAP processors too, given minor tweaks. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "omap_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA25X + tristate "PXA 25x or IXP 4xx" + depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX + help + Intel's PXA 25x series XScale ARM-5TE processors include + an integrated full speed USB 1.1 device controller. The + controller in the IXP 4xx series is register-compatible. + + It has fifteen fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa25x_udc" and force all + gadget drivers to also be dynamically linked. + +# 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_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_R8A66597 + tristate "Renesas R8A66597 USB Peripheral Controller" + depends on HAS_DMA + help + R8A66597 is a discrete USB host and peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has nine configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "r8a66597_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_RENESAS_USBHS_UDC + tristate 'Renesas USBHS controller' + depends on USB_RENESAS_USBHS + help + Renesas USBHS is a discrete USB host and peripheral controller chip + that supports both full and high speed USB 2.0 data transfers. + It has nine or more configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "renesas_usbhs" and force all + gadget drivers to also be dynamically linked. + +config USB_PXA27X + tristate "PXA 27x" + help + Intel's PXA 27x series XScale ARM v5TE processors include + an integrated full speed USB 1.1 device controller. + + It has up to 23 endpoints, as well as endpoint zero (for + control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "pxa27x_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_S3C2410 + tristate "S3C2410 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2410 is an ARM-4 processor with an integrated + full speed USB 1.1 device controller. It has 4 configurable + endpoints, as well as endpoint zero (for control transfers). + + This driver has been tested on the S3C2410, S3C2412, and + S3C2440 processors. + +config USB_S3C2410_DEBUG + boolean "S3C2410 udc debug messages" + depends on USB_S3C2410 + +config USB_S3C_HSUDC + tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C24XX + help + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. + + This driver has been tested on S3C2416 and S3C2450 processors. + +config USB_MV_UDC + tristate "Marvell USB2.0 Device Controller" + depends on HAS_DMA + help + Marvell Socs (including PXA and MMP series) include a high speed + USB2.0 OTG controller, which can be configured as high speed or + full speed USB peripheral. + +config USB_MV_U3D + depends on HAS_DMA + tristate "MARVELL PXA2128 USB 3.0 controller" + help + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. + +# +# Controllers available in both integrated and discrete versions +# + +config USB_M66592 + tristate "Renesas M66592 USB Peripheral Controller" + help + M66592 is a discrete USB peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has seven configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "m66592_udc" and force all + gadget drivers to also be dynamically linked. + +# +# Controllers available only in discrete form (and all PCI controllers) +# + +config USB_AMD5536UDC + tristate "AMD5536 UDC" + depends on PCI + help + The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. + It is a USB Highspeed DMA capable USB device controller. Beside ep0 + it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). + The UDC port supports OTG operation, and may be used as a host port + if it's not being used to implement peripheral or OTG roles. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "amd5536udc" and force all + gadget drivers to also be dynamically linked. + +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 + QE/CPM2 USB controller, which support device mode with 4 + programmable endpoints. This driver supports the + controller in the MPC8360 and MPC8272, and should work with + controllers having QE or CPM2, given minor tweaks. + + Set CONFIG_USB_GADGET to "m" to build this driver as a + dynamically linked module called "fsl_qe_udc". + +config USB_NET2272 + tristate "PLX NET2272" + 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 && HAS_DMA + help + The NET2272 part can optionally support an external DMA + controller, but your board has to have support in the + driver itself. + + If unsure, say "N" here. The driver works fine in PIO mode. + +config USB_NET2280 + tristate "NetChip 228x / PLX USB338x" + depends on PCI + help + NetChip 2280 / 2282 is a PCI based USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + + It has six configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + PLX 3380 / 3382 is a PCIe based USB peripheral controller which + supports full, high speed USB 2.0 and super speed USB 3.0 + data transfers. + + It has eight configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "net2280" and force all + gadget drivers to also be dynamically linked. + +config USB_GOKU + tristate "Toshiba TC86C001 'Goku-S'" + depends on PCI + help + The Toshiba TC86C001 is a PCI device which includes controllers + for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). + + The device controller has three configurable (bulk or interrupt) + endpoints, plus endpoint zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "goku_udc" and to force all + gadget drivers to also be dynamically linked. + +config USB_EG20T + tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" + depends on PCI + help + This is a USB device driver for EG20T PCH. + EG20T PCH is the platform controller hub that is used in Intel's + general embedded platform. EG20T PCH has USB device interface. + Using this interface, it is able to access system devices connected + to USB device. + This driver enables USB device function. + USB device is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + This driver supports both control transfer and bulk transfer modes. + This driver dose not support interrupt transfer or isochronous + transfer modes. + + This driver also can be used for LAPIS Semiconductor's ML7213 which is + for IVI(In-Vehicle Infotainment) use. + ML7831 is for general purpose use. + ML7213/ML7831 is companion chip for Intel Atom E6xx series. + ML7213/ML7831 is completely compatible for Intel EG20T PCH. + + This driver can be used with Intel's Quark X1000 SOC platform +# +# LAST -- dummy/emulated controller +# + +config USB_DUMMY_HCD + tristate "Dummy HCD (DEVELOPMENT)" + depends on USB=y || (USB=m && USB_GADGET=m) + 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. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "dummy_hcd" and force all + gadget drivers to also be dynamically linked. + +# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears +# first and will be selected by default. + +endmenu diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile new file mode 100644 index 000000000000..4096122bb283 --- /dev/null +++ b/drivers/usb/gadget/udc/Makefile @@ -0,0 +1,31 @@ +# +# USB peripheral controller drivers +# +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 +obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_GOKU) += goku_udc.o +obj-$(CONFIG_USB_OMAP) += omap_udc.o +obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o +obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o +obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o +fsl_usb2_udc-y := fsl_udc_core.o +fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o +obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o +obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o +obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o +obj-$(CONFIG_USB_EG20T) += pch_udc.o +obj-$(CONFIG_USB_MV_UDC) += mv_udc.o +mv_udc-y := mv_udc_core.o +obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o +obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o +obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +obj-$(CONFIG_USB_GR_UDC) += gr_udc.o diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index 41b062eb4de0..41b062eb4de0 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 6744d3b83109..6744d3b83109 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index cfd18bcca723..cfd18bcca723 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 017524663381..017524663381 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 76023ce449a3..c9fe67e29d35 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; - for (i = 1; i < USBA_NR_DMAS; i++) + for (i = 1; i <= USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); } @@ -1979,7 +1979,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, return eps; } -static int __init usba_udc_probe(struct platform_device *pdev) +static int usba_udc_probe(struct platform_device *pdev) { struct resource *regs, *fifo; struct clk *pclk, *hclk; diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e8cb02..a70706e8cb02 100644 --- a/drivers/usb/gadget/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index e969eb809a85..e969eb809a85 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 2b54955d3166..2b54955d3166 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index e143d69f6017..e143d69f6017 100644 --- a/drivers/usb/gadget/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/udc/fotg210.h index bbf991bcbe7c..bbf991bcbe7c 100644 --- a/drivers/usb/gadget/fotg210.h +++ b/drivers/usb/gadget/udc/fotg210.h diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c index 9b140fc4d3bc..f16e149c5b3e 100644 --- a/drivers/usb/gadget/fsl_mxc_udc.c +++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c @@ -18,6 +18,8 @@ #include <linux/platform_device.h> #include <linux/io.h> +#include "fsl_usb2_udc.h" + static struct clk *mxc_ahb_clk; static struct clk *mxc_per_clk; static struct clk *mxc_ipg_clk; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index ad5483335167..732430804841 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -2539,7 +2539,7 @@ static int qe_udc_probe(struct platform_device *ofdev) goto err2; /* create a buf for ZLP send, need to remain zeroed */ - udc->nullbuf = kzalloc(256, GFP_KERNEL); + udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL); if (udc->nullbuf == NULL) { dev_err(udc->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; @@ -2547,10 +2547,10 @@ static int qe_udc_probe(struct platform_device *ofdev) } /* buffer for data of get_status request */ - udc->statusbuf = kzalloc(2, GFP_KERNEL); + udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL); if (udc->statusbuf == NULL) { ret = -ENOMEM; - goto err4; + goto err3; } udc->nullp = virt_to_phys((void *)udc->nullbuf); @@ -2581,13 +2581,13 @@ static int qe_udc_probe(struct platform_device *ofdev) if (ret) { dev_err(udc->dev, "cannot request irq %d err %d\n", udc->usb_irq, ret); - goto err5; + goto err4; } ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget, qe_udc_release); if (ret) - goto err6; + goto err5; platform_set_drvdata(ofdev, udc); dev_info(udc->dev, @@ -2595,9 +2595,9 @@ static int qe_udc_probe(struct platform_device *ofdev) (udc->soc_type == PORT_QE) ? "QE" : "CPM"); return 0; -err6: - free_irq(udc->usb_irq, udc); err5: + free_irq(udc->usb_irq, udc); +err4: irq_dispose_mapping(udc->usb_irq); err_noirq: if (udc->nullmap) { @@ -2610,9 +2610,6 @@ err_noirq: udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); -err4: - kfree(udc->nullbuf); err3: ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); @@ -2660,8 +2657,6 @@ static int qe_udc_remove(struct platform_device *ofdev) udc->nullp, 256, DMA_TO_DEVICE); } - kfree(udc->statusbuf); - kfree(udc->nullbuf); ep = &udc->eps[0]; cpm_muram_free(cpm_muram_offset(ep->rxbase)); diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 7026919fc901..7026919fc901 100644 --- a/drivers/usb/gadget/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 28e4fc957026..75b23ea077a7 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -59,9 +59,9 @@ static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; -static struct usb_dr_device *dr_regs; +static struct usb_dr_device __iomem *dr_regs; -static struct usb_sys_interface *usb_sys_regs; +static struct usb_sys_interface __iomem *usb_sys_regs; /* it is initialized in probe() */ static struct fsl_udc *udc_controller = NULL; @@ -159,6 +159,8 @@ static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {} * request is still in progress. *--------------------------------------------------------------*/ static void done(struct fsl_ep *ep, struct fsl_req *req, int status) +__releases(ep->udc->lock) +__acquires(ep->udc->lock) { struct fsl_udc *udc = NULL; unsigned char stopped = ep->stopped; @@ -1392,6 +1394,8 @@ stall: static void setup_received_irq(struct fsl_udc *udc, struct usb_ctrlrequest *setup) +__releases(udc->lock) +__acquires(udc->lock) { u16 wValue = le16_to_cpu(setup->wValue); u16 wIndex = le16_to_cpu(setup->wIndex); @@ -1957,8 +1961,7 @@ static int fsl_udc_start(struct usb_gadget *g, &udc_controller->gadget); if (retval < 0) { ERR("can't bind to transceiver\n"); - driver->unbind(&udc_controller->gadget); - udc_controller->driver = 0; + udc_controller->driver = NULL; return retval; } } @@ -2246,7 +2249,7 @@ static void fsl_udc_release(struct device *dev) * init resource for globle controller * Return the udc handle on success or NULL on failure ------------------------------------------------------------------*/ -static int __init struct_udc_setup(struct fsl_udc *udc, +static int struct_udc_setup(struct fsl_udc *udc, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; @@ -2298,7 +2301,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc, * ep0out is not used so do nothing here * ep0in should be taken care *--------------------------------------------------------------*/ -static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, +static int struct_ep_setup(struct fsl_udc *udc, unsigned char index, char *name, int link) { struct fsl_ep *ep = &udc->eps[index]; @@ -2331,7 +2334,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, * all intialization operations implemented here except enabling usb_intr reg * board setup should have been done in the platform code */ -static int __init fsl_udc_probe(struct platform_device *pdev) +static int fsl_udc_probe(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; struct resource *res; @@ -2380,7 +2383,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } - pdata->regs = (void *)dr_regs; + pdata->regs = (void __iomem *)dr_regs; /* * do platform specific init: check the clock, grab/config pins, etc. diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index c6703bb07b23..84715625b2b3 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -12,6 +12,9 @@ #ifndef __FSL_USB2_UDC_H #define __FSL_USB2_UDC_H +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + /* ### define USB registers here */ #define USB_MAX_CTRL_PAYLOAD 64 diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 3deb4e938071..5c5d1adda7eb 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1325,8 +1325,6 @@ static int fusb300_udc_stop(struct usb_gadget *g, { struct fusb300 *fusb300 = to_fusb300(g); - driver->unbind(&fusb300->gadget); - init_controller(fusb300); fusb300->driver = NULL; @@ -1359,7 +1357,7 @@ static int __exit fusb300_remove(struct platform_device *pdev) return 0; } -static int __init fusb300_probe(struct platform_device *pdev) +static int fusb300_probe(struct platform_device *pdev) { struct resource *res, *ires, *ires1; void __iomem *reg = NULL; @@ -1400,13 +1398,17 @@ static int __init fusb300_probe(struct platform_device *pdev) /* initialize udc */ fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); - if (fusb300 == NULL) + if (fusb300 == NULL) { + ret = -ENOMEM; goto clean_up; + } for (i = 0; i < FUSB300_MAX_NUM_EP; i++) { _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL); - if (_ep[i] == NULL) + if (_ep[i] == NULL) { + ret = -ENOMEM; goto clean_up; + } fusb300->ep[i] = _ep[i]; } diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h index ae811d8d38b4..ae811d8d38b4 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/udc/fusb300_udc.h diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/udc/gadget_chips.h index bcd04bc66b98..bcd04bc66b98 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/udc/gadget_chips.h diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 6c85839e15ad..6c85839e15ad 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 86d2adafe149..86d2adafe149 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index c7004ee89c90..08df5c4f46ce 100644 --- a/drivers/usb/gadget/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2213,7 +2213,7 @@ out: return retval; } -static struct of_device_id gr_match[] = { +static const struct of_device_id gr_match[] = { {.name = "GAISLER_USBDC"}, {.name = "01_021"}, {}, diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h index 8388897d9ec3..8388897d9ec3 100644 --- a/drivers/usb/gadget/gr_udc.h +++ b/drivers/usb/gadget/udc/gr_udc.h diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index e471580a2a3b..1629ad7dcb80 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3036,7 +3036,7 @@ struct lpc32xx_usbd_cfg lpc32xx_usbddata = { static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; -static int __init lpc32xx_udc_probe(struct platform_device *pdev) +static int lpc32xx_udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct lpc32xx_udc *udc; @@ -3045,11 +3045,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) dma_addr_t dma_handle; struct device_node *isp1301_node; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; - memcpy(udc, &controller_template, sizeof(*udc)); for (i = 0; i <= 15; i++) udc->ep[i].udc = udc; udc->gadget.ep0 = &udc->ep[0].ep; @@ -3397,7 +3396,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev) #endif #ifdef CONFIG_OF -static struct of_device_id lpc32xx_udc_of_match[] = { +static const struct of_device_id lpc32xx_udc_of_match[] = { { .compatible = "nxp,lpc3220-udc", }, { }, }; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 0d17174b86f8..de88d33b44b2 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1492,8 +1492,6 @@ static int m66592_udc_stop(struct usb_gadget *g, m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); - driver->unbind(&m66592->gadget); - init_controller(m66592); disable_controller(m66592); @@ -1553,7 +1551,7 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init m66592_probe(struct platform_device *pdev) +static int m66592_probe(struct platform_device *pdev) { struct resource *res, *ires; void __iomem *reg = NULL; diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h index 96d49d7bfb6b..96d49d7bfb6b 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/udc/m66592-udc.h diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index e32a787ac373..e32a787ac373 100644 --- a/drivers/usb/gadget/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 16248711c152..16248711c152 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h index be77f207dbaf..be77f207dbaf 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/udc/mv_udc.h diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index fcff3a571b45..040fb169b162 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -332,7 +332,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req) /* clear active and halt bit, in case set from a previous error */ dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); - /* Ensure that updates to the QH will occure before priming. */ + /* Ensure that updates to the QH will occur before priming. */ wmb(); /* Prime the Endpoint */ @@ -1656,7 +1656,7 @@ static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", setup->bRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); - /* We process some stardard setup requests here */ + /* We process some standard setup requests here */ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (setup->bRequest) { case USB_REQ_GET_STATUS: diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/udc/net2272.c index ca15405583e2..059cfe527982 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -1453,7 +1453,7 @@ static int net2272_start(struct usb_gadget *_gadget, struct net2272 *dev; unsigned i; - if (!driver || !driver->unbind || !driver->setup || + if (!driver || !driver->setup || driver->max_speed != USB_SPEED_HIGH) return -EINVAL; diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/udc/net2272.h index e59505789359..e59505789359 100644 --- a/drivers/usb/gadget/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c new file mode 100644 index 000000000000..f4eac113690e --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.c @@ -0,0 +1,3827 @@ +/* + * Driver for the PLX NET2280 USB device controller. + * Specs and errata are available from <http://www.plxtech.com>. + * + * PLX Technology Inc. (formerly NetChip Technology) supported the + * development of this driver. + * + * + * CODE STATUS HIGHLIGHTS + * + * This driver should work well with most "gadget" drivers, including + * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers + * as well as Gadget Zero and Gadgetfs. + * + * DMA is enabled by default. Drivers using transfer queues might use + * DMA chaining to remove IRQ latencies between transfers. (Except when + * short OUT transfers happen.) Drivers can use the req->no_interrupt + * hint to completely eliminate some IRQs, if a later IRQ is guaranteed + * and DMA chaining is enabled. + * + * MSI is enabled by default. The legacy IRQ is used if MSI couldn't + * be enabled. + * + * Note that almost all the errata workarounds here are only needed for + * rev1 chips. Rev1a silicon (0110) fixes almost all of them. + */ + +/* + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2005 PLX Technology, Inc. + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility + * with 2282 chip + * + * Modified Ricardo Ribalda Qtechnology AS to provide compatibility + * with usb 338x chip. Based on PLX driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/prefetch.h> +#include <linux/io.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/unaligned.h> + +#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" +#define DRIVER_VERSION "2005 Sept 27/v3.0" + +#define EP_DONTUSE 13 /* nonzero */ + +#define USE_RDK_LEDS /* GPIO pins control three LEDs */ + + +static const char driver_name[] = "net2280"; +static const char driver_desc[] = DRIVER_DESC; + +static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 }; +static const char ep0name[] = "ep0"; +static const char *const ep_name[] = { + ep0name, + "ep-a", "ep-b", "ep-c", "ep-d", + "ep-e", "ep-f", "ep-g", "ep-h", +}; + +/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) + * use_dma_chaining -- dma descriptor queueing gives even more irq reduction + * + * The net2280 DMA engines are not tightly integrated with their FIFOs; + * not all cases are (yet) handled well in this driver or the silicon. + * Some gadget drivers work better with the dma support here than others. + * These two parameters let you use PIO or more aggressive DMA. + */ +static bool use_dma = true; +static bool use_dma_chaining; +static bool use_msi = true; + +/* "modprobe net2280 use_dma=n" etc */ +module_param(use_dma, bool, 0444); +module_param(use_dma_chaining, bool, 0444); +module_param(use_msi, bool, 0444); + +/* mode 0 == ep-{a,b,c,d} 1K fifo each + * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable + * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable + */ +static ushort fifo_mode; + +/* "modprobe net2280 fifo_mode=1" etc */ +module_param(fifo_mode, ushort, 0644); + +/* enable_suspend -- When enabled, the driver will respond to + * USB suspend requests by powering down the NET2280. Otherwise, + * USB suspend requests will be ignored. This is acceptable for + * self-powered devices + */ +static bool enable_suspend; + +/* "modprobe net2280 enable_suspend=1" etc */ +module_param(enable_suspend, bool, 0444); + +/* force full-speed operation */ +static bool full_speed; +module_param(full_speed, bool, 0444); +MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); + +#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") + +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"; + } + return "control"; +} + +#include "net2280.h" + +#define valid_bit cpu_to_le32(BIT(VALID_BIT)) +#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) + +/*-------------------------------------------------------------------------*/ +static inline void enable_pciirqenb(struct net2280_ep *ep) +{ + u32 tmp = readl(&ep->dev->regs->pciirqenb0); + + if (ep->dev->quirks & PLX_LEGACY) + tmp |= BIT(ep->num); + else + tmp |= BIT(ep_bit[ep->num]); + writel(tmp, &ep->dev->regs->pciirqenb0); + + return; +} + +static int +net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct net2280 *dev; + struct net2280_ep *ep; + u32 max, tmp; + unsigned long flags; + static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; + + ep = container_of(_ep, struct net2280_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; + + /* erratum 0119 workaround ties up an endpoint number */ + if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) + return -EDOM; + + if (dev->quirks & PLX_SUPERSPEED) { + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) + return -EDOM; + ep->is_in = !!usb_endpoint_dir_in(desc); + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) + return -EINVAL; + } + + /* sanity check ep-e/ep-f since their fifos are small */ + max = usb_endpoint_maxp(desc) & 0x1fff; + if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) + return -ERANGE; + + spin_lock_irqsave(&dev->lock, flags); + _ep->maxpacket = max & 0x7ff; + ep->desc = desc; + + /* ep_reset() has already been called */ + ep->stopped = 0; + ep->wedged = 0; + ep->out_overflow = 0; + + /* set speed-dependent max packet; may kick in high bandwidth */ + set_max_speed(ep, max); + + /* FIFO lines can't go to different packets. PIO is ok, so + * use it instead of troublesome (non-bulk) multi-packet DMA. + */ + if (ep->dma && (max % 4) != 0 && use_dma_chaining) { + ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n", + ep->ep.name, ep->ep.maxpacket); + ep->dma = NULL; + } + + /* set type, direction, address; reset fifo counters */ + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (tmp == USB_ENDPOINT_XFER_INT) { + /* erratum 0105 workaround prevents hs NYET */ + if (dev->chiprev == 0100 && + dev->gadget.speed == USB_SPEED_HIGH && + !(desc->bEndpointAddress & USB_DIR_IN)) + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + } else if (tmp == USB_ENDPOINT_XFER_BULK) { + /* catch some particularly blatant driver bugs */ + if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || + (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 = (tmp == USB_ENDPOINT_XFER_ISOC); + /* Enable this endpoint */ + if (dev->quirks & PLX_LEGACY) { + tmp <<= ENDPOINT_TYPE; + tmp |= desc->bEndpointAddress; + /* default full fifo lines */ + tmp |= (4 << ENDPOINT_BYTE_COUNT); + tmp |= BIT(ENDPOINT_ENABLE); + ep->is_in = (tmp & USB_DIR_IN) != 0; + } else { + /* In Legacy mode, only OUT endpoints are used */ + if (dev->enhanced_mode && ep->is_in) { + tmp <<= IN_ENDPOINT_TYPE; + tmp |= BIT(IN_ENDPOINT_ENABLE); + /* Not applicable to Legacy */ + tmp |= BIT(ENDPOINT_DIRECTION); + } else { + tmp <<= OUT_ENDPOINT_TYPE; + tmp |= BIT(OUT_ENDPOINT_ENABLE); + tmp |= (ep->is_in << ENDPOINT_DIRECTION); + } + + tmp |= usb_endpoint_num(desc); + tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); + } + + /* Make sure all the registers are written before ep_rsp*/ + wmb(); + + /* for OUT transfers, block the rx fifo until a read is posted */ + if (!ep->is_in) + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + else if (!(dev->quirks & PLX_2280)) { + /* Added for 2282, Don't use nak packets on an in endpoint, + * this was ignored on 2280 + */ + writel(BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); + } + + writel(tmp, &ep->cfg->ep_cfg); + + /* enable irqs */ + if (!ep->dma) { /* pio, per-packet */ + enable_pciirqenb(ep); + + tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); + if (dev->quirks & PLX_2280) + tmp |= readl(&ep->regs->ep_irqenb); + writel(tmp, &ep->regs->ep_irqenb); + } else { /* dma, per-request */ + tmp = BIT((8 + ep->num)); /* completion */ + tmp |= readl(&dev->regs->pciirqenb1); + writel(tmp, &dev->regs->pciirqenb1); + + /* for short OUT transfers, dma completions can't + * advance the queue; do it pio-style, by hand. + * NOTE erratum 0112 workaround #2 + */ + if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { + tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); + writel(tmp, &ep->regs->ep_irqenb); + + enable_pciirqenb(ep); + } + } + + tmp = desc->bEndpointAddress; + ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n", + _ep->name, tmp & 0x0f, DIR_STRING(tmp), + type_string(desc->bmAttributes), + ep->dma ? "dma" : "pio", max); + + /* pci writes may still be posted */ + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl(ptr); + if (result == ~(u32)0) /* "device unplugged" */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +static const struct usb_ep_ops net2280_ep_ops; + +static void ep_reset_228x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT) | + BIT(DMA_ABORT), + &ep->dma->dmastat); + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep->num); + writel(tmp, ®s->pciirqenb0); + } else { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + writel(0, &ep->regs->ep_irqenb); + + /* init to our chosen defaults, notably so that we NAK OUT + * packets until the driver queues a read (+note erratum 0112) + */ + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) { + tmp = BIT(SET_NAK_OUT_PACKETS_MODE) | + BIT(SET_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } else { + /* added for 2282 */ + tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_INTERRUPT_MODE); + } + + if (ep->num != 0) { + tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) | + BIT(CLEAR_ENDPOINT_HALT); + } + writel(tmp, &ep->regs->ep_rsp); + + /* scrub most status bits, and flush any fifo state */ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(FIFO_FLUSH) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + + /* fifo size is handled separately */ +} + +static void ep_reset_338x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp, dmastat; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel(BIT(DMA_ABORT_DONE_INTERRUPT) | + BIT(DMA_PAUSE_DONE_INTERRUPT) | + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_TRANSACTION_DONE_INTERRUPT), + /* | BIT(DMA_ABORT), */ + &ep->dma->dmastat); + + dmastat = readl(&ep->dma->dmastat); + if (dmastat == 0x5002) { + ep_warn(ep->dev, "The dmastat return = %x!!\n", + dmastat); + writel(0x5a, &ep->dma->dmastat); + } + + tmp = readl(®s->pciirqenb0); + tmp &= ~BIT(ep_bit[ep->num]); + writel(tmp, ®s->pciirqenb0); + } else { + if (ep->num < 5) { + tmp = readl(®s->pciirqenb1); + tmp &= ~BIT((8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + } + writel(0, &ep->regs->ep_irqenb); + + writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(FIFO_OVERFLOW) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); +} + +static void nuke(struct net2280_ep *); + +static int net2280_disable(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !ep->desc || _ep->name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + nuke(ep); + + if (ep->dev->quirks & PLX_SUPERSPEED) + ep_reset_338x(ep->dev->regs, ep); + else + ep_reset_228x(ep->dev->regs, ep); + + ep_vdbg(ep->dev, "disabled %s %s\n", + ep->dma ? "dma" : "pio", _ep->name); + + /* synch memory views with the device */ + (void)readl(&ep->cfg->ep_cfg); + + if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) + ep->dma = &ep->dev->dma[ep->num - 1]; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request +*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + if (!_ep) + return NULL; + ep = container_of(_ep, struct net2280_ep, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + /* this dma descriptor may be swapped with the previous dummy */ + if (ep->dma) { + struct net2280_dma *td; + + td = pci_pool_alloc(ep->dev->requests, gfp_flags, + &req->td_dma); + if (!td) { + kfree(req); + return NULL; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + req->td = td; + } + return &req->req; +} + +static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || !_req) + return; + + req = container_of(_req, struct net2280_request, req); + WARN_ON(!list_empty(&req->queue)); + if (req->td) + pci_pool_free(ep->dev->requests, req->td, req->td_dma); + kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* load a packet into the fifo we use for usb IN transfers. + * works for all endpoints. + * + * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo + * at a time, but this code is simpler because it knows it only writes + * one packet. ep-a..ep-d should use dma instead. + */ +static void write_fifo(struct net2280_ep *ep, struct usb_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf; + u32 tmp; + unsigned count, total; + + /* INVARIANT: fifo is currently empty. (testable) */ + + if (req) { + buf = req->buf + req->actual; + prefetch(buf); + total = req->length - req->actual; + } else { + total = 0; + buf = NULL; + } + + /* write just one packet at a time */ + count = ep->ep.maxpacket; + if (count > total) /* min() cannot be used on a bitfield */ + count = total; + + ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", + ep->ep.name, count, + (count != ep->ep.maxpacket) ? " (short)" : "", + req); + while (count >= 4) { + /* NOTE be careful if you try to align these. fifo lines + * should normally be full (4 bytes) and successive partial + * lines are ok only in certain cases. + */ + tmp = get_unaligned((u32 *)buf); + cpu_to_le32s(&tmp); + writel(tmp, ®s->ep_data); + buf += 4; + count -= 4; + } + + /* last fifo entry is "short" unless we wrote a full packet. + * also explicitly validate last word in (periodic) transfers + * when maxpacket is not a multiple of 4 bytes. + */ + if (count || total < ep->ep.maxpacket) { + tmp = count ? get_unaligned((u32 *)buf) : count; + cpu_to_le32s(&tmp); + set_fifo_bytecount(ep, count & 0x03); + writel(tmp, ®s->ep_data); + } + + /* pci writes may still be posted */ +} + +/* work around erratum 0106: PCI and USB race over the OUT fifo. + * caller guarantees chiprev 0100, out endpoint is NAKing, and + * there's no real data in the fifo. + * + * NOTE: also used in cases where that erratum doesn't apply: + * where the host wrote "too much" data to us. + */ +static void out_flush(struct net2280_ep *ep) +{ + u32 __iomem *statp; + u32 tmp; + + ASSERT_OUT_NAKING(ep); + + statp = &ep->regs->ep_stat; + writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT), + statp); + writel(BIT(FIFO_FLUSH), statp); + /* Make sure that stap is written */ + mb(); + tmp = readl(statp); + if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) && + /* high speed did bulk NYET; fifo isn't filling */ + ep->dev->gadget.speed == USB_SPEED_FULL) { + unsigned usec; + + usec = 50; /* 64 byte bulk/interrupt */ + handshake(statp, BIT(USB_OUT_PING_NAK_SENT), + BIT(USB_OUT_PING_NAK_SENT), usec); + /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ + } +} + +/* unload packet(s) from the fifo we use for usb OUT transfers. + * returns true iff the request completed, because of short packet + * or the request buffer having filled with full packets. + * + * for ep-a..ep-d this will read multiple packets out when they + * have been accepted. + */ +static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) +{ + struct net2280_ep_regs __iomem *regs = ep->regs; + u8 *buf = req->req.buf + req->req.actual; + unsigned count, tmp, is_short; + unsigned cleanup = 0, prevent = 0; + + /* erratum 0106 ... packets coming in during fifo reads might + * be incompletely rejected. not all cases have workarounds. + */ + if (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed == USB_SPEED_FULL) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS))) + cleanup = 1; + else if ((tmp & BIT(FIFO_FULL))) { + start_out_naking(ep); + prevent = 1; + } + /* else: hope we don't see the problem */ + } + + /* never overflow the rx buffer. the fifo reads packets until + * it sees a short one; we might not be ready for them all. + */ + prefetchw(buf); + count = readl(®s->ep_avail); + if (unlikely(count == 0)) { + udelay(1); + tmp = readl(&ep->regs->ep_stat); + count = readl(®s->ep_avail); + /* handled that data already? */ + if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0) + return 0; + } + + tmp = req->req.length - req->req.actual; + if (count > tmp) { + /* as with DMA, data overflow gets flushed */ + if ((tmp % ep->ep.maxpacket) != 0) { + ep_err(ep->dev, + "%s out fifo %d bytes, expected %d\n", + ep->ep.name, count, tmp); + req->req.status = -EOVERFLOW; + cleanup = 1; + /* NAK_OUT_PACKETS will be set, so flushing is safe; + * the next read will start with the next packet + */ + } /* else it's a ZLP, no worries */ + count = tmp; + } + req->req.actual += count; + + is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); + + ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", + ep->ep.name, count, is_short ? " (short)" : "", + cleanup ? " flush" : "", prevent ? " nak" : "", + req, req->req.actual, req->req.length); + + while (count >= 4) { + tmp = readl(®s->ep_data); + cpu_to_le32s(&tmp); + put_unaligned(tmp, (u32 *)buf); + buf += 4; + count -= 4; + } + if (count) { + tmp = readl(®s->ep_data); + /* LE conversion is implicit here: */ + do { + *buf++ = (u8) tmp; + tmp >>= 8; + } while (--count); + } + if (cleanup) + out_flush(ep); + if (prevent) { + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + (void) readl(&ep->regs->ep_rsp); + } + + return is_short || ((req->req.actual == req->req.length) && + !req->req.zero); +} + +/* fill out dma descriptor to match a given request */ +static void fill_dma_desc(struct net2280_ep *ep, + struct net2280_request *req, int valid) +{ + struct net2280_dma *td = req->td; + u32 dmacount = req->req.length; + + /* don't let DMA continue after a short OUT packet, + * so overruns can't affect the next transfer. + * in case of overruns on max-size packets, we can't + * stop the fifo from filling but we can flush it. + */ + if (ep->is_in) + dmacount |= BIT(DMA_DIRECTION); + if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || + !(ep->dev->quirks & PLX_2280)) + dmacount |= BIT(END_OF_CHAIN); + + req->valid = valid; + if (valid) + dmacount |= BIT(VALID_BIT); + if (likely(!req->req.no_interrupt || !use_dma_chaining)) + dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); + + /* td->dmadesc = previously set by caller */ + td->dmaaddr = cpu_to_le32 (req->req.dma); + + /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ + wmb(); + td->dmacount = cpu_to_le32(dmacount); +} + +static const u32 dmactl_default = + BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | + BIT(DMA_CLEAR_COUNT_ENABLE) | + /* erratum 0116 workaround part 1 (use POLLING) */ + (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | + BIT(DMA_VALID_BIT_POLLING_ENABLE) | + BIT(DMA_VALID_BIT_ENABLE) | + BIT(DMA_SCATTER_GATHER_ENABLE) | + /* erratum 0116 workaround part 2 (no AUTOSTART) */ + BIT(DMA_ENABLE); + +static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma) +{ + handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50); +} + +static inline void stop_dma(struct net2280_dma_regs __iomem *dma) +{ + writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl); + spin_stop_dma(dma); +} + +static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) +{ + struct net2280_dma_regs __iomem *dma = ep->dma; + unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION); + + if (!(ep->dev->quirks & PLX_2280)) + tmp |= BIT(END_OF_CHAIN); + + writel(tmp, &dma->dmacount); + writel(readl(&dma->dmastat), &dma->dmastat); + + writel(td_dma, &dma->dmadesc); + if (ep->dev->quirks & PLX_SUPERSPEED) + dmactl |= BIT(DMA_REQUEST_OUTSTANDING); + writel(dmactl, &dma->dmactl); + + /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ + (void) readl(&ep->dev->pci->pcimstctl); + + writel(BIT(DMA_START), &dma->dmastat); + + if (!ep->is_in) + stop_out_naking(ep); +} + +static void start_dma(struct net2280_ep *ep, struct net2280_request *req) +{ + u32 tmp; + struct net2280_dma_regs __iomem *dma = ep->dma; + + /* FIXME can't use DMA for ZLPs */ + + /* on this path we "know" there's no dma active (yet) */ + WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE)); + writel(0, &ep->dma->dmactl); + + /* previous OUT packet might have been short */ + if (!ep->is_in && (readl(&ep->regs->ep_stat) & + BIT(NAK_OUT_PACKETS))) { + writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT), + &ep->regs->ep_stat); + + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + writel(readl(&dma->dmastat), &dma->dmastat); + + /* transfer all/some fifo data */ + writel(req->req.dma, &dma->dmaaddr); + tmp = min(tmp, req->req.length); + + /* dma irq, faking scatterlist status */ + req->td->dmacount = cpu_to_le32(req->req.length - tmp); + writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp, + &dma->dmacount); + req->td->dmadesc = 0; + req->valid = 1; + + writel(BIT(DMA_ENABLE), &dma->dmactl); + writel(BIT(DMA_START), &dma->dmastat); + return; + } + } + + tmp = dmactl_default; + + /* force packet boundaries between dma requests, but prevent the + * controller from automagically writing a last "short" packet + * (zero length) unless the driver explicitly said to do that. + */ + if (ep->is_in) { + if (likely((req->req.length % ep->ep.maxpacket) || + req->req.zero)){ + tmp |= BIT(DMA_FIFO_VALIDATE); + ep->in_fifo_validate = 1; + } else + ep->in_fifo_validate = 0; + } + + /* init req->td, pointing to the current dummy */ + req->td->dmadesc = cpu_to_le32 (ep->td_dma); + fill_dma_desc(ep, req, 1); + + if (!use_dma_chaining) + req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); + + start_queue(ep, tmp, req->td_dma); +} + +static inline void resume_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); + + ep->dma_started = true; +} + +static inline void ep_stop_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl); + spin_stop_dma(ep->dma); + + ep->dma_started = false; +} + +static inline void +queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) +{ + struct net2280_dma *end; + dma_addr_t tmp; + + /* swap new dummy for old, link; fill and maybe activate */ + end = ep->dummy; + ep->dummy = req->td; + req->td = end; + + tmp = ep->td_dma; + ep->td_dma = req->td_dma; + req->td_dma = tmp; + + end->dmadesc = cpu_to_le32 (ep->td_dma); + + fill_dma_desc(ep, req, valid); +} + +static void +done(struct net2280_ep *ep, struct net2280_request *req, int status) +{ + struct net2280 *dev; + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); + + if (status && status != -ESHUTDOWN) + ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +static int +net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct net2280_request *req; + struct net2280_ep *ep; + struct net2280 *dev; + unsigned long flags; + + /* we always require a cpu-view buffer, so that we can + * always use pio (as fallback or whatever). + */ + req = container_of(_req, struct net2280_request, req); + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) + return -EINVAL; + if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) + return -EDOM; + ep = container_of(_ep, struct net2280_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; + + /* FIXME implement PIO fallback for ZLPs with DMA */ + if (ep->dma && _req->length == 0) + return -EOPNOTSUPP; + + /* set up dma mapping in case the caller didn't */ + if (ep->dma) { + int ret; + + ret = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (ret) + return ret; + } + +#if 0 + ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); +#endif + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + /* DMA request while EP halted */ + if (ep->dma && + (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) && + (dev->quirks & PLX_SUPERSPEED)) { + int valid = 1; + if (ep->is_in) { + int expect; + expect = likely(req->req.zero || + ((req->req.length % + ep->ep.maxpacket) != 0)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + } + /* use DMA if the endpoint supports it, else pio */ + else if (ep->dma) + start_dma(ep, req); + else { + /* maybe there's no control data, just status ack */ + if (ep->num == 0 && _req->length == 0) { + allow_status(ep); + done(ep, req, 0); + ep_vdbg(dev, "%s status ack\n", ep->ep.name); + goto done; + } + + /* PIO ... stuff the fifo, or unblock it. */ + if (ep->is_in) + write_fifo(ep, _req); + else if (list_empty(&ep->queue)) { + u32 s; + + /* OUT FIFO might have packet(s) buffered */ + s = readl(&ep->regs->ep_stat); + if ((s & BIT(FIFO_EMPTY)) == 0) { + /* note: _req->short_not_ok is + * ignored here since PIO _always_ + * stops queue advance here, and + * _req->status doesn't change for + * short reads (only _req->actual) + */ + if (read_fifo(ep, req) && + ep->num == 0) { + done(ep, req, 0); + allow_status(ep); + /* don't queue it */ + req = NULL; + } else if (read_fifo(ep, req) && + ep->num != 0) { + done(ep, req, 0); + req = NULL; + } else + s = readl(&ep->regs->ep_stat); + } + + /* don't NAK, let the fifo fill */ + if (req && (s & BIT(NAK_OUT_PACKETS))) + writel(BIT(CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } + } + + } else if (ep->dma) { + int valid = 1; + + if (ep->is_in) { + int expect; + + /* preventing magic zlps is per-engine state, not + * per-transfer; irq logic must recover hiccups. + */ + expect = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + + } /* else the irq handler advances the queue. */ + + ep->responded = 1; + if (req) + list_add_tail(&req->queue, &ep->queue); +done: + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static inline void +dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, + int status) +{ + req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); + done(ep, req, status); +} + +static void restart_dma(struct net2280_ep *ep); + +static void scan_dma_completions(struct net2280_ep *ep) +{ + /* only look at descriptors that were "naturally" retired, + * so fifo and list head state won't matter + */ + while (!list_empty(&ep->queue)) { + struct net2280_request *req; + u32 tmp; + + req = list_entry(ep->queue.next, + struct net2280_request, queue); + if (!req->valid) + break; + rmb(); + tmp = le32_to_cpup(&req->td->dmacount); + if ((tmp & BIT(VALID_BIT)) != 0) + break; + + /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" + * cases where DMA must be aborted; this code handles + * all non-abort DMA completions. + */ + if (unlikely(req->td->dmadesc == 0)) { + /* paranoia */ + tmp = readl(&ep->dma->dmacount); + if (tmp & DMA_BYTE_COUNT_MASK) + break; + /* single transfer mode */ + dma_done(ep, req, tmp, 0); + break; + } else if (!ep->is_in && + (req->req.length % ep->ep.maxpacket) != 0) { + tmp = readl(&ep->regs->ep_stat); + if (ep->dev->quirks & PLX_SUPERSPEED) + return dma_done(ep, req, tmp, 0); + + /* AVOID TROUBLE HERE by not issuing short reads from + * your gadget driver. That helps avoids errata 0121, + * 0122, and 0124; not all cases trigger the warning. + */ + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_warn(ep->dev, "%s lost packet sync!\n", + ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + tmp = readl(&ep->regs->ep_avail); + if (tmp) { + /* fifo gets flushed later */ + ep->out_overflow = 1; + ep_dbg(ep->dev, + "%s dma, discard %d len %d\n", + ep->ep.name, tmp, + req->req.length); + req->req.status = -EOVERFLOW; + } + } + } + dma_done(ep, req, tmp, 0); + } +} + +static void restart_dma(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 dmactl = dmactl_default; + + if (ep->stopped) + return; + req = list_entry(ep->queue.next, struct net2280_request, queue); + + if (!use_dma_chaining) { + start_dma(ep, req); + return; + } + + /* the 2280 will be processing the queue unless queue hiccups after + * the previous transfer: + * IN: wanted automagic zlp, head doesn't (or vice versa) + * DMA_FIFO_VALIDATE doesn't init from dma descriptors. + * OUT: was "usb-short", we must restart. + */ + if (ep->is_in && !req->valid) { + struct net2280_request *entry, *prev = NULL; + int reqmode, done = 0; + + ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); + ep->in_fifo_validate = likely(req->req.zero || + (req->req.length % ep->ep.maxpacket) != 0); + if (ep->in_fifo_validate) + dmactl |= BIT(DMA_FIFO_VALIDATE); + list_for_each_entry(entry, &ep->queue, queue) { + __le32 dmacount; + + if (entry == req) + continue; + dmacount = entry->td->dmacount; + if (!done) { + reqmode = likely(entry->req.zero || + (entry->req.length % ep->ep.maxpacket)); + if (reqmode == ep->in_fifo_validate) { + entry->valid = 1; + dmacount |= valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + continue; + } else { + /* force a hiccup */ + prev->td->dmacount |= dma_done_ie; + done = 1; + } + } + + /* walk the rest of the queue so unlinks behave */ + entry->valid = 0; + dmacount &= ~valid_bit; + entry->td->dmacount = dmacount; + prev = entry; + } + } + + writel(0, &ep->dma->dmactl); + start_queue(ep, dmactl, req->td_dma); +} + +static void abort_dma_228x(struct net2280_ep *ep) +{ + /* abort the current transfer */ + if (likely(!list_empty(&ep->queue))) { + /* FIXME work around errata 0121, 0122, 0124 */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + } else + stop_dma(ep->dma); + scan_dma_completions(ep); +} + +static void abort_dma_338x(struct net2280_ep *ep) +{ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); +} + +static void abort_dma(struct net2280_ep *ep) +{ + if (ep->dev->quirks & PLX_LEGACY) + return abort_dma_228x(ep); + return abort_dma_338x(ep); +} + +/* dequeue ALL requests */ +static void nuke(struct net2280_ep *ep) +{ + struct net2280_request *req; + + /* called with spinlock held */ + ep->stopped = 1; + if (ep->dma) + abort_dma(ep); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, + queue); + done(ep, req, -ESHUTDOWN); + } +} + +/* dequeue JUST ONE request */ +static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct net2280_ep *ep; + struct net2280_request *req; + unsigned long flags; + u32 dmactl; + int stopped; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0) || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + stopped = ep->stopped; + + /* quiesce dma while we patch the queue */ + dmactl = 0; + ep->stopped = 1; + if (ep->dma) { + dmactl = readl(&ep->dma->dmactl); + /* WARNING erratum 0127 may kick in ... */ + stop_dma(ep->dma); + scan_dma_completions(ep); + } + + /* 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) { + if (ep->dma) { + ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name); + _req->status = -ECONNRESET; + abort_dma(ep); + if (likely(ep->queue.next == &req->queue)) { + /* NOTE: misreports single-transfer mode*/ + req->td->dmacount = 0; /* invalidate */ + dma_done(ep, req, + readl(&ep->dma->dmacount), + -ECONNRESET); + } + } else { + ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name); + done(ep, req, -ECONNRESET); + } + req = NULL; + + /* patch up hardware chaining data */ + } else if (ep->dma && use_dma_chaining) { + if (req->queue.prev == ep->queue.next) { + writel(le32_to_cpu(req->td->dmadesc), + &ep->dma->dmadesc); + if (req->td->dmacount & dma_done_ie) + writel(readl(&ep->dma->dmacount) | + le32_to_cpu(dma_done_ie), + &ep->dma->dmacount); + } else { + struct net2280_request *prev; + + prev = list_entry(req->queue.prev, + struct net2280_request, queue); + prev->td->dmadesc = req->td->dmadesc; + if (req->td->dmacount & dma_done_ie) + prev->td->dmacount |= dma_done_ie; + } + } + + if (req) + done(ep, req, -ECONNRESET); + ep->stopped = stopped; + + if (ep->dma) { + /* turn off dma on inactive queues */ + if (list_empty(&ep->queue)) + stop_dma(ep->dma); + else if (!ep->stopped) { + /* resume current request, or start new one */ + if (req) + writel(dmactl, &ep->dma->dmactl); + else + start_dma(ep, list_entry(ep->queue.next, + struct net2280_request, queue)); + } + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int net2280_fifo_status(struct usb_ep *_ep); + +static int +net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) +{ + struct net2280_ep *ep; + unsigned long flags; + int retval = 0; + + ep = container_of(_ep, struct net2280_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 */ && (ep->desc->bmAttributes & 0x03) + == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + if (!list_empty(&ep->queue)) + retval = -EAGAIN; + else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) + retval = -EAGAIN; + else { + ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, + value ? "set" : "clear", + wedged ? "wedge" : "halt"); + /* set/clear, then synch memory views with the device */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt(ep); + if (wedged) + ep->wedged = 1; + } else { + clear_halt(ep); + if (ep->dev->quirks & PLX_SUPERSPEED && + !list_empty(&ep->queue) && ep->td_dma) + restart_dma(ep); + ep->wedged = 0; + } + (void) readl(&ep->regs->ep_rsp); + } + spin_unlock_irqrestore(&ep->dev->lock, flags); + + return retval; +} + +static int net2280_set_halt(struct usb_ep *_ep, int value) +{ + return net2280_set_halt_and_wedge(_ep, value, 0); +} + +static int net2280_set_wedge(struct usb_ep *_ep) +{ + if (!_ep || _ep->name == ep0name) + return -EINVAL; + return net2280_set_halt_and_wedge(_ep, 1, 1); +} + +static int net2280_fifo_status(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + u32 avail; + + ep = container_of(_ep, struct net2280_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 = readl(&ep->regs->ep_avail) & (BIT(12) - 1); + if (avail > ep->fifo_size) + return -EOVERFLOW; + if (ep->is_in) + avail = ep->fifo_size - avail; + return avail; +} + +static void net2280_fifo_flush(struct usb_ep *_ep) +{ + struct net2280_ep *ep; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep || (!ep->desc && ep->num != 0)) + return; + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return; + + writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); + (void) readl(&ep->regs->ep_rsp); +} + +static const struct usb_ep_ops net2280_ep_ops = { + .enable = net2280_enable, + .disable = net2280_disable, + + .alloc_request = net2280_alloc_request, + .free_request = net2280_free_request, + + .queue = net2280_queue, + .dequeue = net2280_dequeue, + + .set_halt = net2280_set_halt, + .set_wedge = net2280_set_wedge, + .fifo_status = net2280_fifo_status, + .fifo_flush = net2280_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int net2280_get_frame(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + unsigned long flags; + u16 retval; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + spin_lock_irqsave(&dev->lock, flags); + retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff; + spin_unlock_irqrestore(&dev->lock, flags); + return retval; +} + +static int net2280_wakeup(struct usb_gadget *_gadget) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE)) + writel(BIT(GENERATE_RESUME), &dev->usb->usbstat); + spin_unlock_irqrestore(&dev->lock, flags); + + /* pci writes may still be posted */ + return 0; +} + +static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + if (value) { + tmp |= BIT(SELF_POWERED_STATUS); + dev->selfpowered = 1; + } else { + tmp &= ~BIT(SELF_POWERED_STATUS); + dev->selfpowered = 0; + } + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + tmp = readl(&dev->usb->usbctl); + dev->softconnect = (is_on != 0); + if (is_on) + tmp |= BIT(USB_DETECT_ENABLE); + else + tmp &= ~BIT(USB_DETECT_ENABLE); + writel(tmp, &dev->usb->usbctl); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); + +static const struct usb_gadget_ops net2280_ops = { + .get_frame = net2280_get_frame, + .wakeup = net2280_wakeup, + .set_selfpowered = net2280_set_selfpowered, + .pullup = net2280_pullup, + .udc_start = net2280_start, + .udc_stop = net2280_stop, +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +/* FIXME move these into procfs, and use seq_file. + * Sysfs _still_ doesn't behave for arbitrarily sized files, + * and also doesn't help products using this with 2.4 kernels. + */ + +/* "function" sysfs attribute */ +static ssize_t function_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + if (!dev->driver || !dev->driver->function || + strlen(dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR_RO(function); + +static ssize_t registers_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size, t; + unsigned long flags; + int i; + u32 t1, t2; + 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 " DRIVER_VERSION + ", chiprev %04x, dma %s\n\n" + "devinit %03x fifoctl %08x gadget '%s'\n" + "pci irqenb0 %02x irqenb1 %08x " + "irqstat0 %04x irqstat1 %08x\n", + driver_name, dev->chiprev, + use_dma + ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + readl(&dev->regs->devinit), + readl(&dev->regs->fifoctl), + s, + readl(&dev->regs->pciirqenb0), + readl(&dev->regs->pciirqenb1), + readl(&dev->regs->irqstat0), + readl(&dev->regs->irqstat1)); + size -= t; + next += t; + + /* USB Control Registers */ + t1 = readl(&dev->usb->usbctl); + t2 = readl(&dev->usb->usbstat); + if (t1 & BIT(VBUS_PIN)) { + if (t2 & BIT(HIGH_SPEED)) + s = "high speed"; + else if (dev->gadget.speed == USB_SPEED_UNKNOWN) + s = "powered"; + else + s = "full speed"; + /* full speed bit (6) not working?? */ + } else + s = "not attached"; + t = scnprintf(next, size, + "stdrsp %08x usbctl %08x usbstat %08x " + "addr 0x%02x (%s)\n", + readl(&dev->usb->stdrsp), t1, t2, + readl(&dev->usb->ouraddr), s); + size -= t; + next += t; + + /* PCI Master Control Registers */ + + /* DMA Control Registers */ + + /* Configurable EP Control Registers */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->desc) + continue; + + t1 = readl(&ep->cfg->ep_cfg); + t2 = readl(&ep->regs->ep_rsp) & 0xff; + t = scnprintf(next, size, + "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" + "irqenb %02x\n", + ep->ep.name, t1, t2, + (t2 & BIT(CLEAR_NAK_OUT_PACKETS)) + ? "NAK " : "", + (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE)) + ? "hide " : "", + (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR)) + ? "CRC " : "", + (t2 & BIT(CLEAR_INTERRUPT_MODE)) + ? "interrupt " : "", + (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) + ? "status " : "", + (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE)) + ? "NAKmode " : "", + (t2 & BIT(CLEAR_ENDPOINT_TOGGLE)) + ? "DATA1 " : "DATA0 ", + (t2 & BIT(CLEAR_ENDPOINT_HALT)) + ? "HALT " : "", + readl(&ep->regs->ep_irqenb)); + size -= t; + next += t; + + t = scnprintf(next, size, + "\tstat %08x avail %04x " + "(ep%d%s-%s)%s\n", + readl(&ep->regs->ep_stat), + readl(&ep->regs->ep_avail), + t1 & 0x0f, DIR_STRING(t1), + type_string(t1 >> 8), + ep->stopped ? "*" : ""); + size -= t; + next += t; + + if (!ep->dma) + continue; + + t = scnprintf(next, size, + " dma\tctl %08x stat %08x count %08x\n" + "\taddr %08x desc %08x\n", + readl(&ep->dma->dmactl), + readl(&ep->dma->dmastat), + readl(&ep->dma->dmacount), + readl(&ep->dma->dmaaddr), + readl(&ep->dma->dmadesc)); + size -= t; + next += t; + + } + + /* Indexed Registers (none yet) */ + + /* Statistics */ + t = scnprintf(next, size, "\nirqs: "); + size -= t; + next += t; + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep; + + ep = &dev->ep[i]; + if (i && !ep->irqs) + continue; + t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs); + size -= t; + next += t; + + } + t = scnprintf(next, size, "\n"); + size -= t; + next += t; + + spin_unlock_irqrestore(&dev->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(registers); + +static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, + char *buf) +{ + struct net2280 *dev; + char *next; + unsigned size; + unsigned long flags; + int i; + + dev = dev_get_drvdata(_dev); + next = buf; + size = PAGE_SIZE; + spin_lock_irqsave(&dev->lock, flags); + + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + struct net2280_request *req; + int t; + + if (i != 0) { + const struct usb_endpoint_descriptor *d; + + d = ep->desc; + if (!d) + continue; + t = d->bEndpointAddress; + t = scnprintf(next, size, + "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", + ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, + (t & USB_DIR_IN) ? "in" : "out", + type_string(d->bmAttributes), + usb_endpoint_maxp(d) & 0x1fff, + ep->dma ? "dma" : "pio", ep->fifo_size + ); + } else /* ep0 should only have one transfer queued */ + t = scnprintf(next, size, "ep0 max 64 pio %s\n", + ep->is_in ? "in" : "out"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "\t(nothing queued)\n"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + continue; + } + list_for_each_entry(req, &ep->queue, queue) { + if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc)) + t = scnprintf(next, size, + "\treq %p len %d/%d " + "buf %p (dmacount %08x)\n", + &req->req, req->req.actual, + req->req.length, req->req.buf, + readl(&ep->dma->dmacount)); + else + t = scnprintf(next, size, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (ep->dma) { + struct net2280_dma *td; + + td = req->td; + t = scnprintf(next, size, "\t td %08x " + " count %08x buf %08x desc %08x\n", + (u32) req->td_dma, + le32_to_cpu(td->dmacount), + le32_to_cpu(td->dmaaddr), + le32_to_cpu(td->dmadesc)); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } + } + } + +done: + spin_unlock_irqrestore(&dev->lock, flags); + return PAGE_SIZE - size; +} +static DEVICE_ATTR_RO(queues); + + +#else + +#define device_create_file(a, b) (0) +#define device_remove_file(a, b) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +/* another driver-specific mode might be a request type doing dma + * to/from another device fifo instead of to/from memory. + */ + +static void set_fifo_mode(struct net2280 *dev, int mode) +{ + /* keeping high bits preserves BAR2 */ + writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); + + /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); + switch (mode) { + case 0: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; + break; + case 1: + dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048; + break; + case 2: + list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); + dev->ep[1].fifo_size = 2048; + dev->ep[2].fifo_size = 1024; + break; + } + /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ + list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list); + list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list); +} + +static void defect7374_disable_data_eps(struct net2280 *dev) +{ + /* + * For Defect 7374, disable data EPs (and more): + * - This phase undoes the earlier phase of the Defect 7374 workaround, + * returing ep regs back to normal. + */ + struct net2280_ep *ep; + int i; + unsigned char ep_sel; + u32 tmp_reg; + + for (i = 1; i < 5; i++) { + ep = &dev->ep[i]; + writel(0, &ep->cfg->ep_cfg); + } + + /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ + for (i = 0; i < 6; i++) + writel(0, &dev->dep[i].dep_cfg); + + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); + + if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + /* Change settings on some selected endpoints */ + tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); + tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR); + writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + tmp_reg |= BIT(EP_INITIALIZED); + writel(tmp_reg, &dev->plregs->pl_ep_ctrl); + } +} + +static void defect7374_enable_data_eps_zero(struct net2280 *dev) +{ + u32 tmp = 0, tmp_reg; + u32 fsmvalue, scratch; + int i; + unsigned char ep_sel; + + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + /*See if firmware needs to set up for workaround*/ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_warn(dev, "Operate Defect 7374 workaround soft this time"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + + /*GPEPs:*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | + (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | + ((dev->enhanced_mode) ? + BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | + BIT(IN_ENDPOINT_ENABLE)); + + for (i = 1; i < 5; i++) + writel(tmp, &dev->ep[i].cfg->ep_cfg); + + /* CSRIN, PCIIN, STATIN, RCIN*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); + writel(tmp, &dev->dep[1].dep_cfg); + writel(tmp, &dev->dep[3].dep_cfg); + writel(tmp, &dev->dep[4].dep_cfg); + writel(tmp, &dev->dep[5].dep_cfg); + + /*Implemented for development and debug. + * Can be refined/tuned later.*/ + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), + &dev->plregs->pl_ep_ctrl); + + if (ep_sel == 1) { + tmp = + (readl(&dev->plregs->pl_ep_ctrl) | + BIT(CLEAR_ACK_ERROR_CODE) | 0); + writel(tmp, &dev->plregs->pl_ep_ctrl); + continue; + } + + if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + tmp = (readl(&dev->plregs->pl_ep_cfg_4) | + BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); + writel(tmp, &dev->plregs->pl_ep_cfg_4); + + tmp = readl(&dev->plregs->pl_ep_ctrl) & + ~BIT(EP_INITIALIZED); + writel(tmp, &dev->plregs->pl_ep_ctrl); + + } + + /* Set FSM to focus on the first Control Read: + * - Tip: Connection speed is known upon the first + * setup request.*/ + scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; + set_idx_reg(dev->regs, SCRATCH, scratch); + + } else{ + ep_warn(dev, "Defect 7374 workaround soft will NOT operate"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); + } +} + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + * + * most of the work to support multiple net2280 controllers would + * be to associate this gadget driver (yes?) with all of them, or + * perhaps to bind specific drivers to specific devices. + */ + +static void usb_reset_228x(struct net2280 *dev) +{ + u32 tmp; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void) readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), + writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), + + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + writel(tmp, &dev->regs->devinit); + + /* standard fifo and endpoint allocations */ + set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0); +} + +static void usb_reset_338x(struct net2280 *dev) +{ + u32 tmp; + u32 fsmvalue; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void)readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if firmware needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__, + fsmvalue); + } else { + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + } + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); + + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + BIT(PCI_ENABLE) | + BIT(FIFO_SOFT_RESET) | + BIT(USB_SOFT_RESET) | + BIT(M8051_RESET); + + writel(tmp, &dev->regs->devinit); + } + + /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + + for (tmp = 1; tmp < dev->n_ep; tmp++) + list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); + +} + +static void usb_reset(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reset_228x(dev); + return usb_reset_338x(dev); +} + +static void usb_reinit_228x(struct net2280 *dev) +{ + u32 tmp; + int init_dma; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (tmp = 0; tmp < 7; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp]; + + ep->ep.name = ep_name[tmp]; + ep->dev = dev; + ep->num = tmp; + + if (tmp > 0 && tmp <= 4) { + ep->fifo_size = 1024; + if (init_dma) + ep->dma = &dev->dma[tmp - 1]; + } else + ep->fifo_size = 64; + ep->regs = &dev->epregs[tmp]; + ep->cfg = &dev->epregs[tmp]; + ep_reset_228x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* we want to prevent lowlevel/insecure access from the USB host, + * but erratum 0119 means this enable bit is ignored + */ + for (tmp = 0; tmp < 5; tmp++) + writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg); +} + +static void usb_reinit_338x(struct net2280 *dev) +{ + int init_dma; + int i; + u32 tmp, val; + u32 fsmvalue; + static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; + static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0xC0 }; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + + if (i > 0 && i <= 4 && init_dma) + ep->dma = &dev->dma[i - 1]; + + if (dev->enhanced_mode) { + ep->cfg = &dev->epregs[ne[i]]; + ep->regs = (struct net2280_ep_regs __iomem *) + (((void __iomem *)&dev->epregs[ne[i]]) + + ep_reg_addr[i]); + ep->fiforegs = &dev->fiforegs[i]; + } else { + ep->cfg = &dev->epregs[i]; + ep->regs = &dev->epregs[i]; + ep->fiforegs = &dev->fiforegs[i]; + } + + ep->fifo_size = (i != 0) ? 2048 : 512; + + ep_reset_338x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + + /* Link layer set up */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if driver needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", + __func__, fsmvalue); + else { + tmp = readl(&dev->usb_ext->usbctl2) & + ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); + writel(tmp, &dev->usb_ext->usbctl2); + } + + /* Hardware Defect and Workaround */ + val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val &= ~(0xf << TIMER_LFPS_6US); + val |= 0x5 << TIMER_LFPS_6US; + writel(val, &dev->ll_lfps_regs->ll_lfps_5); + + val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val &= ~(0xffff << TIMER_LFPS_80US); + val |= 0x0100 << TIMER_LFPS_80US; + writel(val, &dev->ll_lfps_regs->ll_lfps_6); + + /* + * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB + * Hot Reset Exit Handshake may Fail in Specific Case using + * Default Register Settings. Workaround for Enumeration test. + */ + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val &= ~(0x1f << HOT_TX_NORESET_TS2); + val |= 0x10 << HOT_TX_NORESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val &= ~(0x1f << HOT_RX_RESET_TS2); + val |= 0x3 << HOT_RX_RESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + + /* + * Set Recovery Idle to Recover bit: + * - On SS connections, setting Recovery Idle to Recover Fmw improves + * link robustness with various hosts and hubs. + * - It is safe to set for all connection speeds; all chip revisions. + * - R-M-W to leave other bits undisturbed. + * - Reference PLX TT-7372 + */ + val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); + writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* disable dedicated endpoints */ + writel(0x0D, &dev->dep[0].dep_cfg); + writel(0x0D, &dev->dep[1].dep_cfg); + writel(0x0E, &dev->dep[2].dep_cfg); + writel(0x0E, &dev->dep[3].dep_cfg); + writel(0x0F, &dev->dep[4].dep_cfg); + writel(0x0C, &dev->dep[5].dep_cfg); +} + +static void usb_reinit(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return usb_reinit_228x(dev); + return usb_reinit_338x(dev); +} + +static void ep0_start_228x(struct net2280 *dev) +{ + writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) | + BIT(GET_DEVICE_STATUS) | + BIT(GET_INTERFACE_STATUS), + &dev->usb->stdrsp); + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + BIT(SELF_POWERED_USB_DEVICE) | + BIT(REMOTE_WAKEUP_SUPPORT) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(SELF_POWERED_STATUS), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) | + BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void) readl(&dev->usb->usbctl); +} + +static void ep0_start_338x(struct net2280 *dev) +{ + u32 fsmvalue; + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__, + fsmvalue); + else + writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | + BIT(SET_EP_HIDE_STATUS_PHASE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel(BIT(SET_ISOCHRONOUS_DELAY) | + BIT(SET_SEL) | + BIT(SET_TEST_MODE) | + BIT(SET_ADDRESS) | + BIT(GET_INTERFACE_STATUS) | + BIT(GET_DEVICE_STATUS), + &dev->usb->stdrsp); + dev->wakeup_enable = 1; + writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | + (dev->softconnect << USB_DETECT_ENABLE) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | + BIT(ENDPOINT_0_INTERRUPT_ENABLE), + &dev->regs->pciirqenb0); + writel(BIT(PCI_INTERRUPT_ENABLE) | + BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | + BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | + BIT(VBUS_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void)readl(&dev->usb->usbctl); +} + +static void ep0_start(struct net2280 *dev) +{ + if (dev->quirks & PLX_LEGACY) + return ep0_start_228x(dev); + return ep0_start_338x(dev); +} + +/* 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 net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + int retval; + unsigned i; + + /* insist on high speed support from the driver, since + * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) + * "must not be used in normal operation" + */ + if (!driver || driver->max_speed < USB_SPEED_HIGH || + !driver->setup) + return -EINVAL; + + dev = container_of(_gadget, struct net2280, gadget); + + for (i = 0; i < dev->n_ep; i++) + dev->ep[i].irqs = 0; + + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + + retval = device_create_file(&dev->pdev->dev, &dev_attr_function); + if (retval) + goto err_unbind; + retval = device_create_file(&dev->pdev->dev, &dev_attr_queues); + if (retval) + goto err_func; + + /* Enable force-full-speed testing mode, if desired */ + if (full_speed && (dev->quirks & PLX_LEGACY)) + writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + net2280_led_active(dev, 1); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_enable_data_eps_zero(dev); + + ep0_start(dev); + + ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n", + driver->driver.name, + readl(&dev->usb->usbctl), + readl(&dev->usb->stdrsp)); + + /* pci writes may still be posted */ + return 0; + +err_func: + device_remove_file(&dev->pdev->dev, &dev_attr_function); +err_unbind: + dev->driver = NULL; + return retval; +} + +static void stop_activity(struct net2280 *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. + */ + usb_reset(dev); + for (i = 0; i < dev->n_ep; i++) + nuke(&dev->ep[i]); + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + usb_reinit(dev); +} + +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) +{ + struct net2280 *dev; + unsigned long flags; + + dev = container_of(_gadget, struct net2280, gadget); + + spin_lock_irqsave(&dev->lock, flags); + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + dev->driver = NULL; + + net2280_led_active(dev, 0); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); + + device_remove_file(&dev->pdev->dev, &dev_attr_function); + device_remove_file(&dev->pdev->dev, &dev_attr_queues); + + ep_dbg(dev, "unregistered driver '%s'\n", + driver ? driver->driver.name : ""); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. + * also works for dma-capable endpoints, in pio mode or just + * to manually advance the queue after short OUT transfers. + */ +static void handle_ep_small(struct net2280_ep *ep) +{ + struct net2280_request *req; + u32 t; + /* 0 error, 1 mid-data, 2 done */ + int mode = 1; + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + + /* ack all, and handle what we care about */ + t = readl(&ep->regs->ep_stat); + ep->irqs++; +#if 0 + ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", + ep->ep.name, t, req ? &req->req : 0); +#endif + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) + writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); + else + /* Added for 2282 */ + writel(t, &ep->regs->ep_stat); + + /* for ep0, monitor token irqs to catch data stage length errors + * and to synchronize on status. + * + * also, to defer reporting of protocol stalls ... here's where + * data or status first appears, handling stalls here should never + * cause trouble on the host side.. + * + * control requests could be slightly faster without token synch for + * status, but status can jam up that way. + */ + if (unlikely(ep->num == 0)) { + if (ep->is_in) { + /* status; stop NAKing */ + if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + if (!req) + allow_status(ep); + mode = 2; + /* reply to extra IN data tokens with a zlp */ + } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + mode = 2; + } else if (ep->responded && + !req && !ep->stopped) + write_fifo(ep, NULL); + } + } else { + /* status; stop NAKing */ + if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { + if (ep->dev->protocol_stall) { + ep->stopped = 1; + set_halt(ep); + } + mode = 2; + /* an extra OUT token is an error */ + } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) && + req && + req->req.actual == req->req.length) || + (ep->responded && !req)) { + ep->dev->protocol_stall = 1; + set_halt(ep); + ep->stopped = 1; + if (req) + done(ep, req, -EOVERFLOW); + req = NULL; + } + } + } + + if (unlikely(!req)) + return; + + /* manual DMA queue advance after short OUT */ + if (likely(ep->dma)) { + if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { + u32 count; + int stopped = ep->stopped; + + /* TRANSFERRED works around OUT_DONE erratum 0112. + * we expect (N <= maxpacket) bytes; host wrote M. + * iff (M < N) we won't ever see a DMA interrupt. + */ + ep->stopped = 1; + for (count = 0; ; t = readl(&ep->regs->ep_stat)) { + + /* any preceding dma transfers must finish. + * dma handles (M >= N), may empty the queue + */ + scan_dma_completions(ep); + if (unlikely(list_empty(&ep->queue) || + ep->out_overflow)) { + req = NULL; + break; + } + req = list_entry(ep->queue.next, + struct net2280_request, queue); + + /* here either (M < N), a "real" short rx; + * or (M == N) and the queue didn't empty + */ + if (likely(t & BIT(FIFO_EMPTY))) { + count = readl(&ep->dma->dmacount); + count &= DMA_BYTE_COUNT_MASK; + if (readl(&ep->dma->dmadesc) + != req->td_dma) + req = NULL; + break; + } + udelay(1); + } + + /* stop DMA, leave ep NAKing */ + writel(BIT(DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); + + if (likely(req)) { + req->td->dmacount = 0; + t = readl(&ep->regs->ep_avail); + dma_done(ep, req, count, + (ep->out_overflow || t) + ? -EOVERFLOW : 0); + } + + /* also flush to prevent erratum 0106 trouble */ + if (unlikely(ep->out_overflow || + (ep->dev->chiprev == 0x0100 && + ep->dev->gadget.speed + == USB_SPEED_FULL))) { + out_flush(ep); + ep->out_overflow = 0; + } + + /* (re)start dma if needed, stop NAKing */ + ep->stopped = stopped; + if (!list_empty(&ep->queue)) + restart_dma(ep); + } else + ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n", + ep->ep.name, t); + return; + + /* data packet(s) received (in the fifo, OUT) */ + } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) { + if (read_fifo(ep, req) && ep->num != 0) + mode = 2; + + /* data packet(s) transmitted (IN) */ + } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) { + unsigned len; + + len = req->req.length - req->req.actual; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; + + /* if we wrote it all, we're usually done */ + /* send zlps until the status stage */ + if ((req->req.actual == req->req.length) && + (!req->req.zero || len != ep->ep.maxpacket) && ep->num) + mode = 2; + + /* there was nothing to do ... */ + } else if (mode == 1) + return; + + /* done */ + if (mode == 2) { + /* stream endpoints often resubmit/unlink in completion */ + done(ep, req, 0); + + /* maybe advance queue to next request */ + if (ep->num == 0) { + /* NOTE: net2280 could let gadget driver start the + * status stage later. since not all controllers let + * them control that, the api doesn't (yet) allow it. + */ + if (!ep->stopped) + allow_status(ep); + req = NULL; + } else { + if (!list_empty(&ep->queue) && !ep->stopped) + req = list_entry(ep->queue.next, + struct net2280_request, queue); + else + req = NULL; + if (req && !ep->is_in) + stop_out_naking(ep); + } + } + + /* is there a buffer for the next packet? + * for best streaming performance, make sure there is one. + */ + if (req && !ep->stopped) { + + /* load IN fifo with next packet (may be zlp) */ + if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) + write_fifo(ep, &req->req); + } +} + +static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex) +{ + struct net2280_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; +} + +static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) +{ + u32 scratch, fsmvalue; + u32 ack_wait_timeout, state; + + /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && + (r.bRequestType & USB_DIR_IN))) + return; + + /* This is the first Control Read for this connection: */ + if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) { + /* + * Connection is NOT SS: + * - Connection must be FS or HS. + * - This FSM state should allow workaround software to + * run after the next USB connection. + */ + scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; + goto restore_data_eps; + } + + /* Connection is SS: */ + for (ack_wait_timeout = 0; + ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; + ack_wait_timeout++) { + + state = readl(&dev->plregs->pl_ep_status_1) + & (0xff << STATE); + if ((state >= (ACK_GOOD_NORMAL << STATE)) && + (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { + scratch |= DEFECT7374_FSM_SS_CONTROL_READ; + break; + } + + /* + * We have not yet received host's Data Phase ACK + * - Wait and try again. + */ + udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); + + continue; + } + + + if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { + ep_err(dev, "FAIL: Defect 7374 workaround waited but failed " + "to detect SS host's data phase ACK."); + ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" + "got 0x%2.2x.\n", state >> STATE); + } else { + ep_warn(dev, "INFO: Defect 7374 workaround waited about\n" + "%duSec for Control Read Data Phase ACK\n", + DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); + } + +restore_data_eps: + /* + * Restore data EPs to their pre-workaround settings (disabled, + * initialized, and other details). + */ + defect7374_disable_data_eps(dev); + + set_idx_reg(dev->regs, SCRATCH, scratch); + + return; +} + +static void ep_stall(struct net2280_ep *ep, int stall) +{ + struct net2280 *dev = ep->dev; + u32 val; + static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; + + if (stall) { + writel(BIT(SET_ENDPOINT_HALT) | + /* BIT(SET_NAK_PACKETS) | */ + BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &ep->regs->ep_rsp); + ep->is_halt = 1; + } else { + if (dev->gadget.speed == USB_SPEED_SUPER) { + /* + * Workaround for SS SeqNum not cleared via + * Endpoint Halt (Clear) bit. select endpoint + */ + val = readl(&dev->plregs->pl_ep_ctrl); + val = (val & ~0x1f) | ep_pl[ep->num]; + writel(val, &dev->plregs->pl_ep_ctrl); + + val |= BIT(SEQUENCE_NUMBER_RESET); + writel(val, &dev->plregs->pl_ep_ctrl); + } + val = readl(&ep->regs->ep_rsp); + val |= BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE); + writel(val, + /* | BIT(CLEAR_NAK_PACKETS),*/ + &ep->regs->ep_rsp); + ep->is_halt = 0; + val = readl(&ep->regs->ep_rsp); + } +} + +static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged) +{ + /* set/clear, then synch memory views with the device */ + if (value) { + ep->stopped = 1; + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else { + if (ep->dma) + ep_stop_dma(ep); + ep_stall(ep, true); + } + + if (wedged) + ep->wedged = 1; + } else { + ep->stopped = 0; + ep->wedged = 0; + + ep_stall(ep, false); + + /* Flush the queue */ + if (!list_empty(&ep->queue)) { + struct net2280_request *req = + list_entry(ep->queue.next, struct net2280_request, + queue); + if (ep->dma) + resume_dma(ep); + else { + if (ep->is_in) + write_fifo(ep, &req->req); + else { + if (read_fifo(ep, req)) + done(ep, req, 0); + } + } + } + } +} + +static void handle_stat0_irqs_superspeed(struct net2280 *dev, + struct net2280_ep *ep, struct usb_ctrlrequest r) +{ + int tmp = 0; + +#define w_value le16_to_cpu(r.wValue) +#define w_index le16_to_cpu(r.wIndex) +#define w_length le16_to_cpu(r.wLength) + + switch (r.bRequest) { + struct net2280_ep *e; + u16 status; + + case USB_REQ_SET_CONFIGURATION: + dev->addressed_state = !w_value; + goto usb3_delegate; + + case USB_REQ_GET_STATUS: + switch (r.bRequestType) { + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + status = dev->wakeup_enable ? 0x02 : 0x00; + if (dev->selfpowered) + status |= BIT(0); + status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | + dev->ltm_enable << 4); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + status = readl(&e->regs->ep_rsp) & + BIT(CLEAR_ENDPOINT_HALT); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + default: + break; + } + } + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 0; + writel(readl(&dev->usb->usbctl) & + ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + if (w_value != USB_ENDPOINT_HALT) + goto do_stall3; + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + ep_stall(e, false); + if (!list_empty(&e->queue) && e->td_dma) + restart_dma(e); + allow_status(ep); + ep->stopped = 1; + break; + + default: + goto usb3_delegate; + } + break; + case USB_REQ_SET_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + BIT(LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + default: + break; + } + } + + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 1; + writel(readl(&dev->usb->usbctl) | + BIT(DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e || (w_value != USB_ENDPOINT_HALT)) + goto do_stall3; + ep_stdrsp(e, true, false); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + + break; + default: + +usb3_delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", + r.bRequestType, r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &r); + spin_lock(&dev->lock); + } +do_stall3: + if (tmp < 0) { + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + r.bRequestType, r.bRequest, tmp); + dev->protocol_stall = 1; + /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ + ep_stall(ep, true); + } + +next_endpoints3: + +#undef w_value +#undef w_index +#undef w_length + + return; +} + +static void handle_stat0_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 num, scratch; + + /* most of these don't need individual acks */ + stat &= ~BIT(INTA_ASSERTED); + if (!stat) + return; + /* ep_dbg(dev, "irqstat0 %04x\n", stat); */ + + /* starting a control request? */ + if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) { + union { + u32 raw[2]; + struct usb_ctrlrequest r; + } u; + int tmp; + struct net2280_request *req; + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) { + u32 val = readl(&dev->usb->usbstat); + if (val & BIT(SUPER_SPEED)) { + dev->gadget.speed = USB_SPEED_SUPER; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_SS_MAX_PACKET_SIZE); + } else if (val & BIT(HIGH_SPEED)) { + dev->gadget.speed = USB_SPEED_HIGH; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } else { + dev->gadget.speed = USB_SPEED_FULL; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } + net2280_led_speed(dev, dev->gadget.speed); + ep_dbg(dev, "%s\n", + usb_speed_string(dev->gadget.speed)); + } + + ep = &dev->ep[0]; + ep->irqs++; + + /* make sure any leftover request state is cleared */ + stat &= ~BIT(ENDPOINT_0_INTERRUPT); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct net2280_request, queue); + done(ep, req, (req->req.actual == req->req.length) + ? 0 : -EPROTO); + } + ep->stopped = 0; + dev->protocol_stall = 0; + if (dev->quirks & PLX_SUPERSPEED) + ep->is_halt = 0; + else{ + if (ep->dev->quirks & PLX_2280) + tmp = BIT(FIFO_OVERFLOW) | + BIT(FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | BIT(TIMEOUT) | + BIT(USB_STALL_SENT) | + BIT(USB_IN_NAK_SENT) | + BIT(USB_IN_ACK_RCVD) | + BIT(USB_OUT_PING_NAK_SENT) | + BIT(USB_OUT_ACK_SENT) | + BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | + BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | + BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT), + &ep->regs->ep_stat); + } + u.raw[0] = readl(&dev->usb->setup0123); + u.raw[1] = readl(&dev->usb->setup4567); + + cpu_to_le32s(&u.raw[0]); + cpu_to_le32s(&u.raw[1]); + + if (dev->quirks & PLX_SUPERSPEED) + defect7374_workaround(dev, u.r); + + tmp = 0; + +#define w_value le16_to_cpu(u.r.wValue) +#define w_index le16_to_cpu(u.r.wIndex) +#define w_length le16_to_cpu(u.r.wLength) + + /* ack the irq */ + writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0); + stat ^= BIT(SETUP_PACKET_INTERRUPT); + + /* watch control traffic at the token level, and force + * synchronization before letting the status stage happen. + * FIXME ignore tokens we'll NAK, until driver responds. + * that'll mean a lot less irqs for some drivers. + */ + ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; + if (ep->is_in) { + scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + stop_out_naking(ep); + } else + scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) | + BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | + BIT(DATA_IN_TOKEN_INTERRUPT); + writel(scratch, &dev->epregs[0].ep_irqenb); + + /* we made the hardware handle most lowlevel requests; + * everything else goes uplevel to the gadget code. + */ + ep->responded = 1; + + if (dev->gadget.speed == USB_SPEED_SUPER) { + handle_stat0_irqs_superspeed(dev, ep, u.r); + goto next_endpoints; + } + + switch (u.r.bRequest) { + case USB_REQ_GET_STATUS: { + struct net2280_ep *e; + __le32 status; + + /* hw handles device and interface status */ + if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) + goto delegate; + e = get_ep_by_addr(dev, w_index); + if (!e || w_length > 2) + goto do_stall; + + if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT)) + status = cpu_to_le32(1); + else + status = cpu_to_le32(0); + + /* don't bother with a request object! */ + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, w_length); + writel((__force u32)status, &dev->epregs[0].ep_data); + allow_status(ep); + ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status); + goto next_endpoints; + } + break; + case USB_REQ_CLEAR_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->wedged) { + ep_vdbg(dev, "%s wedged, halt not cleared\n", + ep->ep.name); + } else { + ep_vdbg(dev, "%s clear halt\n", e->ep.name); + clear_halt(e); + if ((ep->dev->quirks & PLX_SUPERSPEED) && + !list_empty(&e->queue) && e->td_dma) + restart_dma(e); + } + allow_status(ep); + goto next_endpoints; + } + break; + case USB_REQ_SET_FEATURE: { + struct net2280_ep *e; + + /* hw handles device features */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (w_value != USB_ENDPOINT_HALT || w_length != 0) + goto do_stall; + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall; + if (e->ep.name == ep0name) + goto do_stall; + set_halt(e); + if ((dev->quirks & PLX_SUPERSPEED) && e->dma) + abort_dma(e); + allow_status(ep); + ep_vdbg(dev, "%s set halt\n", ep->ep.name); + goto next_endpoints; + } + break; + default: +delegate: + ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x " + "ep_cfg %08x\n", + u.r.bRequestType, u.r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + ep->responded = 0; + 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: + ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, tmp); + dev->protocol_stall = 1; + } + + /* some in/out token irq should follow; maybe stall then. + * driver must queue a request (even zlp) or halt ep0 + * before the host times out. + */ + } + +#undef w_value +#undef w_index +#undef w_length + +next_endpoints: + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; + + ep = &dev->ep[num]; + handle_ep_small(ep); + } + + if (stat) + ep_dbg(dev, "unhandled irqstat0 %08x\n", stat); +} + +#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \ + BIT(DMA_C_INTERRUPT) | \ + BIT(DMA_B_INTERRUPT) | \ + BIT(DMA_A_INTERRUPT)) +#define PCI_ERROR_INTERRUPTS ( \ + BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \ + BIT(PCI_RETRY_ABORT_INTERRUPT)) + +static void handle_stat1_irqs(struct net2280 *dev, u32 stat) +{ + struct net2280_ep *ep; + u32 tmp, num, mask, scratch; + + /* after disconnect there's nothing else to do! */ + tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT); + mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED); + + /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and + * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT + * only indicates a change in the reset state). + */ + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) && + (readl(&dev->usb->usbstat) & mask)) || + ((readl(&dev->usb->usbctl) & + BIT(VBUS_PIN)) == 0)) && + (dev->gadget.speed != USB_SPEED_UNKNOWN)) { + ep_dbg(dev, "disconnect %s\n", + dev->driver->driver.name); + stop_activity(dev, dev->driver); + ep0_start(dev); + return; + } + stat &= ~tmp; + + /* vBUS can bounce ... one of many reasons to ignore the + * notion of hotplug events on bus connect/disconnect! + */ + if (!stat) + return; + } + + /* NOTE: chip stays in PCI D0 state for now, but it could + * enter D1 to save more power + */ + tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT); + if (stat & tmp) { + writel(tmp, &dev->regs->irqstat1); + if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + if (!enable_suspend) + stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT); + } else { + if (dev->driver->resume) + dev->driver->resume(&dev->gadget); + /* at high speed, note erratum 0133 */ + } + stat &= ~tmp; + } + + /* clear any other status/irqs */ + if (stat) + writel(stat, &dev->regs->irqstat1); + + /* some status we can just ignore */ + if (dev->quirks & PLX_2280) + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(SUSPEND_REQUEST_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_INTERRUPT)); + else + stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | + BIT(RESUME_INTERRUPT) | + BIT(SOF_DOWN_INTERRUPT) | + BIT(SOF_INTERRUPT)); + + if (!stat) + return; + /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/ + + /* DMA status, for ep-{a,b,c,d} */ + scratch = stat & DMA_INTERRUPTS; + stat &= ~DMA_INTERRUPTS; + scratch >>= 9; + for (num = 0; scratch; num++) { + struct net2280_dma_regs __iomem *dma; + + tmp = BIT(num); + if ((tmp & scratch) == 0) + continue; + scratch ^= tmp; + + ep = &dev->ep[num + 1]; + dma = ep->dma; + + if (!dma) + continue; + + /* clear ep's dma status */ + tmp = readl(&dma->dmastat); + writel(tmp, &dma->dmastat); + + /* dma sync*/ + if (dev->quirks & PLX_SUPERSPEED) { + u32 r_dmacount = readl(&dma->dmacount); + if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && + (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) + continue; + } + + /* chaining should stop on abort, short OUT from fifo, + * or (stat0 codepath) short OUT transfer. + */ + if (!use_dma_chaining) { + if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { + ep_dbg(ep->dev, "%s no xact done? %08x\n", + ep->ep.name, tmp); + continue; + } + stop_dma(ep->dma); + } + + /* OUT transfers terminate when the data from the + * host is in our memory. Process whatever's done. + * On this path, we know transfer's last packet wasn't + * less than req->length. NAK_OUT_PACKETS may be set, + * or the FIFO may already be holding new packets. + * + * IN transfers can linger in the FIFO for a very + * long time ... we ignore that for now, accounting + * precisely (like PIO does) needs per-packet irqs + */ + scan_dma_completions(ep); + + /* disable dma on inactive queues; else maybe restart */ + if (list_empty(&ep->queue)) { + if (use_dma_chaining) + stop_dma(ep->dma); + } else { + tmp = readl(&dma->dmactl); + if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0) + restart_dma(ep); + else if (ep->is_in && use_dma_chaining) { + struct net2280_request *req; + __le32 dmacount; + + /* the descriptor at the head of the chain + * may still have VALID_BIT clear; that's + * used to trigger changing DMA_FIFO_VALIDATE + * (affects automagic zlp writes). + */ + req = list_entry(ep->queue.next, + struct net2280_request, queue); + dmacount = req->td->dmacount; + dmacount &= cpu_to_le32(BIT(VALID_BIT) | + DMA_BYTE_COUNT_MASK); + if (dmacount && (dmacount & valid_bit) == 0) + restart_dma(ep); + } + } + ep->irqs++; + } + + /* NOTE: there are other PCI errors we might usefully notice. + * if they appear very often, here's where to try recovering. + */ + if (stat & PCI_ERROR_INTERRUPTS) { + ep_err(dev, "pci dma error; stat %08x\n", stat); + stat &= ~PCI_ERROR_INTERRUPTS; + /* these are fatal errors, but "maybe" they won't + * happen again ... + */ + stop_activity(dev, dev->driver); + ep0_start(dev); + stat = 0; + } + + if (stat) + ep_dbg(dev, "unhandled irqstat1 %08x\n", stat); +} + +static irqreturn_t net2280_irq(int irq, void *_dev) +{ + struct net2280 *dev = _dev; + + /* shared interrupt, not ours */ + if ((dev->quirks & PLX_LEGACY) && + (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED)))) + return IRQ_NONE; + + spin_lock(&dev->lock); + + /* handle disconnect, dma, and more */ + handle_stat1_irqs(dev, readl(&dev->regs->irqstat1)); + + /* control requests and PIO */ + handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); + + if (dev->quirks & PLX_SUPERSPEED) { + /* re-enable interrupt to trigger any possible new interrupt */ + u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); + writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); + writel(pciirqenb1, &dev->regs->pciirqenb1); + } + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static void gadget_release(struct device *_dev) +{ + struct net2280 *dev = dev_get_drvdata(_dev); + + kfree(dev); +} + +/* tear down the binding between this driver and the pci device */ + +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() */ + net2280_led_shutdown(dev); + if (dev->requests) { + int i; + for (i = 1; i < 5; i++) { + if (!dev->ep[i].dummy) + continue; + pci_pool_free(dev->requests, dev->ep[i].dummy, + dev->ep[i].td_dma); + } + pci_pool_destroy(dev->requests); + } + if (dev->got_irq) + free_irq(pdev->irq, dev); + if (use_msi && dev->quirks & PLX_SUPERSPEED) + pci_disable_msi(pdev); + if (dev->regs) + iounmap(dev->regs); + if (dev->region) + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (dev->enabled) + pci_disable_device(pdev); + device_remove_file(&pdev->dev, &dev_attr_registers); + + ep_info(dev, "unbind\n"); +} + +/* wrap this driver around the specified device, but + * don't respond over USB until a gadget driver binds to us. + */ + +static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net2280 *dev; + unsigned long resource, len; + void __iomem *base = NULL; + int retval, i; + + if (!use_dma) + use_dma_chaining = 0; + + /* alloc, and start init */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + retval = -ENOMEM; + goto done; + } + + pci_set_drvdata(pdev, dev); + spin_lock_init(&dev->lock); + dev->quirks = id->driver_data; + dev->pdev = pdev; + dev->gadget.ops = &net2280_ops; + dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ? + USB_SPEED_SUPER : USB_SPEED_HIGH; + + /* the "gadget" abstracts/virtualizes the controller */ + dev->gadget.name = driver_name; + + /* now all the pci goodies ... */ + if (pci_enable_device(pdev) < 0) { + retval = -ENODEV; + goto done; + } + dev->enabled = 1; + + /* BAR 0 holds all the registers + * BAR 1 is 8051 memory; unused here (note erratum 0103) + * BAR 2 is fifo memory; unused here + */ + resource = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!request_mem_region(resource, len, driver_name)) { + ep_dbg(dev, "controller already in use\n"); + retval = -EBUSY; + goto done; + } + dev->region = 1; + + /* FIXME provide firmware download interface to put + * 8051 code into the chip, e.g. to turn on PCI PM. + */ + + base = ioremap_nocache(resource, len); + if (base == NULL) { + ep_dbg(dev, "can't map memory\n"); + retval = -EFAULT; + goto done; + } + dev->regs = (struct net2280_regs __iomem *) base; + dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); + dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); + dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); + dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); + dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); + + if (dev->quirks & PLX_SUPERSPEED) { + u32 fsmvalue; + u32 usbstat; + dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) + (base + 0x00b4); + dev->fiforegs = (struct usb338x_fifo_regs __iomem *) + (base + 0x0500); + dev->llregs = (struct usb338x_ll_regs __iomem *) + (base + 0x0700); + dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) + (base + 0x0748); + dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) + (base + 0x077c); + dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) + (base + 0x079c); + dev->plregs = (struct usb338x_pl_regs __iomem *) + (base + 0x0800); + usbstat = readl(&dev->usb->usbstat); + dev->enhanced_mode = !!(usbstat & BIT(11)); + dev->n_ep = (dev->enhanced_mode) ? 9 : 5; + /* put into initial config, link up all endpoints */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + /* See if firmware needs to set up for workaround: */ + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) + writel(0, &dev->usb->usbctl); + } else{ + dev->enhanced_mode = 0; + dev->n_ep = 7; + /* put into initial config, link up all endpoints */ + writel(0, &dev->usb->usbctl); + } + + usb_reset(dev); + usb_reinit(dev); + + /* irq setup after old hardware is cleaned up */ + if (!pdev->irq) { + ep_err(dev, "No IRQ. Check PCI setup!\n"); + retval = -ENODEV; + goto done; + } + + if (use_msi && (dev->quirks & PLX_SUPERSPEED)) + if (pci_enable_msi(pdev)) + ep_err(dev, "Failed to enable MSI mode\n"); + + if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED, + driver_name, dev)) { + ep_err(dev, "request interrupt %d failed\n", pdev->irq); + retval = -EBUSY; + goto done; + } + dev->got_irq = 1; + + /* DMA setup */ + /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ + dev->requests = pci_pool_create("requests", pdev, + sizeof(struct net2280_dma), + 0 /* no alignment requirements */, + 0 /* or page-crossing issues */); + if (!dev->requests) { + ep_dbg(dev, "can't get request pool\n"); + retval = -ENOMEM; + goto done; + } + for (i = 1; i < 5; i++) { + struct net2280_dma *td; + + td = pci_pool_alloc(dev->requests, GFP_KERNEL, + &dev->ep[i].td_dma); + if (!td) { + ep_dbg(dev, "can't get dummy %d\n", i); + retval = -ENOMEM; + goto done; + } + td->dmacount = 0; /* not VALID */ + td->dmadesc = td->dmaaddr; + dev->ep[i].dummy = td; + } + + /* enable lower-overhead pci memory bursts during DMA */ + if (dev->quirks & PLX_LEGACY) + writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) | + /* + * 256 write retries may not be enough... + BIT(PCI_RETRY_ABORT_ENABLE) | + */ + BIT(DMA_READ_MULTIPLE_ENABLE) | + BIT(DMA_READ_LINE_ENABLE), + &dev->pci->pcimstctl); + /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + /* ... also flushes any posted pci writes */ + dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff; + + /* done */ + ep_info(dev, "%s\n", driver_desc); + ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", + pdev->irq, base, dev->chiprev); + ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n", + use_dma ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + dev->enhanced_mode ? "enhanced mode" : "legacy mode"); + retval = device_create_file(&pdev->dev, &dev_attr_registers); + if (retval) + goto done; + + retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto done; + return 0; + +done: + if (dev) + net2280_remove(pdev); + return retval; +} + +/* make sure the board is quiescent; otherwise it will continue + * generating IRQs across the upcoming reboot. + */ + +static void net2280_shutdown(struct pci_dev *pdev) +{ + struct net2280 *dev = pci_get_drvdata(pdev); + + /* disable IRQs */ + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + + /* disable the pullup so the host will think we're gone */ + writel(0, &dev->usb->usbctl); + + /* Disable full-speed test mode */ + if (dev->quirks & PLX_LEGACY) + writel(0, &dev->usb->xcvrdiag); +} + + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id pci_ids[] = { { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2280, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY | PLX_2280, + }, { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX_LEGACY, + .device = 0x2282, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_LEGACY, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3380, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x3382, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_SUPERSPEED, + }, +{ /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct pci_driver net2280_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_ids, + + .probe = net2280_probe, + .remove = net2280_remove, + .shutdown = net2280_shutdown, + + /* FIXME add power management support */ +}; + +module_pci_driver(net2280_pci_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h new file mode 100644 index 000000000000..03f15242d794 --- /dev/null +++ b/drivers/usb/gadget/udc/net2280.h @@ -0,0 +1,403 @@ +/* + * NetChip 2280 high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/usb/net2280.h> +#include <linux/usb/usb338x.h> + +/*-------------------------------------------------------------------------*/ + +#ifdef __KERNEL__ + +/* indexed registers [11.10] are accessed indirectly + * caller must own the device lock. + */ + +static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index) +{ + writel(index, ®s->idxaddr); + /* NOTE: synchs device/cpu memory views */ + return readl(®s->idxdata); +} + +static inline void +set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value) +{ + writel(index, ®s->idxaddr); + writel(value, ®s->idxdata); + /* posted, may not be visible yet */ +} + +#endif /* __KERNEL__ */ + +#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc + +#define PLX_LEGACY BIT(0) +#define PLX_2280 BIT(1) +#define PLX_SUPERSPEED BIT(2) + +#define REG_DIAG 0x0 +#define RETRY_COUNTER 16 +#define FORCE_PCI_SERR 11 +#define FORCE_PCI_INTERRUPT 10 +#define FORCE_USB_INTERRUPT 9 +#define FORCE_CPU_INTERRUPT 8 +#define ILLEGAL_BYTE_ENABLES 5 +#define FAST_TIMES 4 +#define FORCE_RECEIVE_ERROR 2 +#define FORCE_TRANSMIT_CRC_ERROR 0 +#define REG_FRAME 0x02 /* from last sof */ +#define REG_CHIPREV 0x03 /* in bcd */ +#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ + +#define CHIPREV_1 0x0100 +#define CHIPREV_1A 0x0110 + +/* DEFECT 7374 */ +#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200 +#define DEFECT_7374_PROCESSOR_WAIT_TIME 10 + +/* ep0 max packet size */ +#define EP0_SS_MAX_PACKET_SIZE 0x200 +#define EP0_HS_MAX_PACKET_SIZE 0x40 +#ifdef __KERNEL__ + +/*-------------------------------------------------------------------------*/ + +/* [8.3] for scatter/gather i/o + * use struct net2280_dma_regs bitfields + */ +struct net2280_dma { + __le32 dmacount; + __le32 dmaaddr; /* the buffer */ + __le32 dmadesc; /* next dma descriptor */ + __le32 _reserved; +} __aligned(16); + +/*-------------------------------------------------------------------------*/ + +/* DRIVER DATA STRUCTURES and UTILITIES */ + +struct net2280_ep { + struct usb_ep ep; + struct net2280_ep_regs __iomem *cfg; + struct net2280_ep_regs __iomem *regs; + struct net2280_dma_regs __iomem *dma; + struct net2280_dma *dummy; + struct usb338x_fifo_regs __iomem *fiforegs; + dma_addr_t td_dma; /* of dummy */ + struct net2280 *dev; + unsigned long irqs; + unsigned is_halt:1, dma_started:1; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num : 8, + fifo_size : 12, + in_fifo_validate : 1, + out_overflow : 1, + stopped : 1, + wedged : 1, + is_in : 1, + is_iso : 1, + responded : 1; +}; + +static inline void allow_status(struct net2280_ep *ep) +{ + /* ep0 only */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + BIT(CLEAR_NAK_OUT_PACKETS) | + BIT(CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); + ep->stopped = 1; +} + +static void allow_status_338x(struct net2280_ep *ep) +{ + /* + * Control Status Phase Handshake was set by the chip when the setup + * packet arrived. While set, the chip automatically NAKs the host's + * Status Phase tokens. + */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp); + + ep->stopped = 1; + + /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */ + ep->responded = 0; +} + +struct net2280_request { + struct usb_request req; + struct net2280_dma *td; + dma_addr_t td_dma; + struct list_head queue; + unsigned mapped : 1, + valid : 1; +}; + +struct net2280 { + /* each pci device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; + struct net2280_ep ep[9]; + struct usb_gadget_driver *driver; + unsigned enabled : 1, + protocol_stall : 1, + softconnect : 1, + got_irq : 1, + region:1, + u1_enable:1, + u2_enable:1, + ltm_enable:1, + wakeup_enable:1, + selfpowered:1, + addressed_state:1; + u16 chiprev; + int enhanced_mode; + int n_ep; + kernel_ulong_t quirks; + + + /* pci state used to access those endpoints */ + struct pci_dev *pdev; + struct net2280_regs __iomem *regs; + struct net2280_usb_regs __iomem *usb; + struct usb338x_usb_ext_regs __iomem *usb_ext; + struct net2280_pci_regs __iomem *pci; + struct net2280_dma_regs __iomem *dma; + struct net2280_dep_regs __iomem *dep; + struct net2280_ep_regs __iomem *epregs; + struct usb338x_fifo_regs __iomem *fiforegs; + struct usb338x_ll_regs __iomem *llregs; + struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; + struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; + struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; + struct usb338x_pl_regs __iomem *plregs; + + struct pci_pool *requests; + /* statistics...*/ +}; + +static inline void set_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | + /* set NAK_OUT for erratum 0114 */ + ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) | + BIT(SET_ENDPOINT_HALT), + &ep->regs->ep_rsp); +} + +static inline void clear_halt(struct net2280_ep *ep) +{ + /* ep0 and bulk/intr endpoints */ + writel(BIT(CLEAR_ENDPOINT_HALT) | + BIT(CLEAR_ENDPOINT_TOGGLE) | + /* + * unless the gadget driver left a short packet in the + * fifo, this reverses the erratum 0114 workaround. + */ + ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); +} + +/* + * FSM value for Defect 7374 (U1U2 Test) is managed in + * chip's SCRATCH register: + */ +#define DEFECT7374_FSM_FIELD 28 + +/* Waiting for Control Read: + * - A transition to this state indicates a fresh USB connection, + * before the first Setup Packet. The connection speed is not + * known. Firmware is waiting for the first Control Read. + * - Starting state: This state can be thought of as the FSM's typical + * starting state. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD) + +/* Non-SS Control Read: + * - A transition to this state indicates detection of the first HS + * or FS Control Read. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD) + +/* SS Control Read: + * - A transition to this state indicates detection of the + * first SS Control Read. + * - This state indicates workaround completion. Workarounds no longer + * need to be applied (as long as the chip remains powered up). + * - Tip: Once in this state the FSM state does not change (until + * the chip's power is lost and restored). + * - This can be thought of as the final state of the FSM; + * the FSM 'locks-up' in this state until the chip loses power. + */ +#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD) + +#ifdef USE_RDK_LEDS + +static inline void net2280_led_init(struct net2280 *dev) +{ + /* LED3 (green) is on during USB activity. note erratum 0113. */ + writel(BIT(GPIO3_LED_SELECT) | + BIT(GPIO3_OUTPUT_ENABLE) | + BIT(GPIO2_OUTPUT_ENABLE) | + BIT(GPIO1_OUTPUT_ENABLE) | + BIT(GPIO0_OUTPUT_ENABLE), + &dev->regs->gpioctl); +} + +/* indicate speed with bi-color LED 0/1 */ +static inline +void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed) +{ + u32 val = readl(&dev->regs->gpioctl); + switch (speed) { + case USB_SPEED_SUPER: /* green + red */ + val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA); + break; + case USB_SPEED_HIGH: /* green */ + val &= ~BIT(GPIO0_DATA); + val |= BIT(GPIO1_DATA); + break; + case USB_SPEED_FULL: /* red */ + val &= ~BIT(GPIO1_DATA); + val |= BIT(GPIO0_DATA); + break; + default: /* (off/black) */ + val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA)); + break; + } + writel(val, &dev->regs->gpioctl); +} + +/* indicate power with LED 2 */ +static inline void net2280_led_active(struct net2280 *dev, int is_active) +{ + u32 val = readl(&dev->regs->gpioctl); + + /* FIXME this LED never seems to turn on.*/ + if (is_active) + val |= GPIO2_DATA; + else + val &= ~GPIO2_DATA; + writel(val, &dev->regs->gpioctl); +} + +static inline void net2280_led_shutdown(struct net2280 *dev) +{ + /* turn off all four GPIO*_DATA bits */ + writel(readl(&dev->regs->gpioctl) & ~0x0f, + &dev->regs->gpioctl); +} + +#else + +#define net2280_led_init(dev) do { } while (0) +#define net2280_led_speed(dev, speed) do { } while (0) +#define net2280_led_shutdown(dev) do { } while (0) + +#endif + +/*-------------------------------------------------------------------------*/ + +#define ep_dbg(ndev, fmt, args...) \ + dev_dbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_vdbg(ndev, fmt, args...) \ + dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_info(ndev, fmt, args...) \ + dev_info((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_warn(ndev, fmt, args...) \ + dev_warn((&((ndev)->pdev->dev)), fmt, ##args) + +#define ep_err(ndev, fmt, args...) \ + dev_err((&((ndev)->pdev->dev)), fmt, ##args) + +/*-------------------------------------------------------------------------*/ + +static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count) +{ + if (ep->dev->pdev->vendor == 0x17cc) + writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); + else{ + u32 tmp = readl(&ep->cfg->ep_cfg) & + (~(0x07 << EP_FIFO_BYTE_COUNT)); + writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg); + } +} + +static inline void start_out_naking(struct net2280_ep *ep) +{ + /* NOTE: hardware races lurk here, and PING protocol issues */ + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + /* synch with device */ + readl(&ep->regs->ep_rsp); +} + +#ifdef DEBUG +static inline void assert_out_naking(struct net2280_ep *ep, const char *where) +{ + u32 tmp = readl(&ep->regs->ep_stat); + + if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { + ep_dbg(ep->dev, "%s %s %08x !NAK\n", + ep->ep.name, where, tmp); + writel(BIT(SET_NAK_OUT_PACKETS), + &ep->regs->ep_rsp); + } +} +#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) +#else +#define ASSERT_OUT_NAKING(ep) do {} while (0) +#endif + +static inline void stop_out_naking(struct net2280_ep *ep) +{ + u32 tmp; + + tmp = readl(&ep->regs->ep_stat); + if ((tmp & BIT(NAK_OUT_PACKETS)) != 0) + writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); +} + + +static inline void set_max_speed(struct net2280_ep *ep, u32 max) +{ + u32 reg; + static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80, + 0x50, 0x20, 0x70, 0x40, 0x90 }; + + if (ep->dev->enhanced_mode) + reg = ep_enhanced[ep->num]; + else{ + reg = (ep->num + 1) * 0x10; + if (ep->dev->gadget.speed != USB_SPEED_HIGH) + reg += 1; + } + + set_idx_reg(ep->dev->regs, reg, max); +} + +#endif /* __KERNEL__ */ diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 2ae4f6d69f74..e731373fd4d7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2079,10 +2079,7 @@ static int omap_udc_start(struct usb_gadget *g, &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); - if (driver->unbind) { - driver->unbind(&udc->gadget); - udc->driver = NULL; - } + udc->driver = NULL; goto done; } } else { diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h index cfadeb5fc5de..cfadeb5fc5de 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/udc/omap_udc.h diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index eb8c3bedb57a..460d953c91b6 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -343,6 +343,7 @@ struct pch_vbus_gpio_data { * @setup_data: Received setup data * @phys_addr: of device memory * @base_addr: for mapped device memory + * @bar: Indicates which PCI BAR for USB regs * @irq: IRQ line for the device * @cfg_data: current cfg, intf, and alt in use * @vbus_gpio: GPIO informaton for detecting VBUS @@ -370,14 +371,17 @@ struct pch_udc_dev { struct usb_ctrlrequest setup_data; unsigned long phys_addr; void __iomem *base_addr; + unsigned bar; unsigned irq; struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio; }; #define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget)) +#define PCH_UDC_PCI_BAR_QUARK_X1000 0 #define PCH_UDC_PCI_BAR 1 #define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808 +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939 #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D #define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808 @@ -3076,7 +3080,7 @@ static void pch_udc_remove(struct pci_dev *pdev) iounmap(dev->base_addr); if (dev->mem_region) release_mem_region(dev->phys_addr, - pci_resource_len(pdev, PCH_UDC_PCI_BAR)); + pci_resource_len(pdev, dev->bar)); if (dev->active) pci_disable_device(pdev); kfree(dev); @@ -3144,9 +3148,15 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->active = 1; pci_set_drvdata(pdev, dev); + /* Determine BAR based on PCI ID */ + if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC) + dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000; + else + dev->bar = PCH_UDC_PCI_BAR; + /* PCI resource allocation */ - resource = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); + resource = pci_resource_start(pdev, dev->bar); + len = pci_resource_len(pdev, dev->bar); if (!request_mem_region(resource, len, KBUILD_MODNAME)) { dev_err(&pdev->dev, "%s: pci device used already\n", __func__); @@ -3212,6 +3222,12 @@ finished: static const struct pci_device_id pch_udc_pcidev_id[] = { { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, .class_mask = 0xffffffff, diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 9984437d2952..251e4d5ee152 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -16,6 +16,7 @@ /* #define VERBOSE_DEBUG */ #include <linux/device.h> +#include <linux/gpio.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -40,7 +41,6 @@ #include <asm/byteorder.h> #include <asm/dma.h> -#include <asm/gpio.h> #include <asm/mach-types.h> #include <asm/unaligned.h> @@ -2105,11 +2105,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) { - retval = PTR_ERR(dev->clk); - goto err_clk; - } + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", @@ -2120,15 +2118,16 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = dev_get_platdata(&pdev->dev); - dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { - if ((retval = gpio_request(dev->mach->gpio_pullup, - "pca25x_udc GPIO PULLUP"))) { + retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, + "pca25x_udc GPIO PULLUP"); + if (retval) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); - goto err_gpio_pullup; + goto err; } gpio_direction_output(dev->mach->gpio_pullup, 0); } @@ -2146,30 +2145,32 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->vbus = 0; /* irq setup after old hardware state is cleaned up */ - retval = request_irq(irq, pxa25x_udc_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0, + driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); - goto err_irq1; + goto err; } dev->got_irq = 1; #ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { - retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); - goto err_irq_lub; + goto err; } - retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - 0, driver_name, dev); + retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ, + lubbock_vbus_irq, 0, driver_name, + dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); - goto lubbock_fail0; + goto err; } } else #endif @@ -2180,22 +2181,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev) return retval; remove_debug_files(dev); -#ifdef CONFIG_ARCH_LUBBOCK -lubbock_fail0: - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - err_irq_lub: - free_irq(irq, dev); -#endif - err_irq1: - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - err_gpio_pullup: - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + err: + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } - clk_put(dev->clk); - err_clk: return retval; } @@ -2217,25 +2205,8 @@ static int pxa25x_udc_remove(struct platform_device *pdev) remove_debug_files(dev); - if (dev->got_irq) { - free_irq(platform_get_irq(pdev, 0), dev); - dev->got_irq = 0; - } -#ifdef CONFIG_ARCH_LUBBOCK - if (machine_is_lubbock()) { - free_irq(LUBBOCK_USB_DISC_IRQ, dev); - free_irq(LUBBOCK_USB_IRQ, dev); - } -#endif - if (gpio_is_valid(dev->mach->gpio_pullup)) - gpio_free(dev->mach->gpio_pullup); - - clk_put(dev->clk); - - if (!IS_ERR_OR_NULL(dev->transceiver)) { - usb_put_phy(dev->transceiver); + if (!IS_ERR_OR_NULL(dev->transceiver)) dev->transceiver = NULL; - } the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 3fe5931dc21a..3fe5931dc21a 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index cdf4d678be96..597d39f89420 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2446,6 +2446,9 @@ static int pxa_udc_probe(struct platform_device *pdev) retval = PTR_ERR(udc->clk); goto err_clk; } + retval = clk_prepare(udc->clk); + if (retval) + goto err_clk_prepare; retval = -ENOMEM; udc->regs = ioremap(regs->start, resource_size(regs)); @@ -2483,6 +2486,8 @@ err_add_udc: err_irq: iounmap(udc->regs); err_map: + clk_unprepare(udc->clk); +err_clk_prepare: clk_put(udc->clk); udc->clk = NULL; err_clk: @@ -2509,6 +2514,7 @@ static int pxa_udc_remove(struct platform_device *_dev) udc->transceiver = NULL; the_controller = NULL; + clk_unprepare(udc->clk); clk_put(udc->clk); iounmap(udc->regs); diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 28f2b53530f5..28f2b53530f5 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index b698a490cc7d..de2a8713b428 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1826,18 +1826,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); - iounmap(r8a66597->reg); - if (r8a66597->pdata->sudmac) - iounmap(r8a66597->sudmac_reg); - free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); } - kfree(r8a66597); return 0; } @@ -1845,28 +1839,24 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, +static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, struct platform_device *pdev) { struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); - if (!res) { - dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); - return -ENODEV; - } - - r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); - if (r8a66597->sudmac_reg == NULL) { + r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(r8a66597->sudmac_reg)) { dev_err(&pdev->dev, "ioremap error(sudmac).\n"); - return -ENOMEM; + return PTR_ERR(r8a66597->sudmac_reg); } return 0; } -static int __init r8a66597_probe(struct platform_device *pdev) +static int r8a66597_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; char clk_name[8]; struct resource *res, *ires; int irq; @@ -1877,39 +1867,27 @@ static int __init r8a66597_probe(struct platform_device *pdev) unsigned long irq_trigger; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_resource error.\n"); - goto clean_up; - } + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; if (irq < 0) { - ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_irq error.\n"); - goto clean_up; - } - - reg = ioremap(res->start, resource_size(res)); - if (reg == NULL) { - ret = -ENOMEM; - dev_err(&pdev->dev, "ioremap error.\n"); - goto clean_up; + dev_err(dev, "platform_get_irq error.\n"); + return -ENODEV; } /* initialize ucd */ - r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); - if (r8a66597 == NULL) { - ret = -ENOMEM; - goto clean_up; - } + r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL); + if (r8a66597 == NULL) + return -ENOMEM; spin_lock_init(&r8a66597->lock); platform_set_drvdata(pdev, r8a66597); - r8a66597->pdata = dev_get_platdata(&pdev->dev); + r8a66597->pdata = dev_get_platdata(dev); r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; r8a66597->gadget.ops = &r8a66597_gadget_ops; @@ -1923,12 +1901,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (r8a66597->pdata->on_chip) { snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); - r8a66597->clk = clk_get(&pdev->dev, clk_name); + r8a66597->clk = devm_clk_get(dev, clk_name); if (IS_ERR(r8a66597->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", - clk_name); - ret = PTR_ERR(r8a66597->clk); - goto clean_up; + dev_err(dev, "cannot get clock \"%s\"\n", clk_name); + return PTR_ERR(r8a66597->clk); } clk_prepare_enable(r8a66597->clk); } @@ -1941,10 +1917,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, - udc_name, r8a66597); + ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED, + udc_name, r8a66597); if (ret < 0) { - dev_err(&pdev->dev, "request_irq error (%d)\n", ret); + dev_err(dev, "request_irq error (%d)\n", ret); goto clean_up2; } @@ -1978,37 +1954,25 @@ static int __init r8a66597_probe(struct platform_device *pdev) GFP_KERNEL); if (r8a66597->ep0_req == NULL) { ret = -ENOMEM; - goto clean_up3; + goto clean_up2; } r8a66597->ep0_req->complete = nop_completion; - ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget); + ret = usb_add_gadget_udc(dev, &r8a66597->gadget); if (ret) goto err_add_udc; - dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + dev_info(dev, "version %s\n", DRIVER_VERSION); return 0; err_add_udc: r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); -clean_up3: - free_irq(irq, r8a66597); clean_up2: - if (r8a66597->pdata->on_chip) { + if (r8a66597->pdata->on_chip) clk_disable_unprepare(r8a66597->clk); - clk_put(r8a66597->clk); - } -clean_up: - if (r8a66597) { - if (r8a66597->sudmac_reg) - iounmap(r8a66597->sudmac_reg); - if (r8a66597->ep0_req) - r8a66597_free_request(&r8a66597->ep[0].ep, - r8a66597->ep0_req); - kfree(r8a66597); - } - if (reg) - iounmap(reg); + + if (r8a66597->ep0_req) + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); return ret; } diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h index 45c4b2df1785..45c4b2df1785 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/udc/r8a66597-udc.h diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 10c6a128250c..10c6a128250c 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 7987aa049fab..357b58e0087b 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1788,7 +1788,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(usb_bus_clock); } - clk_enable(usb_bus_clock); + clk_prepare_enable(usb_bus_clock); udc_clock = clk_get(NULL, "usb-device"); if (IS_ERR(udc_clock)) { @@ -1796,7 +1796,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return PTR_ERR(udc_clock); } - clk_enable(udc_clock); + clk_prepare_enable(udc_clock); mdelay(10); @@ -1952,13 +1952,13 @@ static int s3c2410_udc_remove(struct platform_device *pdev) release_mem_region(rsrc_start, rsrc_len); if (!IS_ERR(udc_clock) && udc_clock != NULL) { - clk_disable(udc_clock); + clk_disable_unprepare(udc_clock); clk_put(udc_clock); udc_clock = NULL; } if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { - clk_disable(usb_bus_clock); + clk_disable_unprepare(usb_bus_clock); clk_put(usb_bus_clock); usb_bus_clock = NULL; } diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h index 93bf225f1969..93bf225f1969 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/udc/s3c2410_udc.h diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index b0d98172bc07..b0d98172bc07 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 03314f861bee..82800a775501 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -37,6 +37,14 @@ config USB_XHCI_MVEBU Say 'Y' to enable the support for the xHCI host controller found in Marvell Armada 375/38x ARM SOCs. +config USB_XHCI_RCAR + tristate "xHCI support for Renesas R-Car SoCs" + select USB_XHCI_PLATFORM + depends on ARCH_SHMOBILE || COMPILE_TEST + ---help--- + Say 'Y' to enable the support for the xHCI host controller + found in Renesas R-Car ARM SoCs. + endif # USB_XHCI_HCD config USB_EHCI_HCD diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index af89a903d97e..144c038ef70f 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -22,6 +22,9 @@ ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) ifneq ($(CONFIG_USB_XHCI_MVEBU), ) xhci-hcd-y += xhci-mvebu.o endif +ifneq ($(CONFIG_USB_XHCI_RCAR), ) + xhci-hcd-y += xhci-rcar.o +endif endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index d1c76216350f..cda0a2f5c467 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -88,7 +88,7 @@ static int exynos_ehci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index cc305c71ac3d..6130b7574908 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1230,7 +1230,7 @@ int ehci_hub_control( if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { spin_unlock_irqrestore(&ehci->lock, flags); retval = ehset_single_step_set_feature(hcd, - wIndex); + wIndex + 1); spin_lock_irqsave(&ehci->lock, flags); break; } diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index c0fb6a8ae6a3..b6205fac3567 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -209,7 +209,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) ehci->periodic = (__le32 *) dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller, ehci->periodic_size * sizeof(__le32), - &ehci->periodic_dma, 0); + &ehci->periodic_dma, flags); if (ehci->periodic == NULL) { goto fail; } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 982c09bebe0f..934b39d5e44a 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -190,7 +190,7 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = { .resume = ehci_msm_pm_resume, }; -static struct of_device_id msm_ehci_dt_match[] = { +static const struct of_device_id msm_ehci_dt_match[] = { { .compatible = "qcom,ehci-host", }, {} }; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 3e86bf4371b3..ca7b964124af 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -35,6 +35,21 @@ static const char hcd_name[] = "ehci-pci"; #define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 /*-------------------------------------------------------------------------*/ +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939 +static inline bool is_intel_quark_x1000(struct pci_dev *pdev) +{ + return pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC; +} + +/* + * 0x84 is the offset of in/out threshold register, + * and it is the same offset as the register of 'hostpc'. + */ +#define intel_quark_x1000_insnreg01 hostpc + +/* Maximum usable threshold value is 0x7f dwords for both IN and OUT */ +#define INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD 0x007f007f /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) @@ -50,6 +65,16 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) if (!retval) ehci_dbg(ehci, "MWI active\n"); + /* Reset the threshold limit */ + if (is_intel_quark_x1000(pdev)) { + /* + * For the Intel QUARK X1000, raise the I/O threshold to the + * maximum usable value in order to improve performance. + */ + ehci_writel(ehci, INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD, + ehci->regs->intel_quark_x1000_insnreg01); + } + return 0; } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 1d59958ad0ce..1355ff0946b9 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -150,7 +150,7 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) return 0; } -static struct of_device_id spear_ehci_id_table[] = { +static const struct of_device_id spear_ehci_id_table[] = { { .compatible = "st,spear600-ehci", }, { }, }; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6fdcb8ad2296..7aafb05e7a40 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -46,6 +46,7 @@ #define DRV_NAME "tegra-ehci" static struct hc_driver __read_mostly tegra_ehci_hc_driver; +static bool usb1_reset_attempted; struct tegra_ehci_soc_config { bool has_hostpc; @@ -60,6 +61,61 @@ struct tegra_ehci_hcd { enum tegra_usb_phy_port_speed port_speed; }; +/* + * The 1st USB controller contains some UTMI pad registers that are global for + * all the controllers on the chip. Those registers are also cleared when + * reset is asserted to the 1st controller. This means that the 1st controller + * can only be reset when no other controlled has finished probing. So we'll + * reset the 1st controller before doing any other setup on any of the + * controllers, and then never again. + * + * Since this is a PHY issue, the Tegra PHY driver should probably be doing + * the resetting of the USB controllers. But to keep compatibility with old + * device trees that don't have reset phandles in the PHYs, do it here. + * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI + * device isn't the first one to finish probing, so warn them. + */ +static int tegra_reset_usb_controller(struct platform_device *pdev) +{ + struct device_node *phy_np; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct tegra_ehci_hcd *tegra = + (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; + + phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); + if (!phy_np) + return -ENOENT; + + if (!usb1_reset_attempted) { + struct reset_control *usb1_reset; + + usb1_reset = of_reset_control_get(phy_np, "usb"); + if (IS_ERR(usb1_reset)) { + dev_warn(&pdev->dev, + "can't get utmi-pads reset from the PHY\n"); + dev_warn(&pdev->dev, + "continuing, but please update your DT\n"); + } else { + reset_control_assert(usb1_reset); + udelay(1); + reset_control_deassert(usb1_reset); + } + + reset_control_put(usb1_reset); + usb1_reset_attempted = true; + } + + if (!of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) { + reset_control_assert(tegra->rst); + udelay(1); + reset_control_deassert(tegra->rst); + } + + of_node_put(phy_np); + + return 0; +} + static int tegra_ehci_internal_port_reset( struct ehci_hcd *ehci, u32 __iomem *portsc_reg @@ -326,7 +382,7 @@ static const struct tegra_ehci_soc_config tegra20_soc_config = { .has_hostpc = false, }; -static struct of_device_id tegra_ehci_of_match[] = { +static const struct of_device_id tegra_ehci_of_match[] = { { .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config }, { }, @@ -389,9 +445,9 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (err) goto cleanup_hcd_create; - reset_control_assert(tegra->rst); - udelay(1); - reset_control_deassert(tegra->rst); + err = tegra_reset_usb_controller(pdev); + if (err) + goto cleanup_clk_en; u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { @@ -479,10 +535,11 @@ static int tegra_ehci_remove(struct platform_device *pdev) usb_phy_shutdown(hcd->phy); usb_remove_hcd(hcd); - usb_put_hcd(hcd); clk_disable_unprepare(tegra->clk); + usb_put_hcd(hcd); + return 0; } diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index f238cb37305c..b58e7a60913a 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -129,11 +129,7 @@ void fhci_dfs_destroy(struct fhci_hcd *fhci) if (!fhci->dfs_root) return; - if (fhci->dfs_irq_stat) - debugfs_remove(fhci->dfs_irq_stat); - - if (fhci->dfs_regs) - debugfs_remove(fhci->dfs_regs); - + debugfs_remove(fhci->dfs_irq_stat); + debugfs_remove(fhci->dfs_regs); debugfs_remove(fhci->dfs_root); } diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 98a89d16cc3e..adcd2050dced 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -5838,41 +5838,17 @@ static int fotg210_hcd_probe(struct platform_device *pdev) goto fail_create_hcd; } + hcd->has_tt = 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto failed; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->has_tt = 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - fotg210_fotg210_hc_driver.description)) { - dev_dbg(dev, "controller already in use\n"); - retval = -EBUSY; - goto fail_request_resource; - } - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->regs = ioremap_nocache(res->start, resource_size(res)); - if (hcd->regs == NULL) { - dev_dbg(dev, "error mapping memory\n"); - retval = -EFAULT; - goto fail_ioremap; - } fotg210 = hcd_to_fotg210(hcd); @@ -5880,24 +5856,20 @@ static int fotg210_hcd_probe(struct platform_device *pdev) retval = fotg210_setup(hcd); if (retval) - goto fail_add_hcd; + goto failed; fotg210_init(fotg210); retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) { dev_err(dev, "failed to add hcd with err %d\n", retval); - goto fail_add_hcd; + goto failed; } device_wakeup_enable(hcd->self.controller); return retval; -fail_add_hcd: - iounmap(hcd->regs); -fail_ioremap: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -fail_request_resource: +failed: usb_put_hcd(hcd); fail_create_hcd: dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); @@ -5918,8 +5890,6 @@ static int fotg210_hcd_remove(struct platform_device *pdev) return 0; usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 858efcfda50b..6234c75da33f 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -102,6 +102,15 @@ enum scheduling_pass { SCHED_PASS_DONE }; +/* Bit numbers for max3421_hcd->todo: */ +enum { + ENABLE_IRQ = 0, + RESET_HCD, + RESET_PORT, + CHECK_UNLINK, + IOPIN_UPDATE +}; + struct max3421_dma_buf { u8 data[2]; }; @@ -146,11 +155,7 @@ struct max3421_hcd { u8 hien; u8 mode; u8 iopins[2]; - unsigned int do_enable_irq:1; - unsigned int do_reset_hcd:1; - unsigned int do_reset_port:1; - unsigned int do_check_unlink:1; - unsigned int do_iopin_update:1; + unsigned long todo; #ifdef DEBUG unsigned long err_stat[16]; #endif @@ -1165,10 +1170,8 @@ max3421_irq_handler(int irq, void *dev_id) if (max3421_hcd->spi_thread && max3421_hcd->spi_thread->state != TASK_RUNNING) wake_up_process(max3421_hcd->spi_thread); - if (!max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 1; + if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo)) disable_irq_nosync(spi->irq); - } return IRQ_HANDLED; } @@ -1423,10 +1426,8 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien); set_current_state(TASK_INTERRUPTIBLE); - if (max3421_hcd->do_enable_irq) { - max3421_hcd->do_enable_irq = 0; + if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo)) enable_irq(spi->irq); - } schedule(); __set_current_state(TASK_RUNNING); } @@ -1440,23 +1441,18 @@ max3421_spi_thread(void *dev_id) else if (!max3421_hcd->curr_urb) i_worked |= max3421_select_and_start_urb(hcd); - if (max3421_hcd->do_reset_hcd) { + if (test_and_clear_bit(RESET_HCD, &max3421_hcd->todo)) /* reset the HCD: */ - max3421_hcd->do_reset_hcd = 0; i_worked |= max3421_reset_hcd(hcd); - } - if (max3421_hcd->do_reset_port) { + if (test_and_clear_bit(RESET_PORT, &max3421_hcd->todo)) { /* perform a USB bus reset: */ - max3421_hcd->do_reset_port = 0; spi_wr8(hcd, MAX3421_REG_HCTL, BIT(MAX3421_HCTL_BUSRST_BIT)); i_worked = 1; } - if (max3421_hcd->do_check_unlink) { - max3421_hcd->do_check_unlink = 0; + if (test_and_clear_bit(CHECK_UNLINK, &max3421_hcd->todo)) i_worked |= max3421_check_unlink(hcd); - } - if (max3421_hcd->do_iopin_update) { + if (test_and_clear_bit(IOPIN_UPDATE, &max3421_hcd->todo)) { /* * IOPINS1/IOPINS2 do not auto-increment, so we can't * use spi_wr_buf(). @@ -1469,7 +1465,6 @@ max3421_spi_thread(void *dev_id) spi_wr8(hcd, MAX3421_REG_IOPINS1 + i, val); max3421_hcd->iopins[i] = val; } - max3421_hcd->do_iopin_update = 0; i_worked = 1; } } @@ -1485,7 +1480,8 @@ max3421_reset_port(struct usb_hcd *hcd) max3421_hcd->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED); - max3421_hcd->do_reset_port = 1; + max3421_hcd->port_status |= USB_PORT_STAT_RESET; + set_bit(RESET_PORT, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1498,7 +1494,7 @@ max3421_reset(struct usb_hcd *hcd) hcd->self.sg_tablesize = 0; hcd->speed = HCD_USB2; hcd->self.root_hub->speed = USB_SPEED_FULL; - max3421_hcd->do_reset_hcd = 1; + set_bit(RESET_HCD, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); return 0; } @@ -1551,7 +1547,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) max3421_ep = urb->ep->hcpriv; if (!max3421_ep) { /* gets freed in max3421_endpoint_disable: */ - max3421_ep = kzalloc(sizeof(struct max3421_ep), mem_flags); + max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC); if (!max3421_ep) { retval = -ENOMEM; goto out; @@ -1590,7 +1586,7 @@ max3421_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ retval = usb_hcd_check_unlink_urb(hcd, urb, status); if (retval == 0) { - max3421_hcd->do_check_unlink = 1; + set_bit(CHECK_UNLINK, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } spin_unlock_irqrestore(&max3421_hcd->lock, flags); @@ -1690,7 +1686,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value) max3421_hcd->iopins[idx] |= mask; else max3421_hcd->iopins[idx] &= ~mask; - max3421_hcd->do_iopin_update = 1; + set_bit(IOPIN_UPDATE, &max3421_hcd->todo); wake_up_process(max3421_hcd->spi_thread); } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 45032e933e18..04f2186939d2 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -236,7 +236,7 @@ ohci_dump_roothub ( } } -static void ohci_dump (struct ohci_hcd *controller, int verbose) +static void ohci_dump(struct ohci_hcd *controller) { ohci_dbg (controller, "OHCI controller state\n"); @@ -464,15 +464,16 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed) static ssize_t fill_async_buffer(struct debug_buffer *buf) { struct ohci_hcd *ohci; - size_t temp; + size_t temp, size; unsigned long flags; ohci = buf->ohci; + size = PAGE_SIZE; /* display control and bulk lists together, for simplicity */ spin_lock_irqsave (&ohci->lock, flags); - temp = show_list(ohci, buf->page, buf->count, ohci->ed_controltail); - temp += show_list(ohci, buf->page + temp, buf->count - temp, + temp = show_list(ohci, buf->page, size, ohci->ed_controltail); + temp += show_list(ohci, buf->page + temp, size - temp, ohci->ed_bulktail); spin_unlock_irqrestore (&ohci->lock, flags); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 060a6a414750..a72ab8fe8cd3 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -87,7 +87,7 @@ static int exynos_ohci_get_phy(struct device *dev, return -EINVAL; } - phy = devm_of_phy_get(dev, child, 0); + phy = devm_of_phy_get(dev, child, NULL); of_node_put(child); if (IS_ERR(phy)) { ret = PTR_ERR(phy); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f98d03f3144c..46987735a2e3 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -72,12 +72,14 @@ static const char hcd_name [] = "ohci_hcd"; #define STATECHANGE_DELAY msecs_to_jiffies(300) +#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) #include "ohci.h" #include "pci-quirks.h" -static void ohci_dump (struct ohci_hcd *ohci, int verbose); -static void ohci_stop (struct usb_hcd *hcd); +static void ohci_dump(struct ohci_hcd *ohci); +static void ohci_stop(struct usb_hcd *hcd); +static void io_watchdog_func(unsigned long _ohci); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -109,6 +111,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake"); /*-------------------------------------------------------------------------*/ +static int number_of_tds(struct urb *urb) +{ + int len, i, num, this_sg_len; + struct scatterlist *sg; + + len = urb->transfer_buffer_length; + i = urb->num_mapped_sgs; + + if (len > 0 && i > 0) { /* Scatter-gather transfer */ + num = 0; + sg = urb->sg; + for (;;) { + this_sg_len = min_t(int, sg_dma_len(sg), len); + num += DIV_ROUND_UP(this_sg_len, 4096); + len -= this_sg_len; + if (--i <= 0 || len <= 0) + break; + sg = sg_next(sg); + } + + } else { /* Non-SG transfer */ + /* one TD for every 4096 Bytes (could be up to 8K) */ + num = DIV_ROUND_UP(len, 4096); + } + return num; +} + /* * queue up an urb for anything except the root hub */ @@ -142,12 +171,8 @@ static int ohci_urb_enqueue ( // case PIPE_INTERRUPT: // case PIPE_BULK: default: - /* one TD for every 4096 Bytes (can be up to 8K) */ - size += urb->transfer_buffer_length / 4096; - /* ... and for any remaining bytes ... */ - if ((urb->transfer_buffer_length % 4096) != 0) - size++; - /* ... and maybe a zero length packet to wrap it up */ + size += number_of_tds(urb); + /* maybe a zero-length packet to wrap it up */ if (size == 0) size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 @@ -202,6 +227,16 @@ static int ohci_urb_enqueue ( usb_hcd_unlink_urb_from_ep(hcd, urb); goto fail; } + + /* Start up the I/O watchdog timer, if it's not running */ + if (!timer_pending(&ohci->io_watchdog) && + list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = ohci_frame_no(ohci); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + list_add(&ed->in_use_list, &ohci->eds_in_use); + if (ed->type == PIPE_ISOCHRONOUS) { u16 frame = ohci_frame_no(ohci); @@ -277,30 +312,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; int rc; + urb_priv_t *urb_priv; spin_lock_irqsave (&ohci->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (rc) { - ; /* Do nothing */ - } else if (ohci->rh_state == OHCI_RH_RUNNING) { - urb_priv_t *urb_priv; + if (rc == 0) { /* Unless an IRQ completed the unlink while it was being * handed to us, flag it for unlink and giveback, and force * some upcoming INTR_SF to call finish_unlinks() */ urb_priv = urb->hcpriv; - if (urb_priv) { - if (urb_priv->ed->state == ED_OPER) - start_ed_unlink (ohci, urb_priv->ed); + if (urb_priv->ed->state == ED_OPER) + start_ed_unlink(ohci, urb_priv->ed); + + if (ohci->rh_state != OHCI_RH_RUNNING) { + /* With HC dead, we can clean up right away */ + ohci_work(ohci); } - } else { - /* - * with HC dead, we won't respect hc queue pointers - * any more ... just clean up every urb's memory. - */ - if (urb->hcpriv) - finish_urb(ohci, urb, status); } spin_unlock_irqrestore (&ohci->lock, flags); return rc; @@ -332,9 +361,7 @@ rescan: if (ohci->rh_state != OHCI_RH_RUNNING) { sanitize: ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - finish_unlinks (ohci, 0); + ohci_work(ohci); } switch (ed->state) { @@ -342,11 +369,6 @@ sanitize: /* major IRQ delivery trouble loses INTR_SF too... */ if (limit-- == 0) { ohci_warn(ohci, "ED unlink timeout\n"); - if (quirk_zfmicro(ohci)) { - ohci_warn(ohci, "Attempting ZF TD recovery\n"); - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - } goto sanitize; } spin_unlock_irqrestore (&ohci->lock, flags); @@ -406,93 +428,7 @@ ohci_shutdown (struct usb_hcd *hcd) udelay(10); ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval); -} - -static int check_ed(struct ohci_hcd *ohci, struct ed *ed) -{ - return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0 - && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK) - == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK) - && !list_empty(&ed->td_list); -} - -/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes - * an interrupt TD but neglects to add it to the donelist. On systems with - * this chipset, we need to periodically check the state of the queues to look - * for such "lost" TDs. - */ -static void unlink_watchdog_func(unsigned long _ohci) -{ - unsigned long flags; - unsigned max; - unsigned seen_count = 0; - unsigned i; - struct ed **seen = NULL; - struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; - - spin_lock_irqsave(&ohci->lock, flags); - max = ohci->eds_scheduled; - if (!max) - goto done; - - if (ohci->ed_to_check) - goto out; - - seen = kcalloc(max, sizeof *seen, GFP_ATOMIC); - if (!seen) - goto out; - - for (i = 0; i < NUM_INTS; i++) { - struct ed *ed = ohci->periodic[i]; - - while (ed) { - unsigned temp; - - /* scan this branch of the periodic schedule tree */ - for (temp = 0; temp < seen_count; temp++) { - if (seen[temp] == ed) { - /* we've checked it and what's after */ - ed = NULL; - break; - } - } - if (!ed) - break; - seen[seen_count++] = ed; - if (!check_ed(ohci, ed)) { - ed = ed->ed_next; - continue; - } - - /* HC's TD list is empty, but HCD sees at least one - * TD that's not been sent through the donelist. - */ - ohci->ed_to_check = ed; - ohci->zf_delay = 2; - - /* The HC may wait until the next frame to report the - * TD as done through the donelist and INTR_WDH. (We - * just *assume* it's not a multi-TD interrupt URB; - * those could defer the IRQ more than one frame, using - * DI...) Check again after the next INTR_SF. - */ - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrstatus); - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrenable); - - /* flush those writes */ - (void) ohci_readl(ohci, &ohci->regs->control); - - goto out; - } - } -out: - kfree(seen); - if (ohci->eds_scheduled) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); -done: - spin_unlock_irqrestore(&ohci->lock, flags); + ohci->rh_state = OHCI_RH_HALTED; } /*-------------------------------------------------------------------------* @@ -506,6 +442,9 @@ static int ohci_init (struct ohci_hcd *ohci) int ret; struct usb_hcd *hcd = ohci_to_hcd(ohci); + /* Accept arbitrarily long scatter-gather lists */ + hcd->self.sg_tablesize = ~0; + if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; @@ -558,8 +497,12 @@ static int ohci_init (struct ohci_hcd *ohci) if (ohci->hcca) return 0; + setup_timer(&ohci->io_watchdog, io_watchdog_func, + (unsigned long) ohci); + set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20)); + ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); + sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL); if (!ohci->hcca) return -ENOMEM; @@ -735,16 +678,7 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((val >> 23) & 0x1fe); - if (quirk_zfmicro(ohci)) { - /* Create timer to watch for bad queue state on ZF Micro */ - setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func, - (unsigned long) ohci); - - ohci->eds_scheduled = 0; - ohci->ed_to_check = NULL; - } - - ohci_dump (ohci, 1); + ohci_dump(ohci); return 0; } @@ -777,6 +711,142 @@ static int ohci_start(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ +/* + * Some OHCI controllers are known to lose track of completed TDs. They + * don't add the TDs to the hardware done queue, which means we never see + * them as being completed. + * + * This watchdog routine checks for such problems. Without some way to + * tell when those TDs have completed, we would never take their EDs off + * the unlink list. As a result, URBs could never be dequeued and + * endpoints could never be released. + */ +static void io_watchdog_func(unsigned long _ohci) +{ + struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; + bool takeback_all_pending = false; + u32 status; + u32 head; + struct ed *ed; + struct td *td, *td_start, *td_next; + unsigned frame_no; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + /* + * One way to lose track of completed TDs is if the controller + * never writes back the done queue head. If it hasn't been + * written back since the last time this function ran and if it + * was non-empty at that time, something is badly wrong with the + * hardware. + */ + status = ohci_readl(ohci, &ohci->regs->intrstatus); + if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { + if (ohci->prev_donehead) { + ohci_err(ohci, "HcDoneHead not written back; disabled\n"); + died: + usb_hc_died(ohci_to_hcd(ohci)); + ohci_dump(ohci); + ohci_shutdown(ohci_to_hcd(ohci)); + goto done; + } else { + /* No write back because the done queue was empty */ + takeback_all_pending = true; + } + } + + /* Check every ED which might have pending TDs */ + list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) { + if (ed->pending_td) { + if (takeback_all_pending || + OKAY_TO_TAKEBACK(ohci, ed)) { + unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO); + + ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n", + 0x007f & tmp, + (0x000f & (tmp >> 7)) + + ((tmp & ED_IN) >> 5)); + add_to_done_list(ohci, ed->pending_td); + } + } + + /* Starting from the latest pending TD, */ + td = ed->pending_td; + + /* or the last TD on the done list, */ + if (!td) { + list_for_each_entry(td_next, &ed->td_list, td_list) { + if (!td_next->next_dl_td) + break; + td = td_next; + } + } + + /* find the last TD processed by the controller. */ + head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK; + td_start = td; + td_next = list_prepare_entry(td, &ed->td_list, td_list); + list_for_each_entry_continue(td_next, &ed->td_list, td_list) { + if (head == (u32) td_next->td_dma) + break; + td = td_next; /* head pointer has passed this TD */ + } + if (td != td_start) { + /* + * In case a WDH cycle is in progress, we will wait + * for the next two cycles to complete before assuming + * this TD will never get on the done queue. + */ + ed->takeback_wdh_cnt = ohci->wdh_cnt + 2; + ed->pending_td = td; + } + } + + ohci_work(ohci); + + if (ohci->rh_state == OHCI_RH_RUNNING) { + + /* + * Sometimes a controller just stops working. We can tell + * by checking that the frame counter has advanced since + * the last time we ran. + * + * But be careful: Some controllers violate the spec by + * stopping their frame counter when no ports are active. + */ + frame_no = ohci_frame_no(ohci); + if (frame_no == ohci->prev_frame_no) { + int active_cnt = 0; + int i; + unsigned tmp; + + for (i = 0; i < ohci->num_ports; ++i) { + tmp = roothub_portstatus(ohci, i); + /* Enabled and not suspended? */ + if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) + ++active_cnt; + } + + if (active_cnt > 0) { + ohci_err(ohci, "frame counter not updating; disabled\n"); + goto died; + } + } + if (!list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = frame_no; + ohci->prev_wdh_cnt = ohci->wdh_cnt; + ohci->prev_donehead = ohci_readl(ohci, + &ohci->regs->donehead); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } + } + + done: + spin_unlock_irqrestore(&ohci->lock, flags); +} + /* an interrupt happens */ static irqreturn_t ohci_irq (struct usb_hcd *hcd) @@ -825,7 +895,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hc_died(hcd); } - ohci_dump (ohci, 1); + ohci_dump(ohci); ohci_usb_reset (ohci); } @@ -863,58 +933,30 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) usb_hcd_resume_root_hub(hcd); } - if (ints & OHCI_INTR_WDH) { - spin_lock (&ohci->lock); - dl_done_list (ohci); - spin_unlock (&ohci->lock); - } - - if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) { - spin_lock(&ohci->lock); - if (ohci->ed_to_check) { - struct ed *ed = ohci->ed_to_check; - - if (check_ed(ohci, ed)) { - /* HC thinks the TD list is empty; HCD knows - * at least one TD is outstanding - */ - if (--ohci->zf_delay == 0) { - struct td *td = list_entry( - ed->td_list.next, - struct td, td_list); - ohci_warn(ohci, - "Reclaiming orphan TD %p\n", - td); - takeback_td(ohci, td); - ohci->ed_to_check = NULL; - } - } else - ohci->ed_to_check = NULL; - } - spin_unlock(&ohci->lock); - } + spin_lock(&ohci->lock); + if (ints & OHCI_INTR_WDH) + update_done_list(ohci); /* could track INTR_SO to reduce available PCI/... bandwidth */ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled * when there's still unlinking to be done (next frame). */ - spin_lock (&ohci->lock); - if (ohci->ed_rm_list) - finish_unlinks (ohci, ohci_frame_no(ohci)); - if ((ints & OHCI_INTR_SF) != 0 - && !ohci->ed_rm_list - && !ohci->ed_to_check + ohci_work(ohci); + if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && ohci->rh_state == OHCI_RH_RUNNING) ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); - spin_unlock (&ohci->lock); if (ohci->rh_state == OHCI_RH_RUNNING) { ohci_writel (ohci, ints, ®s->intrstatus); + if (ints & OHCI_INTR_WDH) + ++ohci->wdh_cnt; + ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); // flush those writes (void) ohci_readl (ohci, &ohci->regs->control); } + spin_unlock(&ohci->lock); return IRQ_HANDLED; } @@ -925,18 +967,17 @@ static void ohci_stop (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dump (ohci, 1); + ohci_dump(ohci); if (quirk_nec(ohci)) flush_work(&ohci->nec_work); + del_timer_sync(&ohci->io_watchdog); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_usb_reset(ohci); free_irq(hcd->irq, hcd); hcd->irq = 0; - if (quirk_zfmicro(ohci)) - del_timer(&ohci->unlink_watchdog); if (quirk_amdiso(ohci)) usb_amd_dev_put(); @@ -993,7 +1034,7 @@ int ohci_restart(struct ohci_hcd *ohci) if (!urb->unlinked) urb->unlinked = -ESHUTDOWN; } - finish_unlinks (ohci, 0); + ohci_work(ohci); spin_unlock_irq(&ohci->lock); /* paranoia, in case that didn't work: */ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index b4940de1eba3..17d32b0ea565 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -39,8 +39,8 @@ #define OHCI_SCHED_ENABLES \ (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) -static void dl_done_list (struct ohci_hcd *); -static void finish_unlinks (struct ohci_hcd *, u16); +static void update_done_list(struct ohci_hcd *); +static void ohci_work(struct ohci_hcd *); #ifdef CONFIG_PM static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) @@ -87,8 +87,8 @@ __acquires(ohci->lock) msleep (8); spin_lock_irq (&ohci->lock); } - dl_done_list (ohci); - finish_unlinks (ohci, ohci_frame_no(ohci)); + update_done_list(ohci); + ohci_work(ohci); /* * Some controllers don't handle "global" suspend properly if @@ -309,6 +309,9 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) else rc = ohci_rh_suspend (ohci, 0); spin_unlock_irq (&ohci->lock); + + if (rc == 0) + del_timer_sync(&ohci->io_watchdog); return rc; } diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 2f20d3dc895b..c9e315c6808a 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ohci->next_statechange = jiffies; spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); + INIT_LIST_HEAD(&ohci->eds_in_use); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index d4253e319428..1463c398d322 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) ed->ed_prev = NULL; ed->ed_next = NULL; ed->hwNextED = 0; - if (quirk_zfmicro(ohci) - && (ed->type == PIPE_INTERRUPT) - && !(ohci->eds_scheduled++)) - mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ)); wmb (); /* we care about rm_list when setting CLE/BLE in case the HC was at @@ -311,8 +307,7 @@ static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed) * - ED_OPER: when there's any request queued, the ED gets rescheduled * immediately. HC should be working on them. * - * - ED_IDLE: when there's no TD queue. there's no reason for the HC - * to care about this ED; safe to disable the endpoint. + * - ED_IDLE: when there's no TD queue or the HC isn't running. * * When finish_unlinks() runs later, after SOF interrupt, it will often * complete one or more URB unlinks before making that state change. @@ -602,6 +597,8 @@ static void td_submit_urb ( u32 info = 0; int is_out = usb_pipeout (urb->pipe); int periodic = 0; + int i, this_sg_len, n; + struct scatterlist *sg; /* OHCI handles the bulk/interrupt data toggles itself. We just * use the device toggle bits for resetting, and rely on the fact @@ -615,10 +612,24 @@ static void td_submit_urb ( list_add (&urb_priv->pending, &ohci->pending); - if (data_len) - data = urb->transfer_dma; - else - data = 0; + i = urb->num_mapped_sgs; + if (data_len > 0 && i > 0) { + sg = urb->sg; + data = sg_dma_address(sg); + + /* + * urb->transfer_buffer_length may be smaller than the + * size of the scatterlist (or vice versa) + */ + this_sg_len = min_t(int, sg_dma_len(sg), data_len); + } else { + sg = NULL; + if (data_len) + data = urb->transfer_dma; + else + data = 0; + this_sg_len = data_len; + } /* NOTE: TD_CC is set so we can tell which TDs the HC processed by * using TD_CC_GET, as well as by seeing them on the done list. @@ -639,17 +650,29 @@ static void td_submit_urb ( ? TD_T_TOGGLE | TD_CC | TD_DP_OUT : TD_T_TOGGLE | TD_CC | TD_DP_IN; /* TDs _could_ transfer up to 8K each */ - while (data_len > 4096) { - td_fill (ohci, info, data, 4096, urb, cnt); - data += 4096; - data_len -= 4096; + for (;;) { + n = min(this_sg_len, 4096); + + /* maybe avoid ED halt on final TD short read */ + if (n >= data_len || (i == 1 && n >= this_sg_len)) { + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + info |= TD_R; + } + td_fill(ohci, info, data, n, urb, cnt); + this_sg_len -= n; + data_len -= n; + data += n; cnt++; + + if (this_sg_len <= 0) { + if (--i <= 0 || data_len <= 0) + break; + sg = sg_next(sg); + data = sg_dma_address(sg); + this_sg_len = min_t(int, sg_dma_len(sg), + data_len); + } } - /* maybe avoid ED halt on final TD short read */ - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) - info |= TD_R; - td_fill (ohci, info, data, data_len, urb, cnt); - cnt++; if ((urb->transfer_flags & URB_ZERO_PACKET) && cnt < urb_priv->length) { td_fill (ohci, info, 0, 0, urb, cnt); @@ -869,13 +892,46 @@ static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc) } } -/* replies to the request have to be on a FIFO basis so - * we unreverse the hc-reversed done-list - */ -static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) +/* Add a TD to the done list */ +static void add_to_done_list(struct ohci_hcd *ohci, struct td *td) +{ + struct td *td2, *td_prev; + struct ed *ed; + + if (td->next_dl_td) + return; /* Already on the list */ + + /* Add all the TDs going back until we reach one that's on the list */ + ed = td->ed; + td2 = td_prev = td; + list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) { + if (td2->next_dl_td) + break; + td2->next_dl_td = td_prev; + td_prev = td2; + } + + if (ohci->dl_end) + ohci->dl_end->next_dl_td = td_prev; + else + ohci->dl_start = td_prev; + + /* + * Make td->next_dl_td point to td itself, to mark the fact + * that td is on the done list. + */ + ohci->dl_end = td->next_dl_td = td; + + /* Did we just add the latest pending TD? */ + td2 = ed->pending_td; + if (td2 && td2->next_dl_td) + ed->pending_td = NULL; +} + +/* Get the entries on the hardware done queue and put them on our list */ +static void update_done_list(struct ohci_hcd *ohci) { u32 td_dma; - struct td *td_rev = NULL; struct td *td = NULL; td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head); @@ -883,7 +939,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) wmb(); /* get TD from hc's singly linked list, and - * prepend to ours. ed->td_list changes later. + * add to ours. ed->td_list changes later. */ while (td_dma) { int cc; @@ -905,19 +961,17 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) && (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H))) ed_halted(ohci, td, cc); - td->next_dl_td = td_rev; - td_rev = td; td_dma = hc32_to_cpup (ohci, &td->hwNextTD); + add_to_done_list(ohci, td); } - return td_rev; } /*-------------------------------------------------------------------------*/ /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ -static void -finish_unlinks (struct ohci_hcd *ohci, u16 tick) +static void finish_unlinks(struct ohci_hcd *ohci) { + unsigned tick = ohci_frame_no(ohci); struct ed *ed, **last; rescan_all: @@ -926,41 +980,48 @@ rescan_all: int completed, modified; __hc32 *prev; + /* Is this ED already invisible to the hardware? */ + if (ed->state == ED_IDLE) + goto ed_idle; + /* only take off EDs that the HC isn't using, accounting for * frame counter wraps and EDs with partially retired TDs */ - if (likely(ohci->rh_state == OHCI_RH_RUNNING)) { - if (tick_before (tick, ed->tick)) { + if (likely(ohci->rh_state == OHCI_RH_RUNNING) && + tick_before(tick, ed->tick)) { skip_ed: - last = &ed->ed_next; - continue; - } + last = &ed->ed_next; + continue; + } + if (!list_empty(&ed->td_list)) { + struct td *td; + u32 head; - if (!list_empty (&ed->td_list)) { - struct td *td; - u32 head; - - td = list_entry (ed->td_list.next, struct td, - td_list); - head = hc32_to_cpu (ohci, ed->hwHeadP) & - TD_MASK; - - /* INTR_WDH may need to clean up first */ - if (td->td_dma != head) { - if (ed == ohci->ed_to_check) - ohci->ed_to_check = NULL; - else - goto skip_ed; - } - } + td = list_first_entry(&ed->td_list, struct td, td_list); + + /* INTR_WDH may need to clean up first */ + head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK; + if (td->td_dma != head && + ohci->rh_state == OHCI_RH_RUNNING) + goto skip_ed; + + /* Don't mess up anything already on the done list */ + if (td->next_dl_td) + goto skip_ed; } + /* ED's now officially unlinked, hc doesn't see */ + ed->state = ED_IDLE; + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); + ed->hwNextED = 0; + wmb(); + ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE); +ed_idle: + /* reentrancy: if we drop the schedule lock, someone might * have modified this list. normally it's just prepending * entries (which we'd ignore), but paranoia won't hurt. */ - *last = ed->ed_next; - ed->ed_next = NULL; modified = 0; /* unlink urbs as requested, but rescan the list after @@ -1018,19 +1079,21 @@ rescan_this: if (completed && !list_empty (&ed->td_list)) goto rescan_this; - /* ED's now officially unlinked, hc doesn't see */ - ed->state = ED_IDLE; - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) - ohci->eds_scheduled--; - ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); - ed->hwNextED = 0; - wmb (); - ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE); - - /* but if there's work queued, reschedule */ - if (!list_empty (&ed->td_list)) { - if (ohci->rh_state == OHCI_RH_RUNNING) - ed_schedule (ohci, ed); + /* + * If no TDs are queued, take ED off the ed_rm_list. + * Otherwise, if the HC is running, reschedule. + * If not, leave it on the list for further dequeues. + */ + if (list_empty(&ed->td_list)) { + *last = ed->ed_next; + ed->ed_next = NULL; + list_del(&ed->in_use_list); + } else if (ohci->rh_state == OHCI_RH_RUNNING) { + *last = ed->ed_next; + ed->ed_next = NULL; + ed_schedule(ohci, ed); + } else { + last = &ed->ed_next; } if (modified) @@ -1082,12 +1145,7 @@ rescan_this: /*-------------------------------------------------------------------------*/ -/* - * Used to take back a TD from the host controller. This would normally be - * called from within dl_done_list, however it may be called directly if the - * HC no longer sees the TD and it has not appeared on the donelist (after - * two frames). This bug has been observed on ZF Micro systems. - */ +/* Take back a TD from the host controller */ static void takeback_td(struct ohci_hcd *ohci, struct td *td) { struct urb *urb = td->urb; @@ -1134,37 +1192,43 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) * * This is the main path for handing urbs back to drivers. The only other * normal path is finish_unlinks(), which unlinks URBs using ed_rm_list, - * instead of scanning the (re-reversed) donelist as this does. There's - * an abnormal path too, handling a quirk in some Compaq silicon: URBs - * with TDs that appear to be orphaned are directly reclaimed. + * instead of scanning the (re-reversed) donelist as this does. */ -static void -dl_done_list (struct ohci_hcd *ohci) +static void process_done_list(struct ohci_hcd *ohci) { - struct td *td = dl_reverse_done_list (ohci); + struct td *td; - while (td) { - struct td *td_next = td->next_dl_td; - struct ed *ed = td->ed; + while (ohci->dl_start) { + td = ohci->dl_start; + if (td == ohci->dl_end) + ohci->dl_start = ohci->dl_end = NULL; + else + ohci->dl_start = td->next_dl_td; - /* - * Some OHCI controllers (NVIDIA for sure, maybe others) - * occasionally forget to add TDs to the done queue. Since - * TDs for a given endpoint are always processed in order, - * if we find a TD on the donelist then all of its - * predecessors must be finished as well. - */ - for (;;) { - struct td *td2; + takeback_td(ohci, td); + } +} - td2 = list_first_entry(&ed->td_list, struct td, - td_list); - if (td2 == td) - break; - takeback_td(ohci, td2); - } +/* + * TD takeback and URB giveback must be single-threaded. + * This routine takes care of it all. + */ +static void ohci_work(struct ohci_hcd *ohci) +{ + if (ohci->working) { + ohci->restart_work = 1; + return; + } + ohci->working = 1; - takeback_td(ohci, td); - td = td_next; + restart: + process_done_list(ohci); + if (ohci->ed_rm_list) + finish_unlinks(ohci); + + if (ohci->restart_work) { + ohci->restart_work = 0; + goto restart; } + ohci->working = 0; } diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 8b29a0c04c23..8d5876692e7c 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -162,7 +162,7 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev) } #endif -static struct of_device_id spear_ohci_id_table[] = { +static const struct of_device_id spear_ohci_id_table[] = { { .compatible = "st,spear600-ohci", }, { }, }; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 05e02a709d4f..59f424567a8d 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -47,6 +47,7 @@ struct ed { struct ed *ed_next; /* on schedule or rm_list */ struct ed *ed_prev; /* for non-interrupt EDs */ struct list_head td_list; /* "shadow list" of our TDs */ + struct list_head in_use_list; /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... @@ -66,6 +67,13 @@ struct ed { /* HC may see EDs on rm_list until next frame (frame_no == tick) */ u16 tick; + + /* Detect TDs not added to the done queue */ + unsigned takeback_wdh_cnt; + struct td *pending_td; +#define OKAY_TO_TAKEBACK(ohci, ed) \ + ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0) + } __attribute__ ((aligned(16))); #define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */ @@ -380,7 +388,9 @@ struct ohci_hcd { struct dma_pool *td_cache; struct dma_pool *ed_cache; struct td *td_hash [TD_HASH_SIZE]; + struct td *dl_start, *dl_end; /* the done list */ struct list_head pending; + struct list_head eds_in_use; /* all EDs with at least 1 TD */ /* * driver state @@ -392,6 +402,8 @@ struct ohci_hcd { unsigned long next_statechange; /* suspend/resume */ u32 fminterval; /* saved register */ unsigned autostop:1; /* rh auto stopping/stopped */ + unsigned working:1; + unsigned restart_work:1; unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ @@ -409,13 +421,12 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic - struct work_struct nec_work; /* Worker for NEC quirk */ + unsigned prev_frame_no; + unsigned wdh_cnt, prev_wdh_cnt; + u32 prev_donehead; + struct timer_list io_watchdog; - /* Needed for ZF Micro quirk */ - struct timer_list unlink_watchdog; - unsigned eds_scheduled; - struct ed *ed_to_check; - unsigned zf_delay; + struct work_struct nec_work; /* Worker for NEC quirk */ struct dentry *debug_dir; struct dentry *debug_async; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e07248b6ab67..da5fb0e3c363 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -3826,49 +3826,36 @@ static int oxu_drv_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no registers address! Check %s setup!\n", - dev_name(&pdev->dev)); - return -ENODEV; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto error; } memstart = res->start; memlen = resource_size(res); - dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); - if (!request_mem_region(memstart, memlen, - oxu_hc_driver.description)) { - dev_dbg(&pdev->dev, "memory area already in use\n"); - return -EBUSY; - } ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); if (ret) { dev_err(&pdev->dev, "error setting irq type\n"); ret = -EFAULT; - goto error_set_irq_type; - } - - base = ioremap(memstart, memlen); - if (!base) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto error_ioremap; + goto error; } /* Allocate a driver data struct to hold useful info for both * SPH & OTG devices */ - info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL); if (!info) { dev_dbg(&pdev->dev, "error allocating memory\n"); ret = -EFAULT; - goto error_alloc; + goto error; } platform_set_drvdata(pdev, info); ret = oxu_init(pdev, memstart, memlen, base, irq); if (ret < 0) { dev_dbg(&pdev->dev, "cannot init USB devices\n"); - goto error_init; + goto error; } dev_info(&pdev->dev, "devices enabled and running\n"); @@ -3876,16 +3863,7 @@ static int oxu_drv_probe(struct platform_device *pdev) return 0; -error_init: - kfree(info); - -error_alloc: - iounmap(base); - -error_set_irq_type: -error_ioremap: - release_mem_region(memstart, memlen); - +error: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); return ret; } @@ -3899,18 +3877,10 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) static int oxu_drv_remove(struct platform_device *pdev) { struct oxu_info *info = platform_get_drvdata(pdev); - unsigned long memstart = info->hcd[0]->rsrc_start, - memlen = info->hcd[0]->rsrc_len; - void *base = info->hcd[0]->regs; oxu_remove(pdev, info->hcd[0]); oxu_remove(pdev, info->hcd[1]); - iounmap(base); - release_mem_region(memstart, memlen); - - kfree(info); - return 0; } diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index ab25dc397e8b..05f57ffdf9ab 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -17,6 +17,7 @@ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu */ +#include <linux/device.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -113,24 +114,17 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); - rv = -EBUSY; - goto err_rmr; - } - irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); rv = -EBUSY; - goto err_irq; + goto err_usb; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - printk(KERN_ERR "%s: ioremap failed\n", __FILE__); - rv = -ENOMEM; - goto err_ioremap; + hcd->regs = devm_ioremap_resource(&op->dev, &res); + if (IS_ERR(hcd->regs)) { + rv = PTR_ERR(hcd->regs); + goto err_irq; } uhci = hcd_to_uhci(hcd); @@ -139,18 +133,14 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) rv = usb_add_hcd(hcd, irq, 0); if (rv) - goto err_uhci; + goto err_irq; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_ioremap: - irq_dispose_mapping(irq); err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err_rmr: + irq_dispose_mapping(irq); +err_usb: usb_put_hcd(hcd); return rv; @@ -164,10 +154,7 @@ static int uhci_hcd_grlib_remove(struct platform_device *op) usb_remove_hcd(hcd); - iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 27f35e8f161b..a7de8e8bb458 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -591,7 +591,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->frame = dma_alloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), - &uhci->frame_dma_handle, 0); + &uhci->frame_dma_handle, GFP_KERNEL); if (!uhci->frame) { dev_err(uhci_dev(uhci), "unable to allocate consistent memory for frame list\n"); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 01833ab2b5c3..b987f1d10058 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -8,6 +8,7 @@ */ #include <linux/of.h> +#include <linux/device.h> #include <linux/platform_device.h> static int uhci_platform_init(struct usb_hcd *hcd) @@ -88,33 +89,22 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_err("%s: request_mem_region failed\n", __func__); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto err_rmr; } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_err("%s: ioremap failed\n", __func__); - ret = -ENOMEM; - goto err_irq; - } uhci = hcd_to_uhci(hcd); uhci->regs = hcd->regs; ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret) - goto err_uhci; + goto err_rmr; device_wakeup_enable(hcd->self.controller); return 0; -err_uhci: - iounmap(hcd->regs); -err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_rmr: usb_put_hcd(hcd); @@ -126,8 +116,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index e20520f42753..c22a3e15a16e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -33,7 +33,7 @@ #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 #define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_ASROCK_P67 0x7023 +#define PCI_DEVICE_ID_EJ168 0x7023 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -101,6 +101,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; + + if (pdev->vendor == PCI_VENDOR_ID_AMD) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_INTEL_HOST; @@ -140,9 +144,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_SPURIOUS_REBOOT; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_ASROCK_P67) { + pdev->device == PCI_DEVICE_ID_EJ168) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_TRUST_TX_LENGTH; + xhci->quirks |= XHCI_BROKEN_STREAMS; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && pdev->device == 0x0015) @@ -150,6 +155,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; + /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ + if (pdev->vendor == PCI_VENDOR_ID_VIA && + pdev->device == 0x3432) + xhci->quirks |= XHCI_BROKEN_STREAMS; + if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Resetting on resume"); @@ -230,7 +240,8 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && + HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 29d8adb5c8d1..1a0cf9f31e43 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -17,9 +17,11 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/usb/xhci_pdriver.h> #include "xhci.h" #include "xhci-mvebu.h" +#include "xhci-rcar.h" static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { @@ -34,11 +36,27 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) /* called during probe() after chip reset completes */ static int xhci_plat_setup(struct usb_hcd *hcd) { + struct device_node *of_node = hcd->self.controller->of_node; + int ret; + + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) { + ret = xhci_rcar_init_quirk(hcd); + if (ret) + return ret; + } + return xhci_gen_setup(hcd, xhci_plat_quirks); } static int xhci_plat_start(struct usb_hcd *hcd) { + struct device_node *of_node = hcd->self.controller->of_node; + + if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") || + of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) + xhci_rcar_start(hcd); + return xhci_run(hcd); } @@ -90,10 +108,15 @@ static const struct hc_driver xhci_plat_xhci_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + + .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, + .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, }; static int xhci_plat_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev); const struct hc_driver *driver; struct xhci_hcd *xhci; struct resource *res; @@ -140,20 +163,12 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) { - dev_dbg(&pdev->dev, "controller already in use\n"); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto put_hcd; } - hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - ret = -EFAULT; - goto release_mem_region; - } - /* * Not all platforms have a clk so it is not an error if the * clock does not exists. @@ -162,7 +177,7 @@ static int xhci_plat_probe(struct platform_device *pdev) if (!IS_ERR(clk)) { ret = clk_prepare_enable(clk); if (ret) - goto unmap_registers; + goto put_hcd; } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); @@ -182,6 +197,9 @@ static int xhci_plat_probe(struct platform_device *pdev) goto dealloc_usb2_hcd; } + if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || + (pdata && pdata->usb3_lpm_capable)) + xhci->quirks |= XHCI_LPM_SUPPORT; /* * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) * is called by usb_add_hcd(). @@ -207,12 +225,6 @@ disable_clk: if (!IS_ERR(clk)) clk_disable_unprepare(clk); -unmap_registers: - iounmap(hcd->regs); - -release_mem_region: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - put_hcd: usb_put_hcd(hcd); @@ -231,8 +243,6 @@ static int xhci_plat_remove(struct platform_device *dev) usb_remove_hcd(hcd); if (!IS_ERR(clk)) clk_disable_unprepare(clk); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); kfree(xhci); @@ -270,6 +280,8 @@ static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "xhci-platform" }, { .compatible = "marvell,armada-375-xhci"}, { .compatible = "marvell,armada-380-xhci"}, + { .compatible = "renesas,xhci-r8a7790"}, + { .compatible = "renesas,xhci-r8a7791"}, { }, }; MODULE_DEVICE_TABLE(of, usb_xhci_of_match); diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c new file mode 100644 index 000000000000..ff0d1b44ea58 --- /dev/null +++ b/drivers/usb/host/xhci-rcar.c @@ -0,0 +1,148 @@ +/* + * xHCI host controller driver for R-Car SoCs + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/phy.h> + +#include "xhci.h" +#include "xhci-rcar.h" + +#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem" +MODULE_FIRMWARE(FIRMWARE_NAME); + +/*** Register Offset ***/ +#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ +#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ +#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ + +#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ +#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ +#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ +#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ +#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ +#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ + +/*** Register Settings ***/ +/* Interrupt Enable */ +#define RCAR_USB3_INT_XHC_ENA 0x00000001 +#define RCAR_USB3_INT_PME_ENA 0x00000002 +#define RCAR_USB3_INT_HSE_ENA 0x00000004 +#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ + RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) + +/* FW Download Control & Status */ +#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 +#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 +#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 + +/* LCLK Select */ +#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 + +/* USB3.0 Configuration */ +#define RCAR_USB3_CONF1_VAL 0x00030204 +#define RCAR_USB3_CONF2_VAL 0x00030300 +#define RCAR_USB3_CONF3_VAL 0x13802007 + +/* USB3.0 Polarity */ +#define RCAR_USB3_RX_POL_VAL BIT(21) +#define RCAR_USB3_TX_POL_VAL BIT(4) + +void xhci_rcar_start(struct usb_hcd *hcd) +{ + u32 temp; + + if (hcd->regs != NULL) { + /* Interrupt Enable */ + temp = readl(hcd->regs + RCAR_USB3_INT_ENA); + temp |= RCAR_USB3_INT_ENA_VAL; + writel(temp, hcd->regs + RCAR_USB3_INT_ENA); + /* LCLK Select */ + writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); + /* USB3.0 Configuration */ + writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); + writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); + writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); + /* USB3.0 Polarity */ + writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); + writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); + } +} + +static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs) +{ + const struct firmware *fw; + int retval, index, j, time; + int timeout = 10000; + u32 data, val, temp; + + /* request R-Car USB3.0 firmware */ + retval = request_firmware(&fw, FIRMWARE_NAME, dev); + if (retval) + return retval; + + /* download R-Car USB3.0 firmware */ + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (index = 0; index < fw->size; index += 4) { + /* to avoid reading beyond the end of the buffer */ + for (data = 0, j = 3; j >= 0; j--) { + if ((j + index) < fw->size) + data |= fw->data[index + j] << (8 * j); + } + writel(data, regs + RCAR_USB3_FW_DATA0); + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0) + break; + udelay(1); + } + if (time == timeout) { + retval = -ETIMEDOUT; + break; + } + } + + temp = readl(regs + RCAR_USB3_DL_CTRL); + temp &= ~RCAR_USB3_DL_CTRL_ENABLE; + writel(temp, regs + RCAR_USB3_DL_CTRL); + + for (time = 0; time < timeout; time++) { + val = readl(regs + RCAR_USB3_DL_CTRL); + if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) { + retval = 0; + break; + } + udelay(1); + } + if (time == timeout) + retval = -ETIMEDOUT; + + release_firmware(fw); + + return retval; +} + +/* This function needs to initialize a "phy" of usb before */ +int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + /* If hcd->regs is NULL, we don't just call the following function */ + if (!hcd->regs) + return 0; + + return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs); +} diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h new file mode 100644 index 000000000000..58501256715d --- /dev/null +++ b/drivers/usb/host/xhci-rcar.h @@ -0,0 +1,27 @@ +/* + * drivers/usb/host/xhci-rcar.h + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef _XHCI_RCAR_H +#define _XHCI_RCAR_H + +#if IS_ENABLED(CONFIG_USB_XHCI_RCAR) +void xhci_rcar_start(struct usb_hcd *hcd); +int xhci_rcar_init_quirk(struct usb_hcd *hcd); +#else +static inline void xhci_rcar_start(struct usb_hcd *hcd) +{ +} + +static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd) +{ + return 0; +} +#endif +#endif /* _XHCI_RCAR_H */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 749fc68eb5c1..abed30b82905 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -364,32 +364,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, } } -/* - * Find the segment that trb is in. Start searching in start_seg. - * If we must move past a segment that has a link TRB with a toggle cycle state - * bit set, then we will toggle the value pointed at by cycle_state. - */ -static struct xhci_segment *find_trb_seg( - struct xhci_segment *start_seg, - union xhci_trb *trb, int *cycle_state) -{ - struct xhci_segment *cur_seg = start_seg; - struct xhci_generic_trb *generic_trb; - - while (cur_seg->trbs > trb || - &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { - generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE)) - *cycle_state ^= 0x1; - cur_seg = cur_seg->next; - if (cur_seg == start_seg) - /* Looped over the entire list. Oops! */ - return NULL; - } - return cur_seg; -} - - static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id) @@ -459,9 +433,12 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_virt_device *dev = xhci->devs[slot_id]; struct xhci_virt_ep *ep = &dev->eps[ep_index]; struct xhci_ring *ep_ring; - struct xhci_generic_trb *trb; + struct xhci_segment *new_seg; + union xhci_trb *new_deq; dma_addr_t addr; u64 hw_dequeue; + bool cycle_found = false; + bool td_last_trb_found = false; ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, ep_index, stream_id); @@ -486,45 +463,45 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, hw_dequeue = le64_to_cpu(ep_ctx->deq); } - /* Find virtual address and segment of hardware dequeue pointer */ - state->new_deq_seg = ep_ring->deq_seg; - state->new_deq_ptr = ep_ring->dequeue; - while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr) - != (dma_addr_t)(hw_dequeue & ~0xf)) { - next_trb(xhci, ep_ring, &state->new_deq_seg, - &state->new_deq_ptr); - if (state->new_deq_ptr == ep_ring->dequeue) { - WARN_ON(1); - return; - } - } + new_seg = ep_ring->deq_seg; + new_deq = ep_ring->dequeue; + state->new_cycle_state = hw_dequeue & 0x1; + /* - * Find cycle state for last_trb, starting at old cycle state of - * hw_dequeue. If there is only one segment ring, find_trb_seg() will - * return immediately and cannot toggle the cycle state if this search - * wraps around, so add one more toggle manually in that case. + * We want to find the pointer, segment and cycle state of the new trb + * (the one after current TD's last_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * found. */ - state->new_cycle_state = hw_dequeue & 0x1; - if (ep_ring->first_seg == ep_ring->first_seg->next && - cur_td->last_trb < state->new_deq_ptr) - state->new_cycle_state ^= 0x1; + do { + if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq) + == (dma_addr_t)(hw_dequeue & ~0xf)) { + cycle_found = true; + if (td_last_trb_found) + break; + } + if (new_deq == cur_td->last_trb) + td_last_trb_found = true; - state->new_deq_ptr = cur_td->last_trb; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Finding segment containing last TRB in TD."); - state->new_deq_seg = find_trb_seg(state->new_deq_seg, - state->new_deq_ptr, &state->new_cycle_state); - if (!state->new_deq_seg) { - WARN_ON(1); - return; - } + if (cycle_found && + TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) && + new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE)) + state->new_cycle_state ^= 0x1; - /* Increment to find next TRB after last_trb. Cycle if appropriate. */ - trb = &state->new_deq_ptr->generic; - if (TRB_TYPE_LINK_LE32(trb->field[3]) && - (trb->field[3] & cpu_to_le32(LINK_TOGGLE))) - state->new_cycle_state ^= 0x1; - next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); + next_trb(xhci, ep_ring, &new_seg, &new_deq); + + /* Search wrapped around, bail out */ + if (new_deq == ep->ring->dequeue) { + xhci_err(xhci, "Error: Failed finding new dequeue state\n"); + state->new_deq_seg = NULL; + state->new_deq_ptr = NULL; + return; + } + + } while (!cycle_found || !td_last_trb_found); + + state->new_deq_seg = new_seg; + state->new_deq_ptr = new_deq; /* Don't update the ring cycle state for the producer (us). */ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -1118,6 +1095,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n"); + return; + } xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Queueing configure endpoint command"); xhci_queue_configure_endpoint(xhci, command, @@ -2483,7 +2464,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, * last TRB of the previous TD. The command completion handle * will take care the rest. */ - if (!event_seg && trb_comp_code == COMP_STOP_INVAL) { + if (!event_seg && (trb_comp_code == COMP_STOP || + trb_comp_code == COMP_STOP_INVAL)) { ret = 0; goto cleanup; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7436d5f5e67a..c020b094fe7d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1553,6 +1553,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ if (!(ep->ep_state & EP_HALT_PENDING)) { command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + if (!command) { + ret = -ENOMEM; + goto done; + } ep->ep_state |= EP_HALT_PENDING; ep->stop_cmds_pending++; ep->stop_cmd_timer.expires = jiffies + @@ -1586,12 +1590,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; struct xhci_input_control_ctx *ctrl_ctx; - struct xhci_slot_ctx *slot_ctx; - unsigned int last_ctx; unsigned int ep_index; struct xhci_ep_ctx *ep_ctx; u32 drop_flag; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; int ret; ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); @@ -1638,24 +1640,13 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); - last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we deleted the last one */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) > - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); - xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1678,11 +1669,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_hcd *xhci; struct xhci_container_ctx *in_ctx, *out_ctx; unsigned int ep_index; - struct xhci_slot_ctx *slot_ctx; struct xhci_input_control_ctx *ctrl_ctx; u32 added_ctxs; - unsigned int last_ctx; - u32 new_add_flags, new_drop_flags, new_slot_info; + u32 new_add_flags, new_drop_flags; struct xhci_virt_device *virt_dev; int ret = 0; @@ -1697,7 +1686,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; added_ctxs = xhci_get_endpoint_flag(&ep->desc); - last_ctx = xhci_last_valid_endpoint(added_ctxs); if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) { /* FIXME when we have to issue an evaluate endpoint command to * deal with ep0 max packet size changing once we get the @@ -1763,24 +1751,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, */ new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); - slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); - /* Update the last valid endpoint context, if we just added one past */ - if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) < - LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - } - new_slot_info = le32_to_cpu(slot_ctx->dev_info); - /* Store the usb_device pointer for later use */ ep->hcpriv = udev; - xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", + xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, (unsigned int) new_drop_flags, - (unsigned int) new_add_flags, - (unsigned int) new_slot_info); + (unsigned int) new_add_flags); return 0; } @@ -1830,15 +1808,15 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_ENOMEM: - dev_warn(&udev->dev, "Not enough host controller resources " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough host controller resources for new device state.\n"); ret = -ENOMEM; /* FIXME: can we allocate more resources for the HC? */ break; case COMP_BW_ERR: case COMP_2ND_BW_ERR: - dev_warn(&udev->dev, "Not enough bandwidth " - "for new device state.\n"); + dev_warn(&udev->dev, + "Not enough bandwidth for new device state.\n"); ret = -ENOSPC; /* FIXME: can we go back to the old state? */ break; @@ -1850,8 +1828,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for endpoint " - "configure command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for endpoint configure command.\n"); ret = -ENODEV; break; case COMP_SUCCESS: @@ -1860,8 +1838,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -1881,24 +1859,24 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = -ETIME; break; case COMP_EINVAL: - dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "WARN: xHCI driver setup invalid evaluate context command.\n"); ret = -EINVAL; break; case COMP_EBADSLT: - dev_warn(&udev->dev, "WARN: slot not enabled for" - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: slot not enabled for evaluate context command.\n"); ret = -EINVAL; break; case COMP_CTX_STATE: - dev_warn(&udev->dev, "WARN: invalid context state for " - "evaluate context command.\n"); + dev_warn(&udev->dev, + "WARN: invalid context state for evaluate context command.\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for evaluate " - "context command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for evaluate context command.\n"); ret = -ENODEV; break; case COMP_MEL_ERR: @@ -1912,8 +1890,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ret = 0; break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", *cmd_status); + xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n", + *cmd_status); ret = -EINVAL; break; } @@ -2750,8 +2728,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ret = 0; goto command_cleanup; } - xhci_dbg(xhci, "New Input Control Context:\n"); + /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + for (i = 31; i >= 1; i--) { + __le32 le32 = cpu_to_le32(BIT(i)); + + if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32)) + || (ctrl_ctx->add_flags & le32) || i == 1) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i)); + break; + } + } + xhci_dbg(xhci, "New Input Control Context:\n"); xhci_dbg_ctx(xhci, virt_dev->in_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); @@ -2891,6 +2880,9 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, ep_index, ep->stopped_stream, ep->stopped_td, &deq_state); + if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg) + return; + /* HW with the reset endpoint quirk will use the saved dequeue state to * issue a configure endpoint command later. */ @@ -3163,7 +3155,8 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, num_streams); /* MaxPSASize value 0 (2 streams) means streams are not supported */ - if (HCC_MAX_PSA(xhci->hcc_params) < 4) { + if ((xhci->quirks & XHCI_BROKEN_STREAMS) || + HCC_MAX_PSA(xhci->hcc_params) < 4) { xhci_dbg(xhci, "xHCI controller does not support streams.\n"); return -ENOSYS; } @@ -4303,8 +4296,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, return USB3_LPM_DISABLED; } -/* Returns the hub-encoded U1 timeout value. - * The U1 timeout should be the maximum of the following values: +/* The U1 timeout should be the maximum of the following values: * - For control endpoints, U1 system exit latency (SEL) * 3 * - For bulk endpoints, U1 SEL * 5 * - For interrupt endpoints: @@ -4312,7 +4304,8 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev, * - Periodic EPs, max(105% of bInterval, U1 SEL * 2) * - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2) */ -static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u1_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4344,11 +4337,28 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return 0; } - /* The U1 timeout is encoded in 1us intervals. */ - timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); - /* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */ + return timeout_ns; +} + +/* Returns the hub-encoded U1 timeout value. */ +static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); + else + timeout_ns = udev->u1_params.sel; + + /* The U1 timeout is encoded in 1us intervals. + * Don't return a timeout of zero, because that's USB3_LPM_DISABLED. + */ if (timeout_ns == USB3_LPM_DISABLED) - timeout_ns++; + timeout_ns = 1; + else + timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000); /* If the necessary timeout value is bigger than what we can set in the * USB 3.0 hub, we have to disable hub-initiated U1. @@ -4360,14 +4370,14 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev, return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1); } -/* Returns the hub-encoded U2 timeout value. - * The U2 timeout should be the maximum of: +/* The U2 timeout should be the maximum of: * - 10 ms (to avoid the bandwidth impact on the scheduler) * - largest bInterval of any active periodic endpoint (to avoid going * into lower power link states between intervals). * - the U2 Exit Latency of the device */ -static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, +static unsigned long long xhci_calculate_intel_u2_timeout( + struct usb_device *udev, struct usb_endpoint_descriptor *desc) { unsigned long long timeout_ns; @@ -4383,6 +4393,21 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev, if (u2_del_ns > timeout_ns) timeout_ns = u2_del_ns; + return timeout_ns; +} + +/* Returns the hub-encoded U2 timeout value. */ +static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_endpoint_descriptor *desc) +{ + unsigned long long timeout_ns; + + if (xhci->quirks & XHCI_INTEL_HOST) + timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); + else + timeout_ns = udev->u2_params.sel; + /* The U2 timeout is encoded in 256us intervals */ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000); /* If the necessary timeout value is bigger than what we can set in the @@ -4401,13 +4426,10 @@ static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci, enum usb3_link_state state, u16 *timeout) { - if (state == USB3_LPM_U1) { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u1_timeout(udev, desc); - } else { - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_calculate_intel_u2_timeout(udev, desc); - } + if (state == USB3_LPM_U1) + return xhci_calculate_u1_timeout(xhci, udev, desc); + else if (state == USB3_LPM_U2) + return xhci_calculate_u2_timeout(xhci, udev, desc); return USB3_LPM_DISABLED; } @@ -4484,7 +4506,8 @@ static int xhci_check_tier_policy(struct xhci_hcd *xhci, { if (xhci->quirks & XHCI_INTEL_HOST) return xhci_check_intel_tier_policy(udev, state); - return -EINVAL; + else + return 0; } /* Returns the U1 or U2 timeout that should be enabled. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9ffecd56600d..dace5152e179 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1558,6 +1558,8 @@ struct xhci_hcd { #define XHCI_PLAT (1 << 16) #define XHCI_SLOW_SUSPEND (1 << 17) #define XHCI_SPURIOUS_WAKEUP (1 << 18) +/* For controllers with a broken beyond repair streams implementation */ +#define XHCI_BROKEN_STREAMS (1 << 19) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 1bca274dc3b5..76d77206e011 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -248,3 +248,10 @@ config USB_HSIC_USB3503 select REGMAP_I2C help This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. + +config USB_LINK_LAYER_TEST + tristate "USB Link Layer Test driver" + help + This driver is for generating specific traffic for Super Speed Link + Layer Test Device. Say Y only when you want to conduct USB Super Speed + Link Layer Test for host controllers. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index e748fd5dbe94..65b0402c1ca1 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_USB_YUREX) += yurex.o obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ +obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c new file mode 100644 index 000000000000..7d589c156fb1 --- /dev/null +++ b/drivers/usb/misc/lvstest.c @@ -0,0 +1,460 @@ +/* + * drivers/usb/misc/lvstest.c + * + * Test pattern generation for Link Layer Validation System Tests + * + * Copyright (C) 2014 ST Microelectronics + * Pratyush Anand <pratyush.anand@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/ch11.h> +#include <linux/usb/hcd.h> +#include <linux/usb/phy.h> + +struct lvs_rh { + /* root hub interface */ + struct usb_interface *intf; + /* if lvs device connected */ + bool present; + /* port no at which lvs device is present */ + int portnum; + /* urb buffer */ + u8 buffer[8]; + /* class descriptor */ + struct usb_hub_descriptor descriptor; + /* urb for polling interrupt pipe */ + struct urb *urb; + /* LVS RH work queue */ + struct workqueue_struct *rh_queue; + /* LVH RH work */ + struct work_struct rh_work; + /* RH port status */ + struct usb_port_status port_status; +}; + +static struct usb_device *create_lvs_device(struct usb_interface *intf) +{ + struct usb_device *udev, *hdev; + struct usb_hcd *hcd; + struct lvs_rh *lvs = usb_get_intfdata(intf); + + if (!lvs->present) { + dev_err(&intf->dev, "No LVS device is present\n"); + return NULL; + } + + hdev = interface_to_usbdev(intf); + hcd = bus_to_hcd(hdev->bus); + + udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum); + if (!udev) { + dev_err(&intf->dev, "Could not allocate lvs udev\n"); + return NULL; + } + udev->speed = USB_SPEED_SUPER; + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); + usb_set_device_state(udev, USB_STATE_DEFAULT); + + if (hcd->driver->enable_device) { + if (hcd->driver->enable_device(hcd, udev) < 0) { + dev_err(&intf->dev, "Failed to enable\n"); + usb_put_dev(udev); + return NULL; + } + } + + return udev; +} + +static void destroy_lvs_device(struct usb_device *udev) +{ + struct usb_device *hdev = udev->parent; + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + + if (hcd->driver->free_dev) + hcd->driver->free_dev(hcd, udev); + + usb_put_dev(udev); +} + +static int lvs_rh_clear_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static int lvs_rh_set_port_feature(struct usb_device *hdev, + int port1, int feature) +{ + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, + NULL, 0, 1000); +} + +static ssize_t u3_entry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 entry %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_entry); + +static ssize_t u3_exit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + struct usb_device *udev; + int ret; + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + return -ENOMEM; + } + + ret = lvs_rh_clear_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_SUSPEND); + if (ret < 0) + dev_err(dev, "can't issue U3 exit %d\n", ret); + + destroy_lvs_device(udev); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(u3_exit); + +static ssize_t hot_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + int ret; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum, + USB_PORT_FEAT_RESET); + if (ret < 0) { + dev_err(dev, "can't issue hot reset %d\n", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(hot_reset); + +static ssize_t u2_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U2_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u2_timeout); + +static ssize_t u1_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *hdev = interface_to_usbdev(intf); + struct lvs_rh *lvs = usb_get_intfdata(intf); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "couldn't parse string %d\n", ret); + return ret; + } + + if (val < 0 || val > 127) + return -EINVAL; + + ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8), + USB_PORT_FEAT_U1_TIMEOUT); + if (ret < 0) { + dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val); + return ret; + } + + return count; +} +static DEVICE_ATTR_WO(u1_timeout); + +static ssize_t get_dev_desc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_device *udev; + struct usb_device_descriptor *descriptor; + int ret; + + descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL); + if (!descriptor) { + dev_err(dev, "failed to allocate descriptor memory\n"); + return -ENOMEM; + } + + udev = create_lvs_device(intf); + if (!udev) { + dev_err(dev, "failed to create lvs device\n"); + ret = -ENOMEM; + goto free_desc; + } + + ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN, + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, + 0, descriptor, sizeof(*descriptor), + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + dev_err(dev, "can't read device descriptor %d\n", ret); + + destroy_lvs_device(udev); + +free_desc: + kfree(descriptor); + + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_WO(get_dev_desc); + +static struct attribute *lvs_attributes[] = { + &dev_attr_get_dev_desc.attr, + &dev_attr_u1_timeout.attr, + &dev_attr_u2_timeout.attr, + &dev_attr_hot_reset.attr, + &dev_attr_u3_entry.attr, + &dev_attr_u3_exit.attr, + NULL +}; + +static const struct attribute_group lvs_attr_group = { + .attrs = lvs_attributes, +}; + +static void lvs_rh_work(struct work_struct *work) +{ + struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work); + struct usb_interface *intf = lvs->intf; + struct usb_device *hdev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(hdev->bus); + struct usb_hub_descriptor *descriptor = &lvs->descriptor; + struct usb_port_status *port_status = &lvs->port_status; + int i, ret = 0; + u16 portchange; + + /* Examine each root port */ + for (i = 1; i <= descriptor->bNbrPorts; i++) { + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i, + port_status, sizeof(*port_status), 1000); + if (ret < 4) + continue; + + portchange = le16_to_cpu(port_status->wPortChange); + + if (portchange & USB_PORT_STAT_C_LINK_STATE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_ENABLE) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_ENABLE); + if (portchange & USB_PORT_STAT_C_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_RESET); + if (portchange & USB_PORT_STAT_C_BH_RESET) + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_BH_PORT_RESET); + if (portchange & USB_PORT_STAT_C_CONNECTION) { + lvs_rh_clear_port_feature(hdev, i, + USB_PORT_FEAT_C_CONNECTION); + + if (le16_to_cpu(port_status->wPortStatus) & + USB_PORT_STAT_CONNECTION) { + lvs->present = true; + lvs->portnum = i; + if (hcd->phy) + usb_phy_notify_connect(hcd->phy, + USB_SPEED_SUPER); + } else { + lvs->present = false; + if (hcd->phy) + usb_phy_notify_disconnect(hcd->phy, + USB_SPEED_SUPER); + } + break; + } + } + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret != 0 && ret != -ENODEV && ret != -EPERM) + dev_err(&intf->dev, "urb resubmit error %d\n", ret); +} + +static void lvs_rh_irq(struct urb *urb) +{ + struct lvs_rh *lvs = urb->context; + + queue_work(lvs->rh_queue, &lvs->rh_work); +} + +static int lvs_rh_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *hdev; + struct usb_host_interface *desc; + struct usb_endpoint_descriptor *endpoint; + struct lvs_rh *lvs; + unsigned int pipe; + int ret, maxp; + + hdev = interface_to_usbdev(intf); + desc = intf->cur_altsetting; + endpoint = &desc->endpoint[0].desc; + + /* valid only for SS root hub */ + if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) { + dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n"); + return -EINVAL; + } + + lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL); + if (!lvs) + return -ENOMEM; + + lvs->intf = intf; + usb_set_intfdata(intf, lvs); + + /* how many number of ports this root hub has */ + ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_SS_HUB << 8, 0, &lvs->descriptor, + USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT); + if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) { + dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret); + return ret; + } + + /* submit urb to poll interrupt endpoint */ + lvs->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!lvs->urb) { + dev_err(&intf->dev, "couldn't allocate lvs urb\n"); + return -ENOMEM; + } + + lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue"); + if (!lvs->rh_queue) { + dev_err(&intf->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto free_urb; + } + + INIT_WORK(&lvs->rh_work, lvs_rh_work); + + ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group); + if (ret < 0) { + dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret); + goto destroy_queue; + } + + pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); + usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp, + lvs_rh_irq, lvs, endpoint->bInterval); + + ret = usb_submit_urb(lvs->urb, GFP_KERNEL); + if (ret < 0) { + dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret); + goto sysfs_remove; + } + + return ret; + +sysfs_remove: + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); +destroy_queue: + destroy_workqueue(lvs->rh_queue); +free_urb: + usb_free_urb(lvs->urb); + return ret; +} + +static void lvs_rh_disconnect(struct usb_interface *intf) +{ + struct lvs_rh *lvs = usb_get_intfdata(intf); + + sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group); + destroy_workqueue(lvs->rh_queue); + usb_free_urb(lvs->urb); +} + +static struct usb_driver lvs_driver = { + .name = "lvs", + .probe = lvs_rh_probe, + .disconnect = lvs_rh_disconnect, +}; + +module_usb_driver(lvs_driver); + +MODULE_DESCRIPTION("Link Layer Validation System Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 06b5d77cd9ad..633caf643122 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3250,6 +3250,7 @@ static const struct usb_device_id sisusb_table[] = { { USB_DEVICE(0x0711, 0x0918) }, { USB_DEVICE(0x0711, 0x0920) }, { USB_DEVICE(0x0711, 0x0950) }, + { USB_DEVICE(0x0711, 0x5200) }, { USB_DEVICE(0x182d, 0x021c) }, { USB_DEVICE(0x182d, 0x0269) }, { } diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index f43c61989cef..47cb143716a1 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -149,8 +149,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) case USB3503_MODE_STANDBY: usb3503_reset(hub, 0); - - hub->mode = mode; dev_info(dev, "switched to STANDBY mode\n"); break; @@ -192,7 +190,8 @@ static int usb3503_probe(struct usb3503 *hub) clk = devm_clk_get(dev, "refclk"); if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) { - dev_err(dev, "unable to request refclk (%d)\n", err); + dev_err(dev, "unable to request refclk (%ld)\n", + PTR_ERR(clk)); return PTR_ERR(clk); } @@ -346,6 +345,37 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } +#ifdef CONFIG_PM_SLEEP +static int usb3503_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + usb3503_switch_mode(hub, USB3503_MODE_STANDBY); + + if (hub->clk) + clk_disable_unprepare(hub->clk); + + return 0; +} + +static int usb3503_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb3503 *hub = i2c_get_clientdata(client); + + if (hub->clk) + clk_prepare_enable(hub->clk); + + usb3503_switch_mode(hub, hub->mode); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend, + usb3503_i2c_resume); + static const struct i2c_device_id usb3503_id[] = { { USB3503_I2C_NAME, 0 }, { } @@ -364,6 +394,7 @@ MODULE_DEVICE_TABLE(of, usb3503_of_match); static struct i2c_driver usb3503_i2c_driver = { .driver = { .name = USB3503_I2C_NAME, + .pm = &usb3503_i2c_pm_ops, .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_i2c_probe, diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index d40d5f0b5528..ac4422b33dcd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -455,7 +455,7 @@ static int bfin_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -464,7 +464,7 @@ static int bfin_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } musb->dev.parent = &pdev->dev; @@ -478,7 +478,7 @@ static int bfin_probe(struct platform_device *pdev) glue->phy = usb_phy_generic_register(); if (IS_ERR(glue->phy)) - goto err2; + goto err1; platform_set_drvdata(pdev, glue); memset(musb_resources, 0x00, sizeof(*musb_resources) * @@ -498,31 +498,28 @@ static int bfin_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err3; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err3; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err3; + goto err2; } return 0; -err3: - usb_phy_generic_unregister(glue->phy); - err2: - platform_device_put(musb); + usb_phy_generic_unregister(glue->phy); err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -534,7 +531,6 @@ static int bfin_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index de8492b06e46..110b78415bf0 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -519,23 +519,23 @@ static int davinci_probe(struct platform_device *pdev) int ret = -ENOMEM; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; } - clk = clk_get(&pdev->dev, "usb"); + clk = devm_clk_get(&pdev->dev, "usb"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err0; } ret = clk_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err0; } glue->dev = &pdev->dev; @@ -579,20 +579,14 @@ static int davinci_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err5; + goto err1; } return 0; -err5: +err1: clk_disable(clk); -err4: - clk_put(clk); - -err3: - kfree(glue); - err0: return ret; } @@ -604,8 +598,6 @@ static int davinci_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(); clk_disable(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5f30537f1927..d1187290d4e3 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/usb/usb_phy_generic.h> #include "musb_core.h" @@ -80,6 +81,7 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = { static int jz4740_musb_init(struct musb *musb) { + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS UDC: no transceiver configured\n"); @@ -182,6 +184,7 @@ static int jz4740_remove(struct platform_device *pdev) struct jz4740_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); + usb_phy_generic_unregister(pdev); clk_disable_unprepare(glue->clk); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index eff3c5cf84f4..b841ee0bff06 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -850,7 +850,8 @@ b_host: /* handle babble condition */ if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) - schedule_work(&musb->recover_work); + schedule_delayed_work(&musb->recover_work, + msecs_to_jiffies(100)); #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or @@ -1517,7 +1518,7 @@ irqreturn_t musb_interrupt(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", - (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", + is_host_active(musb) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); /* the core can interrupt us for multiple reasons; docs have @@ -1531,7 +1532,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* handle endpoint 0 first */ if (musb->int_tx & 1) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); else retval |= musb_g_ep0_irq(musb); @@ -1545,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval = ep->rx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, ep_num); else musb_g_rx(musb, ep_num); @@ -1563,7 +1564,7 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval |= ep->tx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, ep_num); else musb_g_tx(musb, ep_num); @@ -1585,15 +1586,13 @@ MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) { - u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - /* called with controller lock already held */ if (!epnum) { #ifndef CONFIG_USB_TUSB_OMAP_DMA if (!is_cppi_enabled()) { /* endpoint 0 */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); @@ -1602,13 +1601,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) } else { /* endpoints 1..15 */ if (transmit) { - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_tx(musb, epnum); else musb_g_tx(musb, epnum); } else { /* receive */ - if (devctl & MUSB_DEVCTL_HM) + if (is_host_active(musb)) musb_host_rx(musb, epnum); else musb_g_rx(musb, epnum); @@ -1753,20 +1752,22 @@ static void musb_irq_work(struct work_struct *data) /* Recover from babble interrupt conditions */ static void musb_recover_work(struct work_struct *data) { - struct musb *musb = container_of(data, struct musb, recover_work); - int status; + struct musb *musb = container_of(data, struct musb, recover_work.work); + int status, ret; - musb_platform_reset(musb); + ret = musb_platform_reset(musb); + if (ret) + return; usb_phy_vbus_off(musb->xceiv); - udelay(100); + usleep_range(100, 200); usb_phy_vbus_on(musb->xceiv); - udelay(100); + usleep_range(100, 200); /* - * When a babble condition occurs, the musb controller removes the - * session bit and the endpoint config is lost. + * When a babble condition occurs, the musb controller + * removes the session bit and the endpoint config is lost. */ if (musb->dyn_fifo) status = ep_config_from_table(musb); @@ -1945,7 +1946,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); - INIT_WORK(&musb->recover_work, musb_recover_work); + INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2041,7 +2042,7 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2107,7 +2108,7 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); - cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d155a156f240..414e57a984bb 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -192,7 +192,7 @@ struct musb_platform_ops { int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); - void (*reset)(struct musb *musb); + int (*reset)(struct musb *musb); int (*vbus_status)(struct musb *musb); void (*set_vbus)(struct musb *musb, int on); @@ -297,7 +297,7 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct work_struct recover_work; + struct delayed_work recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; @@ -555,10 +555,12 @@ static inline void musb_platform_try_idle(struct musb *musb, musb->ops->try_idle(musb, timeout); } -static inline void musb_platform_reset(struct musb *musb) +static inline int musb_platform_reset(struct musb *musb) { - if (musb->ops->reset) - musb->ops->reset(musb); + if (!musb->ops->reset) + return -EINVAL; + + return musb->ops->reset(musb); } static inline int musb_platform_get_vbus_status(struct musb *musb) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 5341bb223b7c..47ae6455d073 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -39,7 +39,6 @@ struct cppi41_dma_channel { u32 transferred; u32 packet_sz; struct list_head tx_check; - struct work_struct dma_completion; }; #define MUSB_DMA_NUM_CHANNELS 15 @@ -74,15 +73,18 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel) static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel) { + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct musb *musb = hw_ep->musb; u16 csr; u8 toggle; if (cppi41_channel->is_tx) return; - if (!is_host_active(cppi41_channel->controller->musb)) + if (!is_host_active(musb)) return; - csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + musb_ep_select(musb->mregs, hw_ep->epnum); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; /* @@ -107,24 +109,13 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep) void __iomem *epio = musb->endpoints[epnum].regs; u16 csr; + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_TXPKTRDY) return false; return true; } -static bool is_isoc(struct musb_hw_ep *hw_ep, bool in) -{ - if (in && hw_ep->in_qh) { - if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC) - return true; - } else if (hw_ep->out_qh) { - if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC) - return true; - } - return false; -} - static void cppi41_dma_callback(void *private_data); static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) @@ -139,6 +130,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) cppi41_channel->channel.actual_len = cppi41_channel->transferred; cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + cppi41_channel->channel.rx_packet_done = true; musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx); } else { /* next iteration, reload */ @@ -172,6 +164,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) dma_async_issue_pending(dc); if (!cppi41_channel->is_tx) { + musb_ep_select(musb->mregs, hw_ep->epnum); csr = musb_readw(epio, MUSB_RXCSR); csr |= MUSB_RXCSR_H_REQPKT; musb_writew(epio, MUSB_RXCSR, csr); @@ -179,32 +172,6 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel) } } -static void cppi_trans_done_work(struct work_struct *work) -{ - unsigned long flags; - struct cppi41_dma_channel *cppi41_channel = - container_of(work, struct cppi41_dma_channel, dma_completion); - struct cppi41_dma_controller *controller = cppi41_channel->controller; - struct musb *musb = controller->musb; - struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; - bool empty; - - if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) { - spin_lock_irqsave(&musb->lock, flags); - cppi41_trans_done(cppi41_channel); - spin_unlock_irqrestore(&musb->lock, flags); - } else { - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { - spin_lock_irqsave(&musb->lock, flags); - cppi41_trans_done(cppi41_channel); - spin_unlock_irqrestore(&musb->lock, flags); - } else { - schedule_work(&cppi41_channel->dma_completion); - } - } -} - static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) { struct cppi41_dma_controller *controller; @@ -233,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) if (!list_empty(&controller->early_tx_list)) { ret = HRTIMER_RESTART; hrtimer_forward_now(&controller->early_tx, - ktime_set(0, 150 * NSEC_PER_USEC)); + ktime_set(0, 50 * NSEC_PER_USEC)); } spin_unlock_irqrestore(&musb->lock, flags); @@ -268,14 +235,6 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - if (!cppi41_channel->is_tx) { - if (is_isoc(hw_ep, 1)) - schedule_work(&cppi41_channel->dma_completion); - else - cppi41_trans_done(cppi41_channel); - goto out; - } - empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); @@ -312,15 +271,13 @@ static void cppi41_dma_callback(void *private_data) goto out; } } - if (is_isoc(hw_ep, 0)) { - schedule_work(&cppi41_channel->dma_completion); - goto out; - } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); if (!hrtimer_is_queued(&controller->early_tx)) { + unsigned long usecs = cppi41_channel->total_len / 10; + hrtimer_start_range_ns(&controller->early_tx, - ktime_set(0, 140 * NSEC_PER_USEC), + ktime_set(0, usecs * NSEC_PER_USEC), 40 * NSEC_PER_USEC, HRTIMER_MODE_REL); } @@ -450,6 +407,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel, dma_desc->callback = cppi41_dma_callback; dma_desc->callback_param = channel; cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + cppi41_channel->channel.rx_packet_done = false; save_rx_toggle(cppi41_channel); dma_async_issue_pending(dc); @@ -672,8 +630,6 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) cppi41_channel->port_num = port; cppi41_channel->is_tx = is_tx; INIT_LIST_HEAD(&cppi41_channel->tx_check); - INIT_WORK(&cppi41_channel->dma_completion, - cppi_trans_done_work); musb_dma = &cppi41_channel->channel; musb_dma->private_data = cppi41_channel; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1345a4ff041a..1d44faa86252 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -129,6 +129,7 @@ struct dma_channel { size_t actual_len; enum dma_channel_status status; bool desired_mode; + bool rx_packet_done; }; /* diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 09529f94e72d..c791ba5da91a 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -56,16 +56,24 @@ static const struct of_device_id musb_dsps_of_match[]; * dependent on musb core layer symbols. */ static inline u8 dsps_readb(const void __iomem *addr, unsigned offset) - { return __raw_readb(addr + offset); } +{ + return __raw_readb(addr + offset); +} static inline u32 dsps_readl(const void __iomem *addr, unsigned offset) - { return __raw_readl(addr + offset); } +{ + return __raw_readl(addr + offset); +} static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data) - { __raw_writeb(data, addr + offset); } +{ + __raw_writeb(data, addr + offset); +} static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data) - { __raw_writel(data, addr + offset); } +{ + __raw_writel(data, addr + offset); +} /** * DSPS musb wrapper register offset. @@ -136,6 +144,7 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ + bool sw_babble_enabled; struct dsps_context context; struct debugfs_regset32 regset; @@ -469,6 +478,19 @@ static int dsps_musb_init(struct musb *musb) val &= ~(1 << wrp->otg_disable); dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); + /* + * Check whether the dsps version has babble control enabled. + * In latest silicon revision the babble control logic is enabled. + * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control + * logic enabled. + */ + val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + if (val == MUSB_BABBLE_RCV_DISABLE) { + glue->sw_babble_enabled = true; + val |= MUSB_BABBLE_SW_SESSION_CTRL; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val); + } + ret = dsps_musb_dbg_init(musb, glue); if (ret) return ret; @@ -535,14 +557,82 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) return 0; } -static void dsps_musb_reset(struct musb *musb) +static bool sw_babble_control(struct musb *musb) +{ + u8 babble_ctl; + bool session_restart = false; + + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n", + babble_ctl); + /* + * check line monitor flag to check whether babble is + * due to noise + */ + dev_dbg(musb->controller, "STUCK_J is %s\n", + babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset"); + + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + int timeout = 10; + + /* + * babble is due to noise, then set transmit idle (d7 bit) + * to resume normal operation + */ + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE; + dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl); + + /* wait till line monitor flag cleared */ + dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n"); + do { + babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); + udelay(1); + } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--); + + /* check whether stuck_at_j bit cleared */ + if (babble_ctl & MUSB_BABBLE_STUCK_J) { + /* + * real babble condition has occurred + * restart the controller to start the + * session again + */ + dev_dbg(musb->controller, "J not cleared, misc (%x)\n", + babble_ctl); + session_restart = true; + } + } else { + session_restart = true; + } + + return session_restart; +} + +static int dsps_musb_reset(struct musb *musb) { struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; + int session_restart = 0; + + if (glue->sw_babble_enabled) + session_restart = sw_babble_control(musb); + /* + * In case of new silicon version babble condition can be recovered + * without resetting the MUSB. But for older silicon versions, MUSB + * reset is needed + */ + if (session_restart || !glue->sw_babble_enabled) { + dev_info(musb->controller, "Restarting MUSB to recover from Babble\n"); + dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); + usleep_range(100, 200); + usb_phy_shutdown(musb->xceiv); + usleep_range(100, 200); + usb_phy_init(musb->xceiv); + session_restart = 1; + } - dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); - udelay(100); + return !session_restart; } static struct musb_platform_ops dsps_ops = { diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index eb06291a40c8..855793d701bb 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -120,7 +120,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) if (csr != lastcsr) dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); lastcsr = csr; - csr |= MUSB_TXCSR_FLUSHFIFO; + csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY; musb_writew(epio, MUSB_TXCSR, csr); csr = musb_readw(epio, MUSB_TXCSR); if (WARN(retries-- < 1, @@ -1295,7 +1295,7 @@ done: if (status) { if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); } /* do the proper sequence to abort the transfer in the @@ -1640,7 +1640,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* clean up dma and collect transfer count */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; } musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); @@ -1671,7 +1671,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); + musb->dma_controller->channel_abort(dma); xfer_len = dma->actual_len; done = true; } @@ -1734,10 +1734,11 @@ void musb_host_rx(struct musb *musb, u8 epnum) } } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket); + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length + || dma->actual_len < qh->maxpacket + || dma->rx_packet_done); } /* send IN token for next packet, without AUTOREQ */ @@ -1957,7 +1958,7 @@ static int musb_schedule( struct musb_qh *qh, int is_in) { - int idle; + int idle = 0; int best_diff; int best_end, epnum; struct musb_hw_ep *hw_ep = NULL; diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 03f2655af290..b9bcda5e3945 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -72,6 +72,12 @@ #define MUSB_DEVCTL_HR 0x02 #define MUSB_DEVCTL_SESSION 0x01 +/* BABBLE_CTL */ +#define MUSB_BABBLE_FORCE_TXIDLE 0x80 +#define MUSB_BABBLE_SW_SESSION_CTRL 0x40 +#define MUSB_BABBLE_STUCK_J 0x20 +#define MUSB_BABBLE_RCV_DISABLE 0x04 + /* MUSB ULPI VBUSCONTROL */ #define MUSB_ULPI_USE_EXTVBUS 0x01 #define MUSB_ULPI_USE_EXTVBUSIND 0x02 @@ -246,6 +252,7 @@ */ #define MUSB_DEVCTL 0x60 /* 8 bit */ +#define MUSB_BABBLE_CTL 0x61 /* 8 bit */ /* These are always controlled through the INDEX register */ #define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 159ef4be1ef2..7dfc6cb732c9 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -22,6 +22,7 @@ #include <linux/usb.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/device.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/usb/usb_phy_generic.h> @@ -1160,12 +1161,12 @@ static int tusb_probe(struct platform_device *pdev) struct platform_device *musb; struct tusb6010_glue *glue; struct platform_device_info pinfo; - int ret = -ENOMEM; + int ret; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); - goto err0; + return -ENOMEM; } glue->dev = &pdev->dev; @@ -1204,16 +1205,10 @@ static int tusb_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err3; + return ret; } return 0; - -err3: - kfree(glue); - -err0: - return ret; } static int tusb_remove(struct platform_device *pdev) @@ -1222,7 +1217,6 @@ static int tusb_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); usb_phy_generic_unregister(glue->phy); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index f202e5088461..dc666e96f45f 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -246,7 +246,7 @@ static int ux500_probe(struct platform_device *pdev) } } - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "failed to allocate glue context\n"); goto err0; @@ -255,20 +255,20 @@ static int ux500_probe(struct platform_device *pdev) musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err1; + goto err0; } - clk = clk_get(&pdev->dev, NULL); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); ret = PTR_ERR(clk); - goto err3; + goto err1; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); - goto err4; + goto err1; } musb->dev.parent = &pdev->dev; @@ -301,34 +301,28 @@ static int ux500_probe(struct platform_device *pdev) ARRAY_SIZE(musb_resources)); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err5; + goto err2; } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err5; + goto err2; } ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err5; + goto err2; } return 0; -err5: +err2: clk_disable_unprepare(clk); -err4: - clk_put(clk); - -err3: - platform_device_put(musb); - err1: - kfree(glue); + platform_device_put(musb); err0: return ret; @@ -340,8 +334,6 @@ static int ux500_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); clk_disable_unprepare(glue->clk); - clk_put(glue->clk); - kfree(glue); return 0; } diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 9aad00f11bd5..221faed9f074 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -96,7 +96,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, struct musb *musb = ux500_channel->controller->private_data; dev_dbg(musb->controller, - "packet_sz=%d, mode=%d, dma_addr=0x%llu, len=%d is_tx=%d\n", + "packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n", packet_sz, mode, (unsigned long long) dma_addr, len, ux500_channel->is_tx); diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 585e50cb1980..b70e05537180 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -122,16 +122,10 @@ static int am335x_phy_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops am335x_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) -}; - -#define DEV_PM_OPS (&am335x_pm_ops) -#else -#define DEV_PM_OPS NULL #endif +static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume); + static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } @@ -144,7 +138,7 @@ static struct platform_driver am335x_phy_driver = { .driver = { .name = "am335x-phy-driver", .owner = THIS_MODULE, - .pm = DEV_PM_OPS, + .pm = &am335x_pm_ops, .of_match_table = am335x_phy_ids, }, }; diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 69462e09d014..f4b14bd97e14 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -253,15 +253,15 @@ static int gpio_vbus_probe(struct platform_device *pdev) return -EINVAL; gpio = pdata->gpio_vbus; - gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data), + GFP_KERNEL); if (!gpio_vbus) return -ENOMEM; - gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); - if (!gpio_vbus->phy.otg) { - kfree(gpio_vbus); + gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), + GFP_KERNEL); + if (!gpio_vbus->phy.otg) return -ENOMEM; - } platform_set_drvdata(pdev, gpio_vbus); gpio_vbus->dev = &pdev->dev; @@ -274,11 +274,11 @@ static int gpio_vbus_probe(struct platform_device *pdev) gpio_vbus->phy.otg->phy = &gpio_vbus->phy; gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral; - err = gpio_request(gpio, "vbus_detect"); + err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect"); if (err) { dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", gpio, err); - goto err_gpio; + return err; } gpio_direction_input(gpio); @@ -296,27 +296,27 @@ static int gpio_vbus_probe(struct platform_device *pdev) /* if data line pullup is in use, initialize it to "not pulling up" */ gpio = pdata->gpio_pullup; if (gpio_is_valid(gpio)) { - err = gpio_request(gpio, "udc_pullup"); + err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup"); if (err) { dev_err(&pdev->dev, "can't request pullup gpio %d, err: %d\n", gpio, err); - gpio_free(pdata->gpio_vbus); - goto err_gpio; + return err; } gpio_direction_output(gpio, pdata->gpio_pullup_inverted); } - err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev); + err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags, + "vbus_detect", pdev); if (err) { dev_err(&pdev->dev, "can't request irq %i, err: %d\n", irq, err); - goto err_irq; + return err; } INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw"); if (IS_ERR(gpio_vbus->vbus_draw)) { dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", PTR_ERR(gpio_vbus->vbus_draw)); @@ -328,44 +328,23 @@ static int gpio_vbus_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - goto err_otg; + return err; } device_init_wakeup(&pdev->dev, pdata->wakeup); return 0; -err_otg: - regulator_put(gpio_vbus->vbus_draw); - free_irq(irq, pdev); -err_irq: - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(pdata->gpio_vbus); -err_gpio: - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return err; } static int gpio_vbus_remove(struct platform_device *pdev) { struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); - struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev); - int gpio = pdata->gpio_vbus; device_init_wakeup(&pdev->dev, 0); cancel_delayed_work_sync(&gpio_vbus->work); - regulator_put(gpio_vbus->vbus_draw); usb_remove_phy(&gpio_vbus->phy); - free_irq(gpio_vbus->irq, pdev); - if (gpio_is_valid(pdata->gpio_pullup)) - gpio_free(pdata->gpio_pullup); - gpio_free(gpio); - kfree(gpio_vbus->phy.otg); - kfree(gpio_vbus); - return 0; } diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c929370cdaa6..afc09087ec36 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -279,11 +279,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) static int msm_otg_phy_clk_reset(struct msm_otg *motg) { - int ret; + int ret = 0; - if (motg->pdata->phy_clk_reset) + if (motg->pdata->phy_clk_reset && motg->phy_reset_clk) ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); - else + else if (motg->phy_rst) ret = reset_control_reset(motg->phy_rst); if (ret) @@ -1429,7 +1429,7 @@ static void msm_otg_debugfs_cleanup(void) debugfs_remove(msm_otg_dbg_root); } -static struct of_device_id msm_otg_dt_match[] = { +static const struct of_device_id msm_otg_dt_match[] = { { .compatible = "qcom,usb-otg-ci", .data = (void *) CI_45NM_INTEGRATED_PHY @@ -1466,7 +1466,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); if (IS_ERR(motg->phy_rst)) - return PTR_ERR(motg->phy_rst); + motg->phy_rst = NULL; pdata->mode = of_usb_get_dr_mode(node); if (pdata->mode == USB_DR_MODE_UNKNOWN) @@ -1558,7 +1558,7 @@ static int msm_otg_probe(struct platform_device *pdev) np ? "phy" : "usb_phy_clk"); if (IS_ERR(motg->phy_reset_clk)) { dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - return PTR_ERR(motg->phy_reset_clk); + motg->phy_reset_clk = NULL; } motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); @@ -1601,8 +1601,8 @@ static int msm_otg_probe(struct platform_device *pdev) */ if (motg->phy_number) { phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4); - if (IS_ERR(phy_select)) - return PTR_ERR(phy_select); + if (!phy_select) + return -ENOMEM; /* Enable second PHY with the OTG port */ writel(0x1, phy_select); } diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 68771bfd1825..80eedd45a20a 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -216,7 +216,7 @@ #define EXYNOS5_DRD_PHYPARAM1 (0x20) -#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x3f << 0) #define PHYPARAM1_PCS_TXDEEMPH (0x1c) #define EXYNOS5_DRD_PHYTERM (0x24) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index bbe4f8e6e8d7..13b4fa287da8 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -33,7 +33,6 @@ #include <linux/usb/otg.h> #include <linux/usb/ulpi.h> #include <linux/usb/of.h> -#include <asm/mach-types.h> #include <linux/usb/ehci_def.h> #include <linux/usb/tegra_usb_phy.h> #include <linux/regulator/consumer.h> @@ -686,10 +685,8 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return gpio_direction_output(phy->reset_gpio, 0); } -static void tegra_usb_phy_close(struct usb_phy *x) +static void tegra_usb_phy_close(struct tegra_usb_phy *phy) { - struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); - if (!IS_ERR(phy->vbus)) regulator_disable(phy->vbus); @@ -965,7 +962,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_extra_tuning_parameters = true, }; -static struct of_device_id tegra_usb_phy_id_table[] = { +static const struct of_device_id tegra_usb_phy_id_table[] = { { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config }, { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config }, { }, @@ -1061,14 +1058,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) if (err < 0) return err; - tegra_phy->u_phy.shutdown = tegra_usb_phy_close; tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend; platform_set_drvdata(pdev, tegra_phy); err = usb_add_phy_dev(&tegra_phy->u_phy); if (err < 0) { - tegra_usb_phy_close(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return err; } @@ -1080,6 +1076,7 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev); usb_remove_phy(&tegra_phy->u_phy); + tegra_usb_phy_close(tegra_phy); return 0; } diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 36b6bce33b20..045cd309367a 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -147,7 +147,7 @@ err0: } EXPORT_SYMBOL_GPL(usb_get_phy); - /** +/** * devm_usb_get_phy_by_phandle - find the USB PHY by phandle * @dev - device that requests this phy * @phandle - name of the property holding the phy phandle value @@ -232,6 +232,9 @@ struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) phy = __usb_find_phy_dev(dev, &phy_bind_list, index); if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { dev_dbg(dev, "unable to find transceiver\n"); + if (!IS_ERR(phy)) + phy = ERR_PTR(-ENODEV); + goto err0; } diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index bc8aef4311a1..9e47f477b6d2 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 17267b0a2e95..1b9bf8d83235 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -15,12 +15,14 @@ * */ #include <linux/err.h> +#include <linux/gpio.h> #include <linux/io.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/sysfs.h> #include "common.h" +#include "rcar2.h" /* * image of renesas_usbhs @@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv) /* * platform default param */ + +/* commonly used on old SH-Mobile SoCs */ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_CONTROL, USB_ENDPOINT_XFER_ISOC, @@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_INT, }; +/* commonly used on newer SH-Mobile and R-Car SoCs */ +static u32 usbhsc_new_pipe_type[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, +}; + /* * power control */ @@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev) int ret; /* check platform information */ - if (!info || - !info->platform_callback.get_id) { + if (!info) { dev_err(&pdev->dev, "no platform information\n"); return -EINVAL; } @@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev) /* * care platform info */ - memcpy(&priv->pfunc, - &info->platform_callback, - sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, &info->driver_param, sizeof(struct renesas_usbhs_driver_param)); + switch (priv->dparam.type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + priv->pfunc = usbhs_rcar2_ops; + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_new_pipe_type; + priv->dparam.pipe_size = + ARRAY_SIZE(usbhsc_new_pipe_type); + } + break; + default: + if (!info->platform_callback.get_id) { + dev_err(&pdev->dev, "no platform callbacks"); + return -EINVAL; + } + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + break; + } + /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; @@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev) */ usbhs_sys_clock_ctrl(priv, 0); + /* check GPIO determining if USB function should be enabled */ + if (priv->dparam.enable_gpio) { + gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL); + ret = !gpio_get_value(priv->dparam.enable_gpio); + gpio_free(priv->dparam.enable_gpio); + if (ret) { + dev_warn(&pdev->dev, + "USB function not selected (GPIO %d)\n", + priv->dparam.enable_gpio); + ret = -ENOTSUPP; + goto probe_end_mod_exit; + } + } + /* * platform call * diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index c69dd2fba360..a7996da6a1bd 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -268,6 +268,8 @@ struct usbhs_priv { * fifo control */ struct usbhs_fifo_info fifo_info; + + struct usb_phy *phy; }; /* diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 458f3766bef1..04e6505777d0 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -600,8 +600,10 @@ static int usbhsg_ep_enable(struct usb_ep *ep, static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); usbhsg_pipe_disable(uep); + usbhs_pipe_free(pipe); uep->pipe->mod_private = NULL; uep->pipe = NULL; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 7926e1c700f1..75fbcf6b102e 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -640,6 +640,11 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) return pipe; } +static void usbhsp_put_pipe(struct usbhs_pipe *pipe) +{ + usbhsp_flags_init(pipe); +} + void usbhs_pipe_init(struct usbhs_priv *priv, int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { @@ -710,6 +715,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); + usbhs_pipe_clear(pipe); usbhs_pipe_sequence_data0(pipe); @@ -726,6 +732,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return pipe; } +void usbhs_pipe_free(struct usbhs_pipe *pipe) +{ + usbhsp_put_pipe(pipe); +} + void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo) { if (pipe->fifo) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3e5349879838..406f36d050e4 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -75,6 +75,7 @@ struct usbhs_pipe_info { char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); +void usbhs_pipe_free(struct usbhs_pipe *pipe); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c new file mode 100644 index 000000000000..e6b9dcc1c289 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -0,0 +1,77 @@ +/* + * Renesas USB driver R-Car Gen. 2 initialization and power control + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/gpio-rcar.h> +#include <linux/usb/phy.h> +#include "common.h" +#include "rcar2.h" + +static int usbhs_rcar2_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct usb_phy *phy; + + phy = usb_get_phy_dev(&pdev->dev, 0); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + priv->phy = phy; + return 0; +} + +static int usbhs_rcar2_hardware_exit(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return 0; + + usb_put_phy(priv->phy); + priv->phy = NULL; + + return 0; +} + +static int usbhs_rcar2_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return -ENODEV; + + if (enable) { + int retval = usb_phy_init(priv->phy); + + if (!retval) + retval = usb_phy_set_suspend(priv->phy, 0); + return retval; + } + + usb_phy_set_suspend(priv->phy, 1); + usb_phy_shutdown(priv->phy); + return 0; +} + +static int usbhs_rcar2_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = { + .hardware_init = usbhs_rcar2_hardware_init, + .hardware_exit = usbhs_rcar2_hardware_exit, + .power_ctrl = usbhs_rcar2_power_ctrl, + .get_id = usbhs_rcar2_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h new file mode 100644 index 000000000000..f07f10d9b3b2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.h @@ -0,0 +1,4 @@ +#include "common.h" + +extern const struct renesas_usbhs_platform_callback + usbhs_rcar2_ops; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 330df5ce435b..e4bb62225cb9 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -856,9 +856,6 @@ static int cp210x_startup(struct usb_serial *serial) struct usb_host_interface *cur_altsetting; struct cp210x_serial_private *spriv; - /* cp210x buffers behave strangely unless device is reset */ - usb_reset_device(serial->dev); - spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8a3813be1b28..824ea5e7ec8b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -87,7 +87,6 @@ struct ftdi_sio_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); static int ftdi_NDI_device_setup(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); @@ -98,10 +97,6 @@ static struct ftdi_sio_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = { - .probe = ftdi_mtxorb_hack_setup, -}; - static struct ftdi_sio_quirk ftdi_NDI_device_quirk = { .probe = ftdi_NDI_device_setup, }; @@ -151,6 +146,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_BM_ATOM_NANO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, @@ -256,14 +252,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, @@ -302,18 +296,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID), - .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, @@ -673,6 +661,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) }, + { USB_DEVICE(XSENS_VID, XSENS_MTW_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) }, { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, @@ -945,6 +935,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_2_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_3_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_4_PID) }, + /* ekey Devices */ + { USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) }, /* Infineon Devices */ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, { } /* Terminating entry */ @@ -1559,45 +1551,40 @@ static void ftdi_determine_type(struct usb_serial_port *port) } -/* Determine the maximum packet size for the device. This depends on the chip - * type and the USB host capabilities. The value should be obtained from the - * device descriptor as the chip will use the appropriate values for the host.*/ +/* + * Determine the maximum packet size for the device. This depends on the chip + * type and the USB host capabilities. The value should be obtained from the + * device descriptor as the chip will use the appropriate values for the host. + */ static void ftdi_set_max_packet_size(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct usb_device *udev = serial->dev; - - struct usb_interface *interface = serial->interface; + struct usb_interface *interface = port->serial->interface; struct usb_endpoint_descriptor *ep_desc; - unsigned num_endpoints; unsigned i; num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; - dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); - if (!num_endpoints) return; - /* NOTE: some customers have programmed FT232R/FT245R devices - * with an endpoint size of 0 - not good. In this case, we + /* + * NOTE: Some customers have programmed FT232R/FT245R devices + * with an endpoint size of 0 - not good. In this case, we * want to override the endpoint descriptor setting and use a - * value of 64 for wMaxPacketSize */ + * value of 64 for wMaxPacketSize. + */ for (i = 0; i < num_endpoints; i++) { - dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, - interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); ep_desc = &interface->cur_altsetting->endpoint[i].desc; - if (ep_desc->wMaxPacketSize == 0) { + if (!ep_desc->wMaxPacketSize) { ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i); + dev_warn(&port->dev, "Overriding wMaxPacketSize on endpoint %d\n", + usb_endpoint_num(ep_desc)); } } - /* set max packet size based on descriptor */ + /* Set max packet size based on last descriptor. */ priv->max_packet_size = usb_endpoint_maxp(ep_desc); - - dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } @@ -1866,24 +1853,6 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) return 0; } -/* - * The Matrix Orbital VK204-25-USB has an invalid IN endpoint. - * We have to correct it if we want to read from it. - */ -static int ftdi_mtxorb_hack_setup(struct usb_serial *serial) -{ - struct usb_host_endpoint *ep = serial->dev->ep_in[1]; - struct usb_endpoint_descriptor *ep_desc = &ep->desc; - - if (ep->enabled && ep_desc->wMaxPacketSize == 0) { - ep_desc->wMaxPacketSize = cpu_to_le16(0x40); - dev_info(&serial->dev->dev, - "Fixing invalid wMaxPacketSize on read pipe\n"); - } - - return 0; -} - static int ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index c4777bc6aee0..70b0b1d88ae9 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -42,6 +42,8 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ +#define FTDI_BM_ATOM_NANO_PID 0xa559 /* Basic Micro ATOM Nano USB2Serial */ + /* * Texas Instruments XDS100v2 JTAG / BeagleBone A3 * http://processors.wiki.ti.com/index.php/XDS100 @@ -140,12 +142,15 @@ /* * Xsens Technologies BV products (http://www.xsens.com). */ -#define XSENS_CONVERTER_0_PID 0xD388 -#define XSENS_CONVERTER_1_PID 0xD389 +#define XSENS_VID 0x2639 +#define XSENS_CONVERTER_PID 0xD00D /* Xsens USB-serial converter */ +#define XSENS_MTW_PID 0x0200 /* Xsens MTw */ +#define XSENS_CONVERTER_0_PID 0xD388 /* Xsens USB converter */ +#define XSENS_CONVERTER_1_PID 0xD389 /* Xsens Wireless Receiver */ #define XSENS_CONVERTER_2_PID 0xD38A -#define XSENS_CONVERTER_3_PID 0xD38B -#define XSENS_CONVERTER_4_PID 0xD38C -#define XSENS_CONVERTER_5_PID 0xD38D +#define XSENS_CONVERTER_3_PID 0xD38B /* Xsens USB-serial converter */ +#define XSENS_CONVERTER_4_PID 0xD38C /* Xsens Wireless Receiver */ +#define XSENS_CONVERTER_5_PID 0xD38D /* Xsens Awinda Station */ #define XSENS_CONVERTER_6_PID 0xD38E #define XSENS_CONVERTER_7_PID 0xD38F @@ -1375,3 +1380,8 @@ #define BRAINBOXES_US_160_6_PID 0x9006 /* US-160 16xRS232 1Mbaud Port 11 and 12 */ #define BRAINBOXES_US_160_7_PID 0x9007 /* US-160 16xRS232 1Mbaud Port 13 and 14 */ #define BRAINBOXES_US_160_8_PID 0x9008 /* US-160 16xRS232 1Mbaud Port 15 and 16 */ + +/* + * ekey biometric systems GmbH (http://ekey.net/) + */ +#define FTDI_EKEY_CONV_USB_PID 0xCB08 /* Converter USB */ diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index d7440b7557af..e020ad28a00c 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -62,8 +62,6 @@ static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int klsi_105_tiocmget(struct tty_struct *tty); -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); static void klsi_105_process_read_urb(struct urb *urb); static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size); @@ -93,7 +91,6 @@ static struct usb_serial_driver kl5kusb105d_device = { .set_termios = klsi_105_set_termios, /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, - .tiocmset = klsi_105_tiocmset, .port_probe = klsi_105_port_probe, .port_remove = klsi_105_port_remove, .throttle = usb_serial_generic_throttle, @@ -602,33 +599,6 @@ static int klsi_105_tiocmget(struct tty_struct *tty) return (int)line_state; } -static int klsi_105_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - int retval = -EINVAL; - -/* if this ever gets implemented, it should be done something like this: - struct usb_serial *serial = port->serial; - struct klsi_105_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int control; - - spin_lock_irqsave (&priv->lock, flags); - if (set & TIOCM_RTS) - priv->control_state |= TIOCM_RTS; - if (set & TIOCM_DTR) - priv->control_state |= TIOCM_DTR; - if (clear & TIOCM_RTS) - priv->control_state &= ~TIOCM_RTS; - if (clear & TIOCM_DTR) - priv->control_state &= ~TIOCM_DTR; - control = priv->control_state; - spin_unlock_irqrestore (&priv->lock, flags); - retval = mct_u232_set_modem_ctrl(serial, control); -*/ - return retval; -} - module_usb_serial_driver(serial_drivers, id_table); MODULE_AUTHOR(DRIVER_AUTHOR); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 393be562d875..3d88eefdf1d1 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1181,10 +1181,7 @@ static void mos7840_close(struct usb_serial_port *port) /* Freeing Write URBs */ for (j = 0; j < NUM_URBS; ++j) { if (mos7840_port->write_urb_pool[j]) { - if (mos7840_port->write_urb_pool[j]->transfer_buffer) - kfree(mos7840_port->write_urb_pool[j]-> - transfer_buffer); - + kfree(mos7840_port->write_urb_pool[j]->transfer_buffer); usb_free_urb(mos7840_port->write_urb_pool[j]); } } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a9688940543d..54a8120897a6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -275,8 +275,12 @@ static void option_instat_callback(struct urb *urb); #define ZTE_PRODUCT_MF622 0x0001 #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 -#define ZTE_PRODUCT_MC2718 0xffe8 #define ZTE_PRODUCT_AC2726 0xfff1 +#define ZTE_PRODUCT_CDMA_TECH 0xfffe +#define ZTE_PRODUCT_AC8710T 0xffff +#define ZTE_PRODUCT_MC2718 0xffe8 +#define ZTE_PRODUCT_AD3812 0xffeb +#define ZTE_PRODUCT_MC2716 0xffed #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -494,6 +498,10 @@ static void option_instat_callback(struct urb *urb); #define INOVIA_VENDOR_ID 0x20a6 #define INOVIA_SEW858 0x1105 +/* VIA Telecom */ +#define VIATELECOM_VENDOR_ID 0x15eb +#define VIATELECOM_PRODUCT_CDS7 0x0001 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -527,10 +535,18 @@ static const struct option_blacklist_info zte_k3765_z_blacklist = { .reserved = BIT(4), }; +static const struct option_blacklist_info zte_ad3812_z_blacklist = { + .sendsetup = BIT(0) | BIT(1) | BIT(2), +}; + static const struct option_blacklist_info zte_mc2718_z_blacklist = { .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4), }; +static const struct option_blacklist_info zte_mc2716_z_blacklist = { + .sendsetup = BIT(1) | BIT(2) | BIT(3), +}; + static const struct option_blacklist_info huawei_cdc12_blacklist = { .reserved = BIT(1) | BIT(2), }; @@ -1070,6 +1086,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012, 0xff) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ @@ -1544,13 +1561,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, - /* NOTE: most ZTE CDMA devices should be driven by zte_ev, not option */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, @@ -1724,6 +1746,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) }, + { USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1916,6 +1939,8 @@ static void option_instat_callback(struct urb *urb) dev_dbg(dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); } + } else if (status == -ENOENT || status == -ESHUTDOWN) { + dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status); } else dev_err(dev, "%s: error %d\n", __func__, status); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b3d5a35c0d4b..e9bad928039f 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -45,6 +45,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 42bc082896ac..71fd9da1d6e7 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -22,6 +22,7 @@ #define PL2303_PRODUCT_ID_GPRS 0x0609 #define PL2303_PRODUCT_ID_HCR331 0x331a #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 +#define PL2303_PRODUCT_ID_ZTEK 0xe1f1 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 02de3110fe94..475723c006f9 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -764,29 +764,39 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ dev_dbg(ddev, "found bulk in on endpoint %d\n", i); - bulk_in_endpoint[num_bulk_in] = endpoint; - ++num_bulk_in; + if (num_bulk_in < MAX_NUM_PORTS) { + bulk_in_endpoint[num_bulk_in] = endpoint; + ++num_bulk_in; + } } if (usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dev_dbg(ddev, "found bulk out on endpoint %d\n", i); - bulk_out_endpoint[num_bulk_out] = endpoint; - ++num_bulk_out; + if (num_bulk_out < MAX_NUM_PORTS) { + bulk_out_endpoint[num_bulk_out] = endpoint; + ++num_bulk_out; + } } if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in on endpoint %d\n", i); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = + endpoint; + ++num_interrupt_in; + } } if (usb_endpoint_is_int_out(endpoint)) { /* we found an interrupt out endpoint */ dev_dbg(ddev, "found interrupt out on endpoint %d\n", i); - interrupt_out_endpoint[num_interrupt_out] = endpoint; - ++num_interrupt_out; + if (num_interrupt_out < MAX_NUM_PORTS) { + interrupt_out_endpoint[num_interrupt_out] = + endpoint; + ++num_interrupt_out; + } } } @@ -809,8 +819,10 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n"); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = endpoint; + ++num_interrupt_in; + } } } } @@ -850,6 +862,11 @@ static int usb_serial_probe(struct usb_interface *interface, num_ports = type->num_ports; } + if (num_ports > MAX_NUM_PORTS) { + dev_warn(ddev, "too many ports requested: %d\n", num_ports); + num_ports = MAX_NUM_PORTS; + } + serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index e62f2dff8b7d..6c3734d2b45a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -514,6 +514,10 @@ static void command_port_read_callback(struct urb *urb) dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__); return; } + if (!urb->actual_length) { + dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__); + return; + } if (status) { dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status); if (status != -ENOENT) @@ -534,7 +538,8 @@ static void command_port_read_callback(struct urb *urb) /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ dev_dbg(&urb->dev->dev, "%s - event received\n", __func__); - } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { + } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) && + (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) { memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); command_info->command_finished = WHITEHEAT_CMD_COMPLETE; diff --git a/drivers/usb/serial/zte_ev.c b/drivers/usb/serial/zte_ev.c index e40ab739c4a6..1a132e9e947a 100644 --- a/drivers/usb/serial/zte_ev.c +++ b/drivers/usb/serial/zte_ev.c @@ -272,28 +272,8 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) } static const struct usb_device_id id_table[] = { - /* AC8710, AC8710T */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffff, 0xff, 0xff, 0xff) }, - /* AC8700 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xfffe, 0xff, 0xff, 0xff) }, /* MG880 */ { USB_DEVICE(0x19d2, 0xfffd) }, - { USB_DEVICE(0x19d2, 0xfffc) }, - { USB_DEVICE(0x19d2, 0xfffb) }, - /* AC8710_V3 */ - { USB_DEVICE(0x19d2, 0xfff6) }, - { USB_DEVICE(0x19d2, 0xfff7) }, - { USB_DEVICE(0x19d2, 0xfff8) }, - { USB_DEVICE(0x19d2, 0xfff9) }, - { USB_DEVICE(0x19d2, 0xffee) }, - /* AC2716, MC2716 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffed, 0xff, 0xff, 0xff) }, - /* AD3812 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffeb, 0xff, 0xff, 0xff) }, - { USB_DEVICE(0x19d2, 0xffec) }, - { USB_DEVICE(0x05C6, 0x3197) }, - { USB_DEVICE(0x05C6, 0x6000) }, - { USB_DEVICE(0x05C6, 0x9008) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 13b5bfbaf951..715f299af6ea 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -18,9 +18,7 @@ config USB_STORAGE This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' - (BLK_DEV_SD) for most USB storage devices. Some devices also - will require 'Probe all LUNs on each SCSI device' - (SCSI_MULTI_LUN). + (BLK_DEV_SD) for most USB storage devices. To compile this driver as a module, choose M here: the module will be called usb-storage. @@ -193,7 +191,7 @@ config USB_STORAGE_ENE_UB6250 depends on USB_STORAGE ---help--- Say Y here if you wish to control a ENE SD/MS Card reader. - To use SM card, please build driver/staging/keucr/keucr.ko + Note that this driver does not support SM cards. This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index ef6efb55dc31..56f782bef36b 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2344,8 +2344,8 @@ static int ene_ub6250_probe(struct usb_interface *intf, } if (!(misc_reg03 & 0x01)) { - pr_info("ums_eneub6250: The driver only supports SD/MS card. " - "To use SM card, please build driver/staging/keucr\n"); + pr_info("ums_eneub6250: This driver only supports SD/MS cards. " + "It does not support SM cards.\n"); } return result; diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 073a2c32ccc4..38a4504ce450 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1498,7 +1498,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) { int ret; - usb_stor_dbg(us, "LUN=%d\n", srb->device->lun); + usb_stor_dbg(us, "LUN=%d\n", (u8)srb->device->lun); switch (srb->device->lun) { case 0: @@ -1524,7 +1524,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) break; default: - usb_stor_dbg(us, "Invalid LUN %d\n", srb->device->lun); + usb_stor_dbg(us, "Invalid LUN %d\n", (u8)srb->device->lun); ret = USB_STOR_TRANSPORT_ERROR; break; } diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index bb05b984d5f6..503ac5c8d80f 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,32 +9,15 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } -static int uas_isnt_supported(struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - dev_warn(&udev->dev, "The driver for the USB controller %s does not " - "support scatter-gather which is\n", - hcd->driver->description); - dev_warn(&udev->dev, "required by the UAS driver. Please try an" - "alternative USB controller if you wish to use UAS.\n"); - return -ENODEV; -} - static int uas_find_uas_alt_setting(struct usb_interface *intf) { int i; - struct usb_device *udev = interface_to_usbdev(intf); - int sg_supported = udev->bus->sg_tablesize != 0; for (i = 0; i < intf->num_altsetting; i++) { struct usb_host_interface *alt = &intf->altsetting[i]; - if (uas_is_interface(alt)) { - if (!sg_supported) - return uas_isnt_supported(udev); + if (uas_is_interface(alt)) return alt->desc.bAlternateSetting; - } } return -ENODEV; @@ -81,9 +64,6 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (flags & US_FL_IGNORE_UAS) return 0; - if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) - return 0; - alt = uas_find_uas_alt_setting(intf); if (alt < 0) return 0; @@ -92,5 +72,23 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (r < 0) return 0; + if (udev->bus->sg_tablesize == 0) { + dev_warn(&udev->dev, + "The driver for the USB controller %s does not support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, + "required by the UAS driver. Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + + if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) { + dev_warn(&udev->dev, + "USB controller %s does not support streams, which are required by the UAS driver.\n", + hcd_to_bus(hcd)->bus_name); + dev_warn(&udev->dev, + "Please try an other USB controller if you wish to use UAS.\n"); + return 0; + } + return 1; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 511b22953167..3f42785f653c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1026,7 +1026,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) usb_endpoint_num(&eps[3]->desc)); if (udev->speed != USB_SPEED_SUPER) { - devinfo->qdepth = 256; + devinfo->qdepth = 32; devinfo->use_streams = 0; } else { devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 80a5b366255f..7ef99b2f3aaf 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -922,6 +922,12 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +UNUSUAL_DEV( 0x06ca, 0x2003, 0x0100, 0x0100, + "Newer Technology", + "uSCSI", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Adrian Pilchowiec <adi1981@epf.pl> */ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, "RockChip", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index f1c96261a501..cedb29252a92 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -347,14 +347,16 @@ static int usb_stor_control_thread(void * __us) */ else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) { - usb_stor_dbg(us, "Bad target number (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad target number (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } else if (us->srb->device->lun > us->max_lun) { - usb_stor_dbg(us, "Bad LUN (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad LUN (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig new file mode 100644 index 000000000000..bd99e9e47e50 --- /dev/null +++ b/drivers/usb/usbip/Kconfig @@ -0,0 +1,41 @@ +config USBIP_CORE + tristate "USB/IP support" + depends on USB && NET + ---help--- + This enables pushing USB packets over IP to allow remote + machines direct access to USB devices. It provides the + USB/IP core that is required by both drivers. + + For more details, and to get the userspace utility + programs, please see <http://usbip.sourceforge.net/>. + + To compile this as a module, choose M here: the module will + be called usbip-core. + + If unsure, say N. + +config USBIP_VHCI_HCD + tristate "VHCI hcd" + depends on USBIP_CORE + ---help--- + This enables the USB/IP virtual host controller driver, + which is run on the remote machine. + + To compile this driver as a module, choose M here: the + module will be called vhci-hcd. + +config USBIP_HOST + tristate "Host driver" + depends on USBIP_CORE + ---help--- + This enables the USB/IP host driver, which is run on the + machine that is sharing the USB devices. + + To compile this driver as a module, choose M here: the + module will be called usbip-host. + +config USBIP_DEBUG + bool "Debug messages for USB/IP" + depends on USBIP_CORE + ---help--- + This enables the debug messages from the USB/IP drivers. diff --git a/drivers/usb/usbip/Makefile b/drivers/usb/usbip/Makefile new file mode 100644 index 000000000000..9ecd61545be1 --- /dev/null +++ b/drivers/usb/usbip/Makefile @@ -0,0 +1,10 @@ +ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG + +obj-$(CONFIG_USBIP_CORE) += usbip-core.o +usbip-core-y := usbip_common.o usbip_event.o + +obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o +vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o + +obj-$(CONFIG_USBIP_HOST) += usbip-host.o +usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o diff --git a/drivers/usb/usbip/README b/drivers/usb/usbip/README new file mode 100644 index 000000000000..41a2cf2e77a6 --- /dev/null +++ b/drivers/usb/usbip/README @@ -0,0 +1,7 @@ +TODO: + - more discussion about the protocol + - testing + - review of the userspace interface + - document the protocol + +Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h new file mode 100644 index 000000000000..266e2b0ce9a8 --- /dev/null +++ b/drivers/usb/usbip/stub.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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 __USBIP_STUB_H +#define __USBIP_STUB_H + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> + +#define STUB_BUSID_OTHER 0 +#define STUB_BUSID_REMOV 1 +#define STUB_BUSID_ADDED 2 +#define STUB_BUSID_ALLOC 3 + +struct stub_device { + struct usb_interface *interface; + struct usb_device *udev; + + struct usbip_device ud; + __u32 devid; + + /* + * stub_priv preserves private data of each urb. + * It is allocated as stub_priv_cache and assigned to urb->context. + * + * stub_priv is always linked to any one of 3 lists; + * priv_init: linked to this until the comletion of a urb. + * priv_tx : linked to this after the completion of a urb. + * priv_free: linked to this after the sending of the result. + * + * Any of these list operations should be locked by priv_lock. + */ + spinlock_t priv_lock; + struct list_head priv_init; + struct list_head priv_tx; + struct list_head priv_free; + + /* see comments for unlinking in stub_rx.c */ + struct list_head unlink_tx; + struct list_head unlink_free; + + wait_queue_head_t tx_waitq; +}; + +/* private data into urb->priv */ +struct stub_priv { + unsigned long seqnum; + struct list_head list; + struct stub_device *sdev; + struct urb *urb; + + int unlinking; +}; + +struct stub_unlink { + unsigned long seqnum; + struct list_head list; + __u32 status; +}; + +/* same as SYSFS_BUS_ID_SIZE */ +#define BUSID_SIZE 32 + +struct bus_id_priv { + char name[BUSID_SIZE]; + char status; + int interf_count; + struct stub_device *sdev; + struct usb_device *udev; + char shutdown_busid; +}; + +/* stub_priv is allocated from stub_priv_cache */ +extern struct kmem_cache *stub_priv_cache; + +/* stub_dev.c */ +extern struct usb_device_driver stub_driver; + +/* stub_main.c */ +struct bus_id_priv *get_busid_priv(const char *busid); +int del_match_busid(char *busid); +void stub_device_cleanup_urbs(struct stub_device *sdev); + +/* stub_rx.c */ +int stub_rx_loop(void *data); + +/* stub_tx.c */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status); +void stub_complete(struct urb *urb); +int stub_tx_loop(void *data); + +#endif /* __USBIP_STUB_H */ diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c new file mode 100644 index 000000000000..fac20e0434c0 --- /dev/null +++ b/drivers/usb/usbip/stub_dev.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/device.h> +#include <linux/file.h> +#include <linux/kthread.h> +#include <linux/module.h> + +#include "usbip_common.h" +#include "stub.h" + +/* + * usbip_status shows the status of usbip-host as long as this driver is bound + * to the target device. + */ +static ssize_t usbip_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int status; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + spin_lock_irq(&sdev->ud.lock); + status = sdev->ud.status; + spin_unlock_irq(&sdev->ud.lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} +static DEVICE_ATTR_RO(usbip_status); + +/* + * usbip_sockfd gets a socket descriptor of an established TCP connection that + * is used to transfer usbip requests by kernel threads. -1 is a magic number + * by which usbip connection is finished. + */ +static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int sockfd = 0; + struct socket *socket; + int rv; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + rv = sscanf(buf, "%d", &sockfd); + if (rv != 1) + return -EINVAL; + + if (sockfd != -1) { + int err; + + dev_info(dev, "stub up\n"); + + spin_lock_irq(&sdev->ud.lock); + + if (sdev->ud.status != SDEV_ST_AVAILABLE) { + dev_err(dev, "not ready\n"); + goto err; + } + + socket = sockfd_lookup(sockfd, &err); + if (!socket) + goto err; + + sdev->ud.tcp_socket = socket; + + spin_unlock_irq(&sdev->ud.lock); + + sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, + "stub_rx"); + sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, + "stub_tx"); + + spin_lock_irq(&sdev->ud.lock); + sdev->ud.status = SDEV_ST_USED; + spin_unlock_irq(&sdev->ud.lock); + + } else { + dev_info(dev, "stub down\n"); + + spin_lock_irq(&sdev->ud.lock); + if (sdev->ud.status != SDEV_ST_USED) + goto err; + + spin_unlock_irq(&sdev->ud.lock); + + usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); + } + + return count; + +err: + spin_unlock_irq(&sdev->ud.lock); + return -EINVAL; +} +static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); + +static int stub_add_files(struct device *dev) +{ + int err = 0; + + err = device_create_file(dev, &dev_attr_usbip_status); + if (err) + goto err_status; + + err = device_create_file(dev, &dev_attr_usbip_sockfd); + if (err) + goto err_sockfd; + + err = device_create_file(dev, &dev_attr_usbip_debug); + if (err) + goto err_debug; + + return 0; + +err_debug: + device_remove_file(dev, &dev_attr_usbip_sockfd); +err_sockfd: + device_remove_file(dev, &dev_attr_usbip_status); +err_status: + return err; +} + +static void stub_remove_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_usbip_status); + device_remove_file(dev, &dev_attr_usbip_sockfd); + device_remove_file(dev, &dev_attr_usbip_debug); +} + +static void stub_shutdown_connection(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + /* + * When removing an exported device, kernel panic sometimes occurred + * and then EIP was sk_wait_data of stub_rx thread. Is this because + * sk_wait_data returned though stub_rx thread was already finished by + * step 1? + */ + if (ud->tcp_socket) { + dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", + ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* 1. stop threads */ + if (ud->tcp_rx) { + kthread_stop_put(ud->tcp_rx); + ud->tcp_rx = NULL; + } + if (ud->tcp_tx) { + kthread_stop_put(ud->tcp_tx); + ud->tcp_tx = NULL; + } + + /* + * 2. close the socket + * + * tcp_socket is freed after threads are killed so that usbip_xmit does + * not touch NULL socket. + */ + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + + /* 3. free used data */ + stub_device_cleanup_urbs(sdev); + + /* 4. free stub_unlink */ + { + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_del(&unlink->list); + kfree(unlink); + } + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, + list) { + list_del(&unlink->list); + kfree(unlink); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + } +} + +static void stub_device_reset(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct usb_device *udev = sdev->udev; + int ret; + + dev_dbg(&udev->dev, "device reset"); + + ret = usb_lock_device_for_reset(udev, sdev->interface); + if (ret < 0) { + dev_err(&udev->dev, "lock for reset\n"); + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); + return; + } + + /* try to reset the device */ + ret = usb_reset_device(udev); + usb_unlock_device(udev); + + spin_lock_irq(&ud->lock); + if (ret) { + dev_err(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_ERROR; + } else { + dev_info(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_AVAILABLE; + } + spin_unlock_irq(&ud->lock); +} + +static void stub_device_unusable(struct usbip_device *ud) +{ + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); +} + +/** + * stub_device_alloc - allocate a new stub_device struct + * @interface: usb_interface of a new device + * + * Allocates and initializes a new stub_device struct. + */ +static struct stub_device *stub_device_alloc(struct usb_device *udev) +{ + struct stub_device *sdev; + int busnum = udev->bus->busnum; + int devnum = udev->devnum; + + dev_dbg(&udev->dev, "allocating stub device"); + + /* yes, it's a new device */ + sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); + if (!sdev) + return NULL; + + sdev->udev = usb_get_dev(udev); + + /* + * devid is defined with devnum when this driver is first allocated. + * devnum may change later if a device is reset. However, devid never + * changes during a usbip connection. + */ + sdev->devid = (busnum << 16) | devnum; + sdev->ud.side = USBIP_STUB; + sdev->ud.status = SDEV_ST_AVAILABLE; + spin_lock_init(&sdev->ud.lock); + sdev->ud.tcp_socket = NULL; + + INIT_LIST_HEAD(&sdev->priv_init); + INIT_LIST_HEAD(&sdev->priv_tx); + INIT_LIST_HEAD(&sdev->priv_free); + INIT_LIST_HEAD(&sdev->unlink_free); + INIT_LIST_HEAD(&sdev->unlink_tx); + spin_lock_init(&sdev->priv_lock); + + init_waitqueue_head(&sdev->tx_waitq); + + sdev->ud.eh_ops.shutdown = stub_shutdown_connection; + sdev->ud.eh_ops.reset = stub_device_reset; + sdev->ud.eh_ops.unusable = stub_device_unusable; + + usbip_start_eh(&sdev->ud); + + dev_dbg(&udev->dev, "register new device\n"); + + return sdev; +} + +static void stub_device_free(struct stub_device *sdev) +{ + kfree(sdev); +} + +static int stub_probe(struct usb_device *udev) +{ + struct stub_device *sdev = NULL; + const char *udev_busid = dev_name(&udev->dev); + int err = 0; + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + /* check we should claim or not by busid_table */ + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || + (busid_priv->status == STUB_BUSID_OTHER)) { + dev_info(&udev->dev, + "%s is not in match_busid table... skip!\n", + udev_busid); + + /* + * Return value should be ENODEV or ENOXIO to continue trying + * other matched drivers by the driver core. + * See driver_probe_device() in driver/base/dd.c + */ + return -ENODEV; + } + + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { + dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", + udev_busid); + return -ENODEV; + } + + if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { + dev_dbg(&udev->dev, + "%s is attached on vhci_hcd... skip!\n", + udev_busid); + + return -ENODEV; + } + + /* ok, this is my device */ + sdev = stub_device_alloc(udev); + if (!sdev) + return -ENOMEM; + + dev_info(&udev->dev, + "usbip-host: register new device (bus %u dev %u)\n", + udev->bus->busnum, udev->devnum); + + busid_priv->shutdown_busid = 0; + + /* set private data to usb_device */ + dev_set_drvdata(&udev->dev, sdev); + busid_priv->sdev = sdev; + busid_priv->udev = udev; + + /* + * Claim this hub port. + * It doesn't matter what value we pass as owner + * (struct dev_state) as long as it is unique. + */ + rc = usb_hub_claim_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to claim port\n"); + return rc; + } + + err = stub_add_files(&udev->dev); + if (err) { + dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(udev); + kthread_stop_put(sdev->ud.eh); + + busid_priv->sdev = NULL; + stub_device_free(sdev); + return err; + } + busid_priv->status = STUB_BUSID_ALLOC; + + return 0; +} + +static void shutdown_busid(struct bus_id_priv *busid_priv) +{ + if (busid_priv->sdev && !busid_priv->shutdown_busid) { + busid_priv->shutdown_busid = 1; + usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); + + /* wait for the stop of the event handler */ + usbip_stop_eh(&busid_priv->sdev->ud); + } +} + +/* + * called in usb_disconnect() or usb_deregister() + * but only if actconfig(active configuration) exists + */ +static void stub_disconnect(struct usb_device *udev) +{ + struct stub_device *sdev; + const char *udev_busid = dev_name(&udev->dev); + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv) { + BUG(); + return; + } + + sdev = dev_get_drvdata(&udev->dev); + + /* get stub_device */ + if (!sdev) { + dev_err(&udev->dev, "could not get device"); + return; + } + + dev_set_drvdata(&udev->dev, NULL); + + /* + * NOTE: rx/tx threads are invoked for each usb_device. + */ + stub_remove_files(&udev->dev); + + /* release port */ + rc = usb_hub_release_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to release port\n"); + return; + } + + /* If usb reset is called from event handler */ + if (busid_priv->sdev->ud.eh == current) + return; + + /* shutdown the current connection */ + shutdown_busid(busid_priv); + + usb_put_dev(sdev->udev); + + /* free sdev */ + busid_priv->sdev = NULL; + stub_device_free(sdev); + + if (busid_priv->status == STUB_BUSID_ALLOC) { + busid_priv->status = STUB_BUSID_ADDED; + } else { + busid_priv->status = STUB_BUSID_OTHER; + del_match_busid((char *)udev_busid); + } +} + +#ifdef CONFIG_PM + +/* These functions need usb_port_suspend and usb_port_resume, + * which reside in drivers/usb/core/usb.h. Skip for now. */ + +static int stub_suspend(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_suspend\n"); + + return 0; +} + +static int stub_resume(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_resume\n"); + + return 0; +} + +#endif /* CONFIG_PM */ + +struct usb_device_driver stub_driver = { + .name = "usbip-host", + .probe = stub_probe, + .disconnect = stub_disconnect, +#ifdef CONFIG_PM + .suspend = stub_suspend, + .resume = stub_resume, +#endif + .supports_autosuspend = 0, +}; diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c new file mode 100644 index 000000000000..44ab43fc4fcc --- /dev/null +++ b/drivers/usb/usbip/stub_main.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/string.h> +#include <linux/module.h> +#include <linux/device.h> + +#include "usbip_common.h" +#include "stub.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP Host Driver" + +struct kmem_cache *stub_priv_cache; +/* + * busid_tables defines matching busids that usbip can grab. A user can change + * dynamically what device is locally used and what device is exported to a + * remote host. + */ +#define MAX_BUSID 16 +static struct bus_id_priv busid_table[MAX_BUSID]; +static spinlock_t busid_table_lock; + +static void init_busid_table(void) +{ + /* + * This also sets the bus_table[i].status to + * STUB_BUSID_OTHER, which is 0. + */ + memset(busid_table, 0, sizeof(busid_table)); + + spin_lock_init(&busid_table_lock); +} + +/* + * Find the index of the busid by name. + * Must be called with busid_table_lock held. + */ +static int get_busid_idx(const char *busid) +{ + int i; + int idx = -1; + + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { + idx = i; + break; + } + return idx; +} + +struct bus_id_priv *get_busid_priv(const char *busid) +{ + int idx; + struct bus_id_priv *bid = NULL; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx >= 0) + bid = &(busid_table[idx]); + spin_unlock(&busid_table_lock); + + return bid; +} + +static int add_match_busid(char *busid) +{ + int i; + int ret = -1; + + spin_lock(&busid_table_lock); + /* already registered? */ + if (get_busid_idx(busid) >= 0) { + ret = 0; + goto out; + } + + for (i = 0; i < MAX_BUSID; i++) + if (!busid_table[i].name[0]) { + strlcpy(busid_table[i].name, busid, BUSID_SIZE); + if ((busid_table[i].status != STUB_BUSID_ALLOC) && + (busid_table[i].status != STUB_BUSID_REMOV)) + busid_table[i].status = STUB_BUSID_ADDED; + ret = 0; + break; + } + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +int del_match_busid(char *busid) +{ + int idx; + int ret = -1; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx < 0) + goto out; + + /* found */ + ret = 0; + + if (busid_table[idx].status == STUB_BUSID_OTHER) + memset(busid_table[idx].name, 0, BUSID_SIZE); + + if ((busid_table[idx].status != STUB_BUSID_OTHER) && + (busid_table[idx].status != STUB_BUSID_ADDED)) + busid_table[idx].status = STUB_BUSID_REMOV; + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +static ssize_t show_match_busid(struct device_driver *drv, char *buf) +{ + int i; + char *out = buf; + + spin_lock(&busid_table_lock); + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + out += sprintf(out, "%s ", busid_table[i].name); + spin_unlock(&busid_table_lock); + out += sprintf(out, "\n"); + + return out - buf; +} + +static ssize_t store_match_busid(struct device_driver *dev, const char *buf, + size_t count) +{ + int len; + char busid[BUSID_SIZE]; + + if (count < 5) + return -EINVAL; + + /* busid needs to include \0 termination */ + len = strlcpy(busid, buf + 4, BUSID_SIZE); + if (sizeof(busid) <= len) + return -EINVAL; + + if (!strncmp(buf, "add ", 4)) { + if (add_match_busid(busid) < 0) + return -ENOMEM; + + pr_debug("add busid %s\n", busid); + return count; + } + + if (!strncmp(buf, "del ", 4)) { + if (del_match_busid(busid) < 0) + return -ENODEV; + + pr_debug("del busid %s\n", busid); + return count; + } + + return -EINVAL; +} +static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, + store_match_busid); + +static ssize_t rebind_store(struct device_driver *dev, const char *buf, + size_t count) +{ + int ret; + int len; + struct bus_id_priv *bid; + + /* buf length should be less that BUSID_SIZE */ + len = strnlen(buf, BUSID_SIZE); + + if (!(len < BUSID_SIZE)) + return -EINVAL; + + bid = get_busid_priv(buf); + if (!bid) + return -ENODEV; + + ret = device_attach(&bid->udev->dev); + if (ret < 0) { + dev_err(&bid->udev->dev, "rebind failed\n"); + return ret; + } + + return count; +} + +static DRIVER_ATTR_WO(rebind); + +static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) +{ + struct stub_priv *priv, *tmp; + + list_for_each_entry_safe(priv, tmp, listhead, list) { + list_del(&priv->list); + return priv; + } + + return NULL; +} + +static struct stub_priv *stub_priv_pop(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = stub_priv_pop_from_listhead(&sdev->priv_init); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_tx); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_free); + +done: + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +void stub_device_cleanup_urbs(struct stub_device *sdev) +{ + struct stub_priv *priv; + struct urb *urb; + + dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + + while ((priv = stub_priv_pop(sdev))) { + urb = priv->urb; + dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + usb_kill_urb(urb); + + kmem_cache_free(stub_priv_cache, priv); + + kfree(urb->transfer_buffer); + kfree(urb->setup_packet); + usb_free_urb(urb); + } +} + +static int __init usbip_host_init(void) +{ + int ret; + + init_busid_table(); + + stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + if (!stub_priv_cache) { + pr_err("kmem_cache_create failed\n"); + return -ENOMEM; + } + + ret = usb_register_device_driver(&stub_driver, THIS_MODULE); + if (ret) { + pr_err("usb_register failed %d\n", ret); + goto err_usb_register; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_create_file: + usb_deregister_device_driver(&stub_driver); +err_usb_register: + kmem_cache_destroy(stub_priv_cache); + return ret; +} + +static void __exit usbip_host_exit(void) +{ + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + + /* + * deregister() calls stub_disconnect() for all devices. Device + * specific data is cleared in stub_disconnect(). + */ + usb_deregister_device_driver(&stub_driver); + + kmem_cache_destroy(stub_priv_cache); +} + +module_init(usbip_host_init); +module_exit(usbip_host_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c new file mode 100644 index 000000000000..00e475c51a12 --- /dev/null +++ b/drivers/usb/usbip/stub_rx.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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 <asm/byteorder.h> +#include <linux/kthread.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "usbip_common.h" +#include "stub.h" + +static int is_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_CLEAR_FEATURE) && + (req->bRequestType == USB_RECIP_ENDPOINT) && + (req->wValue == USB_ENDPOINT_HALT); +} + +static int is_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_INTERFACE) && + (req->bRequestType == USB_RECIP_INTERFACE); +} + +static int is_set_configuration_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_CONFIGURATION) && + (req->bRequestType == USB_RECIP_DEVICE); +} + +static int is_reset_device_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 value; + __u16 index; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + value = le16_to_cpu(req->wValue); + index = le16_to_cpu(req->wIndex); + + if ((req->bRequest == USB_REQ_SET_FEATURE) && + (req->bRequestType == USB_RT_PORT) && + (value == USB_PORT_FEAT_RESET)) { + usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index); + return 1; + } else + return 0; +} + +static int tweak_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + int target_endp; + int target_dir; + int target_pipe; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + /* + * The stalled endpoint is specified in the wIndex value. The endpoint + * of the urb is the target of this clear_halt request (i.e., control + * endpoint). + */ + target_endp = le16_to_cpu(req->wIndex) & 0x000f; + + /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */ + target_dir = le16_to_cpu(req->wIndex) & 0x0080; + + if (target_dir) + target_pipe = usb_rcvctrlpipe(urb->dev, target_endp); + else + target_pipe = usb_sndctrlpipe(urb->dev, target_endp); + + ret = usb_clear_halt(urb->dev, target_pipe); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_clear_halt error: devnum %d endp %d ret %d\n", + urb->dev->devnum, target_endp, ret); + else + dev_info(&urb->dev->dev, + "usb_clear_halt done: devnum %d endp %d\n", + urb->dev->devnum, target_endp); + + return ret; +} + +static int tweak_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 alternate; + __u16 interface; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + alternate = le16_to_cpu(req->wValue); + interface = le16_to_cpu(req->wIndex); + + usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", + interface, alternate); + + ret = usb_set_interface(urb->dev, interface, alternate); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_set_interface error: inf %u alt %u ret %d\n", + interface, alternate, ret); + else + dev_info(&urb->dev->dev, + "usb_set_interface done: inf %u alt %u\n", + interface, alternate); + + return ret; +} + +static int tweak_set_configuration_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + struct usb_ctrlrequest *req; + __u16 config; + int err; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + config = le16_to_cpu(req->wValue); + + err = usb_set_configuration(sdev->udev, config); + if (err && err != -ENODEV) + dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", + config, err); + return 0; +} + +static int tweak_reset_device_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + + dev_info(&urb->dev->dev, "usb_queue_reset_device\n"); + + /* + * With the implementation of pre_reset and post_reset the driver no + * longer unbinds. This allows the use of synchronous reset. + */ + + if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) { + dev_err(&urb->dev->dev, "could not obtain lock to reset device\n"); + return 0; + } + usb_reset_device(sdev->udev); + usb_unlock_device(sdev->udev); + + return 0; +} + +/* + * clear_halt, set_interface, and set_configuration require special tricks. + */ +static void tweak_special_requests(struct urb *urb) +{ + if (!urb || !urb->setup_packet) + return; + + if (usb_pipetype(urb->pipe) != PIPE_CONTROL) + return; + + if (is_clear_halt_cmd(urb)) + /* tweak clear_halt */ + tweak_clear_halt_cmd(urb); + + else if (is_set_interface_cmd(urb)) + /* tweak set_interface */ + tweak_set_interface_cmd(urb); + + else if (is_set_configuration_cmd(urb)) + /* tweak set_configuration */ + tweak_set_configuration_cmd(urb); + + else if (is_reset_device_cmd(urb)) + tweak_reset_device_cmd(urb); + else + usbip_dbg_stub_rx("no need to tweak\n"); +} + +/* + * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb(). + * By unlinking the urb asynchronously, stub_rx can continuously + * process coming urbs. Even if the urb is unlinked, its completion + * handler will be called and stub_tx will send a return pdu. + * + * See also comments about unlinking strategy in vhci_hcd.c. + */ +static int stub_recv_cmd_unlink(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry(priv, &sdev->priv_init, list) { + if (priv->seqnum != pdu->u.cmd_unlink.seqnum) + continue; + + dev_info(&priv->urb->dev->dev, "unlink urb %p\n", + priv->urb); + + /* + * This matched urb is not completed yet (i.e., be in + * flight in usb hcd hardware/driver). Now we are + * cancelling it. The unlinking flag means that we are + * now not going to return the normal result pdu of a + * submission request, but going to return a result pdu + * of the unlink request. + */ + priv->unlinking = 1; + + /* + * In the case that unlinking flag is on, prev->seqnum + * is changed from the seqnum of the cancelling urb to + * the seqnum of the unlink request. This will be used + * to make the result pdu of the unlink request. + */ + priv->seqnum = pdu->base.seqnum; + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* + * usb_unlink_urb() is now out of spinlocking to avoid + * spinlock recursion since stub_complete() is + * sometimes called in this context but not in the + * interrupt context. If stub_complete() is executed + * before we call usb_unlink_urb(), usb_unlink_urb() + * will return an error value. In this case, stub_tx + * will return the result pdu of this unlink request + * though submission is completed and actual unlinking + * is not executed. OK? + */ + /* In the above case, urb->status is not -ECONNRESET, + * so a driver in a client host will know the failure + * of the unlink request ? + */ + ret = usb_unlink_urb(priv->urb); + if (ret != -EINPROGRESS) + dev_err(&priv->urb->dev->dev, + "failed to unlink a urb %p, ret %d\n", + priv->urb, ret); + + return 0; + } + + usbip_dbg_stub_rx("seqnum %d is not pending\n", + pdu->u.cmd_unlink.seqnum); + + /* + * The urb of the unlink target is not found in priv_init queue. It was + * already completed and its results is/was going to be sent by a + * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only + * return the completeness of this unlink request to vhci_hcd. + */ + stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return 0; +} + +static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) +{ + struct usbip_device *ud = &sdev->ud; + int valid = 0; + + if (pdu->base.devid == sdev->devid) { + spin_lock_irq(&ud->lock); + if (ud->status == SDEV_ST_USED) { + /* A request is valid. */ + valid = 1; + } + spin_unlock_irq(&ud->lock); + } + + return valid; +} + +static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, + struct usbip_header *pdu) +{ + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + unsigned long flags; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC); + if (!priv) { + dev_err(&sdev->interface->dev, "alloc stub_priv\n"); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return NULL; + } + + priv->seqnum = pdu->base.seqnum; + priv->sdev = sdev; + + /* + * After a stub_priv is linked to a list_head, + * our error handler can free allocated data. + */ + list_add_tail(&priv->list, &sdev->priv_init); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +static int get_pipe(struct stub_device *sdev, int epnum, int dir) +{ + struct usb_device *udev = sdev->udev; + struct usb_host_endpoint *ep; + struct usb_endpoint_descriptor *epd = NULL; + + if (dir == USBIP_DIR_IN) + ep = udev->ep_in[epnum & 0x7f]; + else + ep = udev->ep_out[epnum & 0x7f]; + if (!ep) { + dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", + epnum); + BUG(); + } + + epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndctrlpipe(udev, epnum); + else + return usb_rcvctrlpipe(udev, epnum); + } + + if (usb_endpoint_xfer_bulk(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndbulkpipe(udev, epnum); + else + return usb_rcvbulkpipe(udev, epnum); + } + + if (usb_endpoint_xfer_int(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndintpipe(udev, epnum); + else + return usb_rcvintpipe(udev, epnum); + } + + if (usb_endpoint_xfer_isoc(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndisocpipe(udev, epnum); + else + return usb_rcvisocpipe(udev, epnum); + } + + /* NOT REACHED */ + dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + return 0; +} + +static void masking_bogus_flags(struct urb *urb) +{ + int xfertype; + struct usb_device *dev; + struct usb_host_endpoint *ep; + int is_out; + unsigned int allowed; + + if (!urb || urb->hcpriv || !urb->complete) + return; + dev = urb->dev; + if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) + return; + + ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) + return; + + xfertype = usb_endpoint_type(&ep->desc); + if (xfertype == USB_ENDPOINT_XFER_CONTROL) { + struct usb_ctrlrequest *setup = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (!setup) + return; + is_out = !(setup->bRequestType & USB_DIR_IN) || + !setup->wLength; + } else { + is_out = usb_endpoint_dir_out(&ep->desc); + } + + /* enforce simple/standard policy */ + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | + URB_DIR_MASK | URB_FREE_BUFFER); + switch (xfertype) { + case USB_ENDPOINT_XFER_BULK: + if (is_out) + allowed |= URB_ZERO_PACKET; + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_CONTROL: + allowed |= URB_NO_FSBR; /* only affects UHCI */ + /* FALLTHROUGH */ + default: /* all non-iso endpoints */ + if (!is_out) + allowed |= URB_SHORT_NOT_OK; + break; + case USB_ENDPOINT_XFER_ISOC: + allowed |= URB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; +} + +static void stub_recv_cmd_submit(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + struct usb_device *udev = sdev->udev; + int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + + priv = stub_priv_alloc(sdev, pdu); + if (!priv) + return; + + /* setup a urb */ + if (usb_pipeisoc(pipe)) + priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, + GFP_KERNEL); + else + priv->urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!priv->urb) { + dev_err(&sdev->interface->dev, "malloc urb\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* allocate urb transfer buffer, if needed */ + if (pdu->u.cmd_submit.transfer_buffer_length > 0) { + priv->urb->transfer_buffer = + kzalloc(pdu->u.cmd_submit.transfer_buffer_length, + GFP_KERNEL); + if (!priv->urb->transfer_buffer) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + } + + /* copy urb setup packet */ + priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, + GFP_KERNEL); + if (!priv->urb->setup_packet) { + dev_err(&sdev->interface->dev, "allocate setup_packet\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* set other members from the base header of pdu */ + priv->urb->context = (void *) priv; + priv->urb->dev = udev; + priv->urb->pipe = pipe; + priv->urb->complete = stub_complete; + + usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); + + + if (usbip_recv_xbuff(ud, priv->urb) < 0) + return; + + if (usbip_recv_iso(ud, priv->urb) < 0) + return; + + /* no need to submit an intercepted request, but harmless? */ + tweak_special_requests(priv->urb); + + masking_bogus_flags(priv->urb); + /* urb is now ready to submit */ + ret = usb_submit_urb(priv->urb, GFP_KERNEL); + + if (ret == 0) + usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", + pdu->base.seqnum); + else { + dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret); + usbip_dump_header(pdu); + usbip_dump_urb(priv->urb); + + /* + * Pessimistic. + * This connection will be discarded. + */ + usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + } + + usbip_dbg_stub_rx("Leave\n"); +} + +/* recv a pdu */ +static void stub_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct device *dev = &sdev->udev->dev; + + usbip_dbg_stub_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret != sizeof(pdu)) { + dev_err(dev, "recv a header, %d\n", ret); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_stub_rx) + usbip_dump_header(&pdu); + + if (!valid_request(sdev, &pdu)) { + dev_err(dev, "recv invalid request\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + switch (pdu.base.command) { + case USBIP_CMD_UNLINK: + stub_recv_cmd_unlink(sdev, &pdu); + break; + + case USBIP_CMD_SUBMIT: + stub_recv_cmd_submit(sdev, &pdu); + break; + + default: + /* NOTREACHED */ + dev_err(dev, "unknown pdu\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + break; + } +} + +int stub_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + stub_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c new file mode 100644 index 000000000000..dbcabc9dbe0d --- /dev/null +++ b/drivers/usb/usbip/stub_tx.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/kthread.h> +#include <linux/socket.h> + +#include "usbip_common.h" +#include "stub.h" + +static void stub_free_priv_and_urb(struct stub_priv *priv) +{ + struct urb *urb = priv->urb; + + kfree(urb->setup_packet); + kfree(urb->transfer_buffer); + list_del(&priv->list); + kmem_cache_free(stub_priv_cache, priv); + usb_free_urb(urb); +} + +/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status) +{ + struct stub_unlink *unlink; + + unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); + if (!unlink) { + usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + unlink->seqnum = seqnum; + unlink->status = status; + + list_add_tail(&unlink->list, &sdev->unlink_tx); +} + +/** + * stub_complete - completion handler of a usbip urb + * @urb: pointer to the urb completed + * + * When a urb has completed, the USB core driver calls this function mostly in + * the interrupt context. To return the result of a urb, the completed urb is + * linked to the pending list of returning. + * + */ +void stub_complete(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + unsigned long flags; + + usbip_dbg_stub_tx("complete! status %d\n", urb->status); + + switch (urb->status) { + case 0: + /* OK */ + break; + case -ENOENT: + dev_info(&urb->dev->dev, + "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); + return; + case -ECONNRESET: + dev_info(&urb->dev->dev, + "unlinked by a call to usb_unlink_urb()\n"); + break; + case -EPIPE: + dev_info(&urb->dev->dev, "endpoint %d is stalled\n", + usb_pipeendpoint(urb->pipe)); + break; + case -ESHUTDOWN: + dev_info(&urb->dev->dev, "device removed?\n"); + break; + default: + dev_info(&urb->dev->dev, + "urb completion with non-zero status %d\n", + urb->status); + break; + } + + /* link a urb to the queue of tx. */ + spin_lock_irqsave(&sdev->priv_lock, flags); + if (priv->unlinking) { + stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); + stub_free_priv_and_urb(priv); + } else { + list_move_tail(&priv->list, &sdev->priv_tx); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* wake up tx_thread */ + wake_up(&sdev->tx_waitq); +} + +static inline void setup_base_pdu(struct usbip_header_basic *base, + __u32 command, __u32 seqnum) +{ + base->command = command; + base->seqnum = seqnum; + base->devid = 0; + base->ep = 0; + base->direction = 0; +} + +static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + + setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum); + usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1); +} + +static void setup_ret_unlink_pdu(struct usbip_header *rpdu, + struct stub_unlink *unlink) +{ + setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum); + rpdu->u.ret_unlink.status = unlink->status; +} + +static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) { + list_move_tail(&priv->list, &sdev->priv_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return priv; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_submit(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + struct msghdr msg; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); + + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; + + /* 1. setup usbip_header */ + setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; + txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = + urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb %d does not match iso packet sizes %zu\n", + urb->actual_length, + txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_TCP); + return -1; + } + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_MALLOC); + kfree(iov); + return -1; + } + + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; + txsize += len; + iovnum++; + } + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + kfree(iov); + kfree(iso_buffer); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iov); + kfree(iso_buffer); + + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { + stub_free_priv_and_urb(priv); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_move_tail(&unlink->list, &sdev->unlink_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return unlink; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_unlink(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + struct msghdr msg; + struct kvec iov[1]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + setup_ret_unlink_pdu(&pdu_header, unlink); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, + 1, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_stub_tx("send txdata\n"); + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) { + list_del(&unlink->list); + kfree(unlink); + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +int stub_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + /* + * send_ret_submit comes earlier than send_ret_unlink. stub_rx + * looks at only priv_init queue. If the completion of a URB is + * earlier than the receive of CMD_UNLINK, priv is moved to + * priv_tx queue and stub_rx does not find the target priv. In + * this case, vhci_rx receives the result of the submit request + * and then receives the result of the unlink request. The + * result of the submit is given back to the usbcore as the + * completion of the unlink request. The request of the + * unlink is ignored. This is ok because a driver who calls + * usb_unlink_urb() understands the unlink was too late by + * getting the status of the given-backed URB which has the + * status of usb_submit_urb(). + */ + if (stub_send_ret_submit(sdev) < 0) + break; + + if (stub_send_ret_unlink(sdev) < 0) + break; + + wait_event_interruptible(sdev->tx_waitq, + (!list_empty(&sdev->priv_tx) || + !list_empty(&sdev->unlink_tx) || + kthread_should_stop())); + } + + return 0; +} diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c new file mode 100644 index 000000000000..facaaf003f19 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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 <asm/byteorder.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <net/sock.h> + +#include "usbip_common.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>" +#define DRIVER_DESC "USB/IP Core" + +#ifdef CONFIG_USBIP_DEBUG +unsigned long usbip_debug_flag = 0xffffffff; +#else +unsigned long usbip_debug_flag; +#endif +EXPORT_SYMBOL_GPL(usbip_debug_flag); +module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)"); + +/* FIXME */ +struct device_attribute dev_attr_usbip_debug; +EXPORT_SYMBOL_GPL(dev_attr_usbip_debug); + +static ssize_t usbip_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", usbip_debug_flag); +} + +static ssize_t usbip_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + if (sscanf(buf, "%lx", &usbip_debug_flag) != 1) + return -EINVAL; + return count; +} +DEVICE_ATTR_RW(usbip_debug); + +static void usbip_dump_buffer(char *buff, int bufflen) +{ + print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4, + buff, bufflen, false); +} + +static void usbip_dump_pipe(unsigned int p) +{ + unsigned char type = usb_pipetype(p); + unsigned char ep = usb_pipeendpoint(p); + unsigned char dev = usb_pipedevice(p); + unsigned char dir = usb_pipein(p); + + pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT"); + + switch (type) { + case PIPE_ISOCHRONOUS: + pr_debug("ISO\n"); + break; + case PIPE_INTERRUPT: + pr_debug("INT\n"); + break; + case PIPE_CONTROL: + pr_debug("CTRL\n"); + break; + case PIPE_BULK: + pr_debug("BULK\n"); + break; + default: + pr_debug("ERR\n"); + break; + } +} + +static void usbip_dump_usb_device(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + int i; + + dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", + udev->devnum, udev->devpath, usb_speed_string(udev->speed)); + + pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); + + dev_dbg(dev, " "); + for (i = 0; i < 16; i++) + pr_debug(" %2u", i); + pr_debug("\n"); + + dev_dbg(dev, " toggle0(IN) :"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " toggle1(OUT):"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_in :"); + for (i = 0; i < 16; i++) { + if (udev->ep_in[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_out :"); + for (i = 0; i < 16; i++) { + if (udev->ep_out[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); + + dev_dbg(dev, + "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", + &udev->descriptor, udev->config, + udev->actconfig, udev->rawdescriptors); + + dev_dbg(dev, "have_langid %d, string_langid %d\n", + udev->have_langid, udev->string_langid); + + dev_dbg(dev, "maxchild %d\n", udev->maxchild); +} + +static void usbip_dump_request_type(__u8 rt) +{ + switch (rt & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + pr_debug("DEVICE"); + break; + case USB_RECIP_INTERFACE: + pr_debug("INTERF"); + break; + case USB_RECIP_ENDPOINT: + pr_debug("ENDPOI"); + break; + case USB_RECIP_OTHER: + pr_debug("OTHER "); + break; + default: + pr_debug("------"); + break; + } +} + +static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd) +{ + if (!cmd) { + pr_debug(" : null pointer\n"); + return; + } + + pr_debug(" "); + pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ", + cmd->bRequestType, cmd->bRequest, + cmd->wValue, cmd->wIndex, cmd->wLength); + pr_debug("\n "); + + if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + pr_debug("STANDARD "); + switch (cmd->bRequest) { + case USB_REQ_GET_STATUS: + pr_debug("GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + pr_debug("CLEAR_FEAT\n"); + break; + case USB_REQ_SET_FEATURE: + pr_debug("SET_FEAT\n"); + break; + case USB_REQ_SET_ADDRESS: + pr_debug("SET_ADDRRS\n"); + break; + case USB_REQ_GET_DESCRIPTOR: + pr_debug("GET_DESCRI\n"); + break; + case USB_REQ_SET_DESCRIPTOR: + pr_debug("SET_DESCRI\n"); + break; + case USB_REQ_GET_CONFIGURATION: + pr_debug("GET_CONFIG\n"); + break; + case USB_REQ_SET_CONFIGURATION: + pr_debug("SET_CONFIG\n"); + break; + case USB_REQ_GET_INTERFACE: + pr_debug("GET_INTERF\n"); + break; + case USB_REQ_SET_INTERFACE: + pr_debug("SET_INTERF\n"); + break; + case USB_REQ_SYNCH_FRAME: + pr_debug("SYNC_FRAME\n"); + break; + default: + pr_debug("REQ(%02X)\n", cmd->bRequest); + break; + } + usbip_dump_request_type(cmd->bRequestType); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + pr_debug("CLASS\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + pr_debug("VENDOR\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) { + pr_debug("RESERVED\n"); + } +} + +void usbip_dump_urb(struct urb *urb) +{ + struct device *dev; + + if (!urb) { + pr_debug("urb: null pointer!!\n"); + return; + } + + if (!urb->dev) { + pr_debug("urb->dev: null pointer!!\n"); + return; + } + + dev = &urb->dev->dev; + + dev_dbg(dev, " urb :%p\n", urb); + dev_dbg(dev, " dev :%p\n", urb->dev); + + usbip_dump_usb_device(urb->dev); + + dev_dbg(dev, " pipe :%08x ", urb->pipe); + + usbip_dump_pipe(urb->pipe); + + dev_dbg(dev, " status :%d\n", urb->status); + dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); + dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); + dev_dbg(dev, " transfer_buffer_length:%d\n", + urb->transfer_buffer_length); + dev_dbg(dev, " actual_length :%d\n", urb->actual_length); + dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); + + if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) + usbip_dump_usb_ctrlrequest( + (struct usb_ctrlrequest *)urb->setup_packet); + + dev_dbg(dev, " start_frame :%d\n", urb->start_frame); + dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); + dev_dbg(dev, " interval :%d\n", urb->interval); + dev_dbg(dev, " error_count :%d\n", urb->error_count); + dev_dbg(dev, " context :%p\n", urb->context); + dev_dbg(dev, " complete :%p\n", urb->complete); +} +EXPORT_SYMBOL_GPL(usbip_dump_urb); + +void usbip_dump_header(struct usbip_header *pdu) +{ + pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n", + pdu->base.command, + pdu->base.seqnum, + pdu->base.devid, + pdu->base.direction, + pdu->base.ep); + + switch (pdu->base.command) { + case USBIP_CMD_SUBMIT: + pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n", + pdu->u.cmd_submit.transfer_flags, + pdu->u.cmd_submit.transfer_buffer_length, + pdu->u.cmd_submit.start_frame, + pdu->u.cmd_submit.number_of_packets, + pdu->u.cmd_submit.interval); + break; + case USBIP_CMD_UNLINK: + pr_debug("USBIP_CMD_UNLINK: seq %u\n", + pdu->u.cmd_unlink.seqnum); + break; + case USBIP_RET_SUBMIT: + pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", + pdu->u.ret_submit.status, + pdu->u.ret_submit.actual_length, + pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, + pdu->u.ret_submit.error_count); + break; + case USBIP_RET_UNLINK: + pr_debug("USBIP_RET_UNLINK: status %d\n", + pdu->u.ret_unlink.status); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_dump_header); + +/* Receive data over TCP/IP. */ +int usbip_recv(struct socket *sock, void *buf, int size) +{ + int result; + struct msghdr msg; + struct kvec iov; + int total = 0; + + /* for blocks of if (usbip_dbg_flag_xmit) */ + char *bp = buf; + int osize = size; + + usbip_dbg_xmit("enter\n"); + + if (!sock || !buf || !size) { + pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, + size); + return -EINVAL; + } + + do { + sock->sk->sk_allocation = GFP_NOIO; + iov.iov_base = buf; + iov.iov_len = size; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_NOSIGNAL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); + if (result <= 0) { + pr_debug("receive sock %p buf %p size %u ret %d total %d\n", + sock, buf, size, result, total); + goto err; + } + + size -= result; + buf += result; + total += result; + } while (size > 0); + + if (usbip_dbg_flag_xmit) { + if (!in_interrupt()) + pr_debug("%-10s:", current->comm); + else + pr_debug("interrupt :"); + + pr_debug("receiving....\n"); + usbip_dump_buffer(bp, osize); + pr_debug("received, osize %d ret %d size %d total %d\n", + osize, result, size, total); + } + + return total; + +err: + return result; +} +EXPORT_SYMBOL_GPL(usbip_recv); + +/* there may be more cases to tweak the flags. */ +static unsigned int tweak_transfer_flags(unsigned int flags) +{ + flags &= ~URB_NO_TRANSFER_DMA_MAP; + return flags; +} + +static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit; + + /* + * Some members are not still implemented in usbip. I hope this issue + * will be discussed when usbip is ported to other operating systems. + */ + if (pack) { + spdu->transfer_flags = + tweak_transfer_flags(urb->transfer_flags); + spdu->transfer_buffer_length = urb->transfer_buffer_length; + spdu->start_frame = urb->start_frame; + spdu->number_of_packets = urb->number_of_packets; + spdu->interval = urb->interval; + } else { + urb->transfer_flags = spdu->transfer_flags; + urb->transfer_buffer_length = spdu->transfer_buffer_length; + urb->start_frame = spdu->start_frame; + urb->number_of_packets = spdu->number_of_packets; + urb->interval = spdu->interval; + } +} + +static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit; + + if (pack) { + rpdu->status = urb->status; + rpdu->actual_length = urb->actual_length; + rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; + rpdu->error_count = urb->error_count; + } else { + urb->status = rpdu->status; + urb->actual_length = rpdu->actual_length; + urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; + urb->error_count = rpdu->error_count; + } +} + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack) +{ + switch (cmd) { + case USBIP_CMD_SUBMIT: + usbip_pack_cmd_submit(pdu, urb, pack); + break; + case USBIP_RET_SUBMIT: + usbip_pack_ret_submit(pdu, urb, pack); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_pack_pdu); + +static void correct_endian_basic(struct usbip_header_basic *base, int send) +{ + if (send) { + base->command = cpu_to_be32(base->command); + base->seqnum = cpu_to_be32(base->seqnum); + base->devid = cpu_to_be32(base->devid); + base->direction = cpu_to_be32(base->direction); + base->ep = cpu_to_be32(base->ep); + } else { + base->command = be32_to_cpu(base->command); + base->seqnum = be32_to_cpu(base->seqnum); + base->devid = be32_to_cpu(base->devid); + base->direction = be32_to_cpu(base->direction); + base->ep = be32_to_cpu(base->ep); + } +} + +static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu, + int send) +{ + if (send) { + pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags); + + cpu_to_be32s(&pdu->transfer_buffer_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->interval); + } else { + pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags); + + be32_to_cpus(&pdu->transfer_buffer_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->interval); + } +} + +static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, + int send) +{ + if (send) { + cpu_to_be32s(&pdu->status); + cpu_to_be32s(&pdu->actual_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->error_count); + } else { + be32_to_cpus(&pdu->status); + be32_to_cpus(&pdu->actual_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->error_count); + } +} + +static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu, + int send) +{ + if (send) + pdu->seqnum = cpu_to_be32(pdu->seqnum); + else + pdu->seqnum = be32_to_cpu(pdu->seqnum); +} + +static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu, + int send) +{ + if (send) + cpu_to_be32s(&pdu->status); + else + be32_to_cpus(&pdu->status); +} + +void usbip_header_correct_endian(struct usbip_header *pdu, int send) +{ + __u32 cmd = 0; + + if (send) + cmd = pdu->base.command; + + correct_endian_basic(&pdu->base, send); + + if (!send) + cmd = pdu->base.command; + + switch (cmd) { + case USBIP_CMD_SUBMIT: + correct_endian_cmd_submit(&pdu->u.cmd_submit, send); + break; + case USBIP_RET_SUBMIT: + correct_endian_ret_submit(&pdu->u.ret_submit, send); + break; + case USBIP_CMD_UNLINK: + correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send); + break; + case USBIP_RET_UNLINK: + correct_endian_ret_unlink(&pdu->u.ret_unlink, send); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_header_correct_endian); + +static void usbip_iso_packet_correct_endian( + struct usbip_iso_packet_descriptor *iso, int send) +{ + /* does not need all members. but copy all simply. */ + if (send) { + iso->offset = cpu_to_be32(iso->offset); + iso->length = cpu_to_be32(iso->length); + iso->status = cpu_to_be32(iso->status); + iso->actual_length = cpu_to_be32(iso->actual_length); + } else { + iso->offset = be32_to_cpu(iso->offset); + iso->length = be32_to_cpu(iso->length); + iso->status = be32_to_cpu(iso->status); + iso->actual_length = be32_to_cpu(iso->actual_length); + } +} + +static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso, + struct usb_iso_packet_descriptor *uiso, int pack) +{ + if (pack) { + iso->offset = uiso->offset; + iso->length = uiso->length; + iso->status = uiso->status; + iso->actual_length = uiso->actual_length; + } else { + uiso->offset = iso->offset; + uiso->length = iso->length; + uiso->status = iso->status; + uiso->actual_length = iso->actual_length; + } +} + +/* must free buffer */ +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen) +{ + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + ssize_t size = np * sizeof(*iso); + int i; + + iso = kzalloc(size, GFP_KERNEL); + if (!iso) + return NULL; + + for (i = 0; i < np; i++) { + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1); + usbip_iso_packet_correct_endian(&iso[i], 1); + } + + *bufflen = size; + + return iso; +} +EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) +{ + void *buff; + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + int size = np * sizeof(*iso); + int i; + int ret; + int total_length = 0; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* my Bluetooth dongle gets ISO URBs which are np = 0 */ + if (np == 0) + return 0; + + buff = kzalloc(size, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + ret = usbip_recv(ud->tcp_socket, buff, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n", + ret); + kfree(buff); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + iso = (struct usbip_iso_packet_descriptor *) buff; + for (i = 0; i < np; i++) { + usbip_iso_packet_correct_endian(&iso[i], 0); + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; + } + + kfree(buff); + + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets %d not equal to actual length of buffer %d\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_iso); + +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); + +/* some members of urb must be substituted before. */ +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) +{ + int ret; + int size; + + if (ud->side == USBIP_STUB) { + /* the direction of urb must be OUT. */ + if (usb_pipein(urb->pipe)) + return 0; + + size = urb->transfer_buffer_length; + } else { + /* the direction of urb must be IN. */ + if (usb_pipeout(urb->pipe)) + return 0; + + size = urb->actual_length; + } + + /* no need to recv xbuff */ + if (!(size > 0)) + return 0; + + ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); + if (ud->side == USBIP_STUB) { + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + } else { + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return -EPIPE; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_xbuff); + +static int __init usbip_core_init(void) +{ + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return 0; +} + +static void __exit usbip_core_exit(void) +{ + return; +} + +module_init(usbip_core_init); +module_exit(usbip_core_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h new file mode 100644 index 000000000000..86b08475c254 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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 __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/net.h> +#include <linux/printk.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> +#include <uapi/linux/usbip.h> + +#define USBIP_VERSION "1.0.0" + +#undef pr_fmt + +#ifdef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__ +#else +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +enum { + usbip_debug_xmit = (1 << 0), + usbip_debug_sysfs = (1 << 1), + usbip_debug_urb = (1 << 2), + usbip_debug_eh = (1 << 3), + + usbip_debug_stub_cmp = (1 << 8), + usbip_debug_stub_dev = (1 << 9), + usbip_debug_stub_rx = (1 << 10), + usbip_debug_stub_tx = (1 << 11), + + usbip_debug_vhci_rh = (1 << 8), + usbip_debug_vhci_hc = (1 << 9), + usbip_debug_vhci_rx = (1 << 10), + usbip_debug_vhci_tx = (1 << 11), + usbip_debug_vhci_sysfs = (1 << 12) +}; + +#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit) +#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh) +#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc) +#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx) +#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx) +#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx) +#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx) +#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs) + +extern unsigned long usbip_debug_flag; +extern struct device_attribute dev_attr_usbip_debug; + +#define usbip_dbg_with_flag(flag, fmt, args...) \ + do { \ + if (flag & usbip_debug_flag) \ + pr_debug(fmt, ##args); \ + } while (0) + +#define usbip_dbg_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args) +#define usbip_dbg_xmit(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args) +#define usbip_dbg_urb(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args) +#define usbip_dbg_eh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args) + +#define usbip_dbg_vhci_rh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args) +#define usbip_dbg_vhci_hc(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args) +#define usbip_dbg_vhci_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args) +#define usbip_dbg_vhci_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args) +#define usbip_dbg_vhci_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args) + +#define usbip_dbg_stub_cmp(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args) +#define usbip_dbg_stub_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args) +#define usbip_dbg_stub_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args) + +/* + * USB/IP request headers + * + * Each request is transferred across the network to its counterpart, which + * facilitates the normal USB communication. The values contained in the headers + * are basically the same as in a URB. Currently, four request types are + * defined: + * + * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb() + * (client to server) + * + * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT + * (server to client) + * + * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT, + * corresponds to usb_unlink_urb() + * (client to server) + * + * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK + * (server to client) + * + */ +#define USBIP_CMD_SUBMIT 0x0001 +#define USBIP_CMD_UNLINK 0x0002 +#define USBIP_RET_SUBMIT 0x0003 +#define USBIP_RET_UNLINK 0x0004 + +#define USBIP_DIR_OUT 0x00 +#define USBIP_DIR_IN 0x01 + +/** + * struct usbip_header_basic - data pertinent to every request + * @command: the usbip request type + * @seqnum: sequential number that identifies requests; incremented per + * connection + * @devid: specifies a remote USB device uniquely instead of busnum and devnum; + * in the stub driver, this value is ((busnum << 16) | devnum) + * @direction: direction of the transfer + * @ep: endpoint number + */ +struct usbip_header_basic { + __u32 command; + __u32 seqnum; + __u32 devid; + __u32 direction; + __u32 ep; +} __packed; + +/** + * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header + * @transfer_flags: URB flags + * @transfer_buffer_length: the data size for (in) or (out) transfer + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @interval: maximum time for the request on the server-side host controller + * @setup: setup data for a control request + */ +struct usbip_header_cmd_submit { + __u32 transfer_flags; + __s32 transfer_buffer_length; + + /* it is difficult for usbip to sync frames (reserved only?) */ + __s32 start_frame; + __s32 number_of_packets; + __s32 interval; + + unsigned char setup[8]; +} __packed; + +/** + * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header + * @status: return status of a non-iso request + * @actual_length: number of bytes transferred + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @error_count: number of errors for isochronous transfers + */ +struct usbip_header_ret_submit { + __s32 status; + __s32 actual_length; + __s32 start_frame; + __s32 number_of_packets; + __s32 error_count; +} __packed; + +/** + * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header + * @seqnum: the URB seqnum to unlink + */ +struct usbip_header_cmd_unlink { + __u32 seqnum; +} __packed; + +/** + * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header + * @status: return status of the request + */ +struct usbip_header_ret_unlink { + __s32 status; +} __packed; + +/** + * struct usbip_header - common header for all usbip packets + * @base: the basic header + * @u: packet type dependent header + */ +struct usbip_header { + struct usbip_header_basic base; + + union { + struct usbip_header_cmd_submit cmd_submit; + struct usbip_header_ret_submit ret_submit; + struct usbip_header_cmd_unlink cmd_unlink; + struct usbip_header_ret_unlink ret_unlink; + } u; +} __packed; + +/* + * This is the same as usb_iso_packet_descriptor but packed for pdu. + */ +struct usbip_iso_packet_descriptor { + __u32 offset; + __u32 length; /* expected length */ + __u32 actual_length; + __u32 status; +} __packed; + +enum usbip_side { + USBIP_VHCI, + USBIP_STUB, +}; + +/* event handler */ +#define USBIP_EH_SHUTDOWN (1 << 0) +#define USBIP_EH_BYE (1 << 1) +#define USBIP_EH_RESET (1 << 2) +#define USBIP_EH_UNUSABLE (1 << 3) + +#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) +#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) +#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +/* a common structure for stub_device and vhci_device */ +struct usbip_device { + enum usbip_side side; + enum usbip_device_status status; + + /* lock for status */ + spinlock_t lock; + + struct socket *tcp_socket; + + struct task_struct *tcp_rx; + struct task_struct *tcp_tx; + + unsigned long event; + struct task_struct *eh; + wait_queue_head_t eh_waitq; + + struct eh_ops { + void (*shutdown)(struct usbip_device *); + void (*reset)(struct usbip_device *); + void (*unusable)(struct usbip_device *); + } eh_ops; +}; + +#define kthread_get_run(threadfn, data, namefmt, ...) \ +({ \ + struct task_struct *__k \ + = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ + if (!IS_ERR(__k)) { \ + get_task_struct(__k); \ + wake_up_process(__k); \ + } \ + __k; \ +}) + +#define kthread_stop_put(k) \ + do { \ + kthread_stop(k); \ + put_task_struct(k); \ + } while (0) + +/* usbip_common.c */ +void usbip_dump_urb(struct urb *purb); +void usbip_dump_header(struct usbip_header *pdu); + +int usbip_recv(struct socket *sock, void *buf, int size); + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack); +void usbip_header_correct_endian(struct usbip_header *pdu, int send); + +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb); +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); + +/* usbip_event.c */ +int usbip_start_eh(struct usbip_device *ud); +void usbip_stop_eh(struct usbip_device *ud); +void usbip_event_add(struct usbip_device *ud, unsigned long event); +int usbip_event_happened(struct usbip_device *ud); + +static inline int interface_to_busnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->bus->busnum; +} + +static inline int interface_to_devnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->devnum; +} + +#endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c new file mode 100644 index 000000000000..64933b993d7a --- /dev/null +++ b/drivers/usb/usbip/usbip_event.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/kthread.h> +#include <linux/export.h> + +#include "usbip_common.h" + +static int event_handler(struct usbip_device *ud) +{ + usbip_dbg_eh("enter\n"); + + /* + * Events are handled by only this thread. + */ + while (usbip_event_happened(ud)) { + usbip_dbg_eh("pending event %lx\n", ud->event); + + /* + * NOTE: shutdown must come first. + * Shutdown the device. + */ + if (ud->event & USBIP_EH_SHUTDOWN) { + ud->eh_ops.shutdown(ud); + ud->event &= ~USBIP_EH_SHUTDOWN; + } + + /* Reset the device. */ + if (ud->event & USBIP_EH_RESET) { + ud->eh_ops.reset(ud); + ud->event &= ~USBIP_EH_RESET; + } + + /* Mark the device as unusable. */ + if (ud->event & USBIP_EH_UNUSABLE) { + ud->eh_ops.unusable(ud); + ud->event &= ~USBIP_EH_UNUSABLE; + } + + /* Stop the error handler. */ + if (ud->event & USBIP_EH_BYE) + return -1; + } + + return 0; +} + +static int event_handler_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(ud->eh_waitq, + usbip_event_happened(ud) || + kthread_should_stop()); + usbip_dbg_eh("wakeup\n"); + + if (event_handler(ud) < 0) + break; + } + + return 0; +} + +int usbip_start_eh(struct usbip_device *ud) +{ + init_waitqueue_head(&ud->eh_waitq); + ud->event = 0; + + ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); + if (IS_ERR(ud->eh)) { + pr_warn("Unable to start control thread\n"); + return PTR_ERR(ud->eh); + } + + return 0; +} +EXPORT_SYMBOL_GPL(usbip_start_eh); + +void usbip_stop_eh(struct usbip_device *ud) +{ + if (ud->eh == current) + return; /* do not wait for myself */ + + kthread_stop(ud->eh); + usbip_dbg_eh("usbip_eh has finished\n"); +} +EXPORT_SYMBOL_GPL(usbip_stop_eh); + +void usbip_event_add(struct usbip_device *ud, unsigned long event) +{ + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); + ud->event |= event; + wake_up(&ud->eh_waitq); + spin_unlock_irqrestore(&ud->lock, flags); +} +EXPORT_SYMBOL_GPL(usbip_event_add); + +int usbip_event_happened(struct usbip_device *ud) +{ + int happened = 0; + + spin_lock(&ud->lock); + if (ud->event != 0) + happened = 1; + spin_unlock(&ud->lock); + + return happened; +} +EXPORT_SYMBOL_GPL(usbip_event_happened); diff --git a/drivers/usb/usbip/usbip_protocol.txt b/drivers/usb/usbip/usbip_protocol.txt new file mode 100644 index 000000000000..16b6fe27284c --- /dev/null +++ b/drivers/usb/usbip/usbip_protocol.txt @@ -0,0 +1,358 @@ +PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! +28 Jun 2011 + +The USB/IP protocol follows a server/client architecture. The server exports the +USB devices and the clients imports them. The device driver for the exported +USB device runs on the client machine. + +The client may ask for the list of the exported USB devices. To get the list the +client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST +packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent +in one or more pieces at the low level transport layer). The server sends back +the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the +TCP/IP connection is closed. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_DEVLIST | + | ----------------------------------------------> | + | | + | OP_REP_DEVLIST | + | <---------------------------------------------- | + | | + +Once the client knows the list of exported USB devices it may decide to use one +of them. First the client opens a TCP/IP connection towards the server and +sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the +import was successful the TCP/IP connection remains open and will be used +to transfer the URB traffic between the client and the server. The client may +send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and +USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the +server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_IMPORT | + | ----------------------------------------------> | + | | + | OP_REP_IMPORT | + | <---------------------------------------------- | + | | + | | + | USBIP_CMD_SUBMIT(seqnum = n) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = n) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_SUBMIT(seqnum = m) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+1) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+2) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+3) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+1) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+4) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+2) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_UNLINK | + | ----------------------------------------------> | + | | + | USBIP_RET_UNLINK | + | <---------------------------------------------- | + | | + +The fields are in network (big endian) byte order meaning that the most significant +byte (MSB) is stored at the lowest address. + + +OP_REQ_DEVLIST: Retrieve the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 + +OP_REP_DEVLIST: Reply with the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0. +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | n | Number of exported devices: 0 means no exported + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 0x0C | | | From now on the exported n devices are described, + | | | if any. If no devices are exported the message + | | | ends with the previous "number of exported + | | | devices" field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x10C | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x134 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x13A | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x13C | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13F | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x140 | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x141 | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x142 | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x143 | 1 | | bNumInterfaces +-----------+--------+------------+--------------------------------------------------- + 0x144 | | m_0 | From now on each interface is described, all + | | | together bNumInterfaces times, with the + | | | the following 4 fields: +-----------+--------+------------+--------------------------------------------------- + | 1 | | bInterfaceClass +-----------+--------+------------+--------------------------------------------------- + 0x145 | 1 | | bInterfaceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x146 | 1 | | bInterfaceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x147 | 1 | | padding byte for alignment, shall be set to zero +-----------+--------+------------+--------------------------------------------------- + 0xC + | | | The second exported USB device starts at i=1 + i*0x138 + | | | with the busid field. + m_(i-1)*4 | | | + +OP_REQ_IMPORT: Request to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8003 | Command code: import a remote USB device. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 +-----------+--------+------------+--------------------------------------------------- + 8 | 32 | | busid: the busid of the exported device on the + | | | remote host. The possible values are taken + | | | from the message field OP_REP_DEVLIST.busid. + | | | A string closed with zero, the unused bytes + | | | shall be filled with zeros. +-----------+--------+------------+--------------------------------------------------- + +OP_REP_IMPORT: Reply to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0003 | Reply code: Reply to import. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK + | | | 1 for error +-----------+--------+------------+--------------------------------------------------- + 8 | | | From now on comes the details of the imported + | | | device, if the previous status field was OK (0), + | | | otherwise the reply ends with the status field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x108 | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x128 | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x134 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x136 | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x139 | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13A | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x13B | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x13C | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x13D | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bNumInterfaces + +USBIP_CMD_SUBMIT: Submit an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000001 | command: Submit an URB +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the sequence number of the URB to submit +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | transfer_flags: possible values depend on the + | | | URB transfer type, see below +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | | transfer_buffer_length +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: specify the selected frame to + | | | transmit an ISO frame, ignored if URB_ISO_ASAP + | | | is specified at transfer_flags +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets: number of ISO packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | interval: maximum time for the request on the + | | | server-side host controller +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | | | URB data. For ISO transfers the padding between + | | | each ISO packets is not transmitted. + + + Allowed transfer_flags | value | control | interrupt | bulk | isochronous + -------------------------+------------+---------+-----------+----------+------------- + URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no + URB_ISO_ASAP | 0x00000002 | no | no | no | yes + URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes + URB_NO_FSBR | 0x00000020 | yes | no | no | no + URB_ZERO_PACKET | 0x00000040 | no | no | only out | no + URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes + URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes + URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes + + +USBIP_RET_SUBMIT: Reply for submitting an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000003 | command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: zero for successful URB transaction, + | | | otherwise some kind of error happened. +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | n | actual_length: number of URB data bytes +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: for an ISO frame the actually + | | | selected frame for transmit. +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | error_count +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_CMD_UNLINK: Unlink an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000002 | command: URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so? +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number: zero +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | seqnum: the URB sequence number given previously + | | | at USBIP_CMD_SUBMIT.seqnum field +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_RET_UNLINK: Reply for URB unlink + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000004 | command: reply for the URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the unlinked URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: This is the value contained in the + | | | urb->status in the URB completition handler. + | | | FIXME: a better explanation needed. +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h new file mode 100644 index 000000000000..a863a98a91ce --- /dev/null +++ b/drivers/usb/usbip/vhci.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#ifndef __USBIP_VHCI_H +#define __USBIP_VHCI_H + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/wait.h> + +struct vhci_device { + struct usb_device *udev; + + /* + * devid specifies a remote usb device uniquely instead + * of combination of busnum and devnum. + */ + __u32 devid; + + /* speed of a remote device */ + enum usb_device_speed speed; + + /* vhci root-hub port to which this device is attached */ + __u32 rhport; + + struct usbip_device ud; + + /* lock for the below link lists */ + spinlock_t priv_lock; + + /* vhci_priv is linked to one of them. */ + struct list_head priv_tx; + struct list_head priv_rx; + + /* vhci_unlink is linked to one of them */ + struct list_head unlink_tx; + struct list_head unlink_rx; + + /* vhci_tx thread sleeps for this queue */ + wait_queue_head_t waitq_tx; +}; + +/* urb->hcpriv, use container_of() */ +struct vhci_priv { + unsigned long seqnum; + struct list_head list; + + struct vhci_device *vdev; + struct urb *urb; +}; + +struct vhci_unlink { + /* seqnum of this request */ + unsigned long seqnum; + + struct list_head list; + + /* seqnum of the unlink target */ + unsigned long unlink_seqnum; +}; + +/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */ +#define VHCI_NPORTS 8 + +/* for usb_bus.hcpriv */ +struct vhci_hcd { + spinlock_t lock; + + u32 port_status[VHCI_NPORTS]; + + unsigned resuming:1; + unsigned long re_timeout; + + atomic_t seqnum; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + * But, the index of this array begins from 0. + */ + struct vhci_device vdev[VHCI_NPORTS]; +}; + +extern struct vhci_hcd *the_controller; +extern const struct attribute_group dev_attr_group; + +/* vhci_hcd.c */ +void rh_port_connect(int rhport, enum usb_device_speed speed); + +/* vhci_rx.c */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum); +int vhci_rx_loop(void *data); + +/* vhci_tx.c */ +int vhci_tx_loop(void *data); + +static inline struct vhci_device *port_to_vdev(__u32 port) +{ + return &the_controller->vdev[port]; +} + +static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd) +{ + return (struct vhci_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci) +{ + return container_of((void *) vhci, struct usb_hcd, hcd_priv); +} + +static inline struct device *vhci_dev(struct vhci_hcd *vhci) +{ + return vhci_to_hcd(vhci)->self.controller; +} + +#endif /* __USBIP_VHCI_H */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c new file mode 100644 index 000000000000..c02374b6049c --- /dev/null +++ b/drivers/usb/usbip/vhci_hcd.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/init.h> +#include <linux/file.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" + +/* + * TODO + * - update root hub emulation + * - move the emulation code to userland ? + * porting to other operating systems + * minimize kernel code + * - add suspend/resume code + * - clean up everything + */ + +/* See usb gadget dummy hcd */ + +static int vhci_hub_status(struct usb_hcd *hcd, char *buff); +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buff, u16 wLength); +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +static int vhci_start(struct usb_hcd *vhci_hcd); +static void vhci_stop(struct usb_hcd *hcd); +static int vhci_get_frame_number(struct usb_hcd *hcd); + +static const char driver_name[] = "vhci_hcd"; +static const char driver_desc[] = "USB/IP Virtual Host Controller"; + +struct vhci_hcd *the_controller; + +static const char * const bit_desc[] = { + "CONNECTION", /*0*/ + "ENABLE", /*1*/ + "SUSPEND", /*2*/ + "OVER_CURRENT", /*3*/ + "RESET", /*4*/ + "R5", /*5*/ + "R6", /*6*/ + "R7", /*7*/ + "POWER", /*8*/ + "LOWSPEED", /*9*/ + "HIGHSPEED", /*10*/ + "PORT_TEST", /*11*/ + "INDICATOR", /*12*/ + "R13", /*13*/ + "R14", /*14*/ + "R15", /*15*/ + "C_CONNECTION", /*16*/ + "C_ENABLE", /*17*/ + "C_SUSPEND", /*18*/ + "C_OVER_CURRENT", /*19*/ + "C_RESET", /*20*/ + "R21", /*21*/ + "R22", /*22*/ + "R23", /*23*/ + "R24", /*24*/ + "R25", /*25*/ + "R26", /*26*/ + "R27", /*27*/ + "R28", /*28*/ + "R29", /*29*/ + "R30", /*30*/ + "R31", /*31*/ +}; + +static void dump_port_status_diff(u32 prev_status, u32 new_status) +{ + int i = 0; + u32 bit = 1; + + pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); + while (bit) { + u32 prev = prev_status & bit; + u32 new = new_status & bit; + char change; + + if (!prev && new) + change = '+'; + else if (prev && !new) + change = '-'; + else + change = ' '; + + if (prev || new) + pr_debug(" %c%s\n", change, bit_desc[i]); + bit <<= 1; + i++; + } + pr_debug("\n"); +} + +void rh_port_connect(int rhport, enum usb_device_speed speed) +{ + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION + | (1 << USB_PORT_FEAT_C_CONNECTION); + + switch (speed) { + case USB_SPEED_HIGH: + the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + + spin_unlock(&the_controller->lock); + + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +static void rh_port_disconnect(int rhport) +{ + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; + the_controller->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_CONNECTION); + + spin_unlock(&the_controller->lock); + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +#define PORT_C_MASK \ + ((USB_PORT_STAT_C_CONNECTION \ + | USB_PORT_STAT_C_ENABLE \ + | USB_PORT_STAT_C_SUSPEND \ + | USB_PORT_STAT_C_OVERCURRENT \ + | USB_PORT_STAT_C_RESET) << 16) + +/* + * Returns 0 if the status hasn't changed, or the number of bytes in buf. + * Ports are 0-indexed from the HCD point of view, + * and 1-indexed from the USB core pointer of view. + * + * @buf: a bitmap to show which port status has been changed. + * bit 0: reserved + * bit 1: the status of port 0 has been changed. + * bit 2: the status of port 1 has been changed. + * ... + */ +static int vhci_hub_status(struct usb_hcd *hcd, char *buf) +{ + struct vhci_hcd *vhci; + int retval; + int rhport; + int changed = 0; + + retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); + memset(buf, 0, retval); + + vhci = hcd_to_vhci(hcd); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) { + usbip_dbg_vhci_rh("hw accessible flag not on?\n"); + goto done; + } + + /* check pseudo status register for each port */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + if ((vhci->port_status[rhport] & PORT_C_MASK)) { + /* The status of a port has been changed, */ + usbip_dbg_vhci_rh("port %d status changed\n", rhport); + + buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; + changed = 1; + } + } + + if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) + usb_hcd_resume_root_hub(hcd); + +done: + spin_unlock(&vhci->lock); + return changed ? retval : 0; +} + +static inline void hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof(*desc)); + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001)); + desc->bNbrPorts = VHCI_NPORTS; + desc->u.hs.DeviceRemovable[0] = 0xff; + desc->u.hs.DeviceRemovable[1] = 0xff; +} + +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct vhci_hcd *dum; + int retval = 0; + int rhport; + + u32 prev_port_status[VHCI_NPORTS]; + + if (!HCD_HW_ACCESSIBLE(hcd)) + return -ETIMEDOUT; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + */ + usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, + wIndex); + if (wIndex > VHCI_NPORTS) + pr_err("invalid port number %d\n", wIndex); + rhport = ((__u8)(wIndex & 0x00ff)) - 1; + + dum = hcd_to_vhci(hcd); + + spin_lock(&dum->lock); + + /* store old status and compare now and old later */ + if (usbip_dbg_flag_vhci_rh) { + memcpy(prev_port_status, dum->port_status, + sizeof(prev_port_status)); + } + + switch (typeReq) { + case ClearHubFeature: + usbip_dbg_vhci_rh(" ClearHubFeature\n"); + break; + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) { + /* 20msec signaling */ + dum->resuming = 1; + dum->re_timeout = + jiffies + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_POWER: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_POWER\n"); + dum->port_status[rhport] = 0; + dum->resuming = 0; + break; + case USB_PORT_FEAT_C_RESET: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); + switch (dum->vdev[rhport].speed) { + case USB_SPEED_HIGH: + dum->port_status[rhport] |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum->port_status[rhport] |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + default: + usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", + wValue); + dum->port_status[rhport] &= ~(1 << wValue); + break; + } + break; + case GetHubDescriptor: + usbip_dbg_vhci_rh(" GetHubDescriptor\n"); + hub_descriptor((struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + usbip_dbg_vhci_rh(" GetHubStatus\n"); + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); + if (wIndex > VHCI_NPORTS || wIndex < 1) { + pr_err("invalid port number %d\n", wIndex); + retval = -EPIPE; + } + + /* we do not care about resume. */ + + /* whoever resets or resumes must GetPortStatus to + * complete it!! + */ + if (dum->resuming && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_SUSPEND); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_SUSPEND); + dum->resuming = 0; + dum->re_timeout = 0; + } + + if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != + 0 && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_RESET); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_RESET); + dum->re_timeout = 0; + + if (dum->vdev[rhport].ud.status == + VDEV_ST_NOTASSIGNED) { + usbip_dbg_vhci_rh( + " enable rhport %d (status %u)\n", + rhport, + dum->vdev[rhport].ud.status); + dum->port_status[rhport] |= + USB_PORT_STAT_ENABLE; + } + } + ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]); + ((__le16 *) buf)[1] = + cpu_to_le16(dum->port_status[rhport] >> 16); + + usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], + ((u16 *)buf)[1]); + break; + case SetHubFeature: + usbip_dbg_vhci_rh(" SetHubFeature\n"); + retval = -EPIPE; + break; + case SetPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); + break; + case USB_PORT_FEAT_RESET: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_RESET\n"); + /* if it's already running, disconnect first */ + if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { + dum->port_status[rhport] &= + ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + /* FIXME test that code path! */ + } + /* 50msec reset signaling */ + dum->re_timeout = jiffies + msecs_to_jiffies(50); + + /* FALLTHROUGH */ + default: + usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", + wValue); + dum->port_status[rhport] |= (1 << wValue); + break; + } + break; + + default: + pr_err("default: no such request\n"); + + /* "protocol stall" on error */ + retval = -EPIPE; + } + + if (usbip_dbg_flag_vhci_rh) { + pr_debug("port %d\n", rhport); + /* Only dump valid port status */ + if (rhport >= 0) { + dump_port_status_diff(prev_port_status[rhport], + dum->port_status[rhport]); + } + } + usbip_dbg_vhci_rh(" bye\n"); + + spin_unlock(&dum->lock); + + return retval; +} + +static struct vhci_device *get_vdev(struct usb_device *udev) +{ + int i; + + if (!udev) + return NULL; + + for (i = 0; i < VHCI_NPORTS; i++) + if (the_controller->vdev[i].udev == udev) + return port_to_vdev(i); + + return NULL; +} + +static void vhci_tx_urb(struct urb *urb) +{ + struct vhci_device *vdev = get_vdev(urb->dev); + struct vhci_priv *priv; + + if (!vdev) { + pr_err("could not get virtual device"); + return; + } + + priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); + if (!priv) { + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + spin_lock(&vdev->priv_lock); + + priv->seqnum = atomic_inc_return(&the_controller->seqnum); + if (priv->seqnum == 0xffff) + dev_info(&urb->dev->dev, "seqnum max\n"); + + priv->vdev = vdev; + priv->urb = urb; + + urb->hcpriv = (void *) priv; + + list_add_tail(&priv->list, &vdev->priv_tx); + + wake_up(&vdev->waitq_tx); + spin_unlock(&vdev->priv_lock); +} + +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct device *dev = &urb->dev->dev; + int ret = 0; + struct vhci_device *vdev; + + usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", + hcd, urb, mem_flags); + + /* patch to usb_sg_init() is in 2.5.60 */ + BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); + + spin_lock(&the_controller->lock); + + if (urb->status != -EINPROGRESS) { + dev_err(dev, "URB already unlinked!, status %d\n", urb->status); + spin_unlock(&the_controller->lock); + return urb->status; + } + + vdev = port_to_vdev(urb->dev->portnum-1); + + /* refuse enqueue for dead connection */ + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL || + vdev->ud.status == VDEV_ST_ERROR) { + dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + return -ENODEV; + } + spin_unlock(&vdev->ud.lock); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto no_need_unlink; + + /* + * The enumeration process is as follows; + * + * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) + * to get max packet length of default pipe + * + * 2. Set_Address request to DevAddr(0) EndPoint(0) + * + */ + if (usb_pipedevice(urb->pipe) == 0) { + __u8 type = usb_pipetype(urb->pipe); + struct usb_ctrlrequest *ctrlreq = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (type != PIPE_CONTROL || !ctrlreq) { + dev_err(dev, "invalid request to devnum 0\n"); + ret = -EINVAL; + goto no_need_xmit; + } + + switch (ctrlreq->bRequest) { + case USB_REQ_SET_ADDRESS: + /* set_address may come when a device is reset */ + dev_info(dev, "SetAddress Request (%d) to port %d\n", + ctrlreq->wValue, vdev->rhport); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + + spin_lock(&vdev->ud.lock); + vdev->ud.status = VDEV_ST_USED; + spin_unlock(&vdev->ud.lock); + + if (urb->status == -EINPROGRESS) { + /* This request is successfully completed. */ + /* If not -EINPROGRESS, possibly unlinked. */ + urb->status = 0; + } + + goto no_need_xmit; + + case USB_REQ_GET_DESCRIPTOR: + if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8)) + usbip_dbg_vhci_hc( + "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + goto out; + + default: + /* NOT REACHED */ + dev_err(dev, + "invalid request to devnum 0 bRequest %u, wValue %u\n", + ctrlreq->bRequest, + ctrlreq->wValue); + ret = -EINVAL; + goto no_need_xmit; + } + + } + +out: + vhci_tx_urb(urb); + spin_unlock(&the_controller->lock); + + return 0; + +no_need_xmit: + usb_hcd_unlink_urb_from_ep(hcd, urb); +no_need_unlink: + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + return ret; +} + +/* + * vhci_rx gives back the urb after receiving the reply of the urb. If an + * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives + * back its urb. For the driver unlinking the urb, the content of the urb is + * not important, but the calling to its completion handler is important; the + * completion of unlinking is notified by the completion handler. + * + * + * CLIENT SIDE + * + * - When vhci_hcd receives RET_SUBMIT, + * + * - case 1a). the urb of the pdu is not unlinking. + * - normal case + * => just give back the urb + * + * - case 1b). the urb of the pdu is unlinking. + * - usbip.ko will return a reply of the unlinking request. + * => give back the urb now and go to case 2b). + * + * - When vhci_hcd receives RET_UNLINK, + * + * - case 2a). a submit request is still pending in vhci_hcd. + * - urb was really pending in usbip.ko and urb_unlink_urb() was + * completed there. + * => free a pending submit request + * => notify unlink completeness by giving back the urb + * + * - case 2b). a submit request is *not* pending in vhci_hcd. + * - urb was already given back to the core driver. + * => do not give back the urb + * + * + * SERVER SIDE + * + * - When usbip receives CMD_UNLINK, + * + * - case 3a). the urb of the unlink request is now in submission. + * => do usb_unlink_urb(). + * => after the unlink is completed, send RET_UNLINK. + * + * - case 3b). the urb of the unlink request is not in submission. + * - may be already completed or never be received + * => send RET_UNLINK + * + */ +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct vhci_priv *priv; + struct vhci_device *vdev; + + pr_info("dequeue a urb %p\n", urb); + + spin_lock(&the_controller->lock); + + priv = urb->hcpriv; + if (!priv) { + /* URB was never linked! or will be soon given back by + * vhci_rx. */ + spin_unlock(&the_controller->lock); + return 0; + } + + { + int ret = 0; + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) { + spin_unlock(&the_controller->lock); + return ret; + } + } + + /* send unlink request here? */ + vdev = priv->vdev; + + if (!vdev->ud.tcp_socket) { + /* tcp connection is closed */ + spin_lock(&vdev->priv_lock); + + pr_info("device %p seems to be disconnected\n", vdev); + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + spin_unlock(&vdev->priv_lock); + + /* + * If tcp connection is alive, we have sent CMD_UNLINK. + * vhci_rx will receive RET_UNLINK and give back the URB. + * Otherwise, we give back it here. + */ + pr_info("gives back urb %p\n", urb); + + usb_hcd_unlink_urb_from_ep(hcd, urb); + + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + spin_lock(&the_controller->lock); + + } else { + /* tcp connection is alive */ + struct vhci_unlink *unlink; + + spin_lock(&vdev->priv_lock); + + /* setup CMD_UNLINK pdu */ + unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); + if (!unlink) { + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return -ENOMEM; + } + + unlink->seqnum = atomic_inc_return(&the_controller->seqnum); + if (unlink->seqnum == 0xffff) + pr_info("seqnum max\n"); + + unlink->unlink_seqnum = priv->seqnum; + + pr_info("device %p seems to be still connected\n", vdev); + + /* send cmd_unlink and try to cancel the pending URB in the + * peer */ + list_add_tail(&unlink->list, &vdev->unlink_tx); + wake_up(&vdev->waitq_tx); + + spin_unlock(&vdev->priv_lock); + } + + spin_unlock(&the_controller->lock); + + usbip_dbg_vhci_hc("leave\n"); + return 0; +} + +static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + } + + while (!list_empty(&vdev->unlink_rx)) { + struct urb *urb; + + unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, + list); + + /* give back URB of unanswered unlink request */ + pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); + + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + if (!urb) { + pr_info("the urb (seqnum %lu) was already given back\n", + unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + continue; + } + + urb->status = -ENODEV; + + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + kfree(unlink); + } + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); +} + +/* + * The important thing is that only one context begins cleanup. + * This is why error handling and cleanup become simple. + * We do not want to consider race condition as possible. + */ +static void vhci_shutdown_connection(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + /* need this? see stub_dev.c */ + if (ud->tcp_socket) { + pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* kill threads related to this sdev */ + if (vdev->ud.tcp_rx) { + kthread_stop_put(vdev->ud.tcp_rx); + vdev->ud.tcp_rx = NULL; + } + if (vdev->ud.tcp_tx) { + kthread_stop_put(vdev->ud.tcp_tx); + vdev->ud.tcp_tx = NULL; + } + pr_info("stop threads\n"); + + /* active connection is closed */ + if (vdev->ud.tcp_socket) { + sockfd_put(vdev->ud.tcp_socket); + vdev->ud.tcp_socket = NULL; + } + pr_info("release socket\n"); + + vhci_device_unlink_cleanup(vdev); + + /* + * rh_port_disconnect() is a trigger of ... + * usb_disable_device(): + * disable all the endpoints for a USB device. + * usb_disable_endpoint(): + * disable endpoints. pending urbs are unlinked(dequeued). + * + * NOTE: After calling rh_port_disconnect(), the USB device drivers of a + * detached device should release used urbs in a cleanup function (i.e. + * xxx_disconnect()). Therefore, vhci_hcd does not need to release + * pushed urbs and their private data in this function. + * + * NOTE: vhci_dequeue() must be considered carefully. When shutting down + * a connection, vhci_shutdown_connection() expects vhci_dequeue() + * gives back pushed urbs and frees their private data by request of + * the cleanup function of a USB driver. When unlinking a urb with an + * active connection, vhci_dequeue() does not give back the urb which + * is actually given back by vhci_rx after receiving its return pdu. + * + */ + rh_port_disconnect(vdev->rhport); + + pr_info("disconnect device\n"); +} + + +static void vhci_device_reset(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + spin_lock(&ud->lock); + + vdev->speed = 0; + vdev->devid = 0; + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = NULL; + + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + ud->status = VDEV_ST_NULL; + + spin_unlock(&ud->lock); +} + +static void vhci_device_unusable(struct usbip_device *ud) +{ + spin_lock(&ud->lock); + ud->status = VDEV_ST_ERROR; + spin_unlock(&ud->lock); +} + +static void vhci_device_init(struct vhci_device *vdev) +{ + memset(vdev, 0, sizeof(*vdev)); + + vdev->ud.side = USBIP_VHCI; + vdev->ud.status = VDEV_ST_NULL; + spin_lock_init(&vdev->ud.lock); + + INIT_LIST_HEAD(&vdev->priv_rx); + INIT_LIST_HEAD(&vdev->priv_tx); + INIT_LIST_HEAD(&vdev->unlink_tx); + INIT_LIST_HEAD(&vdev->unlink_rx); + spin_lock_init(&vdev->priv_lock); + + init_waitqueue_head(&vdev->waitq_tx); + + vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; + vdev->ud.eh_ops.reset = vhci_device_reset; + vdev->ud.eh_ops.unusable = vhci_device_unusable; + + usbip_start_eh(&vdev->ud); +} + +static int vhci_start(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport; + int err = 0; + + usbip_dbg_vhci_hc("enter vhci_start\n"); + + /* initialize private data of usb_hcd */ + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + vhci_device_init(vdev); + vdev->rhport = rhport; + } + + atomic_set(&vhci->seqnum, 0); + spin_lock_init(&vhci->lock); + + hcd->power_budget = 0; /* no limit */ + hcd->uses_new_polling = 1; + + /* vhci_hcd is now ready to be controlled through sysfs */ + err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + if (err) { + pr_err("create sysfs files\n"); + return err; + } + + return 0; +} + +static void vhci_stop(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport = 0; + + usbip_dbg_vhci_hc("stop VHCI controller\n"); + + /* 1. remove the userland interface of vhci_hcd */ + sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + + /* 2. shutdown all the ports of vhci_hcd */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); + usbip_stop_eh(&vdev->ud); + } +} + +static int vhci_get_frame_number(struct usb_hcd *hcd) +{ + pr_err("Not yet implemented\n"); + return 0; +} + +#ifdef CONFIG_PM + +/* FIXME: suspend/resume */ +static int vhci_bus_suspend(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + hcd->state = HC_STATE_SUSPENDED; + spin_unlock(&vhci->lock); + + return 0; +} + +static int vhci_bus_resume(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rc = 0; + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) + rc = -ESHUTDOWN; + else + hcd->state = HC_STATE_RUNNING; + spin_unlock(&vhci->lock); + + return rc; +} + +#else + +#define vhci_bus_suspend NULL +#define vhci_bus_resume NULL +#endif + +static struct hc_driver vhci_hc_driver = { + .description = driver_name, + .product_desc = driver_desc, + .hcd_priv_size = sizeof(struct vhci_hcd), + + .flags = HCD_USB2, + + .start = vhci_start, + .stop = vhci_stop, + + .urb_enqueue = vhci_urb_enqueue, + .urb_dequeue = vhci_urb_dequeue, + + .get_frame_number = vhci_get_frame_number, + + .hub_status_data = vhci_hub_status, + .hub_control = vhci_hub_control, + .bus_suspend = vhci_bus_suspend, + .bus_resume = vhci_bus_resume, +}; + +static int vhci_hcd_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + int ret; + + usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); + + /* + * Allocate and initialize hcd. + * Our private data is also allocated automatically. + */ + hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + pr_err("create hcd failed\n"); + return -ENOMEM; + } + hcd->has_tt = 1; + + /* this is private data for vhci_hcd */ + the_controller = hcd_to_vhci(hcd); + + /* + * Finish generic HCD structure initialization and register. + * Call the driver's reset() and start() routines. + */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret != 0) { + pr_err("usb_add_hcd failed %d\n", ret); + usb_put_hcd(hcd); + the_controller = NULL; + return ret; + } + + usbip_dbg_vhci_hc("bye\n"); + return 0; +} + +static int vhci_hcd_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + hcd = platform_get_drvdata(pdev); + if (!hcd) + return 0; + + /* + * Disconnects the root hub, + * then reverses the effects of usb_add_hcd(), + * invoking the HCD's stop() methods. + */ + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + the_controller = NULL; + + return 0; +} + +#ifdef CONFIG_PM + +/* what should happen for USB/IP under suspend/resume? */ +static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd; + int rhport = 0; + int connected = 0; + int ret = 0; + + hcd = platform_get_drvdata(pdev); + + spin_lock(&the_controller->lock); + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) + if (the_controller->port_status[rhport] & + USB_PORT_STAT_CONNECTION) + connected += 1; + + spin_unlock(&the_controller->lock); + + if (connected > 0) { + dev_info(&pdev->dev, + "We have %d active connection%s. Do not suspend.\n", + connected, (connected == 1 ? "" : "s")); + ret = -EBUSY; + } else { + dev_info(&pdev->dev, "suspend vhci_hcd"); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } + + return ret; +} + +static int vhci_hcd_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + hcd = platform_get_drvdata(pdev); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + + return 0; +} + +#else + +#define vhci_hcd_suspend NULL +#define vhci_hcd_resume NULL + +#endif + +static struct platform_driver vhci_driver = { + .probe = vhci_hcd_probe, + .remove = vhci_hcd_remove, + .suspend = vhci_hcd_suspend, + .resume = vhci_hcd_resume, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +/* + * The VHCI 'device' is 'virtual'; not a real plug&play hardware. + * We need to add this virtual device as a platform device arbitrarily: + * 1. platform_device_register() + */ +static void the_pdev_release(struct device *dev) +{ +} + +static struct platform_device the_pdev = { + /* should be the same name as driver_name */ + .name = driver_name, + .id = -1, + .dev = { + .release = the_pdev_release, + }, +}; + +static int __init vhci_hcd_init(void) +{ + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = platform_driver_register(&vhci_driver); + if (ret) + goto err_driver_register; + + ret = platform_device_register(&the_pdev); + if (ret) + goto err_platform_device_register; + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_platform_device_register: + platform_driver_unregister(&vhci_driver); +err_driver_register: + return ret; +} + +static void __exit vhci_hcd_exit(void) +{ + platform_device_unregister(&the_pdev); + platform_driver_unregister(&vhci_driver); +} + +module_init(vhci_hcd_init); +module_exit(vhci_hcd_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c new file mode 100644 index 000000000000..00e4a54308e4 --- /dev/null +++ b/drivers/usb/usbip/vhci_rx.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) +{ + struct vhci_priv *priv, *tmp; + struct urb *urb = NULL; + int status; + + list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { + if (priv->seqnum != seqnum) + continue; + + urb = priv->urb; + status = urb->status; + + usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", + urb, priv, seqnum); + + switch (status) { + case -ENOENT: + /* fall through */ + case -ECONNRESET: + dev_info(&urb->dev->dev, + "urb %p was unlinked %ssynchronuously.\n", urb, + status == -ENOENT ? "" : "a"); + break; + case -EINPROGRESS: + /* no info output */ + break; + default: + dev_info(&urb->dev->dev, + "urb %p may be in a error, status %d\n", urb, + status); + } + + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + break; + } + + return urb; +} + +static void vhci_recv_ret_submit(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct usbip_device *ud = &vdev->ud; + struct urb *urb; + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); + pr_info("max seqnum %d\n", + atomic_read(&the_controller->seqnum)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + /* unpack the pdu to a urb */ + usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); + + /* recv transfer buffer */ + if (usbip_recv_xbuff(ud, urb) < 0) + return; + + /* recv iso_packet_descriptor */ + if (usbip_recv_iso(ud, urb) < 0) + return; + + /* restore the padding in iso packets */ + usbip_pad_iso(ud, urb); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_urb(urb); + + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + + usbip_dbg_vhci_rx("Leave\n"); +} + +static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { + pr_info("unlink->seqnum %lu\n", unlink->seqnum); + if (unlink->seqnum == pdu->base.seqnum) { + usbip_dbg_vhci_rx("found pending unlink, %lu\n", + unlink->seqnum); + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + return unlink; + } + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static void vhci_recv_ret_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink; + struct urb *urb; + + usbip_dump_header(pdu); + + unlink = dequeue_pending_unlink(vdev, pdu); + if (!unlink) { + pr_info("cannot find the pending unlink %u\n", + pdu->base.seqnum); + return; + } + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + /* + * I get the result of a unlink request. But, it seems that I + * already received the result of its submit result and gave + * back the URB. + */ + pr_info("the urb (seqnum %d) was already given back\n", + pdu->base.seqnum); + } else { + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + /* If unlink is successful, status is -ECONNRESET */ + urb->status = pdu->u.ret_unlink.status; + pr_info("urb->status %d\n", urb->status); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + } + + kfree(unlink); +} + +static int vhci_priv_tx_empty(struct vhci_device *vdev) +{ + int empty = 0; + + spin_lock(&vdev->priv_lock); + empty = list_empty(&vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + + return empty; +} + +/* recv a pdu */ +static void vhci_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + usbip_dbg_vhci_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret < 0) { + if (ret == -ECONNRESET) + pr_info("connection reset by peer\n"); + else if (ret == -EAGAIN) { + /* ignore if connection was idle */ + if (vhci_priv_tx_empty(vdev)) + return; + pr_info("connection timed out with pending urbs\n"); + } else if (ret != -ERESTARTSYS) + pr_info("xmit failed %d\n", ret); + + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + if (ret == 0) { + pr_info("connection closed"); + usbip_event_add(ud, VDEV_EVENT_DOWN); + return; + } + if (ret != sizeof(pdu)) { + pr_err("received pdu size is %d, should be %d\n", ret, + (unsigned int)sizeof(pdu)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_header(&pdu); + + switch (pdu.base.command) { + case USBIP_RET_SUBMIT: + vhci_recv_ret_submit(vdev, &pdu); + break; + case USBIP_RET_UNLINK: + vhci_recv_ret_unlink(vdev, &pdu); + break; + default: + /* NOT REACHED */ + pr_err("unknown pdu %u\n", pdu.base.command); + usbip_dump_header(&pdu); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + break; + } +} + +int vhci_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + vhci_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c new file mode 100644 index 000000000000..211f43f67ea2 --- /dev/null +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/kthread.h> +#include <linux/file.h> +#include <linux/net.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* TODO: refine locking ?*/ + +/* Sysfs entry to show port status */ +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *out) +{ + char *s = out; + int i = 0; + + BUG_ON(!the_controller || !out); + + spin_lock(&the_controller->lock); + + /* + * output example: + * prt sta spd dev socket local_busid + * 000 004 000 000 c5a7bb80 1-2.3 + * 001 004 000 000 d8cee980 2-3.4 + * + * IP address can be retrieved from a socket pointer address by looking + * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a + * port number and its peer IP address. + */ + out += sprintf(out, + "prt sta spd bus dev socket local_busid\n"); + + for (i = 0; i < VHCI_NPORTS; i++) { + struct vhci_device *vdev = port_to_vdev(i); + + spin_lock(&vdev->ud.lock); + out += sprintf(out, "%03u %03u ", i, vdev->ud.status); + + if (vdev->ud.status == VDEV_ST_USED) { + out += sprintf(out, "%03u %08x ", + vdev->speed, vdev->devid); + out += sprintf(out, "%16p ", vdev->ud.tcp_socket); + out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); + + } else { + out += sprintf(out, "000 000 000 0000000000000000 0-0"); + } + + out += sprintf(out, "\n"); + spin_unlock(&vdev->ud.lock); + } + + spin_unlock(&the_controller->lock); + + return out - s; +} +static DEVICE_ATTR_RO(status); + +/* Sysfs entry to shutdown a virtual connection */ +static int vhci_port_disconnect(__u32 rhport) +{ + struct vhci_device *vdev; + + usbip_dbg_vhci_sysfs("enter\n"); + + /* lock */ + spin_lock(&the_controller->lock); + + vdev = port_to_vdev(rhport); + + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL) { + pr_err("not connected %d\n", vdev->ud.status); + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + return -EINVAL; + } + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); + + return 0; +} + +static ssize_t store_detach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + __u32 rhport = 0; + + if (sscanf(buf, "%u", &rhport) != 1) + return -EINVAL; + + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + dev_err(dev, "invalid port %u\n", rhport); + return -EINVAL; + } + + err = vhci_port_disconnect(rhport); + if (err < 0) + return -EINVAL; + + usbip_dbg_vhci_sysfs("Leave\n"); + + return count; +} +static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); + +/* Sysfs entry to establish a virtual connection */ +static int valid_args(__u32 rhport, enum usb_device_speed speed) +{ + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + pr_err("port %u\n", rhport); + return -EINVAL; + } + + /* check speed */ + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: + break; + default: + pr_err("Failed attach request for unsupported USB speed: %s\n", + usb_speed_string(speed)); + return -EINVAL; + } + + return 0; +} + +/* + * To start a new USB/IP attachment, a userland program needs to setup a TCP + * connection and then write its socket descriptor with remote device + * information into this sysfs file. + * + * A remote device is virtually attached to the root-hub port of @rhport with + * @speed. @devid is embedded into a request to specify the remote device in a + * server host. + * + * write() returns 0 on success, else negative errno. + */ +static ssize_t store_attach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vhci_device *vdev; + struct socket *socket; + int sockfd = 0; + __u32 rhport = 0, devid = 0, speed = 0; + int err; + + /* + * @rhport: port number of vhci_hcd + * @sockfd: socket descriptor of an established TCP connection + * @devid: unique device identifier in a remote host + * @speed: usb device speed in a remote host + */ + if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) + return -EINVAL; + + usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", + rhport, sockfd, devid, speed); + + /* check received parameters */ + if (valid_args(rhport, speed) < 0) + return -EINVAL; + + /* Extract socket from fd. */ + socket = sockfd_lookup(sockfd, &err); + if (!socket) + return -EINVAL; + + /* now need lock until setting vdev status as used */ + + /* begin a lock */ + spin_lock(&the_controller->lock); + vdev = port_to_vdev(rhport); + spin_lock(&vdev->ud.lock); + + if (vdev->ud.status != VDEV_ST_NULL) { + /* end of the lock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + sockfd_put(socket); + + dev_err(dev, "port %d already used\n", rhport); + return -EINVAL; + } + + dev_info(dev, + "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", + rhport, sockfd, devid, speed, usb_speed_string(speed)); + + vdev->devid = devid; + vdev->speed = speed; + vdev->ud.tcp_socket = socket; + vdev->ud.status = VDEV_ST_NOTASSIGNED; + + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + /* end the lock */ + + vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); + vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); + + rh_port_connect(rhport, speed); + + return count; +} +static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); + +static struct attribute *dev_attrs[] = { + &dev_attr_status.attr, + &dev_attr_detach.attr, + &dev_attr_attach.attr, + &dev_attr_usbip_debug.attr, + NULL, +}; + +const struct attribute_group dev_attr_group = { + .attrs = dev_attrs, +}; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c new file mode 100644 index 000000000000..409fd99f3257 --- /dev/null +++ b/drivers/usb/usbip/vhci_tx.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) +{ + struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); + struct vhci_device *vdev = priv->vdev; + + usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", + usb_pipedevice(urb->pipe), vdev->devid); + + pdup->base.command = USBIP_CMD_SUBMIT; + pdup->base.seqnum = priv->seqnum; + pdup->base.devid = vdev->devid; + pdup->base.direction = usb_pipein(urb->pipe) ? + USBIP_DIR_IN : USBIP_DIR_OUT; + pdup->base.ep = usb_pipeendpoint(urb->pipe); + + usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); + + if (urb->setup_packet) + memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); +} + +static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) +{ + struct vhci_priv *priv, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { + list_move_tail(&priv->list, &vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + return priv; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_submit(struct vhci_device *vdev) +{ + struct vhci_priv *priv = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + + /* 1. setup usbip_header */ + setup_cmd_submit_pdu(&pdu_header, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { + iov[1].iov_base = urb->transfer_buffer; + iov[1].iov_len = urb->transfer_buffer_length; + txsize += urb->transfer_buffer_length; + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&vdev->ud, + SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iov[2].iov_base = iso_buffer; + iov[2].iov_len = len; + txsize += len; + } + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + kfree(iso_buffer); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iso_buffer); + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + list_move_tail(&unlink->list, &vdev->unlink_rx); + spin_unlock(&vdev->priv_lock); + return unlink; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_unlink(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + pdu_header.base.command = USBIP_CMD_UNLINK; + pdu_header.base.seqnum = unlink->seqnum; + pdu_header.base.devid = vdev->devid; + pdu_header.base.ep = 0; + pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; + + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +int vhci_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + while (!kthread_should_stop()) { + if (vhci_send_cmd_submit(vdev) < 0) + break; + + if (vhci_send_cmd_unlink(vdev) < 0) + break; + + wait_event_interruptible(vdev->waitq_tx, + (!list_empty(&vdev->priv_tx) || + !list_empty(&vdev->unlink_tx) || + kthread_should_stop())); + + usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); + } + + return 0; +} diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3e2e4ed20157..e279015be466 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2602,6 +2602,7 @@ static void wa_buf_in_cb(struct urb *urb) dev = &wa->usb_iface->dev; --(wa->active_buf_in_urbs); active_buf_in_urbs = wa->active_buf_in_urbs; + rpipe = xfer->ep->hcpriv; if (usb_pipeisoc(xfer->urb->pipe)) { struct usb_iso_packet_descriptor *iso_frame_desc = @@ -2659,7 +2660,6 @@ static void wa_buf_in_cb(struct urb *urb) resubmit_dti = (isoc_data_frame_count == urb_frame_count); } else if (active_buf_in_urbs == 0) { - rpipe = xfer->ep->hcpriv; dev_dbg(dev, "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", xfer, wa_xfer_id(xfer), seg->index, @@ -2685,7 +2685,6 @@ static void wa_buf_in_cb(struct urb *urb) */ resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING; spin_lock_irqsave(&xfer->lock, flags); - rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n", xfer, wa_xfer_id(xfer), seg->index, |