Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 20:06:10 +0000 (13:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 20:06:10 +0000 (13:06 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (143 commits)
  USB: xhci depends on PCI.
  USB: xhci: Add Makefile, MAINTAINERS, and Kconfig entries.
  USB: xhci: Respect critical sections.
  USB: xHCI: Fix interrupt moderation.
  USB: xhci: Remove packed attribute from structures.
  usb; xhci: Fix TRB offset calculations.
  USB: xhci: replace if-elseif-else with switch-case
  USB: xhci: Make xhci-mem.c include linux/dmapool.h
  USB: xhci: drop spinlock in xhci_urb_enqueue() error path.
  USB: Change names of SuperSpeed ep companion descriptor structs.
  USB: xhci: Avoid compiler reordering in Link TRB giveback.
  USB: xhci: Clean up xhci_irq() function.
  USB: xhci: Avoid global namespace pollution.
  USB: xhci: Fix Link TRB handoff bit twiddling.
  USB: xhci: Fix register write order.
  USB: xhci: fix some compiler warnings in xhci.h
  USB: xhci: fix lots of compiler warnings.
  USB: xhci: use xhci_handle_event instead of handle_event
  USB: xhci: URB cancellation support.
  USB: xhci: Scatter gather list support for bulk transfers.
  ...

164 files changed:
MAINTAINERS
arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h [new file with mode: 0644]
arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h [new file with mode: 0644]
drivers/pci/pci.c
drivers/staging/uc2322/aten2011.c
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h
drivers/usb/class/usbtmc.c
drivers/usb/core/Kconfig
drivers/usb/core/Makefile
drivers/usb/core/config.c
drivers/usb/core/driver.c
drivers/usb/core/endpoint.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hcd.h
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/atmel_usba_udc.c
drivers/usb/gadget/audio.c [new file with mode: 0644]
drivers/usb/gadget/ci13xxx_udc.c
drivers/usb/gadget/f_audio.c [new file with mode: 0644]
drivers/usb/gadget/f_rndis.c
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/fsl_mx3_udc.c [new file with mode: 0644]
drivers/usb/gadget/fsl_udc_core.c [moved from drivers/usb/gadget/fsl_usb2_udc.c with 99% similarity]
drivers/usb/gadget/fsl_usb2_udc.h
drivers/usb/gadget/gadget_chips.h
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/imx_udc.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/langwell_udc.c [new file with mode: 0644]
drivers/usb/gadget/langwell_udc.h [new file with mode: 0644]
drivers/usb/gadget/pxa27x_udc.c
drivers/usb/gadget/pxa27x_udc.h
drivers/usb/gadget/s3c-hsotg.c [new file with mode: 0644]
drivers/usb/gadget/u_audio.c [new file with mode: 0644]
drivers/usb/gadget/u_audio.h [new file with mode: 0644]
drivers/usb/gadget/u_serial.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-au1xxx.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-ixp4xx.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci.h
drivers/usb/host/fhci-dbg.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/r8a66597.h
drivers/usb/host/uhci-hcd.c
drivers/usb/host/uhci-q.c
drivers/usb/host/xhci-dbg.c [new file with mode: 0644]
drivers/usb/host/xhci-ext-caps.h [new file with mode: 0644]
drivers/usb/host/xhci-hcd.c [new file with mode: 0644]
drivers/usb/host/xhci-hub.c [new file with mode: 0644]
drivers/usb/host/xhci-mem.c [new file with mode: 0644]
drivers/usb/host/xhci-pci.c [new file with mode: 0644]
drivers/usb/host/xhci-ring.c [new file with mode: 0644]
drivers/usb/host/xhci.h [new file with mode: 0644]
drivers/usb/misc/sisusbvga/Kconfig
drivers/usb/misc/usbtest.c
drivers/usb/mon/mon_text.c
drivers/usb/musb/Kconfig
drivers/usb/musb/blackfin.c
drivers/usb/musb/cppi_dma.c
drivers/usb/musb/cppi_dma.h
drivers/usb/musb/davinci.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_gadget_ep0.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_host.h
drivers/usb/musb/musb_virthub.c
drivers/usb/musb/omap2430.c
drivers/usb/musb/tusb6010.c
drivers/usb/otg/Kconfig
drivers/usb/otg/Makefile
drivers/usb/otg/langwell_otg.c [new file with mode: 0644]
drivers/usb/otg/nop-usb-xceiv.c
drivers/usb/otg/twl4030-usb.c
drivers/usb/serial/aircable.c
drivers/usb/serial/belkin_sa.c
drivers/usb/serial/bus.c
drivers/usb/serial/cp210x.c
drivers/usb/serial/cyberjack.c
drivers/usb/serial/cypress_m8.c
drivers/usb/serial/digi_acceleport.c
drivers/usb/serial/empeg.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/garmin_gps.c
drivers/usb/serial/generic.c
drivers/usb/serial/io_edgeport.c
drivers/usb/serial/io_tables.h
drivers/usb/serial/io_ti.c
drivers/usb/serial/ipaq.c
drivers/usb/serial/iuu_phoenix.c
drivers/usb/serial/keyspan.c
drivers/usb/serial/keyspan.h
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/kl5kusb105.c
drivers/usb/serial/kobil_sct.c
drivers/usb/serial/mct_u232.c
drivers/usb/serial/mos7720.c
drivers/usb/serial/mos7840.c
drivers/usb/serial/omninet.c
drivers/usb/serial/opticon.c
drivers/usb/serial/option.c
drivers/usb/serial/oti6858.c
drivers/usb/serial/pl2303.c
drivers/usb/serial/sierra.c
drivers/usb/serial/spcp8x5.c
drivers/usb/serial/symbolserial.c
drivers/usb/serial/ti_usb_3410_5052.c
drivers/usb/serial/usb-serial.c
drivers/usb/serial/usb_debug.c
drivers/usb/serial/visor.c
drivers/usb/serial/whiteheat.c
drivers/usb/storage/initializers.c
drivers/usb/storage/option_ms.c
drivers/usb/storage/sierra_ms.c
drivers/usb/storage/unusual_devs.h
fs/befs/linuxvfs.c
fs/fat/dir.c
fs/fat/namei_vfat.c
fs/isofs/joliet.c
fs/ncpfs/ncplib_kernel.c
fs/nls/nls_base.c
fs/nls/nls_utf8.c
include/linux/nls.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/usb.h
include/linux/usb/audio.h
include/linux/usb/ch9.h
include/linux/usb/composite.h
include/linux/usb/langwell_otg.h [new file with mode: 0644]
include/linux/usb/langwell_udc.h [new file with mode: 0644]
include/linux/usb/otg.h
include/linux/usb/r8a66597.h [new file with mode: 0644]
include/linux/usb/serial.h

index 09f6b3e5708a17a07076a5ab3da16c3ac3e2a6ad..685784cc023b6e4a242e4efcfc845a54c64c7e57 100644 (file)
@@ -6165,6 +6165,12 @@ L:       linux-wireless@vger.kernel.org
 S:     Maintained
 F:     drivers/net/wireless/rndis_wlan.c
 
+USB XHCI DRIVER
+P:     Sarah Sharp
+M:     sarah.a.sharp@intel.com
+L:     linux-usb@vger.kernel.org
+S:     Supported
+
 USB ZC0301 DRIVER
 P:     Luca Risolia
 M:     luca.risolia@studio.unibo.it
diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
new file mode 100644 (file)
index 0000000..36a85f5
--- /dev/null
@@ -0,0 +1,50 @@
+/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      http://armlinux.simtec.co.uk/
+ *      Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - USB2.0 Highspeed/OtG device PHY registers
+ *
+ * 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.
+*/
+
+/* Note, this is a seperate header file as some of the clock framework
+ * needs to touch this if the clk_48m is used as the USB OHCI or other
+ * peripheral source.
+*/
+
+#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H
+#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__
+
+/* S3C64XX_PA_USB_HSPHY */
+
+#define S3C_HSOTG_PHYREG(x)    ((x) + S3C_VA_USB_HSPHY)
+
+#define S3C_PHYPWR                             S3C_HSOTG_PHYREG(0x00)
+#define SRC_PHYPWR_OTG_DISABLE                 (1 << 4)
+#define SRC_PHYPWR_ANALOG_POWERDOWN            (1 << 3)
+#define SRC_PHYPWR_FORCE_SUSPEND               (1 << 1)
+
+#define S3C_PHYCLK                             S3C_HSOTG_PHYREG(0x04)
+#define S3C_PHYCLK_MODE_USB11                  (1 << 6)
+#define S3C_PHYCLK_EXT_OSC                     (1 << 5)
+#define S3C_PHYCLK_CLK_FORCE                   (1 << 4)
+#define S3C_PHYCLK_ID_PULL                     (1 << 2)
+#define S3C_PHYCLK_CLKSEL_MASK                 (0x3 << 0)
+#define S3C_PHYCLK_CLKSEL_SHIFT                        (0)
+#define S3C_PHYCLK_CLKSEL_48M                  (0x0 << 0)
+#define S3C_PHYCLK_CLKSEL_12M                  (0x2 << 0)
+#define S3C_PHYCLK_CLKSEL_24M                  (0x3 << 0)
+
+#define S3C_RSTCON                             S3C_HSOTG_PHYREG(0x08)
+#define S3C_RSTCON_PHYCLK                      (1 << 2)
+#define S3C_RSTCON_HCLK                                (1 << 2)
+#define S3C_RSTCON_PHY                         (1 << 0)
+
+#define S3C_PHYTUNE                            S3C_HSOTG_PHYREG(0x20)
+
+#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
new file mode 100644 (file)
index 0000000..8d18d9d
--- /dev/null
@@ -0,0 +1,377 @@
+/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *      http://armlinux.simtec.co.uk/
+ *      Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - USB2.0 Highspeed/OtG device block registers
+ *
+ * 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 __PLAT_S3C64XX_REGS_USB_HSOTG_H
+#define __PLAT_S3C64XX_REGS_USB_HSOTG_H __FILE__
+
+#define S3C_HSOTG_REG(x) (x)
+
+#define S3C_GOTGCTL                            S3C_HSOTG_REG(0x000)
+#define S3C_GOTGCTL_BSESVLD                    (1 << 19)
+#define S3C_GOTGCTL_ASESVLD                    (1 << 18)
+#define S3C_GOTGCTL_DBNC_SHORT                 (1 << 17)
+#define S3C_GOTGCTL_CONID_B                    (1 << 16)
+#define S3C_GOTGCTL_DEVHNPEN                   (1 << 11)
+#define S3C_GOTGCTL_HSSETHNPEN                 (1 << 10)
+#define S3C_GOTGCTL_HNPREQ                     (1 << 9)
+#define S3C_GOTGCTL_HSTNEGSCS                  (1 << 8)
+#define S3C_GOTGCTL_SESREQ                     (1 << 1)
+#define S3C_GOTGCTL_SESREQSCS                  (1 << 0)
+
+#define S3C_GOTGINT                            S3C_HSOTG_REG(0x004)
+#define S3C_GOTGINT_DbnceDone                  (1 << 19)
+#define S3C_GOTGINT_ADevTOUTChg                        (1 << 18)
+#define S3C_GOTGINT_HstNegDet                  (1 << 17)
+#define S3C_GOTGINT_HstnegSucStsChng           (1 << 9)
+#define S3C_GOTGINT_SesReqSucStsChng           (1 << 8)
+#define S3C_GOTGINT_SesEndDet                  (1 << 2)
+
+#define S3C_GAHBCFG                            S3C_HSOTG_REG(0x008)
+#define S3C_GAHBCFG_PTxFEmpLvl                 (1 << 8)
+#define S3C_GAHBCFG_NPTxFEmpLvl                        (1 << 7)
+#define S3C_GAHBCFG_DMAEn                      (1 << 5)
+#define S3C_GAHBCFG_HBstLen_MASK               (0xf << 1)
+#define S3C_GAHBCFG_HBstLen_SHIFT              (1)
+#define S3C_GAHBCFG_HBstLen_Single             (0x0 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr               (0x1 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr4              (0x3 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr8              (0x5 << 1)
+#define S3C_GAHBCFG_HBstLen_Incr16             (0x7 << 1)
+#define S3C_GAHBCFG_GlblIntrEn                 (1 << 0)
+
+#define S3C_GUSBCFG                            S3C_HSOTG_REG(0x00C)
+#define S3C_GUSBCFG_PHYLPClkSel                        (1 << 15)
+#define S3C_GUSBCFG_HNPCap                     (1 << 9)
+#define S3C_GUSBCFG_SRPCap                     (1 << 8)
+#define S3C_GUSBCFG_PHYIf16                    (1 << 3)
+#define S3C_GUSBCFG_TOutCal_MASK               (0x7 << 0)
+#define S3C_GUSBCFG_TOutCal_SHIFT              (0)
+#define S3C_GUSBCFG_TOutCal_LIMIT              (0x7)
+#define S3C_GUSBCFG_TOutCal(_x)                        ((_x) << 0)
+
+#define S3C_GRSTCTL                            S3C_HSOTG_REG(0x010)
+
+#define S3C_GRSTCTL_AHBIdle                    (1 << 31)
+#define S3C_GRSTCTL_DMAReq                     (1 << 30)
+#define S3C_GRSTCTL_TxFNum_MASK                        (0x1f << 6)
+#define S3C_GRSTCTL_TxFNum_SHIFT               (6)
+#define S3C_GRSTCTL_TxFNum_LIMIT               (0x1f)
+#define S3C_GRSTCTL_TxFNum(_x)                 ((_x) << 6)
+#define S3C_GRSTCTL_TxFFlsh                    (1 << 5)
+#define S3C_GRSTCTL_RxFFlsh                    (1 << 4)
+#define S3C_GRSTCTL_INTknQFlsh                 (1 << 3)
+#define S3C_GRSTCTL_FrmCntrRst                 (1 << 2)
+#define S3C_GRSTCTL_HSftRst                    (1 << 1)
+#define S3C_GRSTCTL_CSftRst                    (1 << 0)
+
+#define S3C_GINTSTS                            S3C_HSOTG_REG(0x014)
+#define S3C_GINTMSK                            S3C_HSOTG_REG(0x018)
+
+#define S3C_GINTSTS_WkUpInt                    (1 << 31)
+#define S3C_GINTSTS_SessReqInt                 (1 << 30)
+#define S3C_GINTSTS_DisconnInt                 (1 << 29)
+#define S3C_GINTSTS_ConIDStsChng               (1 << 28)
+#define S3C_GINTSTS_PTxFEmp                    (1 << 26)
+#define S3C_GINTSTS_HChInt                     (1 << 25)
+#define S3C_GINTSTS_PrtInt                     (1 << 24)
+#define S3C_GINTSTS_FetSusp                    (1 << 22)
+#define S3C_GINTSTS_incompIP                   (1 << 21)
+#define S3C_GINTSTS_IncomplSOIN                        (1 << 20)
+#define S3C_GINTSTS_OEPInt                     (1 << 19)
+#define S3C_GINTSTS_IEPInt                     (1 << 18)
+#define S3C_GINTSTS_EPMis                      (1 << 17)
+#define S3C_GINTSTS_EOPF                       (1 << 15)
+#define S3C_GINTSTS_ISOutDrop                  (1 << 14)
+#define S3C_GINTSTS_EnumDone                   (1 << 13)
+#define S3C_GINTSTS_USBRst                     (1 << 12)
+#define S3C_GINTSTS_USBSusp                    (1 << 11)
+#define S3C_GINTSTS_ErlySusp                   (1 << 10)
+#define S3C_GINTSTS_GOUTNakEff                 (1 << 7)
+#define S3C_GINTSTS_GINNakEff                  (1 << 6)
+#define S3C_GINTSTS_NPTxFEmp                   (1 << 5)
+#define S3C_GINTSTS_RxFLvl                     (1 << 4)
+#define S3C_GINTSTS_SOF                                (1 << 3)
+#define S3C_GINTSTS_OTGInt                     (1 << 2)
+#define S3C_GINTSTS_ModeMis                    (1 << 1)
+#define S3C_GINTSTS_CurMod_Host                        (1 << 0)
+
+#define S3C_GRXSTSR                            S3C_HSOTG_REG(0x01C)
+#define S3C_GRXSTSP                            S3C_HSOTG_REG(0x020)
+
+#define S3C_GRXSTS_FN_MASK                     (0x7f << 25)
+#define S3C_GRXSTS_FN_SHIFT                    (25)
+
+#define S3C_GRXSTS_PktSts_MASK                 (0xf << 17)
+#define S3C_GRXSTS_PktSts_SHIFT                        (17)
+#define S3C_GRXSTS_PktSts_GlobalOutNAK         (0x1 << 17)
+#define S3C_GRXSTS_PktSts_OutRX                        (0x2 << 17)
+#define S3C_GRXSTS_PktSts_OutDone              (0x3 << 17)
+#define S3C_GRXSTS_PktSts_SetupDone            (0x4 << 17)
+#define S3C_GRXSTS_PktSts_SetupRX              (0x6 << 17)
+
+#define S3C_GRXSTS_DPID_MASK                   (0x3 << 15)
+#define S3C_GRXSTS_DPID_SHIFT                  (15)
+#define S3C_GRXSTS_ByteCnt_MASK                        (0x7ff << 4)
+#define S3C_GRXSTS_ByteCnt_SHIFT               (4)
+#define S3C_GRXSTS_EPNum_MASK                  (0xf << 0)
+#define S3C_GRXSTS_EPNum_SHIFT                 (0)
+
+#define S3C_GRXFSIZ                            S3C_HSOTG_REG(0x024)
+
+#define S3C_GNPTXFSIZ                          S3C_HSOTG_REG(0x028)
+
+#define S3C_GNPTXFSIZ_NPTxFDep_MASK            (0xffff << 16)
+#define S3C_GNPTXFSIZ_NPTxFDep_SHIFT           (16)
+#define S3C_GNPTXFSIZ_NPTxFDep_LIMIT           (0xffff)
+#define S3C_GNPTXFSIZ_NPTxFDep(_x)             ((_x) << 16)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_MASK         (0xffff << 0)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_SHIFT                (0)
+#define S3C_GNPTXFSIZ_NPTxFStAddr_LIMIT                (0xffff)
+#define S3C_GNPTXFSIZ_NPTxFStAddr(_x)          ((_x) << 0)
+
+#define S3C_GNPTXSTS                           S3C_HSOTG_REG(0x02C)
+
+#define S3C_GNPTXSTS_NPtxQTop_MASK             (0x7f << 24)
+#define S3C_GNPTXSTS_NPtxQTop_SHIFT            (24)
+
+#define S3C_GNPTXSTS_NPTxQSpcAvail_MASK                (0xff << 16)
+#define S3C_GNPTXSTS_NPTxQSpcAvail_SHIFT       (16)
+#define S3C_GNPTXSTS_NPTxQSpcAvail_GET(_v)     (((_v) >> 16) & 0xff)
+
+#define S3C_GNPTXSTS_NPTxFSpcAvail_MASK                (0xffff << 0)
+#define S3C_GNPTXSTS_NPTxFSpcAvail_SHIFT       (0)
+#define S3C_GNPTXSTS_NPTxFSpcAvail_GET(_v)     (((_v) >> 0) & 0xffff)
+
+
+#define S3C_HPTXFSIZ                           S3C_HSOTG_REG(0x100)
+
+#define S3C_DPTXFSIZn(_a)                      S3C_HSOTG_REG(0x104 + (((_a) - 1) * 4))
+
+#define S3C_DPTXFSIZn_DPTxFSize_MASK           (0xffff << 16)
+#define S3C_DPTXFSIZn_DPTxFSize_SHIFT          (16)
+#define S3C_DPTXFSIZn_DPTxFSize_GET(_v)                (((_v) >> 16) & 0xffff)
+#define S3C_DPTXFSIZn_DPTxFSize_LIMIT          (0xffff)
+#define S3C_DPTXFSIZn_DPTxFSize(_x)            ((_x) << 16)
+
+#define S3C_DPTXFSIZn_DPTxFStAddr_MASK         (0xffff << 0)
+#define S3C_DPTXFSIZn_DPTxFStAddr_SHIFT                (0)
+
+/* Device mode registers */
+#define S3C_DCFG                               S3C_HSOTG_REG(0x800)
+
+#define S3C_DCFG_EPMisCnt_MASK                 (0x1f << 18)
+#define S3C_DCFG_EPMisCnt_SHIFT                        (18)
+#define S3C_DCFG_EPMisCnt_LIMIT                        (0x1f)
+#define S3C_DCFG_EPMisCnt(_x)                  ((_x) << 18)
+
+#define S3C_DCFG_PerFrInt_MASK                 (0x3 << 11)
+#define S3C_DCFG_PerFrInt_SHIFT                        (11)
+#define S3C_DCFG_PerFrInt_LIMIT                        (0x3)
+#define S3C_DCFG_PerFrInt(_x)                  ((_x) << 11)
+
+#define S3C_DCFG_DevAddr_MASK                  (0x7f << 4)
+#define S3C_DCFG_DevAddr_SHIFT                 (4)
+#define S3C_DCFG_DevAddr_LIMIT                 (0x7f)
+#define S3C_DCFG_DevAddr(_x)                   ((_x) << 4)
+
+#define S3C_DCFG_NZStsOUTHShk                  (1 << 2)
+
+#define S3C_DCFG_DevSpd_MASK                   (0x3 << 0)
+#define S3C_DCFG_DevSpd_SHIFT                  (0)
+#define S3C_DCFG_DevSpd_HS                     (0x0 << 0)
+#define S3C_DCFG_DevSpd_FS                     (0x1 << 0)
+#define S3C_DCFG_DevSpd_LS                     (0x2 << 0)
+#define S3C_DCFG_DevSpd_FS48                   (0x3 << 0)
+
+#define S3C_DCTL                               S3C_HSOTG_REG(0x804)
+
+#define S3C_DCTL_PWROnPrgDone                  (1 << 11)
+#define S3C_DCTL_CGOUTNak                      (1 << 10)
+#define S3C_DCTL_SGOUTNak                      (1 << 9)
+#define S3C_DCTL_CGNPInNAK                     (1 << 8)
+#define S3C_DCTL_SGNPInNAK                     (1 << 7)
+#define S3C_DCTL_TstCtl_MASK                   (0x7 << 4)
+#define S3C_DCTL_TstCtl_SHIFT                  (4)
+#define S3C_DCTL_GOUTNakSts                    (1 << 3)
+#define S3C_DCTL_GNPINNakSts                   (1 << 2)
+#define S3C_DCTL_SftDiscon                     (1 << 1)
+#define S3C_DCTL_RmtWkUpSig                    (1 << 0)
+
+#define S3C_DSTS                               S3C_HSOTG_REG(0x808)
+
+#define S3C_DSTS_SOFFN_MASK                    (0x3fff << 8)
+#define S3C_DSTS_SOFFN_SHIFT                   (8)
+#define S3C_DSTS_SOFFN_LIMIT                   (0x3fff)
+#define S3C_DSTS_SOFFN(_x)                     ((_x) << 8)
+#define S3C_DSTS_ErraticErr                    (1 << 3)
+#define S3C_DSTS_EnumSpd_MASK                  (0x3 << 1)
+#define S3C_DSTS_EnumSpd_SHIFT                 (1)
+#define S3C_DSTS_EnumSpd_HS                    (0x0 << 1)
+#define S3C_DSTS_EnumSpd_FS                    (0x1 << 1)
+#define S3C_DSTS_EnumSpd_LS                    (0x2 << 1)
+#define S3C_DSTS_EnumSpd_FS48                  (0x3 << 1)
+
+#define S3C_DSTS_SuspSts                       (1 << 0)
+
+#define S3C_DIEPMSK                            S3C_HSOTG_REG(0x810)
+
+#define S3C_DIEPMSK_INEPNakEffMsk              (1 << 6)
+#define S3C_DIEPMSK_INTknEPMisMsk              (1 << 5)
+#define S3C_DIEPMSK_INTknTXFEmpMsk             (1 << 4)
+#define S3C_DIEPMSK_TimeOUTMsk                 (1 << 3)
+#define S3C_DIEPMSK_AHBErrMsk                  (1 << 2)
+#define S3C_DIEPMSK_EPDisbldMsk                        (1 << 1)
+#define S3C_DIEPMSK_XferComplMsk               (1 << 0)
+
+#define S3C_DOEPMSK                            S3C_HSOTG_REG(0x814)
+
+#define S3C_DOEPMSK_Back2BackSetup             (1 << 6)
+#define S3C_DOEPMSK_OUTTknEPdisMsk             (1 << 4)
+#define S3C_DOEPMSK_SetupMsk                   (1 << 3)
+#define S3C_DOEPMSK_AHBErrMsk                  (1 << 2)
+#define S3C_DOEPMSK_EPDisbldMsk                        (1 << 1)
+#define S3C_DOEPMSK_XferComplMsk               (1 << 0)
+
+#define S3C_DAINT                              S3C_HSOTG_REG(0x818)
+#define S3C_DAINTMSK                           S3C_HSOTG_REG(0x81C)
+
+#define S3C_DAINT_OutEP_SHIFT                  (16)
+#define S3C_DAINT_OutEP(x)                     (1 << ((x) + 16))
+#define S3C_DAINT_InEP(x)                      (1 << (x))
+
+#define S3C_DTKNQR1                            S3C_HSOTG_REG(0x820)
+#define S3C_DTKNQR2                            S3C_HSOTG_REG(0x824)
+#define S3C_DTKNQR3                            S3C_HSOTG_REG(0x830)
+#define S3C_DTKNQR4                            S3C_HSOTG_REG(0x834)
+
+#define S3C_DVBUSDIS                           S3C_HSOTG_REG(0x828)
+#define S3C_DVBUSPULSE                         S3C_HSOTG_REG(0x82C)
+
+#define S3C_DIEPCTL0                           S3C_HSOTG_REG(0x900)
+#define S3C_DOEPCTL0                           S3C_HSOTG_REG(0xB00)
+#define S3C_DIEPCTL(_a)                                S3C_HSOTG_REG(0x900 + ((_a) * 0x20))
+#define S3C_DOEPCTL(_a)                                S3C_HSOTG_REG(0xB00 + ((_a) * 0x20))
+
+/* EP0 specialness:
+ * bits[29..28] - reserved (no SetD0PID, SetD1PID)
+ * bits[25..22] - should always be zero, this isn't a periodic endpoint
+ * bits[10..0] - MPS setting differenct for EP0
+*/
+#define S3C_D0EPCTL_MPS_MASK                   (0x3 << 0)
+#define S3C_D0EPCTL_MPS_SHIFT                  (0)
+#define S3C_D0EPCTL_MPS_64                     (0x0 << 0)
+#define S3C_D0EPCTL_MPS_32                     (0x1 << 0)
+#define S3C_D0EPCTL_MPS_16                     (0x2 << 0)
+#define S3C_D0EPCTL_MPS_8                      (0x3 << 0)
+
+#define S3C_DxEPCTL_EPEna                      (1 << 31)
+#define S3C_DxEPCTL_EPDis                      (1 << 30)
+#define S3C_DxEPCTL_SetD1PID                   (1 << 29)
+#define S3C_DxEPCTL_SetOddFr                   (1 << 29)
+#define S3C_DxEPCTL_SetD0PID                   (1 << 28)
+#define S3C_DxEPCTL_SetEvenFr                  (1 << 28)
+#define S3C_DxEPCTL_SNAK                       (1 << 27)
+#define S3C_DxEPCTL_CNAK                       (1 << 26)
+#define S3C_DxEPCTL_TxFNum_MASK                        (0xf << 22)
+#define S3C_DxEPCTL_TxFNum_SHIFT               (22)
+#define S3C_DxEPCTL_TxFNum_LIMIT               (0xf)
+#define S3C_DxEPCTL_TxFNum(_x)                 ((_x) << 22)
+
+#define S3C_DxEPCTL_Stall                      (1 << 21)
+#define S3C_DxEPCTL_Snp                                (1 << 20)
+#define S3C_DxEPCTL_EPType_MASK                        (0x3 << 18)
+#define S3C_DxEPCTL_EPType_SHIFT               (18)
+#define S3C_DxEPCTL_EPType_Control             (0x0 << 18)
+#define S3C_DxEPCTL_EPType_Iso                 (0x1 << 18)
+#define S3C_DxEPCTL_EPType_Bulk                        (0x2 << 18)
+#define S3C_DxEPCTL_EPType_Intterupt           (0x3 << 18)
+
+#define S3C_DxEPCTL_NAKsts                     (1 << 17)
+#define S3C_DxEPCTL_DPID                       (1 << 16)
+#define S3C_DxEPCTL_EOFrNum                    (1 << 16)
+#define S3C_DxEPCTL_USBActEp                   (1 << 15)
+#define S3C_DxEPCTL_NextEp_MASK                        (0xf << 11)
+#define S3C_DxEPCTL_NextEp_SHIFT               (11)
+#define S3C_DxEPCTL_NextEp_LIMIT               (0xf)
+#define S3C_DxEPCTL_NextEp(_x)                 ((_x) << 11)
+
+#define S3C_DxEPCTL_MPS_MASK                   (0x7ff << 0)
+#define S3C_DxEPCTL_MPS_SHIFT                  (0)
+#define S3C_DxEPCTL_MPS_LIMIT                  (0x7ff)
+#define S3C_DxEPCTL_MPS(_x)                    ((_x) << 0)
+
+#define S3C_DIEPINT(_a)                                S3C_HSOTG_REG(0x908 + ((_a) * 0x20))
+#define S3C_DOEPINT(_a)                                S3C_HSOTG_REG(0xB08 + ((_a) * 0x20))
+
+#define S3C_DxEPINT_INEPNakEff                 (1 << 6)
+#define S3C_DxEPINT_Back2BackSetup             (1 << 6)
+#define S3C_DxEPINT_INTknEPMis                 (1 << 5)
+#define S3C_DxEPINT_INTknTXFEmp                        (1 << 4)
+#define S3C_DxEPINT_OUTTknEPdis                        (1 << 4)
+#define S3C_DxEPINT_Timeout                    (1 << 3)
+#define S3C_DxEPINT_Setup                      (1 << 3)
+#define S3C_DxEPINT_AHBErr                     (1 << 2)
+#define S3C_DxEPINT_EPDisbld                   (1 << 1)
+#define S3C_DxEPINT_XferCompl                  (1 << 0)
+
+#define S3C_DIEPTSIZ0                          S3C_HSOTG_REG(0x910)
+
+#define S3C_DIEPTSIZ0_PktCnt_MASK              (0x3 << 19)
+#define S3C_DIEPTSIZ0_PktCnt_SHIFT             (19)
+#define S3C_DIEPTSIZ0_PktCnt_LIMIT             (0x3)
+#define S3C_DIEPTSIZ0_PktCnt(_x)               ((_x) << 19)
+
+#define S3C_DIEPTSIZ0_XferSize_MASK            (0x7f << 0)
+#define S3C_DIEPTSIZ0_XferSize_SHIFT           (0)
+#define S3C_DIEPTSIZ0_XferSize_LIMIT           (0x7f)
+#define S3C_DIEPTSIZ0_XferSize(_x)             ((_x) << 0)
+
+
+#define DOEPTSIZ0                              S3C_HSOTG_REG(0xB10)
+#define S3C_DOEPTSIZ0_SUPCnt_MASK              (0x3 << 29)
+#define S3C_DOEPTSIZ0_SUPCnt_SHIFT             (29)
+#define S3C_DOEPTSIZ0_SUPCnt_LIMIT             (0x3)
+#define S3C_DOEPTSIZ0_SUPCnt(_x)               ((_x) << 29)
+
+#define S3C_DOEPTSIZ0_PktCnt                   (1 << 19)
+#define S3C_DOEPTSIZ0_XferSize_MASK            (0x7f << 0)
+#define S3C_DOEPTSIZ0_XferSize_SHIFT           (0)
+
+#define S3C_DIEPTSIZ(_a)                       S3C_HSOTG_REG(0x910 + ((_a) * 0x20))
+#define S3C_DOEPTSIZ(_a)                       S3C_HSOTG_REG(0xB10 + ((_a) * 0x20))
+
+#define S3C_DxEPTSIZ_MC_MASK                   (0x3 << 29)
+#define S3C_DxEPTSIZ_MC_SHIFT                  (29)
+#define S3C_DxEPTSIZ_MC_LIMIT                  (0x3)
+#define S3C_DxEPTSIZ_MC(_x)                    ((_x) << 29)
+
+#define S3C_DxEPTSIZ_PktCnt_MASK               (0x3ff << 19)
+#define S3C_DxEPTSIZ_PktCnt_SHIFT              (19)
+#define S3C_DxEPTSIZ_PktCnt_GET(_v)            (((_v) >> 19) & 0x3ff)
+#define S3C_DxEPTSIZ_PktCnt_LIMIT              (0x3ff)
+#define S3C_DxEPTSIZ_PktCnt(_x)                        ((_x) << 19)
+
+#define S3C_DxEPTSIZ_XferSize_MASK             (0x7ffff << 0)
+#define S3C_DxEPTSIZ_XferSize_SHIFT            (0)
+#define S3C_DxEPTSIZ_XferSize_GET(_v)          (((_v) >> 0) & 0x7ffff)
+#define S3C_DxEPTSIZ_XferSize_LIMIT            (0x7ffff)
+#define S3C_DxEPTSIZ_XferSize(_x)              ((_x) << 0)
+
+
+#define S3C_DIEPDMA(_a)                                S3C_HSOTG_REG(0x914 + ((_a) * 0x20))
+#define S3C_DOEPDMA(_a)                                S3C_HSOTG_REG(0xB14 + ((_a) * 0x20))
+
+#define S3C_EPFIFO(_a)                         S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000))
+
+#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_H */
index 1a91bf9687af969c69f26c8e67fca08747c0d426..07bbb9b3b93fe1a46ba4e15dcd4d309d31b54ba9 100644 (file)
 #include <asm/setup.h>
 #include "pci.h"
 
+const char *pci_power_names[] = {
+       "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
+};
+EXPORT_SYMBOL_GPL(pci_power_names);
+
 unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT;
 
 #ifdef CONFIG_PCI_DOMAINS
index 9c62f787cc9cde11ed7b866a9c53e8f82c5eff18..39d0926d1a9015394043091929bfdbce0ce1ed28 100644 (file)
@@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_serial *serial)
        return 0;
 }
 
-static void ATEN2011_shutdown(struct usb_serial *serial)
+static void ATEN2011_release(struct usb_serial *serial)
 {
        int i;
        struct ATENINTL_port *ATEN2011_port;
@@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_serial_driver = {
        .tiocmget =             ATEN2011_tiocmget,
        .tiocmset =             ATEN2011_tiocmset,
        .attach =               ATEN2011_startup,
-       .shutdown =             ATEN2011_shutdown,
+       .release =              ATEN2011_release,
        .read_bulk_callback =   ATEN2011_bulk_in_callback,
        .read_int_callback =    ATEN2011_interrupt_callback,
 };
index 5eee3f82be5d98d0cb1df531c9d78573c482ddbc..dcd49f1e96d006d1abb04271687d90f0f52a9ef5 100644 (file)
@@ -64,6 +64,7 @@ config USB_ARCH_HAS_EHCI
 config USB
        tristate "Support for Host-side USB"
        depends on USB_ARCH_HAS_HCD
+       select NLS  # for UTF-8 strings
        ---help---
          Universal Serial Bus (USB) is a specification for a serial bus
          subsystem which offers higher speeds and more features than the
index 0a3dc5ece634ba641167a738a1aed2cbe793e269..19cb7d5480d78636f3eb036dbd77c37fc99a16d9 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
 obj-$(CONFIG_USB_OHCI_HCD)     += host/
 obj-$(CONFIG_USB_UHCI_HCD)     += host/
 obj-$(CONFIG_USB_FHCI_HCD)     += host/
+obj-$(CONFIG_USB_XHCI_HCD)     += host/
 obj-$(CONFIG_USB_SL811_HCD)    += host/
 obj-$(CONFIG_USB_U132_HCD)     += host/
 obj-$(CONFIG_USB_R8A66597_HCD) += host/
index ddeb6919253734ebcb3b7a92b6294b30bb305d52..38bfdb0f666058061081e6035512843cb0eaa850 100644 (file)
@@ -937,9 +937,9 @@ static int acm_probe(struct usb_interface *intf,
        int buflen = intf->altsetting->extralen;
        struct usb_interface *control_interface;
        struct usb_interface *data_interface;
-       struct usb_endpoint_descriptor *epctrl;
-       struct usb_endpoint_descriptor *epread;
-       struct usb_endpoint_descriptor *epwrite;
+       struct usb_endpoint_descriptor *epctrl = NULL;
+       struct usb_endpoint_descriptor *epread = NULL;
+       struct usb_endpoint_descriptor *epwrite = NULL;
        struct usb_device *usb_dev = interface_to_usbdev(intf);
        struct acm *acm;
        int minor;
@@ -952,6 +952,7 @@ static int acm_probe(struct usb_interface *intf,
        unsigned long quirks;
        int num_rx_buf;
        int i;
+       int combined_interfaces = 0;
 
        /* normal quirks */
        quirks = (unsigned long)id->driver_info;
@@ -1033,9 +1034,15 @@ next_desc:
                        data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
                        control_interface = intf;
                } else {
-                       dev_dbg(&intf->dev,
-                                       "No union descriptor, giving up\n");
-                       return -ENODEV;
+                       if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
+                               dev_dbg(&intf->dev,"No union descriptor, giving up\n");
+                               return -ENODEV;
+                       } else {
+                               dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
+                               combined_interfaces = 1;
+                               control_interface = data_interface = intf;
+                               goto look_for_collapsed_interface;
+                       }
                }
        } else {
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
@@ -1049,6 +1056,36 @@ next_desc:
        if (data_interface_num != call_interface_num)
                dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
 
+       if (control_interface == data_interface) {
+               /* some broken devices designed for windows work this way */
+               dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
+               combined_interfaces = 1;
+               /* a popular other OS doesn't use it */
+               quirks |= NO_CAP_LINE;
+               if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
+                       dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
+                       return -EINVAL;
+               }
+look_for_collapsed_interface:
+               for (i = 0; i < 3; i++) {
+                       struct usb_endpoint_descriptor *ep;
+                       ep = &data_interface->cur_altsetting->endpoint[i].desc;
+
+                       if (usb_endpoint_is_int_in(ep))
+                               epctrl = ep;
+                       else if (usb_endpoint_is_bulk_out(ep))
+                               epwrite = ep;
+                       else if (usb_endpoint_is_bulk_in(ep))
+                               epread = ep;
+                       else
+                               return -EINVAL;
+               }
+               if (!epctrl || !epread || !epwrite)
+                       return -ENODEV;
+               else
+                       goto made_compressed_probe;
+       }
+
 skip_normal_probe:
 
        /*workaround for switched interfaces */
@@ -1068,10 +1105,11 @@ skip_normal_probe:
        }
 
        /* Accept probe requests only for the control interface */
-       if (intf != control_interface)
+       if (!combined_interfaces && intf != control_interface)
                return -ENODEV;
 
-       if (usb_interface_claimed(data_interface)) { /* valid in this context */
+       if (!combined_interfaces && usb_interface_claimed(data_interface)) {
+               /* valid in this context */
                dev_dbg(&intf->dev, "The data interface isn't available\n");
                return -EBUSY;
        }
@@ -1095,6 +1133,7 @@ skip_normal_probe:
                epread = epwrite;
                epwrite = t;
        }
+made_compressed_probe:
        dbg("interfaces are valid");
        for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
 
@@ -1112,12 +1151,15 @@ skip_normal_probe:
        ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
        readsize = le16_to_cpu(epread->wMaxPacketSize) *
                                (quirks == SINGLE_RX_URB ? 1 : 2);
+       acm->combined_interfaces = combined_interfaces;
        acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
        acm->control = control_interface;
        acm->data = data_interface;
        acm->minor = minor;
        acm->dev = usb_dev;
        acm->ctrl_caps = ac_management_function;
+       if (quirks & NO_CAP_LINE)
+               acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
        acm->ctrlsize = ctrlsize;
        acm->readsize = readsize;
        acm->rx_buflimit = num_rx_buf;
@@ -1223,9 +1265,10 @@ skip_normal_probe:
 
 skip_countries:
        usb_fill_int_urb(acm->ctrlurb, usb_dev,
-                       usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
-                       acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
-                       epctrl->bInterval);
+                        usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
+                        acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
+                        /* works around buggy devices */
+                        epctrl->bInterval ? epctrl->bInterval : 0xff);
        acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        acm->ctrlurb->transfer_dma = acm->ctrl_dma;
 
@@ -1312,7 +1355,8 @@ static void acm_disconnect(struct usb_interface *intf)
                                                                acm->ctrl_dma);
        acm_read_buffers_free(acm);
 
-       usb_driver_release_interface(&acm_driver, intf == acm->control ?
+       if (!acm->combined_interfaces)
+               usb_driver_release_interface(&acm_driver, intf == acm->control ?
                                        acm->data : acm->control);
 
        if (acm->port.count == 0) {
@@ -1451,6 +1495,9 @@ static struct usb_device_id acm_ids[] = {
                                           Maybe we should define a new
                                           quirk for this. */
        },
+       { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
+       .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+       },
 
        /* control interfaces with various AT-command sets */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
index 4c3856420adde6c55761950e0697822d9e496d1b..1602324808bafb510e374520b9eb0a264617dc02 100644 (file)
@@ -125,6 +125,7 @@ struct acm {
        unsigned char clocal;                           /* termios CLOCAL */
        unsigned int ctrl_caps;                         /* control capabilities from the class specific header */
        unsigned int susp_count;                        /* number of suspended interfaces */
+       int combined_interfaces:1;                      /* control and data collapsed */
        struct acm_wb *delayed_wb;                      /* write queued for a device about to be woken */
 };
 
@@ -133,3 +134,4 @@ struct acm {
 /* constants describing various quirks and errors */
 #define NO_UNION_NORMAL                        1
 #define SINGLE_RX_URB                  2
+#define NO_CAP_LINE                    4
index c40a9b284cc94217d9a45c1b09d838904571fa67..3703789d0d2af8a7459138adb5903c548e704425 100644 (file)
@@ -927,21 +927,27 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        switch (cmd) {
        case USBTMC_IOCTL_CLEAR_OUT_HALT:
                retval = usbtmc_ioctl_clear_out_halt(data);
+               break;
 
        case USBTMC_IOCTL_CLEAR_IN_HALT:
                retval = usbtmc_ioctl_clear_in_halt(data);
+               break;
 
        case USBTMC_IOCTL_INDICATOR_PULSE:
                retval = usbtmc_ioctl_indicator_pulse(data);
+               break;
 
        case USBTMC_IOCTL_CLEAR:
                retval = usbtmc_ioctl_clear(data);
+               break;
 
        case USBTMC_IOCTL_ABORT_BULK_OUT:
                retval = usbtmc_ioctl_abort_bulk_out(data);
+               break;
 
        case USBTMC_IOCTL_ABORT_BULK_IN:
                retval = usbtmc_ioctl_abort_bulk_in(data);
+               break;
        }
 
        mutex_unlock(&data->io_mutex);
index e1759d17ac5d17ca9cafeea4d67094954af07462..69280c35b5cbc6d93cc50c99d7f4a7f747c1fc99 100644 (file)
@@ -28,7 +28,7 @@ comment "Miscellaneous USB options"
        depends on USB
 
 config USB_DEVICEFS
-       bool "USB device filesystem"
+       bool "USB device filesystem (DEPRECATED)" if EMBEDDED
        depends on USB
        ---help---
          If you say Y here (and to "/proc file system support" in the "File
@@ -46,11 +46,15 @@ config USB_DEVICEFS
          For the format of the various /proc/bus/usb/ files, please read
          <file:Documentation/usb/proc_usb_info.txt>.
 
-         Usbfs files can't handle Access Control Lists (ACL), which are the
-         default way to grant access to USB devices for untrusted users of a
-         desktop system. The usbfs functionality is replaced by real
-         device-nodes managed by udev. These nodes live in /dev/bus/usb and
-         are used by libusb.
+         Modern Linux systems do not use this.
+
+         Usbfs entries are files and not character devices; usbfs can't
+         handle Access Control Lists (ACL) which are the default way to
+         grant access to USB devices for untrusted users of a desktop
+         system.
+
+         The usbfs functionality is replaced by real device-nodes managed by
+         udev.  These nodes lived in /dev/bus/usb and are used by libusb.
 
 config USB_DEVICE_CLASS
        bool "USB device class-devices (DEPRECATED)"
index b6078706fb939d7f1d3aeb9466f6498f8857f366..ec16e60299050ab046f9a02dfb15e0207e264516 100644 (file)
@@ -4,14 +4,14 @@
 
 usbcore-objs   := usb.o hub.o hcd.o urb.o message.o driver.o \
                        config.o file.o buffer.o sysfs.o endpoint.o \
-                       devio.o notify.o generic.o quirks.o
+                       devio.o notify.o generic.o quirks.o devices.o
 
 ifeq ($(CONFIG_PCI),y)
        usbcore-objs    += hcd-pci.o
 endif
 
 ifeq ($(CONFIG_USB_DEVICEFS),y)
-       usbcore-objs    += inode.o devices.o
+       usbcore-objs    += inode.o
 endif
 
 obj-$(CONFIG_USB)      += usbcore.o
index 568244c99bdc0f5d7b0e03d2177d1153e9d49d49..24dfb33f90cb0fbc2268572ee0ed90ecc15efbb5 100644 (file)
@@ -19,6 +19,32 @@ static inline const char *plural(int n)
        return (n == 1 ? "" : "s");
 }
 
+/* FIXME: this is a kludge */
+static int find_next_descriptor_more(unsigned char *buffer, int size,
+    int dt1, int dt2, int dt3, int *num_skipped)
+{
+       struct usb_descriptor_header *h;
+       int n = 0;
+       unsigned char *buffer0 = buffer;
+
+       /* Find the next descriptor of type dt1 or dt2 or dt3 */
+       while (size > 0) {
+               h = (struct usb_descriptor_header *) buffer;
+               if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 ||
+                               h->bDescriptorType == dt3)
+                       break;
+               buffer += h->bLength;
+               size -= h->bLength;
+               ++n;
+       }
+
+       /* Store the number of descriptors skipped and return the
+        * number of bytes skipped */
+       if (num_skipped)
+               *num_skipped = n;
+       return buffer - buffer0;
+}
+
 static int find_next_descriptor(unsigned char *buffer, int size,
     int dt1, int dt2, int *num_skipped)
 {
@@ -43,6 +69,129 @@ static int find_next_descriptor(unsigned char *buffer, int size,
        return buffer - buffer0;
 }
 
+static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
+               int inum, int asnum, struct usb_host_endpoint *ep,
+               int num_ep, unsigned char *buffer, int size)
+{
+       unsigned char *buffer_start = buffer;
+       struct usb_ss_ep_comp_descriptor        *desc;
+       int retval;
+       int num_skipped;
+       int max_tx;
+       int i;
+
+       /* Allocate space for the SS endpoint companion descriptor */
+       ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp),
+                       GFP_KERNEL);
+       if (!ep->ss_ep_comp)
+               return -ENOMEM;
+       desc = (struct usb_ss_ep_comp_descriptor *) buffer;
+       if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
+               dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
+                               " interface %d altsetting %d ep %d: "
+                               "using minimum values\n",
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE;
+               ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
+               ep->ss_ep_comp->desc.bMaxBurst = 0;
+               /*
+                * Leave bmAttributes as zero, which will mean no streams for
+                * bulk, and isoc won't support multiple bursts of packets.
+                * With bursts of only one packet, and a Mult of 1, the max
+                * amount of data moved per endpoint service interval is one
+                * packet.
+                */
+               if (usb_endpoint_xfer_isoc(&ep->desc) ||
+                               usb_endpoint_xfer_int(&ep->desc))
+                       ep->ss_ep_comp->desc.wBytesPerInterval =
+                               ep->desc.wMaxPacketSize;
+               /*
+                * The next descriptor is for an Endpoint or Interface,
+                * no extra descriptors to copy into the companion structure,
+                * and we didn't eat up any of the buffer.
+                */
+               retval = 0;
+               goto valid;
+       }
+       memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE);
+       desc = &ep->ss_ep_comp->desc;
+       buffer += desc->bLength;
+       size -= desc->bLength;
+
+       /* Eat up the other descriptors we don't care about */
+       ep->ss_ep_comp->extra = buffer;
+       i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+                       USB_DT_INTERFACE, &num_skipped);
+       ep->ss_ep_comp->extralen = i;
+       buffer += i;
+       size -= i;
+       retval = buffer - buffer_start + i;
+       if (num_skipped > 0)
+               dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+                               num_skipped, plural(num_skipped),
+                               "SuperSpeed endpoint companion");
+
+       /* Check the various values */
+       if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
+               dev_warn(ddev, "Control endpoint with bMaxBurst = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to zero\n", desc->bMaxBurst,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bMaxBurst = 0;
+       }
+       if (desc->bMaxBurst > 15) {
+               dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to 15\n", desc->bMaxBurst,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bMaxBurst = 15;
+       }
+       if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc))
+                       && desc->bmAttributes != 0) {
+               dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to zero\n",
+                               usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
+                               desc->bmAttributes,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 0;
+       }
+       if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) {
+               dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to max\n",
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 16;
+       }
+       if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) {
+               dev_warn(ddev, "Isoc endpoint has Mult of %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to 3\n", desc->bmAttributes + 1,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress);
+               desc->bmAttributes = 2;
+       }
+       if (usb_endpoint_xfer_isoc(&ep->desc)) {
+               max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
+                       (desc->bmAttributes + 1);
+       } else if (usb_endpoint_xfer_int(&ep->desc)) {
+               max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
+       } else {
+               goto valid;
+       }
+       if (desc->wBytesPerInterval > max_tx) {
+               dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
+                               "config %d interface %d altsetting %d ep %d: "
+                               "setting to %d\n",
+                               usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
+                               desc->wBytesPerInterval,
+                               cfgno, inum, asnum, ep->desc.bEndpointAddress,
+                               max_tx);
+               desc->wBytesPerInterval = max_tx;
+       }
+valid:
+       return retval;
+}
+
 static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
     int asnum, struct usb_host_interface *ifp, int num_ep,
     unsigned char *buffer, int size)
@@ -50,7 +199,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        unsigned char *buffer0 = buffer;
        struct usb_endpoint_descriptor *d;
        struct usb_host_endpoint *endpoint;
-       int n, i, j;
+       int n, i, j, retval;
 
        d = (struct usb_endpoint_descriptor *) buffer;
        buffer += d->bLength;
@@ -92,6 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        if (usb_endpoint_xfer_int(d)) {
                i = 1;
                switch (to_usb_device(ddev)->speed) {
+               case USB_SPEED_SUPER:
                case USB_SPEED_HIGH:
                        /* Many device manufacturers are using full-speed
                         * bInterval values in high-speed interrupt endpoint
@@ -161,17 +311,39 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
                                cfgno, inum, asnum, d->bEndpointAddress,
                                maxp);
        }
-
-       /* Skip over any Class Specific or Vendor Specific descriptors;
-        * find the next endpoint or interface descriptor */
-       endpoint->extra = buffer;
-       i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
-           USB_DT_INTERFACE, &n);
-       endpoint->extralen = i;
+       /* Allocate room for and parse any SS endpoint companion descriptors */
+       if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) {
+               endpoint->extra = buffer;
+               i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP,
+                               USB_DT_ENDPOINT, USB_DT_INTERFACE, &n);
+               endpoint->extralen = i;
+               buffer += i;
+               size -= i;
+
+               if (size > 0) {
+                       retval = usb_parse_ss_endpoint_companion(ddev, cfgno,
+                                       inum, asnum, endpoint, num_ep, buffer,
+                                       size);
+                       if (retval >= 0) {
+                               buffer += retval;
+                               retval = buffer - buffer0;
+                       }
+               } else {
+                       retval = buffer - buffer0;
+               }
+       } else {
+               /* Skip over any Class Specific or Vendor Specific descriptors;
+                * find the next endpoint or interface descriptor */
+               endpoint->extra = buffer;
+               i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+                               USB_DT_INTERFACE, &n);
+               endpoint->extralen = i;
+               retval = buffer - buffer0 + i;
+       }
        if (n > 0)
                dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
                    n, plural(n), "endpoint");
-       return buffer - buffer0 + i;
+       return retval;
 
 skip_to_next_endpoint_or_interface_descriptor:
        i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
@@ -452,6 +624,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
                kref_init(&intfc->ref);
        }
 
+       /* FIXME: parse the BOS descriptor */
+
        /* Skip over any Class Specific or Vendor Specific descriptors;
         * find the first interface descriptor */
        config->extra = buffer;
index d0a21a5f82017bf246c62108009b5280c5d1c90b..69e5773abfce2459cd65d11a939f9f6250ab7468 100644 (file)
@@ -154,16 +154,11 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
 static int usb_probe_device(struct device *dev)
 {
        struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
-       struct usb_device *udev;
+       struct usb_device *udev = to_usb_device(dev);
        int error = -ENODEV;
 
        dev_dbg(dev, "%s\n", __func__);
 
-       if (!is_usb_device(dev))        /* Sanity check */
-               return error;
-
-       udev = to_usb_device(dev);
-
        /* TODO: Add real matching code */
 
        /* The device should always appear to be in use
@@ -203,18 +198,13 @@ static void usb_cancel_queued_reset(struct usb_interface *iface)
 static int usb_probe_interface(struct device *dev)
 {
        struct usb_driver *driver = to_usb_driver(dev->driver);
-       struct usb_interface *intf;
-       struct usb_device *udev;
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
        const struct usb_device_id *id;
        int error = -ENODEV;
 
        dev_dbg(dev, "%s\n", __func__);
 
-       if (is_usb_device(dev))         /* Sanity check */
-               return error;
-
-       intf = to_usb_interface(dev);
-       udev = interface_to_usbdev(intf);
        intf->needs_binding = 0;
 
        if (udev->authorized == 0) {
@@ -385,7 +375,6 @@ void usb_driver_release_interface(struct usb_driver *driver,
                                        struct usb_interface *iface)
 {
        struct device *dev = &iface->dev;
-       struct usb_device *udev = interface_to_usbdev(iface);
 
        /* this should never happen, don't release something that's not ours */
        if (!dev->driver || dev->driver != &driver->drvwrap.driver)
@@ -394,23 +383,19 @@ void usb_driver_release_interface(struct usb_driver *driver,
        /* don't release from within disconnect() */
        if (iface->condition != USB_INTERFACE_BOUND)
                return;
+       iface->condition = USB_INTERFACE_UNBINDING;
 
-       /* don't release if the interface hasn't been added yet */
+       /* Release via the driver core only if the interface
+        * has already been registered
+        */
        if (device_is_registered(dev)) {
-               iface->condition = USB_INTERFACE_UNBINDING;
                device_release_driver(dev);
        } else {
-               iface->condition = USB_INTERFACE_UNBOUND;
-               usb_cancel_queued_reset(iface);
+               down(&dev->sem);
+               usb_unbind_interface(dev);
+               dev->driver = NULL;
+               up(&dev->sem);
        }
-       dev->driver = NULL;
-       usb_set_intfdata(iface, NULL);
-
-       usb_pm_lock(udev);
-       iface->condition = USB_INTERFACE_UNBOUND;
-       mark_quiesced(iface);
-       iface->needs_remote_wakeup = 0;
-       usb_pm_unlock(udev);
 }
 EXPORT_SYMBOL_GPL(usb_driver_release_interface);
 
@@ -598,7 +583,7 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
                /* TODO: Add real matching code */
                return 1;
 
-       } else {
+       } else if (is_usb_interface(dev)) {
                struct usb_interface *intf;
                struct usb_driver *usb_drv;
                const struct usb_device_id *id;
@@ -630,11 +615,14 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
        /* driver is often null here; dev_dbg() would oops */
        pr_debug("usb %s: uevent\n", dev_name(dev));
 
-       if (is_usb_device(dev))
+       if (is_usb_device(dev)) {
                usb_dev = to_usb_device(dev);
-       else {
+       } else if (is_usb_interface(dev)) {
                struct usb_interface *intf = to_usb_interface(dev);
+
                usb_dev = interface_to_usbdev(intf);
+       } else {
+               return 0;
        }
 
        if (usb_dev->devnum < 0) {
@@ -1762,6 +1750,7 @@ int usb_suspend(struct device *dev, pm_message_t msg)
 int usb_resume(struct device *dev, pm_message_t msg)
 {
        struct usb_device       *udev;
+       int                     status;
 
        udev = to_usb_device(dev);
 
@@ -1771,7 +1760,14 @@ int usb_resume(struct device *dev, pm_message_t msg)
         */
        if (udev->skip_sys_resume)
                return 0;
-       return usb_external_resume_device(udev, msg);
+       status = usb_external_resume_device(udev, msg);
+
+       /* Avoid PM error messages for devices disconnected while suspended
+        * as we'll display regular disconnect messages just a bit later.
+        */
+       if (status == -ENODEV)
+               return 0;
+       return status;
 }
 
 #endif /* CONFIG_PM */
index 40dee2ac0133740ee97e37932a6181247b94e22a..bc39fc40bbde20781d2467ec227ae48a9bdf46f0 100644 (file)
 #include <linux/usb.h>
 #include "usb.h"
 
-#define MAX_ENDPOINT_MINORS (64*128*32)
-static int usb_endpoint_major;
-static DEFINE_IDR(endpoint_idr);
-
 struct ep_device {
        struct usb_endpoint_descriptor *desc;
        struct usb_device *udev;
        struct device dev;
-       int minor;
 };
 #define to_ep_device(_dev) \
        container_of(_dev, struct ep_device, dev)
 
+struct device_type usb_ep_device_type = {
+       .name =         "usb_endpoint",
+};
+
 struct ep_attribute {
        struct attribute attr;
        ssize_t (*show)(struct usb_device *,
@@ -160,118 +159,10 @@ static struct attribute_group *ep_dev_groups[] = {
        NULL
 };
 
-static int usb_endpoint_major_init(void)
-{
-       dev_t dev;
-       int error;
-
-       error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
-                                   "usb_endpoint");
-       if (error) {
-               printk(KERN_ERR "Unable to get a dynamic major for "
-                      "usb endpoints.\n");
-               return error;
-       }
-       usb_endpoint_major = MAJOR(dev);
-
-       return error;
-}
-
-static void usb_endpoint_major_cleanup(void)
-{
-       unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
-                                MAX_ENDPOINT_MINORS);
-}
-
-static int endpoint_get_minor(struct ep_device *ep_dev)
-{
-       static DEFINE_MUTEX(minor_lock);
-       int retval = -ENOMEM;
-       int id;
-
-       mutex_lock(&minor_lock);
-       if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
-               goto exit;
-
-       retval = idr_get_new(&endpoint_idr, ep_dev, &id);
-       if (retval < 0) {
-               if (retval == -EAGAIN)
-                       retval = -ENOMEM;
-               goto exit;
-       }
-       ep_dev->minor = id & MAX_ID_MASK;
-exit:
-       mutex_unlock(&minor_lock);
-       return retval;
-}
-
-static void endpoint_free_minor(struct ep_device *ep_dev)
-{
-       idr_remove(&endpoint_idr, ep_dev->minor);
-}
-
-static struct endpoint_class {
-       struct kref kref;
-       struct class *class;
-} *ep_class;
-
-static int init_endpoint_class(void)
-{
-       int result = 0;
-
-       if (ep_class != NULL) {
-               kref_get(&ep_class->kref);
-               goto exit;
-       }
-
-       ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
-       if (!ep_class) {
-               result = -ENOMEM;
-               goto exit;
-       }
-
-       kref_init(&ep_class->kref);
-       ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
-       if (IS_ERR(ep_class->class)) {
-               result = PTR_ERR(ep_class->class);
-               goto class_create_error;
-       }
-
-       result = usb_endpoint_major_init();
-       if (result)
-               goto endpoint_major_error;
-
-       goto exit;
-
-endpoint_major_error:
-       class_destroy(ep_class->class);
-class_create_error:
-       kfree(ep_class);
-       ep_class = NULL;
-exit:
-       return result;
-}
-
-static void release_endpoint_class(struct kref *kref)
-{
-       /* Ok, we cheat as we know we only have one ep_class */
-       class_destroy(ep_class->class);
-       kfree(ep_class);
-       ep_class = NULL;
-       usb_endpoint_major_cleanup();
-}
-
-static void destroy_endpoint_class(void)
-{
-       if (ep_class)
-               kref_put(&ep_class->kref, release_endpoint_class);
-}
-
 static void ep_device_release(struct device *dev)
 {
        struct ep_device *ep_dev = to_ep_device(dev);
 
-       endpoint_free_minor(ep_dev);
        kfree(ep_dev);
 }
 
@@ -279,62 +170,32 @@ int usb_create_ep_devs(struct device *parent,
                        struct usb_host_endpoint *endpoint,
                        struct usb_device *udev)
 {
-       char name[8];
        struct ep_device *ep_dev;
        int retval;
 
-       retval = init_endpoint_class();
-       if (retval)
-               goto exit;
-
        ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
        if (!ep_dev) {
                retval = -ENOMEM;
-               goto error_alloc;
-       }
-
-       retval = endpoint_get_minor(ep_dev);
-       if (retval) {
-               dev_err(parent, "can not allocate minor number for %s\n",
-                       dev_name(&ep_dev->dev));
-               goto error_register;
+               goto exit;
        }
 
        ep_dev->desc = &endpoint->desc;
        ep_dev->udev = udev;
        ep_dev->dev.groups = ep_dev_groups;
-       ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
-       ep_dev->dev.class = ep_class->class;
+       ep_dev->dev.type = &usb_ep_device_type;
        ep_dev->dev.parent = parent;
        ep_dev->dev.release = ep_device_release;
-       dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
-                udev->bus->busnum, udev->devnum,
-                endpoint->desc.bEndpointAddress);
+       dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
 
        retval = device_register(&ep_dev->dev);
        if (retval)
-               goto error_chrdev;
+               goto error_register;
 
-       /* create the symlink to the old-style "ep_XX" directory */
-       sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-       retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
-       if (retval)
-               goto error_link;
        endpoint->ep_dev = ep_dev;
        return retval;
 
-error_link:
-       device_unregister(&ep_dev->dev);
-       destroy_endpoint_class();
-       return retval;
-
-error_chrdev:
-       endpoint_free_minor(ep_dev);
-
 error_register:
        kfree(ep_dev);
-error_alloc:
-       destroy_endpoint_class();
 exit:
        return retval;
 }
@@ -344,12 +205,7 @@ void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
        struct ep_device *ep_dev = endpoint->ep_dev;
 
        if (ep_dev) {
-               char name[8];
-
-               sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-               sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
                device_unregister(&ep_dev->dev);
                endpoint->ep_dev = NULL;
-               destroy_endpoint_class();
        }
 }
index a4301dc02d275c1f28c77cde42183d1a2d435792..91f2885b6ee10ca8f6ca396289e0b1ea2ee7060b 100644 (file)
@@ -185,194 +185,198 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
 
-
-#ifdef CONFIG_PM
-
 /**
- * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
- * @dev: USB Host Controller being suspended
- * @message: Power Management message describing this state transition
- *
- * Store this function in the HCD's struct pci_driver as .suspend.
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
  */
-int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
+void usb_hcd_pci_shutdown(struct pci_dev *dev)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = pci_get_drvdata(dev);
+       if (!hcd)
+               return;
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int check_root_hub_suspended(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+
+       if (!(hcd->state == HC_STATE_SUSPENDED ||
+                       hcd->state == HC_STATE_HALT)) {
+               dev_warn(dev, "Root hub is not suspended\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static int hcd_pci_suspend(struct device *dev)
 {
-       struct usb_hcd          *hcd = pci_get_drvdata(dev);
-       int                     retval = 0;
-       int                     wake, w;
-       int                     has_pci_pm;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
        /* Root hub suspend should have stopped all downstream traffic,
         * and all bus master traffic.  And done so for both the interface
         * and the stub usb_device (which we check here).  But maybe it
         * didn't; writing sysfs power/state files ignores such rules...
-        *
-        * We must ignore the FREEZE vs SUSPEND distinction here, because
-        * otherwise the swsusp will save (and restore) garbage state.
         */
-       if (!(hcd->state == HC_STATE_SUSPENDED ||
-                       hcd->state == HC_STATE_HALT)) {
-               dev_warn(&dev->dev, "Root hub is not suspended\n");
-               retval = -EBUSY;
-               goto done;
-       }
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
        /* We might already be suspended (runtime PM -- not yet written) */
-       if (dev->current_state != PCI_D0)
-               goto done;
+       if (pci_dev->current_state != PCI_D0)
+               return retval;
 
        if (hcd->driver->pci_suspend) {
-               retval = hcd->driver->pci_suspend(hcd, message);
+               retval = hcd->driver->pci_suspend(hcd);
                suspend_report_result(hcd->driver->pci_suspend, retval);
                if (retval)
-                       goto done;
+                       return retval;
        }
 
-       synchronize_irq(dev->irq);
+       synchronize_irq(pci_dev->irq);
 
        /* Downstream ports from this root hub should already be quiesced, so
         * there will be no DMA activity.  Now we can shut down the upstream
-        * link (except maybe for PME# resume signaling) and enter some PCI
-        * low power state, if the hardware allows.
+        * link (except maybe for PME# resume signaling).  We'll enter a
+        * low power state during suspend_noirq, if the hardware allows.
         */
-       pci_disable_device(dev);
+       pci_disable_device(pci_dev);
+       return retval;
+}
+
+static int hcd_pci_suspend_noirq(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
+
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
-       pci_save_state(dev);
+       pci_save_state(pci_dev);
 
-       /* Don't fail on error to enable wakeup.  We rely on pci code
-        * to reject requests the hardware can't implement, rather
-        * than coding the same thing.
+       /* If the root hub is HALTed rather than SUSPENDed,
+        * disallow remote wakeup.
         */
-       wake = (hcd->state == HC_STATE_SUSPENDED &&
-                       device_may_wakeup(&dev->dev));
-       w = pci_wake_from_d3(dev, wake);
-       if (w < 0)
-               wake = w;
-       dev_dbg(&dev->dev, "wakeup: %d\n", wake);
-
-       /* Don't change state if we don't need to */
-       if (message.event == PM_EVENT_FREEZE ||
-                       message.event == PM_EVENT_PRETHAW) {
-               dev_dbg(&dev->dev, "--> no state change\n");
-               goto done;
-       }
+       if (hcd->state == HC_STATE_HALT)
+               device_set_wakeup_enable(dev, 0);
+       dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
 
-       has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-       if (!has_pci_pm) {
-               dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
+       /* Possibly enable remote wakeup,
+        * choose the appropriate low-power state, and go to that state.
+        */
+       retval = pci_prepare_to_sleep(pci_dev);
+       if (retval == -EIO) {           /* Low-power not supported */
+               dev_dbg(dev, "--> PCI D0 legacy\n");
+               retval = 0;
+       } else if (retval == 0) {
+               dev_dbg(dev, "--> PCI %s\n",
+                               pci_power_name(pci_dev->current_state));
        } else {
-
-               /* NOTE:  dev->current_state becomes nonzero only here, and
-                * only for devices that support PCI PM.  Also, exiting
-                * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
-                * some device state (e.g. as part of clock reinit).
-                */
-               retval = pci_set_power_state(dev, PCI_D3hot);
-               suspend_report_result(pci_set_power_state, retval);
-               if (retval == 0) {
-                       dev_dbg(&dev->dev, "--> PCI D3\n");
-               } else {
-                       dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
-                                       retval);
-                       pci_restore_state(dev);
-               }
+               suspend_report_result(pci_prepare_to_sleep, retval);
+               return retval;
        }
 
 #ifdef CONFIG_PPC_PMAC
-       if (retval == 0) {
-               /* Disable ASIC clocks for USB */
-               if (machine_is(powermac)) {
-                       struct device_node      *of_node;
-
-                       of_node = pci_device_to_OF_node(dev);
-                       if (of_node)
-                               pmac_call_feature(PMAC_FTR_USB_ENABLE,
-                                                       of_node, 0, 0);
-               }
+       /* Disable ASIC clocks for USB */
+       if (machine_is(powermac)) {
+               struct device_node      *of_node;
+
+               of_node = pci_device_to_OF_node(pci_dev);
+               if (of_node)
+                       pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
        }
 #endif
-
- done:
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
 
-/**
- * usb_hcd_pci_resume - power management resume of a PCI-based HCD
- * @dev: USB Host Controller being resumed
- *
- * Store this function in the HCD's struct pci_driver as .resume.
- */
-int usb_hcd_pci_resume(struct pci_dev *dev)
+static int hcd_pci_resume_noirq(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-       int                     retval;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
 
 #ifdef CONFIG_PPC_PMAC
        /* Reenable ASIC clocks for USB */
        if (machine_is(powermac)) {
                struct device_node *of_node;
 
-               of_node = pci_device_to_OF_node(dev);
+               of_node = pci_device_to_OF_node(pci_dev);
                if (of_node)
                        pmac_call_feature(PMAC_FTR_USB_ENABLE,
                                                of_node, 0, 1);
        }
 #endif
 
-       pci_restore_state(dev);
+       /* Go back to D0 and disable remote wakeup */
+       pci_back_from_sleep(pci_dev);
+       return 0;
+}
+
+static int resume_common(struct device *dev, bool hibernated)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
-       hcd = pci_get_drvdata(dev);
        if (hcd->state != HC_STATE_SUSPENDED) {
-               dev_dbg(hcd->self.controller,
-                               "can't resume, not suspended!\n");
+               dev_dbg(dev, "can't resume, not suspended!\n");
                return 0;
        }
 
-       pci_enable_wake(dev, PCI_D0, false);
-
-       retval = pci_enable_device(dev);
+       retval = pci_enable_device(pci_dev);
        if (retval < 0) {
-               dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
-                               retval);
+               dev_err(dev, "can't re-enable after resume, %d!\n", retval);
                return retval;
        }
 
-       pci_set_master(dev);
-
-       /* yes, ignore this result too... */
-       (void) pci_wake_from_d3(dev, 0);
+       pci_set_master(pci_dev);
 
        clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
 
        if (hcd->driver->pci_resume) {
-               retval = hcd->driver->pci_resume(hcd);
+               retval = hcd->driver->pci_resume(hcd, hibernated);
                if (retval) {
-                       dev_err(hcd->self.controller,
-                               "PCI post-resume error %d!\n", retval);
+                       dev_err(dev, "PCI post-resume error %d!\n", retval);
                        usb_hc_died(hcd);
                }
        }
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
 
-#endif /* CONFIG_PM */
-
-/**
- * usb_hcd_pci_shutdown - shutdown host controller
- * @dev: USB Host Controller being shutdown
- */
-void usb_hcd_pci_shutdown(struct pci_dev *dev)
+static int hcd_pci_resume(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-
-       hcd = pci_get_drvdata(dev);
-       if (!hcd)
-               return;
+       return resume_common(dev, false);
+}
 
-       if (hcd->driver->shutdown)
-               hcd->driver->shutdown(hcd);
+static int hcd_pci_restore(struct device *dev)
+{
+       return resume_common(dev, true);
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
 
+struct dev_pm_ops usb_hcd_pci_pm_ops = {
+       .suspend        = hcd_pci_suspend,
+       .suspend_noirq  = hcd_pci_suspend_noirq,
+       .resume_noirq   = hcd_pci_resume_noirq,
+       .resume         = hcd_pci_resume,
+       .freeze         = check_root_hub_suspended,
+       .freeze_noirq   = check_root_hub_suspended,
+       .thaw_noirq     = NULL,
+       .thaw           = NULL,
+       .poweroff       = hcd_pci_suspend,
+       .poweroff_noirq = hcd_pci_suspend_noirq,
+       .restore_noirq  = hcd_pci_resume_noirq,
+       .restore        = hcd_pci_restore,
+};
+EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
index 42b93da1085d00ab52da7cd36843510945520d60..ce3f453f02ef5efe5b5034f3bedfa5a062e0db25 100644 (file)
@@ -128,6 +128,27 @@ static inline int is_root_hub(struct usb_device *udev)
 #define KERNEL_REL     ((LINUX_VERSION_CODE >> 16) & 0x0ff)
 #define KERNEL_VER     ((LINUX_VERSION_CODE >> 8) & 0x0ff)
 
+/* usb 3.0 root hub device descriptor */
+static const u8 usb3_rh_dev_descriptor[18] = {
+       0x12,       /*  __u8  bLength; */
+       0x01,       /*  __u8  bDescriptorType; Device */
+       0x00, 0x03, /*  __le16 bcdUSB; v3.0 */
+
+       0x09,       /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  bDeviceSubClass; */
+       0x03,       /*  __u8  bDeviceProtocol; USB 3.0 hub */
+       0x09,       /*  __u8  bMaxPacketSize0; 2^9 = 512 Bytes */
+
+       0x6b, 0x1d, /*  __le16 idVendor; Linux Foundation */
+       0x02, 0x00, /*  __le16 idProduct; device 0x0002 */
+       KERNEL_VER, KERNEL_REL, /*  __le16 bcdDevice */
+
+       0x03,       /*  __u8  iManufacturer; */
+       0x02,       /*  __u8  iProduct; */
+       0x01,       /*  __u8  iSerialNumber; */
+       0x01        /*  __u8  bNumConfigurations; */
+};
+
 /* usb 2.0 root hub device descriptor */
 static const u8 usb2_rh_dev_descriptor [18] = {
        0x12,       /*  __u8  bLength; */
@@ -273,6 +294,47 @@ static const u8 hs_rh_config_descriptor [] = {
        0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
 };
 
+static const u8 ss_rh_config_descriptor[] = {
+       /* one configuration */
+       0x09,       /*  __u8  bLength; */
+       0x02,       /*  __u8  bDescriptorType; Configuration */
+       0x19, 0x00, /*  __le16 wTotalLength; FIXME */
+       0x01,       /*  __u8  bNumInterfaces; (1) */
+       0x01,       /*  __u8  bConfigurationValue; */
+       0x00,       /*  __u8  iConfiguration; */
+       0xc0,       /*  __u8  bmAttributes;
+                                Bit 7: must be set,
+                                    6: Self-powered,
+                                    5: Remote wakeup,
+                                    4..0: resvd */
+       0x00,       /*  __u8  MaxPower; */
+
+       /* one interface */
+       0x09,       /*  __u8  if_bLength; */
+       0x04,       /*  __u8  if_bDescriptorType; Interface */
+       0x00,       /*  __u8  if_bInterfaceNumber; */
+       0x00,       /*  __u8  if_bAlternateSetting; */
+       0x01,       /*  __u8  if_bNumEndpoints; */
+       0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,       /*  __u8  if_bInterfaceSubClass; */
+       0x00,       /*  __u8  if_bInterfaceProtocol; */
+       0x00,       /*  __u8  if_iInterface; */
+
+       /* one endpoint (status change endpoint) */
+       0x07,       /*  __u8  ep_bLength; */
+       0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+                   /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
+                    * see hub.c:hub_configure() for details. */
+       (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
+       0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
+       /*
+        * All 3.0 hubs should have an endpoint companion descriptor,
+        * but we're ignoring that for now.  FIXME?
+        */
+};
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -426,23 +488,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
                switch (wValue & 0xff00) {
                case USB_DT_DEVICE << 8:
-                       if (hcd->driver->flags & HCD_USB2)
+                       switch (hcd->driver->flags & HCD_MASK) {
+                       case HCD_USB3:
+                               bufp = usb3_rh_dev_descriptor;
+                               break;
+                       case HCD_USB2:
                                bufp = usb2_rh_dev_descriptor;
-                       else if (hcd->driver->flags & HCD_USB11)
+                               break;
+                       case HCD_USB11:
                                bufp = usb11_rh_dev_descriptor;
-                       else
+                               break;
+                       default:
                                goto error;
+                       }
                        len = 18;
                        if (hcd->has_tt)
                                patch_protocol = 1;
                        break;
                case USB_DT_CONFIG << 8:
-                       if (hcd->driver->flags & HCD_USB2) {
+                       switch (hcd->driver->flags & HCD_MASK) {
+                       case HCD_USB3:
+                               bufp = ss_rh_config_descriptor;
+                               len = sizeof ss_rh_config_descriptor;
+                               break;
+                       case HCD_USB2:
                                bufp = hs_rh_config_descriptor;
                                len = sizeof hs_rh_config_descriptor;
-                       } else {
+                               break;
+                       case HCD_USB11:
                                bufp = fs_rh_config_descriptor;
                                len = sizeof fs_rh_config_descriptor;
+                               break;
+                       default:
+                               goto error;
                        }
                        if (device_can_wakeup(&hcd->self.root_hub->dev))
                                patch_wakeup = 1;
@@ -755,23 +833,6 @@ static struct attribute_group usb_bus_attr_group = {
 
 /*-------------------------------------------------------------------------*/
 
-static struct class *usb_host_class;
-
-int usb_host_init(void)
-{
-       int retval = 0;
-
-       usb_host_class = class_create(THIS_MODULE, "usb_host");
-       if (IS_ERR(usb_host_class))
-               retval = PTR_ERR(usb_host_class);
-       return retval;
-}
-
-void usb_host_cleanup(void)
-{
-       class_destroy(usb_host_class);
-}
-
 /**
  * usb_bus_init - shared initialization code
  * @bus: the bus structure being initialized
@@ -818,12 +879,6 @@ static int usb_register_bus(struct usb_bus *bus)
        set_bit (busnum, busmap.busmap);
        bus->busnum = busnum;
 
-       bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),
-                                bus, "usb_host%d", busnum);
-       result = PTR_ERR(bus->dev);
-       if (IS_ERR(bus->dev))
-               goto error_create_class_dev;
-
        /* Add it to the local list of buses */
        list_add (&bus->bus_list, &usb_bus_list);
        mutex_unlock(&usb_bus_list_lock);
@@ -834,8 +889,6 @@ static int usb_register_bus(struct usb_bus *bus)
                  "number %d\n", bus->busnum);
        return 0;
 
-error_create_class_dev:
-       clear_bit(busnum, busmap.busmap);
 error_find_busnum:
        mutex_unlock(&usb_bus_list_lock);
        return result;
@@ -865,8 +918,6 @@ static void usb_deregister_bus (struct usb_bus *bus)
        usb_notify_remove_bus(bus);
 
        clear_bit (bus->busnum, busmap.busmap);
-
-       device_unregister(bus->dev);
 }
 
 /**
@@ -1199,7 +1250,8 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
 
        /* Map the URB's buffers for DMA access.
         * Lower level HCD code should use *_dma exclusively,
-        * unless it uses pio or talks to another transport.
+        * unless it uses pio or talks to another transport,
+        * or uses the provided scatter gather list for bulk.
         */
        if (is_root_hub(urb->dev))
                return 0;
@@ -1520,6 +1572,92 @@ rescan:
        }
 }
 
+/* Check whether a new configuration or alt setting for an interface
+ * will exceed the bandwidth for the bus (or the host controller resources).
+ * Only pass in a non-NULL config or interface, not both!
+ * Passing NULL for both new_config and new_intf means the device will be
+ * de-configured by issuing a set configuration 0 command.
+ */
+int usb_hcd_check_bandwidth(struct usb_device *udev,
+               struct usb_host_config *new_config,
+               struct usb_interface *new_intf)
+{
+       int num_intfs, i, j;
+       struct usb_interface_cache *intf_cache;
+       struct usb_host_interface *alt = 0;
+       int ret = 0;
+       struct usb_hcd *hcd;
+       struct usb_host_endpoint *ep;
+
+       hcd = bus_to_hcd(udev->bus);
+       if (!hcd->driver->check_bandwidth)
+               return 0;
+
+       /* Configuration is being removed - set configuration 0 */
+       if (!new_config && !new_intf) {
+               for (i = 1; i < 16; ++i) {
+                       ep = udev->ep_out[i];
+                       if (ep)
+                               hcd->driver->drop_endpoint(hcd, udev, ep);
+                       ep = udev->ep_in[i];
+                       if (ep)
+                               hcd->driver->drop_endpoint(hcd, udev, ep);
+               }
+               hcd->driver->check_bandwidth(hcd, udev);
+               return 0;
+       }
+       /* Check if the HCD says there's enough bandwidth.  Enable all endpoints
+        * each interface's alt setting 0 and ask the HCD to check the bandwidth
+        * of the bus.  There will always be bandwidth for endpoint 0, so it's
+        * ok to exclude it.
+        */
+       if (new_config) {
+               num_intfs = new_config->desc.bNumInterfaces;
+               /* Remove endpoints (except endpoint 0, which is always on the
+                * schedule) from the old config from the schedule
+                */
+               for (i = 1; i < 16; ++i) {
+                       ep = udev->ep_out[i];
+                       if (ep) {
+                               ret = hcd->driver->drop_endpoint(hcd, udev, ep);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+                       ep = udev->ep_in[i];
+                       if (ep) {
+                               ret = hcd->driver->drop_endpoint(hcd, udev, ep);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+               }
+               for (i = 0; i < num_intfs; ++i) {
+
+                       /* Dig the endpoints for alt setting 0 out of the
+                        * interface cache for this interface
+                        */
+                       intf_cache = new_config->intf_cache[i];
+                       for (j = 0; j < intf_cache->num_altsetting; j++) {
+                               if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
+                                       alt = &intf_cache->altsetting[j];
+                       }
+                       if (!alt) {
+                               printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
+                               continue;
+                       }
+                       for (j = 0; j < alt->desc.bNumEndpoints; j++) {
+                               ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
+                               if (ret < 0)
+                                       goto reset;
+                       }
+               }
+       }
+       ret = hcd->driver->check_bandwidth(hcd, udev);
+reset:
+       if (ret < 0)
+               hcd->driver->reset_bandwidth(hcd, udev);
+       return ret;
+}
+
 /* Disables the endpoint: synchronizes with the hcd to make sure all
  * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
  * have been called previously.  Use for set_configuration, set_interface,
@@ -1897,8 +2035,20 @@ int usb_add_hcd(struct usb_hcd *hcd,
                retval = -ENOMEM;
                goto err_allocate_root_hub;
        }
-       rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
-                       USB_SPEED_FULL;
+
+       switch (hcd->driver->flags & HCD_MASK) {
+       case HCD_USB11:
+               rhdev->speed = USB_SPEED_FULL;
+               break;
+       case HCD_USB2:
+               rhdev->speed = USB_SPEED_HIGH;
+               break;
+       case HCD_USB3:
+               rhdev->speed = USB_SPEED_SUPER;
+               break;
+       default:
+               goto err_allocate_root_hub;
+       }
        hcd->self.root_hub = rhdev;
 
        /* wakeup flag init defaults to "everything works" for root hubs,
index e7d4479de41cd04b692d1fe659275f9cadd8c69a..d397ecfd5b178d881f97c72e51989bfccbe2a064 100644 (file)
@@ -173,6 +173,8 @@ struct hc_driver {
 #define        HCD_LOCAL_MEM   0x0002          /* HC needs local memory */
 #define        HCD_USB11       0x0010          /* USB 1.1 */
 #define        HCD_USB2        0x0020          /* USB 2.0 */
+#define        HCD_USB3        0x0040          /* USB 3.0 */
+#define        HCD_MASK        0x0070
 
        /* called to init HCD and root hub */
        int     (*reset) (struct usb_hcd *hcd);
@@ -182,10 +184,10 @@ struct hc_driver {
         * a whole, not just the root hub; they're for PCI bus glue.
         */
        /* called after suspending the hub, before entering D3 etc */
-       int     (*pci_suspend) (struct usb_hcd *hcd, pm_message_t message);
+       int     (*pci_suspend)(struct usb_hcd *hcd);
 
        /* called after entering D0 (etc), before resuming the hub */
-       int     (*pci_resume) (struct usb_hcd *hcd);
+       int     (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
 
        /* cleanly make HCD stop writing memory and doing I/O */
        void    (*stop) (struct usb_hcd *hcd);
@@ -224,6 +226,43 @@ struct hc_driver {
        void    (*relinquish_port)(struct usb_hcd *, int);
                /* has a port been handed over to a companion? */
        int     (*port_handed_over)(struct usb_hcd *, int);
+
+       /* xHCI specific functions */
+               /* Called by usb_alloc_dev to alloc HC device structures */
+       int     (*alloc_dev)(struct usb_hcd *, struct usb_device *);
+               /* Called by usb_release_dev to free HC device structures */
+       void    (*free_dev)(struct usb_hcd *, struct usb_device *);
+
+       /* Bandwidth computation functions */
+       /* Note that add_endpoint() can only be called once per endpoint before
+        * check_bandwidth() or reset_bandwidth() must be called.
+        * drop_endpoint() can only be called once per endpoint also.
+        * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
+        * add the endpoint to the schedule with possibly new parameters denoted by a
+        * different endpoint descriptor in usb_host_endpoint.
+        * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
+        * not allowed.
+        */
+               /* Allocate endpoint resources and add them to a new schedule */
+       int     (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
+               /* Drop an endpoint from a new schedule */
+       int     (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
+               /* Check that a new hardware configuration, set using
+                * endpoint_enable and endpoint_disable, does not exceed bus
+                * bandwidth.  This must be called before any set configuration
+                * or set interface requests are sent to the device.
+                */
+       int     (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
+               /* Reset the device schedule to the last known good schedule,
+                * which was set from a previous successful call to
+                * check_bandwidth().  This reverts any add_endpoint() and
+                * drop_endpoint() calls since that last successful call.
+                * Used for when a check_bandwidth() call fails due to resource
+                * or bandwidth constraints.
+                */
+       void    (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+               /* Returns the hardware-chosen device address */
+       int     (*address_device)(struct usb_hcd *, struct usb_device *udev);
 };
 
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
@@ -242,6 +281,9 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
 extern void usb_hcd_reset_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep);
 extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
+extern int usb_hcd_check_bandwidth(struct usb_device *udev,
+               struct usb_host_config *new_config,
+               struct usb_interface *new_intf);
 extern int usb_hcd_get_frame_number(struct usb_device *udev);
 
 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
@@ -261,14 +303,11 @@ struct pci_device_id;
 extern int usb_hcd_pci_probe(struct pci_dev *dev,
                                const struct pci_device_id *id);
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
-
-#ifdef CONFIG_PM
-extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
-extern int usb_hcd_pci_resume(struct pci_dev *dev);
-#endif /* CONFIG_PM */
-
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
+#ifdef CONFIG_PM_SLEEP
+extern struct dev_pm_ops       usb_hcd_pci_pm_ops;
+#endif
 #endif /* CONFIG_PCI */
 
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
index be86ae3f40881516d529a894319ed52c71086b82..2af3b4f0605405dd723b1ccd4f853f7f85dc1848 100644 (file)
@@ -155,6 +155,8 @@ static inline char *portspeed(int portstatus)
                return "480 Mb/s";
        else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
                return "1.5 Mb/s";
+       else if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
+               return "5.0 Gb/s";
        else
                return "12 Mb/s";
 }
@@ -457,13 +459,13 @@ static void hub_tt_kevent (struct work_struct *work)
 
        spin_lock_irqsave (&hub->tt.lock, flags);
        while (--limit && !list_empty (&hub->tt.clear_list)) {
-               struct list_head        *temp;
+               struct list_head        *next;
                struct usb_tt_clear     *clear;
                struct usb_device       *hdev = hub->hdev;
                int                     status;
 
-               temp = hub->tt.clear_list.next;
-               clear = list_entry (temp, struct usb_tt_clear, clear_list);
+               next = hub->tt.clear_list.next;
+               clear = list_entry (next, struct usb_tt_clear, clear_list);
                list_del (&clear->clear_list);
 
                /* drop lock so HCD can concurrently report other TT errors */
@@ -951,6 +953,9 @@ static int hub_configure(struct usb_hub *hub,
                                        ret);
                        hub->tt.hub = hdev;
                        break;
+               case 3:
+                       /* USB 3.0 hubs don't have a TT */
+                       break;
                default:
                        dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
                                hdev->descriptor.bDeviceProtocol);
@@ -1323,6 +1328,11 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
  * 0 is reserved by USB for default address; (b) Linux's USB stack
  * uses always #1 for the root hub of the controller. So USB stack's
  * port #1, which is wusb virtual-port #0 has address #2.
+ *
+ * Devices connected under xHCI are not as simple.  The host controller
+ * supports virtualization, so the hardware assigns device addresses and
+ * the HCD must setup data structures before issuing a set address
+ * command to the hardware.
  */
 static void choose_address(struct usb_device *udev)
 {
@@ -1642,6 +1652,9 @@ int usb_new_device(struct usb_device *udev)
        err = usb_configure_device(udev);       /* detect & probe dev/intfs */
        if (err < 0)
                goto fail;
+       dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
+                       udev->devnum, udev->bus->busnum,
+                       (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
        /* export the usbdev device-node for libusb */
        udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
                        (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
@@ -2395,19 +2408,29 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit);
 static int hub_set_address(struct usb_device *udev, int devnum)
 {
        int retval;
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 
-       if (devnum <= 1)
+       /*
+        * The host controller will choose the device address,
+        * instead of the core having chosen it earlier
+        */
+       if (!hcd->driver->address_device && devnum <= 1)
                return -EINVAL;
        if (udev->state == USB_STATE_ADDRESS)
                return 0;
        if (udev->state != USB_STATE_DEFAULT)
                return -EINVAL;
-       retval = usb_control_msg(udev, usb_sndaddr0pipe(),
-               USB_REQ_SET_ADDRESS, 0, devnum, 0,
-               NULL, 0, USB_CTRL_SET_TIMEOUT);
+       if (hcd->driver->address_device) {
+               retval = hcd->driver->address_device(hcd, udev);
+       } else {
+               retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+                               USB_REQ_SET_ADDRESS, 0, devnum, 0,
+                               NULL, 0, USB_CTRL_SET_TIMEOUT);
+               if (retval == 0)
+                       update_address(udev, devnum);
+       }
        if (retval == 0) {
                /* Device now using proper address. */
-               update_address(udev, devnum);
                usb_set_device_state(udev, USB_STATE_ADDRESS);
                usb_ep0_reinit(udev);
        }
@@ -2430,6 +2453,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        static DEFINE_MUTEX(usb_address0_mutex);
 
        struct usb_device       *hdev = hub->hdev;
+       struct usb_hcd          *hcd = bus_to_hcd(hdev->bus);
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
@@ -2452,11 +2476,24 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       /* Reset the device; full speed may morph to high speed */
-       retval = hub_port_reset(hub, port1, udev, delay);
-       if (retval < 0)         /* error or disconnect */
+       if ((hcd->driver->flags & HCD_USB3) && udev->config) {
+               /* FIXME this will need special handling by the xHCI driver. */
+               dev_dbg(&udev->dev,
+                               "xHCI reset of configured device "
+                               "not supported yet.\n");
+               retval = -EINVAL;
                goto fail;
-                               /* success, speed is known */
+       } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+               /* Don't reset USB 3.0 devices during an initial setup */
+               usb_set_device_state(udev, USB_STATE_DEFAULT);
+       } else {
+               /* Reset the device; full speed may morph to high speed */
+               /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+               retval = hub_port_reset(hub, port1, udev, delay);
+               if (retval < 0)         /* error or disconnect */
+                       goto fail;
+               /* success, speed is known */
+       }
        retval = -ENODEV;
 
        if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
@@ -2471,6 +2508,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
         */
        switch (udev->speed) {
+       case USB_SPEED_SUPER:
        case USB_SPEED_VARIABLE:        /* fixed at 512 */
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
                break;
@@ -2496,16 +2534,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        case USB_SPEED_LOW:     speed = "low";  break;
        case USB_SPEED_FULL:    speed = "full"; break;
        case USB_SPEED_HIGH:    speed = "high"; break;
+       case USB_SPEED_SUPER:
+                               speed = "super";
+                               break;
        case USB_SPEED_VARIABLE:
                                speed = "variable";
                                type = "Wireless ";
                                break;
        default:                speed = "?";    break;
        }
-       dev_info (&udev->dev,
-                 "%s %s speed %sUSB device using %s and address %d\n",
-                 (udev->config) ? "reset" : "new", speed, type,
-                 udev->bus->controller->driver->name, devnum);
+       if (udev->speed != USB_SPEED_SUPER)
+               dev_info(&udev->dev,
+                               "%s %s speed %sUSB device using %s and address %d\n",
+                               (udev->config) ? "reset" : "new", speed, type,
+                               udev->bus->controller->driver->name, devnum);
 
        /* Set up TT records, if needed  */
        if (hdev->tt) {
@@ -2530,7 +2572,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * value.
         */
        for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
-               if (USE_NEW_SCHEME(retry_counter)) {
+               /*
+                * An xHCI controller cannot send any packets to a device until
+                * a set address command successfully completes.
+                */
+               if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
                        struct usb_device_descriptor *buf;
                        int r = 0;
 
@@ -2596,7 +2642,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                 * unauthorized address in the Connect Ack sequence;
                 * authorization will assign the final address.
                 */
-               if (udev->wusb == 0) {
+               if (udev->wusb == 0) {
                        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                                retval = hub_set_address(udev, devnum);
                                if (retval >= 0)
@@ -2609,13 +2655,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        devnum, retval);
                                goto fail;
                        }
+                       if (udev->speed == USB_SPEED_SUPER) {
+                               devnum = udev->devnum;
+                               dev_info(&udev->dev,
+                                               "%s SuperSpeed USB device using %s and address %d\n",
+                                               (udev->config) ? "reset" : "new",
+                                               udev->bus->controller->driver->name, devnum);
+                       }
 
                        /* cope with hardware quirkiness:
                         *  - let SET_ADDRESS settle, some device hardware wants it
                         *  - read ep0 maxpacket even for high and low speed,
                         */
                        msleep(10);
-                       if (USE_NEW_SCHEME(retry_counter))
+                       if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
                                break;
                }
 
@@ -2634,8 +2687,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
-       i = udev->descriptor.bMaxPacketSize0 == 0xff?   /* wusb device? */
-           512 : udev->descriptor.bMaxPacketSize0;
+       if (udev->descriptor.bMaxPacketSize0 == 0xff ||
+                       udev->speed == USB_SPEED_SUPER)
+               i = 512;
+       else
+               i = udev->descriptor.bMaxPacketSize0;
        if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
                if (udev->speed != USB_SPEED_FULL ||
                                !(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2847,19 +2903,41 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
 
                usb_set_device_state(udev, USB_STATE_POWERED);
-               udev->speed = USB_SPEED_UNKNOWN;
                udev->bus_mA = hub->mA_per_port;
                udev->level = hdev->level + 1;
                udev->wusb = hub_is_wusb(hub);
 
-               /* set the address */
-               choose_address(udev);
-               if (udev->devnum <= 0) {
-                       status = -ENOTCONN;     /* Don't retry */
-                       goto loop;
+               /*
+                * USB 3.0 devices are reset automatically before the connect
+                * port status change appears, and the root hub port status
+                * shows the correct speed.  We also get port change
+                * notifications for USB 3.0 devices from the USB 3.0 portion of
+                * an external USB 3.0 hub, but this isn't handled correctly yet
+                * FIXME.
+                */
+
+               if (!(hcd->driver->flags & HCD_USB3))
+                       udev->speed = USB_SPEED_UNKNOWN;
+               else if ((hdev->parent == NULL) &&
+                               (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED)))
+                       udev->speed = USB_SPEED_SUPER;
+               else
+                       udev->speed = USB_SPEED_UNKNOWN;
+
+               /*
+                * xHCI needs to issue an address device command later
+                * in the hub_port_init sequence for SS/HS/FS/LS devices.
+                */
+               if (!(hcd->driver->flags & HCD_USB3)) {
+                       /* set the address */
+                       choose_address(udev);
+                       if (udev->devnum <= 0) {
+                               status = -ENOTCONN;     /* Don't retry */
+                               goto loop;
+                       }
                }
 
-               /* reset and get descriptor */
+               /* reset (non-USB 3.0 devices) and get descriptor */
                status = hub_port_init(hub, udev, port1, i);
                if (status < 0)
                        goto loop;
index 2a116ce53c9b8e441f942b5b1b386a06a90cf738..889c0f32a40b729431f2dab51d13aead39f5f3e8 100644 (file)
 #define USB_PORT_FEAT_L1               5       /* L1 suspend */
 #define USB_PORT_FEAT_POWER            8
 #define USB_PORT_FEAT_LOWSPEED         9
+/* This value was never in Table 11-17 */
 #define USB_PORT_FEAT_HIGHSPEED                10
+/* This value is also fake */
+#define USB_PORT_FEAT_SUPERSPEED       11
 #define USB_PORT_FEAT_C_CONNECTION     16
 #define USB_PORT_FEAT_C_ENABLE         17
 #define USB_PORT_FEAT_C_SUSPEND                18
index b626283776541fe192c0442a46ab0e33415cc167..2bed83caacb1a0c714a2e05af21242bbb504dcad 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
+#include <linux/nls.h>
 #include <linux/device.h>
 #include <linux/scatterlist.h>
 #include <linux/usb/quirks.h>
@@ -364,6 +365,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        int i;
        int urb_flags;
        int dma;
+       int use_sg;
 
        if (!io || !dev || !sg
                        || usb_pipecontrol(pipe)
@@ -391,7 +393,19 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        if (io->entries <= 0)
                return io->entries;
 
-       io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
+       /* If we're running on an xHCI host controller, queue the whole scatter
+        * gather list with one call to urb_enqueue().  This is only for bulk,
+        * as that endpoint type does not care how the data gets broken up
+        * across frames.
+        */
+       if (usb_pipebulk(pipe) &&
+                       bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) {
+               io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
+               use_sg = true;
+       } else {
+               io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags);
+               use_sg = false;
+       }
        if (!io->urbs)
                goto nomem;
 
@@ -401,62 +415,92 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
        if (usb_pipein(pipe))
                urb_flags |= URB_SHORT_NOT_OK;
 
-       for_each_sg(sg, sg, io->entries, i) {
-               unsigned len;
-
-               io->urbs[i] = usb_alloc_urb(0, mem_flags);
-               if (!io->urbs[i]) {
-                       io->entries = i;
+       if (use_sg) {
+               io->urbs[0] = usb_alloc_urb(0, mem_flags);
+               if (!io->urbs[0]) {
+                       io->entries = 0;
                        goto nomem;
                }
 
-               io->urbs[i]->dev = NULL;
-               io->urbs[i]->pipe = pipe;
-               io->urbs[i]->interval = period;
-               io->urbs[i]->transfer_flags = urb_flags;
-
-               io->urbs[i]->complete = sg_complete;
-               io->urbs[i]->context = io;
-
-               /*
-                * Some systems need to revert to PIO when DMA is temporarily
-                * unavailable.  For their sakes, both transfer_buffer and
-                * transfer_dma are set when possible.  However this can only
-                * work on systems without:
-                *
-                *  - HIGHMEM, since DMA buffers located in high memory are
-                *    not directly addressable by the CPU for PIO;
-                *
-                *  - IOMMU, since dma_map_sg() is allowed to use an IOMMU to
-                *    make virtually discontiguous buffers be "dma-contiguous"
-                *    so that PIO and DMA need diferent numbers of URBs.
-                *
-                * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL
-                * to prevent stale pointers and to help spot bugs.
-                */
-               if (dma) {
-                       io->urbs[i]->transfer_dma = sg_dma_address(sg);
-                       len = sg_dma_len(sg);
+               io->urbs[0]->dev = NULL;
+               io->urbs[0]->pipe = pipe;
+               io->urbs[0]->interval = period;
+               io->urbs[0]->transfer_flags = urb_flags;
+
+               io->urbs[0]->complete = sg_complete;
+               io->urbs[0]->context = io;
+               /* A length of zero means transfer the whole sg list */
+               io->urbs[0]->transfer_buffer_length = length;
+               if (length == 0) {
+                       for_each_sg(sg, sg, io->entries, i) {
+                               io->urbs[0]->transfer_buffer_length +=
+                                       sg_dma_len(sg);
+                       }
+               }
+               io->urbs[0]->sg = io;
+               io->urbs[0]->num_sgs = io->entries;
+               io->entries = 1;
+       } else {
+               for_each_sg(sg, sg, io->entries, i) {
+                       unsigned len;
+
+                       io->urbs[i] = usb_alloc_urb(0, mem_flags);
+                       if (!io->urbs[i]) {
+                               io->entries = i;
+                               goto nomem;
+                       }
+
+                       io->urbs[i]->dev = NULL;
+                       io->urbs[i]->pipe = pipe;
+                       io->urbs[i]->interval = period;
+                       io->urbs[i]->transfer_flags = urb_flags;
+
+                       io->urbs[i]->complete = sg_complete;
+                       io->urbs[i]->context = io;
+
+                       /*
+                        * Some systems need to revert to PIO when DMA is
+                        * temporarily unavailable.  For their sakes, both
+                        * transfer_buffer and transfer_dma are set when
+                        * possible.  However this can only work on systems
+                        * without:
+                        *
+                        *  - HIGHMEM, since DMA buffers located in high memory
+                        *    are not directly addressable by the CPU for PIO;
+                        *
+                        *  - IOMMU, since dma_map_sg() is allowed to use an
+                        *    IOMMU to make virtually discontiguous buffers be
+                        *    "dma-contiguous" so that PIO and DMA need diferent
+                        *    numbers of URBs.
+                        *
+                        * So when HIGHMEM or IOMMU are in use, transfer_buffer
+                        * is NULL to prevent stale pointers and to help spot
+                        * bugs.
+                        */
+                       if (dma) {
+                               io->urbs[i]->transfer_dma = sg_dma_address(sg);
+                               len = sg_dma_len(sg);
 #if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
-                       io->urbs[i]->transfer_buffer = NULL;
+                               io->urbs[i]->transfer_buffer = NULL;
 #else
-                       io->urbs[i]->transfer_buffer = sg_virt(sg);
+                               io->urbs[i]->transfer_buffer = sg_virt(sg);
 #endif
-               } else {
-                       /* hc may use _only_ transfer_buffer */
-                       io->urbs[i]->transfer_buffer = sg_virt(sg);
-                       len = sg->length;
-               }
+                       } else {
+                               /* hc may use _only_ transfer_buffer */
+                               io->urbs[i]->transfer_buffer = sg_virt(sg);
+                               len = sg->length;
+                       }
 
-               if (length) {
-                       len = min_t(unsigned, len, length);
-                       length -= len;
-                       if (length == 0)
-                               io->entries = i + 1;
+                       if (length) {
+                               len = min_t(unsigned, len, length);
+                               length -= len;
+                               if (length == 0)
+                                       io->entries = i + 1;
+                       }
+                       io->urbs[i]->transfer_buffer_length = len;
                }
-               io->urbs[i]->transfer_buffer_length = len;
+               io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
        }
-       io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT;
 
        /* transaction state */
        io->count = io->entries;
@@ -509,6 +553,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init);
  * could be transferred.  That capability is less useful for low or full
  * speed interrupt endpoints, which allow at most one packet per millisecond,
  * of at most 8 or 64 bytes (respectively).
+ *
+ * It is not necessary to call this function to reserve bandwidth for devices
+ * under an xHCI host controller, as the bandwidth is reserved when the
+ * configuration or interface alt setting is selected.
  */
 void usb_sg_wait(struct usb_sg_request *io)
 {
@@ -759,7 +807,7 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
 }
 
 /**
- * usb_string - returns ISO 8859-1 version of a string descriptor
+ * usb_string - returns UTF-8 version of a string descriptor
  * @dev: the device whose string descriptor is being retrieved
  * @index: the number of the descriptor
  * @buf: where to put the string
@@ -767,17 +815,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
  * Context: !in_interrupt ()
  *
  * This converts the UTF-16LE encoded strings returned by devices, from
- * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
- * that are more usable in most kernel contexts.  Note that all characters
- * in the chosen descriptor that can't be encoded using ISO-8859-1
- * are converted to the question mark ("?") character, and this function
+ * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones
+ * that are more usable in most kernel contexts.  Note that this function
  * chooses strings in the first language supported by the device.
  *
- * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
- * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
- * and is appropriate for use many uses of English and several other
- * Western European languages.  (But it doesn't include the "Euro" symbol.)
- *
  * This call is synchronous, and may not be used in an interrupt context.
  *
  * Returns length of the string (>= 0) or usb_control_msg status (< 0).
@@ -786,7 +827,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 {
        unsigned char *tbuf;
        int err;
-       unsigned int u, idx;
 
        if (dev->state == USB_STATE_SUSPENDED)
                return -EHOSTUNREACH;
@@ -821,16 +861,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
                goto errout;
 
        size--;         /* leave room for trailing NULL char in output buffer */
-       for (idx = 0, u = 2; u < err; u += 2) {
-               if (idx >= size)
-                       break;
-               if (tbuf[u+1])                  /* high byte */
-                       buf[idx++] = '?';  /* non ISO-8859-1 character */
-               else
-                       buf[idx++] = tbuf[u];
-       }
-       buf[idx] = 0;
-       err = idx;
+       err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2,
+                       UTF16_LITTLE_ENDIAN, buf, size);
+       buf[err] = 0;
 
        if (tbuf[1] != USB_DT_STRING)
                dev_dbg(&dev->dev,
@@ -843,6 +876,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 }
 EXPORT_SYMBOL_GPL(usb_string);
 
+/* one UTF-8-encoded 16-bit character has at most three bytes */
+#define MAX_USB_STRING_SIZE (127 * 3 + 1)
+
 /**
  * usb_cache_string - read a string descriptor and cache it for later use
  * @udev: the device whose string descriptor is being read
@@ -860,9 +896,9 @@ char *usb_cache_string(struct usb_device *udev, int index)
        if (index <= 0)
                return NULL;
 
-       buf = kmalloc(256, GFP_KERNEL);
+       buf = kmalloc(MAX_USB_STRING_SIZE, GFP_KERNEL);
        if (buf) {
-               len = usb_string(udev, index, buf, 256);
+               len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
                if (len > 0) {
                        smallbuf = kmalloc(++len, GFP_KERNEL);
                        if (!smallbuf)
@@ -1664,6 +1700,21 @@ free_interfaces:
        if (ret)
                goto free_interfaces;
 
+       /* Make sure we have bandwidth (and available HCD resources) for this
+        * configuration.  Remove endpoints from the schedule if we're dropping
+        * this configuration to set configuration 0.  After this point, the
+        * host controller will not allow submissions to dropped endpoints.  If
+        * this call fails, the device state is unchanged.
+        */
+       if (cp)
+               ret = usb_hcd_check_bandwidth(dev, cp, NULL);
+       else
+               ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
+       if (ret < 0) {
+               usb_autosuspend_device(dev);
+               goto free_interfaces;
+       }
+
        /* if it's already configured, clear out old state first.
         * getting rid of old interfaces means unbinding their drivers.
         */
@@ -1686,6 +1737,7 @@ free_interfaces:
        dev->actconfig = cp;
        if (!cp) {
                usb_set_device_state(dev, USB_STATE_ADDRESS);
+               usb_hcd_check_bandwidth(dev, NULL, NULL);
                usb_autosuspend_device(dev);
                goto free_interfaces;
        }
index c6678919792729b0c0548f6aa15592efc50e1fd1..b5c72e458943f753d5981c20596de1ffe376fb4a 100644 (file)
@@ -552,8 +552,8 @@ static struct attribute *dev_string_attrs[] = {
 static mode_t dev_string_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct usb_device *udev = to_usb_device(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_device *udev = to_usb_device(dev);
 
        if (a == &dev_attr_manufacturer.attr) {
                if (udev->manufacturer == NULL)
@@ -585,8 +585,8 @@ static ssize_t
 read_descriptors(struct kobject *kobj, struct bin_attribute *attr,
                char *buf, loff_t off, size_t count)
 {
-       struct usb_device *udev = to_usb_device(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_device *udev = to_usb_device(dev);
        size_t nleft = count;
        size_t srclen, n;
        int cfgno;
@@ -786,8 +786,8 @@ static struct attribute *intf_assoc_attrs[] = {
 static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct usb_interface *intf = to_usb_interface(
-                       container_of(kobj, struct device, kobj));
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct usb_interface *intf = to_usb_interface(dev);
 
        if (intf->intf_assoc == NULL)
                return 0;
index 3376055f36e7349057531b41c080832c5bd04623..0885d4abdc6265d0b7775da9b9f5503fc2be0dff 100644 (file)
@@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
  * If the USB subsystem can't allocate sufficient bandwidth to perform
  * the periodic request, submitting such a periodic request should fail.
  *
+ * For devices under xHCI, the bandwidth is reserved at configuration time, or
+ * when the alt setting is selected.  If there is not enough bus bandwidth, the
+ * configuration/alt setting request will fail.  Therefore, submissions to
+ * periodic endpoints on devices under xHCI should never fail due to bandwidth
+ * constraints.
+ *
  * Device drivers must explicitly request that repetition, by ensuring that
  * some URB is always on the endpoint's queue (except possibly for short
  * periods during completion callacks).  When there is no longer an urb
@@ -351,6 +357,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
        if (xfertype == USB_ENDPOINT_XFER_ISOC) {
                int     n, len;
 
+               /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */
                /* "high bandwidth" mode, 1-3 packets/uframe? */
                if (dev->speed == USB_SPEED_HIGH) {
                        int     mult = 1 + ((max >> 11) & 0x03);
@@ -426,6 +433,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
                        return -EINVAL;
                /* too big? */
                switch (dev->speed) {
+               case USB_SPEED_SUPER:   /* units are 125us */
+                       /* Handle up to 2^(16-1) microframes */
+                       if (urb->interval > (1 << 15))
+                               return -EINVAL;
+                       max = 1 << 15;
                case USB_SPEED_HIGH:    /* units are microframes */
                        /* NOTE usb handles 2^15 */
                        if (urb->interval > (1024 * 8))
index 927a27dd2f853cd84eeabd2660661bee481bdbc2..a26f73880c32388340fad1ef0639c8233d1c3be4 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/usb.h>
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
+#include <linux/debugfs.h>
 
 #include <asm/io.h>
 #include <linux/scatterlist.h>
@@ -139,8 +140,7 @@ static int __find_interface(struct device *dev, void *data)
        struct find_interface_arg *arg = data;
        struct usb_interface *intf;
 
-       /* can't look at usb devices, only interfaces */
-       if (is_usb_device(dev))
+       if (!is_usb_interface(dev))
                return 0;
 
        intf = to_usb_interface(dev);
@@ -184,11 +184,16 @@ EXPORT_SYMBOL_GPL(usb_find_interface);
 static void usb_release_dev(struct device *dev)
 {
        struct usb_device *udev;
+       struct usb_hcd *hcd;
 
        udev = to_usb_device(dev);
+       hcd = bus_to_hcd(udev->bus);
 
        usb_destroy_configuration(udev);
-       usb_put_hcd(bus_to_hcd(udev->bus));
+       /* Root hubs aren't real devices, so don't free HCD resources */
+       if (hcd->driver->free_dev && udev->parent)
+               hcd->driver->free_dev(hcd, udev);
+       usb_put_hcd(hcd);
        kfree(udev->product);
        kfree(udev->manufacturer);
        kfree(udev->serial);
@@ -359,6 +364,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
                kfree(dev);
                return NULL;
        }
+       /* Root hubs aren't true devices, so don't allocate HCD resources */
+       if (usb_hcd->driver->alloc_dev && parent &&
+               !usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
+               usb_put_hcd(bus_to_hcd(bus));
+               kfree(dev);
+               return NULL;
+       }
 
        device_initialize(&dev->dev);
        dev->dev.bus = &usb_bus_type;
@@ -386,18 +398,24 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
         */
        if (unlikely(!parent)) {
                dev->devpath[0] = '0';
+               dev->route = 0;
 
                dev->dev.parent = bus->controller;
                dev_set_name(&dev->dev, "usb%d", bus->busnum);
                root_hub = 1;
        } else {
                /* match any labeling on the hubs; it's one-based */
-               if (parent->devpath[0] == '0')
+               if (parent->devpath[0] == '0') {
                        snprintf(dev->devpath, sizeof dev->devpath,
                                "%d", port1);
-               else
+                       /* Root ports are not counted in route string */
+                       dev->route = 0;
+               } else {
                        snprintf(dev->devpath, sizeof dev->devpath,
                                "%s.%d", parent->devpath, port1);
+                       dev->route = parent->route +
+                               (port1 << ((parent->level - 1)*4));
+               }
 
                dev->dev.parent = &parent->dev;
                dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
@@ -810,12 +828,12 @@ void usb_buffer_dmasync(struct urb *urb)
                return;
 
        if (controller->dma_mask) {
-               dma_sync_single(controller,
+               dma_sync_single_for_cpu(controller,
                        urb->transfer_dma, urb->transfer_buffer_length,
                        usb_pipein(urb->pipe)
                                ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
                if (usb_pipecontrol(urb->pipe))
-                       dma_sync_single(controller,
+                       dma_sync_single_for_cpu(controller,
                                        urb->setup_dma,
                                        sizeof(struct usb_ctrlrequest),
                                        DMA_TO_DEVICE);
@@ -933,8 +951,8 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
                        || !controller->dma_mask)
                return;
 
-       dma_sync_sg(controller, sg, n_hw_ents,
-                       is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
+                           is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
 #endif
@@ -1012,6 +1030,35 @@ static struct notifier_block usb_bus_nb = {
        .notifier_call = usb_bus_notify,
 };
 
+struct dentry *usb_debug_root;
+EXPORT_SYMBOL_GPL(usb_debug_root);
+
+struct dentry *usb_debug_devices;
+
+static int usb_debugfs_init(void)
+{
+       usb_debug_root = debugfs_create_dir("usb", NULL);
+       if (!usb_debug_root)
+               return -ENOENT;
+
+       usb_debug_devices = debugfs_create_file("devices", 0444,
+                                               usb_debug_root, NULL,
+                                               &usbfs_devices_fops);
+       if (!usb_debug_devices) {
+               debugfs_remove(usb_debug_root);
+               usb_debug_root = NULL;
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static void usb_debugfs_cleanup(void)
+{
+       debugfs_remove(usb_debug_devices);
+       debugfs_remove(usb_debug_root);
+}
+
 /*
  * Init
  */
@@ -1023,6 +1070,10 @@ static int __init usb_init(void)
                return 0;
        }
 
+       retval = usb_debugfs_init();
+       if (retval)
+               goto out;
+
        retval = ksuspend_usb_init();
        if (retval)
                goto out;
@@ -1032,9 +1083,6 @@ static int __init usb_init(void)
        retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
        if (retval)
                goto bus_notifier_failed;
-       retval = usb_host_init();
-       if (retval)
-               goto host_init_failed;
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
@@ -1064,8 +1112,6 @@ usb_devio_init_failed:
 driver_register_failed:
        usb_major_cleanup();
 major_init_failed:
-       usb_host_cleanup();
-host_init_failed:
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
 bus_notifier_failed:
        bus_unregister(&usb_bus_type);
@@ -1090,10 +1136,10 @@ static void __exit usb_exit(void)
        usb_deregister(&usbfs_driver);
        usb_devio_cleanup();
        usb_hub_cleanup();
-       usb_host_cleanup();
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
        bus_unregister(&usb_bus_type);
        ksuspend_usb_cleanup();
+       usb_debugfs_cleanup();
 }
 
 subsys_initcall(usb_init);
index 79d8a9ea559ba7dc635f268419f9918bdfcf6c08..e2a8cfaade1ddbc7c5bb4c43c7d1f91f580c6230 100644 (file)
@@ -41,8 +41,6 @@ extern int  usb_hub_init(void);
 extern void usb_hub_cleanup(void);
 extern int usb_major_init(void);
 extern void usb_major_cleanup(void);
-extern int usb_host_init(void);
-extern void usb_host_cleanup(void);
 
 #ifdef CONFIG_PM
 
@@ -106,6 +104,7 @@ extern struct workqueue_struct *ksuspend_usb_wq;
 extern struct bus_type usb_bus_type;
 extern struct device_type usb_device_type;
 extern struct device_type usb_if_device_type;
+extern struct device_type usb_ep_device_type;
 extern struct usb_device_driver usb_generic_driver;
 
 static inline int is_usb_device(const struct device *dev)
@@ -113,6 +112,16 @@ static inline int is_usb_device(const struct device *dev)
        return dev->type == &usb_device_type;
 }
 
+static inline int is_usb_interface(const struct device *dev)
+{
+       return dev->type == &usb_if_device_type;
+}
+
+static inline int is_usb_endpoint(const struct device *dev)
+{
+       return dev->type == &usb_ep_device_type;
+}
+
 /* Do the same for device drivers and interface drivers. */
 
 static inline int is_usb_device_driver(struct device_driver *drv)
index 080bb1e4b847aebcd862b71480596edcf7f8b37e..5d1ddf485d1eb6d5d55e12a4eca7536e10a75838 100644 (file)
@@ -156,7 +156,7 @@ config USB_ATMEL_USBA
 
 config USB_GADGET_FSL_USB2
        boolean "Freescale Highspeed USB DR Peripheral Controller"
-       depends on FSL_SOC
+       depends on FSL_SOC || ARCH_MXC
        select USB_GADGET_DUALSPEED
        help
           Some of Freescale PowerPC processors have a High Speed
@@ -253,7 +253,7 @@ config USB_PXA25X_SMALL
 
 config USB_GADGET_PXA27X
        boolean "PXA 27x"
-       depends on ARCH_PXA && PXA27x
+       depends on ARCH_PXA && (PXA27x || PXA3xx)
        select USB_OTG_UTILS
        help
           Intel's PXA 27x series XScale ARM v5TE processors include
@@ -272,6 +272,20 @@ config USB_PXA27X
        default USB_GADGET
        select USB_GADGET_SELECTED
 
+config USB_GADGET_S3C_HSOTG
+       boolean "S3C HS/OtG USB Device controller"
+       depends on S3C_DEV_USB_HSOTG
+       select USB_GADGET_S3C_HSOTG_PIO
+       help
+         The Samsung S3C64XX USB2.0 high-speed gadget controller
+         integrated into the S3C64XX series SoC.
+
+config USB_S3C_HSOTG
+       tristate
+       depends on USB_GADGET_S3C_HSOTG
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 config USB_GADGET_S3C2410
        boolean "S3C2410 USB Device Controller"
        depends on ARCH_S3C2410
@@ -460,6 +474,27 @@ config USB_GOKU
        default USB_GADGET
        select USB_GADGET_SELECTED
 
+config USB_GADGET_LANGWELL
+       boolean "Intel Langwell USB Device Controller"
+       depends on PCI
+       select USB_GADGET_DUALSPEED
+       help
+          Intel Langwell USB Device Controller is a High-Speed USB
+          On-The-Go device controller.
+
+          The number of programmable endpoints is different through
+          controller revision.
+
+          Say "y" to link the driver statically, or "m" to build a
+          dynamically linked module called "langwell_udc" and force all
+          gadget drivers to also be dynamically linked.
+
+config USB_LANGWELL
+       tristate
+       depends on USB_GADGET_LANGWELL
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 
 #
 # LAST -- dummy/emulated controller
@@ -566,6 +601,20 @@ config USB_ZERO_HNPTEST
          the "B-Peripheral" role, that device will use HNP to let this
          one serve as the USB host instead (in the "B-Host" role).
 
+config USB_AUDIO
+       tristate "Audio Gadget (EXPERIMENTAL)"
+       depends on SND
+       help
+         Gadget Audio is compatible with USB Audio Class specification 1.0.
+         It will include at least one AudioControl interface, zero or more
+         AudioStream interface and zero or more MIDIStream interface.
+
+         Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
+         playback or capture audio stream.
+
+         Say "y" to link the driver statically, or "m" to build a
+         dynamically linked module called "g_audio".
+
 config USB_ETH
        tristate "Ethernet Gadget (with CDC Ethernet support)"
        depends on NET
index 39a51d746cb76d1eb796fb9798e121b9409ab771..e6017e6bf6da2c7531dae908330231acfbb7649c 100644 (file)
@@ -18,14 +18,21 @@ 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_FSL_USB2)     += fsl_usb2_udc.o
+fsl_usb2_udc-objs              := fsl_udc_core.o
+ifeq ($(CONFIG_ARCH_MXC),y)
+fsl_usb2_udc-objs              += fsl_mx3_udc.o
+endif
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)       += fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)      += ci13xxx_udc.o
+obj-$(CONFIG_USB_S3C_HSOTG)    += s3c-hsotg.o
+obj-$(CONFIG_USB_LANGWELL)     += langwell_udc.o
 
 #
 # USB gadget drivers
 #
 g_zero-objs                    := zero.o
+g_audio-objs                   := audio.o
 g_ether-objs                   := ether.o
 g_serial-objs                  := serial.o
 g_midi-objs                    := gmidi.o
@@ -35,6 +42,7 @@ g_printer-objs                        := printer.o
 g_cdc-objs                     := cdc2.o
 
 obj-$(CONFIG_USB_ZERO)         += g_zero.o
+obj-$(CONFIG_USB_AUDIO)                += g_audio.o
 obj-$(CONFIG_USB_ETH)          += g_ether.o
 obj-$(CONFIG_USB_GADGETFS)     += gadgetfs.o
 obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
index 53bcdd2f8282f1a57d1e775a030d965fbb30c364..72bae8f39d814310ef477df5c987318e79000835 100644 (file)
@@ -485,7 +485,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
                return -ESHUTDOWN;
        }
 
-       tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+       tmp = usb_endpoint_type(desc);
        switch (tmp) {
        case USB_ENDPOINT_XFER_CONTROL:
                DBG("only one control endpoint\n");
@@ -517,7 +517,7 @@ ok:
        local_irq_save(flags);
 
        /* initialize endpoint to match this descriptor */
-       ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
+       ep->is_in = usb_endpoint_dir_in(desc);
        ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
        ep->stopped = 0;
        if (ep->is_in)
index 05c913cc3658f0389ab0ace117a8f50c8a166785..4e970cf0e29ae364b34c5e7436906e95db313952 100644 (file)
@@ -326,13 +326,7 @@ static int vbus_is_present(struct usba_udc *udc)
        return 1;
 }
 
-#if defined(CONFIG_AVR32)
-
-static void toggle_bias(int is_on)
-{
-}
-
-#elif defined(CONFIG_ARCH_AT91)
+#if defined(CONFIG_ARCH_AT91SAM9RL)
 
 #include <mach/at91_pmc.h>
 
@@ -346,7 +340,13 @@ static void toggle_bias(int is_on)
                at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
 }
 
-#endif /* CONFIG_ARCH_AT91 */
+#else
+
+static void toggle_bias(int is_on)
+{
+}
+
+#endif /* CONFIG_ARCH_AT91SAM9RL */
 
 static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
 {
@@ -550,12 +550,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
                        ep->ep.name, ept_cfg, maxpacket);
 
-       if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+       if (usb_endpoint_dir_in(desc)) {
                ep->is_in = 1;
                ept_cfg |= USBA_EPT_DIR_IN;
        }
 
-       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_CONTROL:
                ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
                ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
new file mode 100644 (file)
index 0000000..94de7e8
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * audio.c -- Audio gadget driver
+ *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+
+#include "u_audio.h"
+
+#define DRIVER_DESC            "Linux USB Audio Gadget"
+#define DRIVER_VERSION         "Dec 18, 2008"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+#include "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#include "u_audio.c"
+#include "f_audio.c"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID. */
+#define AUDIO_VENDOR_NUM               0x0525  /* NetChip */
+#define AUDIO_PRODUCT_NUM              0xa4a1  /* Linux-USB Audio Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+       .bLength =              sizeof device_desc,
+       .bDescriptorType =      USB_DT_DEVICE,
+
+       .bcdUSB =               __constant_cpu_to_le16(0x200),
+
+       .bDeviceClass =         USB_CLASS_PER_INTERFACE,
+       .bDeviceSubClass =      0,
+       .bDeviceProtocol =      0,
+       /* .bMaxPacketSize0 = f(hardware) */
+
+       /* Vendor and product id defaults change according to what configs
+        * we support.  (As does bNumConfigurations.)  These values can
+        * also be overridden by module parameters.
+        */
+       .idVendor =             __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
+       .idProduct =            __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
+       /* .bcdDevice = f(hardware) */
+       /* .iManufacturer = DYNAMIC */
+       /* .iProduct = DYNAMIC */
+       /* NO SERIAL NUMBER */
+       .bNumConfigurations =   1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+       .bLength =              sizeof otg_descriptor,
+       .bDescriptorType =      USB_DT_OTG,
+
+       /* REVISIT SRP-only hardware is possible, although
+        * it would not be called "OTG" ...
+        */
+       .bmAttributes =         USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+       (struct usb_descriptor_header *) &otg_descriptor,
+       NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Handle USB audio endpoint set/get command in setup class request
+ */
+
+static int audio_set_endpoint_req(struct usb_configuration *c,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       int                     value = -EOPNOTSUPP;
+       u16                     ep = le16_to_cpu(ctrl->wIndex);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case SET_CUR:
+               value = 0;
+               break;
+
+       case SET_MIN:
+               break;
+
+       case SET_MAX:
+               break;
+
+       case SET_RES:
+               break;
+
+       case SET_MEM:
+               break;
+
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int audio_get_endpoint_req(struct usb_configuration *c,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       int value = -EOPNOTSUPP;
+       u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16 len = le16_to_cpu(ctrl->wLength);
+       u16 w_value = le16_to_cpu(ctrl->wValue);
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+                       ctrl->bRequest, w_value, len, ep);
+
+       switch (ctrl->bRequest) {
+       case GET_CUR:
+       case GET_MIN:
+       case GET_MAX:
+       case GET_RES:
+               value = 3;
+               break;
+       case GET_MEM:
+               break;
+       default:
+               break;
+       }
+
+       return value;
+}
+
+static int
+audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct usb_request *req = cdev->req;
+       int value = -EOPNOTSUPP;
+       u16 w_index = le16_to_cpu(ctrl->wIndex);
+       u16 w_value = le16_to_cpu(ctrl->wValue);
+       u16 w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * Audio class messages; interface activation uses set_alt().
+        */
+       switch (ctrl->bRequestType) {
+       case USB_AUDIO_SET_ENDPOINT:
+               value = audio_set_endpoint_req(c, ctrl);
+               break;
+
+       case USB_AUDIO_GET_ENDPOINT:
+               value = audio_get_endpoint_req(c, ctrl);
+               break;
+
+       default:
+               ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "Audio response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __init audio_do_config(struct usb_configuration *c)
+{
+       /* FIXME alloc iConfiguration string, set it in c->strings */
+
+       if (gadget_is_otg(c->cdev->gadget)) {
+               c->descriptors = otg_desc;
+               c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+       }
+
+       audio_bind_config(c);
+
+       return 0;
+}
+
+static struct usb_configuration audio_config_driver = {
+       .label                  = DRIVER_DESC,
+       .bind                   = audio_do_config,
+       .setup                  = audio_setup,
+       .bConfigurationValue    = 1,
+       /* .iConfiguration = DYNAMIC */
+       .bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init audio_bind(struct usb_composite_dev *cdev)
+{
+       int                     gcnum;
+       int                     status;
+
+       gcnum = usb_gadget_controller_number(cdev->gadget);
+       if (gcnum >= 0)
+               device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+       else {
+               ERROR(cdev, "controller '%s' not recognized; trying %s\n",
+                       cdev->gadget->name,
+                       audio_config_driver.label);
+               device_desc.bcdDevice =
+                       __constant_cpu_to_le16(0x0300 | 0x0099);
+       }
+
+       /* device descriptor strings: manufacturer, product */
+       snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+               init_utsname()->sysname, init_utsname()->release,
+               cdev->gadget->name);
+       status = usb_string_id(cdev);
+       if (status < 0)
+               goto fail;
+       strings_dev[STRING_MANUFACTURER_IDX].id = status;
+       device_desc.iManufacturer = status;
+
+       status = usb_string_id(cdev);
+       if (status < 0)
+               goto fail;
+       strings_dev[STRING_PRODUCT_IDX].id = status;
+       device_desc.iProduct = status;
+
+       status = usb_add_config(cdev, &audio_config_driver);
+       if (status < 0)
+               goto fail;
+
+       INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
+       return 0;
+
+fail:
+       return status;
+}
+
+static int __exit audio_unbind(struct usb_composite_dev *cdev)
+{
+       return 0;
+}
+
+static struct usb_composite_driver audio_driver = {
+       .name           = "g_audio",
+       .dev            = &device_desc,
+       .strings        = audio_strings,
+       .bind           = audio_bind,
+       .unbind         = __exit_p(audio_unbind),
+};
+
+static int __init init(void)
+{
+       return usb_composite_register(&audio_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+       usb_composite_unregister(&audio_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
+MODULE_LICENSE("GPL");
+
index 38e531ecae4d310066e7813f2550420aa33e3ab3..c7cb87a6fee22a4519a0f720fcf3f3451eb22920 100644 (file)
@@ -1977,9 +1977,9 @@ static int ep_enable(struct usb_ep *ep,
        if (!list_empty(&mEp->qh[mEp->dir].queue))
                warn("enabling a non-empty endpoint!");
 
-       mEp->dir  = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX;
-       mEp->num  =  desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-       mEp->type =  desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+       mEp->dir  = usb_endpoint_dir_in(desc) ? TX : RX;
+       mEp->num  = usb_endpoint_num(desc);
+       mEp->type = usb_endpoint_type(desc);
 
        mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
 
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
new file mode 100644 (file)
index 0000000..66527ba
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * f_audio.c -- USB Audio class function driver
+  *
+ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
+ * Copyright (C) 2008 Analog Devices, Inc
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#include "u_audio.h"
+
+#define OUT_EP_MAX_PACKET_SIZE 200
+static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
+module_param(req_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
+
+static int req_count = 256;
+module_param(req_count, int, S_IRUGO);
+MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
+
+static int audio_buf_size = 48000;
+module_param(audio_buf_size, int, S_IRUGO);
+MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+
+/*
+ * DESCRIPTORS ... most are static, but strings and full
+ * configuration descriptors are built on demand.
+ */
+
+/*
+ * We have two interfaces- AudioControl and AudioStreaming
+ * TODO: only supcard playback currently
+ */
+#define F_AUDIO_AC_INTERFACE   0
+#define F_AUDIO_AS_INTERFACE   1
+#define F_AUDIO_NUM_INTERFACES 2
+
+/* B.3.1  Standard AC Interface Descriptor */
+static struct usb_interface_descriptor ac_interface_desc __initdata = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+};
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(2);
+
+#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static struct usb_ac_header_descriptor_2 ac_header_desc = {
+       .bLength =              USB_DT_AC_HEADER_LENGH,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   HEADER,
+       .bcdADC =               __constant_cpu_to_le16(0x0100),
+       .wTotalLength =         __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH),
+       .bInCollection =        F_AUDIO_NUM_INTERFACES,
+       .baInterfaceNr = {
+               [0] =           F_AUDIO_AC_INTERFACE,
+               [1] =           F_AUDIO_AS_INTERFACE,
+       }
+};
+
+#define INPUT_TERMINAL_ID      1
+static struct usb_input_terminal_descriptor input_terminal_desc = {
+       .bLength =              USB_DT_AC_INPUT_TERMINAL_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   INPUT_TERMINAL,
+       .bTerminalID =          INPUT_TERMINAL_ID,
+       .wTerminalType =        USB_AC_TERMINAL_STREAMING,
+       .bAssocTerminal =       0,
+       .wChannelConfig =       0x3,
+};
+
+DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0);
+
+#define FEATURE_UNIT_ID                2
+static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = {
+       .bLength                = USB_DT_AC_FEATURE_UNIT_SIZE(0),
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = FEATURE_UNIT,
+       .bUnitID                = FEATURE_UNIT_ID,
+       .bSourceID              = INPUT_TERMINAL_ID,
+       .bControlSize           = 2,
+       .bmaControls[0]         = (FU_MUTE | FU_VOLUME),
+};
+
+static struct usb_audio_control mute_control = {
+       .list = LIST_HEAD_INIT(mute_control.list),
+       .name = "Mute Control",
+       .type = MUTE_CONTROL,
+       /* Todo: add real Mute control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control volume_control = {
+       .list = LIST_HEAD_INIT(volume_control.list),
+       .name = "Volume Control",
+       .type = VOLUME_CONTROL,
+       /* Todo: add real Volume control code */
+       .set = generic_set_cmd,
+       .get = generic_get_cmd,
+};
+
+static struct usb_audio_control_selector feature_unit = {
+       .list = LIST_HEAD_INIT(feature_unit.list),
+       .id = FEATURE_UNIT_ID,
+       .name = "Mute & Volume Control",
+       .type = FEATURE_UNIT,
+       .desc = (struct usb_descriptor_header *)&feature_unit_desc,
+};
+
+#define OUTPUT_TERMINAL_ID     3
+static struct usb_output_terminal_descriptor output_terminal_desc = {
+       .bLength                = USB_DT_AC_OUTPUT_TERMINAL_SIZE,
+       .bDescriptorType        = USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype     = OUTPUT_TERMINAL,
+       .bTerminalID            = OUTPUT_TERMINAL_ID,
+       .wTerminalType          = USB_AC_OUTPUT_TERMINAL_SPEAKER,
+       .bAssocTerminal         = FEATURE_UNIT_ID,
+       .bSourceID              = FEATURE_UNIT_ID,
+};
+
+/* B.4.1  Standard AS Interface Descriptor */
+static struct usb_interface_descriptor as_interface_alt_0_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+static struct usb_interface_descriptor as_interface_alt_1_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        1,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOSTREAMING,
+};
+
+/* B.4.2  Class-Specific AS Interface Descriptor */
+static struct usb_as_header_descriptor as_header_desc = {
+       .bLength =              USB_DT_AS_HEADER_SIZE,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   AS_GENERAL,
+       .bTerminalLink =        INPUT_TERMINAL_ID,
+       .bDelay =               1,
+       .wFormatTag =           USB_AS_AUDIO_FORMAT_TYPE_I_PCM,
+};
+
+DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1);
+
+static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = {
+       .bLength =              USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   FORMAT_TYPE,
+       .bFormatType =          USB_AS_FORMAT_TYPE_I,
+       .bSubframeSize =        2,
+       .bBitResolution =       16,
+       .bSamFreqType =         1,
+};
+
+/* Standard ISO OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor as_out_ep_desc __initdata = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_AS_ENDPOINT_ADAPTIVE
+                               | USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
+       .bInterval =            4,
+};
+
+/* Class-specific AS ISO OUT Endpoint Descriptor */
+static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = {
+       .bLength =              USB_AS_ISO_ENDPOINT_DESC_SIZE,
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   EP_GENERAL,
+       .bmAttributes =         1,
+       .bLockDelayUnits =      1,
+       .wLockDelay =           __constant_cpu_to_le16(1),
+};
+
+static struct usb_descriptor_header *f_audio_desc[] __initdata = {
+       (struct usb_descriptor_header *)&ac_interface_desc,
+       (struct usb_descriptor_header *)&ac_header_desc,
+
+       (struct usb_descriptor_header *)&input_terminal_desc,
+       (struct usb_descriptor_header *)&output_terminal_desc,
+       (struct usb_descriptor_header *)&feature_unit_desc,
+
+       (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+       (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+       (struct usb_descriptor_header *)&as_header_desc,
+
+       (struct usb_descriptor_header *)&as_type_i_desc,
+
+       (struct usb_descriptor_header *)&as_out_ep_desc,
+       (struct usb_descriptor_header *)&as_iso_out_desc,
+       NULL,
+};
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX                0
+#define STRING_PRODUCT_IDX             1
+
+static char manufacturer[50];
+
+static struct usb_string strings_dev[] = {
+       [STRING_MANUFACTURER_IDX].s = manufacturer,
+       [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = strings_dev,
+};
+
+static struct usb_gadget_strings *audio_strings[] = {
+       &stringtab_dev,
+       NULL,
+};
+
+/*
+ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
+ */
+
+/*-------------------------------------------------------------------------*/
+struct f_audio_buf {
+       u8 *buf;
+       int actual;
+       struct list_head list;
+};
+
+static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
+{
+       struct f_audio_buf *copy_buf;
+
+       copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
+       if (!copy_buf)
+               return (struct f_audio_buf *)-ENOMEM;
+
+       copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
+       if (!copy_buf->buf) {
+               kfree(copy_buf);
+               return (struct f_audio_buf *)-ENOMEM;
+       }
+
+       return copy_buf;
+}
+
+static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
+{
+       kfree(audio_buf->buf);
+       kfree(audio_buf);
+}
+/*-------------------------------------------------------------------------*/
+
+struct f_audio {
+       struct gaudio                   card;
+
+       /* endpoints handle full and/or high speeds */
+       struct usb_ep                   *out_ep;
+       struct usb_endpoint_descriptor  *out_desc;
+
+       spinlock_t                      lock;
+       struct f_audio_buf *copy_buf;
+       struct work_struct playback_work;
+       struct list_head play_queue;
+
+       /* Control Set command */
+       struct list_head cs;
+       u8 set_cmd;
+       struct usb_audio_control *set_con;
+};
+
+static inline struct f_audio *func_to_audio(struct usb_function *f)
+{
+       return container_of(f, struct f_audio, card.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_playback_work(struct work_struct *data)
+{
+       struct f_audio *audio = container_of(data, struct f_audio,
+                                       playback_work);
+       struct f_audio_buf *play_buf;
+
+       spin_lock_irq(&audio->lock);
+       if (list_empty(&audio->play_queue)) {
+               spin_unlock_irq(&audio->lock);
+               return;
+       }
+       play_buf = list_first_entry(&audio->play_queue,
+                       struct f_audio_buf, list);
+       list_del(&play_buf->list);
+       spin_unlock_irq(&audio->lock);
+
+       u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
+       f_audio_buffer_free(play_buf);
+
+       return;
+}
+
+static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       struct usb_composite_dev *cdev = audio->card.func.config->cdev;
+       struct f_audio_buf *copy_buf = audio->copy_buf;
+       int err;
+
+       if (!copy_buf)
+               return -EINVAL;
+
+       /* Copy buffer is full, add it to the play_queue */
+       if (audio_buf_size - copy_buf->actual < req->actual) {
+               list_add_tail(&copy_buf->list, &audio->play_queue);
+               schedule_work(&audio->playback_work);
+               copy_buf = f_audio_buffer_alloc(audio_buf_size);
+               if (copy_buf < 0)
+                       return -ENOMEM;
+       }
+
+       memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
+       copy_buf->actual += req->actual;
+       audio->copy_buf = copy_buf;
+
+       err = usb_ep_queue(ep, req, GFP_ATOMIC);
+       if (err)
+               ERROR(cdev, "%s queue req: %d\n", ep->name, err);
+
+       return 0;
+
+}
+
+static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct f_audio *audio = req->context;
+       int status = req->status;
+       u32 data = 0;
+       struct usb_ep *out_ep = audio->out_ep;
+
+       switch (status) {
+
+       case 0:                         /* normal completion? */
+               if (ep == out_ep)
+                       f_audio_out_ep_complete(ep, req);
+               else if (audio->set_con) {
+                       memcpy(&data, req->buf, req->length);
+                       audio->set_con->set(audio->set_con, audio->set_cmd,
+                                       le16_to_cpu(data));
+                       audio->set_con = NULL;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static int audio_set_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel) {
+                                       audio->set_con = con;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       audio->set_cmd = cmd;
+       req->context = audio;
+       req->complete = f_audio_complete;
+
+       return len;
+}
+
+static int audio_get_intf_req(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u8                      id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+       u16                     len = le16_to_cpu(ctrl->wLength);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u8                      con_sel = (w_value >> 8) & 0xFF;
+       u8                      cmd = (ctrl->bRequest & 0x0F);
+       struct usb_audio_control_selector *cs;
+       struct usb_audio_control *con;
+
+       DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
+                       ctrl->bRequest, w_value, len, id);
+
+       list_for_each_entry(cs, &audio->cs, list) {
+               if (cs->id == id) {
+                       list_for_each_entry(con, &cs->control, list) {
+                               if (con->type == con_sel && con->get) {
+                                       value = con->get(con, cmd);
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+
+       req->context = audio;
+       req->complete = f_audio_complete;
+       memcpy(req->buf, &value, len);
+
+       return len;
+}
+
+static int
+f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
+       int                     value = -EOPNOTSUPP;
+       u16                     w_index = le16_to_cpu(ctrl->wIndex);
+       u16                     w_value = le16_to_cpu(ctrl->wValue);
+       u16                     w_length = le16_to_cpu(ctrl->wLength);
+
+       /* composite driver infrastructure handles everything except
+        * Audio class messages; interface activation uses set_alt().
+        */
+       switch (ctrl->bRequestType) {
+       case USB_AUDIO_SET_INTF:
+               value = audio_set_intf_req(f, ctrl);
+               break;
+
+       case USB_AUDIO_GET_INTF:
+               value = audio_get_intf_req(f, ctrl);
+               break;
+
+       default:
+               ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+       }
+
+       /* respond with data transfer or status phase? */
+       if (value >= 0) {
+               DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
+                       ctrl->bRequestType, ctrl->bRequest,
+                       w_value, w_index, w_length);
+               req->zero = 0;
+               req->length = value;
+               value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+               if (value < 0)
+                       ERROR(cdev, "audio response on err %d\n", value);
+       }
+
+       /* device either stalls (value < 0) or reports success */
+       return value;
+}
+
+static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+       struct f_audio          *audio = func_to_audio(f);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_ep *out_ep = audio->out_ep;
+       struct usb_request *req;
+       int i = 0, err = 0;
+
+       DBG(cdev, "intf %d, alt %d\n", intf, alt);
+
+       if (intf == 1) {
+               if (alt == 1) {
+                       usb_ep_enable(out_ep, audio->out_desc);
+                       out_ep->driver_data = audio;
+                       audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
+
+                       /*
+                        * allocate a bunch of read buffers
+                        * and queue them all at once.
+                        */
+                       for (i = 0; i < req_count && err == 0; i++) {
+                               req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
+                               if (req) {
+                                       req->buf = kzalloc(req_buf_size,
+                                                       GFP_ATOMIC);
+                                       if (req->buf) {
+                                               req->length = req_buf_size;
+                                               req->context = audio;
+                                               req->complete =
+                                                       f_audio_complete;
+                                               err = usb_ep_queue(out_ep,
+                                                       req, GFP_ATOMIC);
+                                               if (err)
+                                                       ERROR(cdev,
+                                                       "%s queue req: %d\n",
+                                                       out_ep->name, err);
+                                       } else
+                                               err = -ENOMEM;
+                               } else
+                                       err = -ENOMEM;
+                       }
+
+               } else {
+                       struct f_audio_buf *copy_buf = audio->copy_buf;
+                       if (copy_buf) {
+                               list_add_tail(&copy_buf->list,
+                                               &audio->play_queue);
+                               schedule_work(&audio->playback_work);
+                       }
+               }
+       }
+
+       return err;
+}
+
+static void f_audio_disable(struct usb_function *f)
+{
+       return;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void f_audio_build_desc(struct f_audio *audio)
+{
+       struct gaudio *card = &audio->card;
+       u8 *sam_freq;
+       int rate;
+
+       /* Set channel numbers */
+       input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
+       as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
+
+       /* Set sample rates */
+       rate = u_audio_get_playback_rate(card);
+       sam_freq = as_type_i_desc.tSamFreq[0];
+       memcpy(sam_freq, &rate, 3);
+
+       /* Todo: Set Sample bits and other parameters */
+
+       return;
+}
+
+/* audio function driver setup/binding */
+static int __init
+f_audio_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_audio          *audio = func_to_audio(f);
+       int                     status;
+       struct usb_ep           *ep;
+
+       f_audio_build_desc(audio);
+
+       /* allocate instance-specific interface IDs, and patch descriptors */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       ac_interface_desc.bInterfaceNumber = status;
+
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       as_interface_alt_0_desc.bInterfaceNumber = status;
+       as_interface_alt_1_desc.bInterfaceNumber = status;
+
+       status = -ENODEV;
+
+       /* allocate instance-specific endpoints */
+       ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
+       if (!ep)
+               goto fail;
+       audio->out_ep = ep;
+       ep->driver_data = cdev; /* claim */
+
+       status = -ENOMEM;
+
+       /* supcard all relevant hardware speeds... we expect that when
+        * hardware is dual speed, all bulk-capable endpoints work at
+        * both speeds
+        */
+
+       /* copy descriptors, and track endpoint copies */
+       if (gadget_is_dualspeed(c->cdev->gadget)) {
+               c->highspeed = true;
+               f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
+       } else
+               f->descriptors = usb_copy_descriptors(f_audio_desc);
+
+       return 0;
+
+fail:
+
+       return status;
+}
+
+static void
+f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_audio          *audio = func_to_audio(f);
+
+       usb_free_descriptors(f->descriptors);
+       kfree(audio);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Todo: add more control selecotor dynamically */
+int __init control_selector_init(struct f_audio *audio)
+{
+       INIT_LIST_HEAD(&audio->cs);
+       list_add(&feature_unit.list, &audio->cs);
+
+       INIT_LIST_HEAD(&feature_unit.control);
+       list_add(&mute_control.list, &feature_unit.control);
+       list_add(&volume_control.list, &feature_unit.control);
+
+       volume_control.data[_CUR] = 0xffc0;
+       volume_control.data[_MIN] = 0xe3a0;
+       volume_control.data[_MAX] = 0xfff0;
+       volume_control.data[_RES] = 0x0030;
+
+       return 0;
+}
+
+/**
+ * audio_bind_config - add USB audio fucntion to a configuration
+ * @c: the configuration to supcard the USB audio function
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ */
+int __init audio_bind_config(struct usb_configuration *c)
+{
+       struct f_audio *audio;
+       int status;
+
+       /* allocate and initialize one new instance */
+       audio = kzalloc(sizeof *audio, GFP_KERNEL);
+       if (!audio)
+               return -ENOMEM;
+
+       audio->card.func.name = "g_audio";
+       audio->card.gadget = c->cdev->gadget;
+
+       INIT_LIST_HEAD(&audio->play_queue);
+       spin_lock_init(&audio->lock);
+
+       /* set up ASLA audio devices */
+       status = gaudio_setup(&audio->card);
+       if (status < 0)
+               goto setup_fail;
+
+       audio->card.func.strings = audio_strings;
+       audio->card.func.bind = f_audio_bind;
+       audio->card.func.unbind = f_audio_unbind;
+       audio->card.func.set_alt = f_audio_set_alt;
+       audio->card.func.setup = f_audio_setup;
+       audio->card.func.disable = f_audio_disable;
+       audio->out_desc = &as_out_ep_desc;
+
+       control_selector_init(audio);
+
+       INIT_WORK(&audio->playback_work, f_audio_playback_work);
+
+       status = usb_add_function(c, &audio->card.func);
+       if (status)
+               goto add_fail;
+
+       INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
+               audio_buf_size, req_buf_size, req_count);
+
+       return status;
+
+add_fail:
+       gaudio_cleanup(&audio->card);
+setup_fail:
+       kfree(audio);
+       return status;
+}
index 3279a47260428a1122cc409ae64ac8994f4c3eee..424a37c5773f5ac341b9659bc4480cb9ee3e9df6 100644 (file)
@@ -475,7 +475,9 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
                if (rndis->port.in_ep->driver_data) {
                        DBG(cdev, "reset rndis\n");
                        gether_disconnect(&rndis->port);
-               } else {
+               }
+
+               if (!rndis->port.in) {
                        DBG(cdev, "init rndis\n");
                        rndis->port.in = ep_choose(cdev->gadget,
                                        rndis->hs.in, rndis->fs.in);
index 381a53b3e11c4a4e4dcd3308bdfcbfd1f2323c6d..1e6aa504d58a9390d196a0b5c3823e3e4f299e69 100644 (file)
 #include <linux/freezer.h>
 #include <linux/utsname.h>
 
+#include <asm/unaligned.h>
+
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
@@ -799,29 +801,9 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
 
 /* Routines for unaligned data access */
 
-static u16 get_be16(u8 *buf)
-{
-       return ((u16) buf[0] << 8) | ((u16) buf[1]);
-}
-
-static u32 get_be32(u8 *buf)
-{
-       return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
-                       ((u32) buf[2] << 8) | ((u32) buf[3]);
-}
-
-static void put_be16(u8 *buf, u16 val)
-{
-       buf[0] = val >> 8;
-       buf[1] = val;
-}
-
-static void put_be32(u8 *buf, u32 val)
+static u32 get_unaligned_be24(u8 *buf)
 {
-       buf[0] = val >> 24;
-       buf[1] = val >> 16;
-       buf[2] = val >> 8;
-       buf[3] = val & 0xff;
+       return 0xffffff & (u32) get_unaligned_be32(buf - 1);
 }
 
 
@@ -1582,9 +1564,9 @@ static int do_read(struct fsg_dev *fsg)
        /* Get the starting Logical Block Address and check that it's
         * not too big */
        if (fsg->cmnd[0] == SC_READ_6)
-               lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
+               lba = get_unaligned_be24(&fsg->cmnd[1]);
        else {
-               lba = get_be32(&fsg->cmnd[2]);
+               lba = get_unaligned_be32(&fsg->cmnd[2]);
 
                /* We allow DPO (Disable Page Out = don't save data in the
                 * cache) and FUA (Force Unit Access = don't read from the
@@ -1717,9 +1699,9 @@ static int do_write(struct fsg_dev *fsg)
        /* Get the starting Logical Block Address and check that it's
         * not too big */
        if (fsg->cmnd[0] == SC_WRITE_6)
-               lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
+               lba = get_unaligned_be24(&fsg->cmnd[1]);
        else {
-               lba = get_be32(&fsg->cmnd[2]);
+               lba = get_unaligned_be32(&fsg->cmnd[2]);
 
                /* We allow DPO (Disable Page Out = don't save data in the
                 * cache) and FUA (Force Unit Access = write directly to the
@@ -1940,7 +1922,7 @@ static int do_verify(struct fsg_dev *fsg)
 
        /* Get the starting Logical Block Address and check that it's
         * not too big */
-       lba = get_be32(&fsg->cmnd[2]);
+       lba = get_unaligned_be32(&fsg->cmnd[2]);
        if (lba >= curlun->num_sectors) {
                curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
                return -EINVAL;
@@ -1953,7 +1935,7 @@ static int do_verify(struct fsg_dev *fsg)
                return -EINVAL;
        }
 
-       verification_length = get_be16(&fsg->cmnd[7]);
+       verification_length = get_unaligned_be16(&fsg->cmnd[7]);
        if (unlikely(verification_length == 0))
                return -EIO;            // No default reply
 
@@ -2103,7 +2085,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
        memset(buf, 0, 18);
        buf[0] = valid | 0x70;                  // Valid, current error
        buf[2] = SK(sd);
-       put_be32(&buf[3], sdinfo);              // Sense information
+       put_unaligned_be32(sdinfo, &buf[3]);    /* Sense information */
        buf[7] = 18 - 8;                        // Additional sense length
        buf[12] = ASC(sd);
        buf[13] = ASCQ(sd);
@@ -2114,7 +2096,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 {
        struct lun      *curlun = fsg->curlun;
-       u32             lba = get_be32(&fsg->cmnd[2]);
+       u32             lba = get_unaligned_be32(&fsg->cmnd[2]);
        int             pmi = fsg->cmnd[8];
        u8              *buf = (u8 *) bh->buf;
 
@@ -2124,8 +2106,9 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
                return -EINVAL;
        }
 
-       put_be32(&buf[0], curlun->num_sectors - 1);     // Max logical block
-       put_be32(&buf[4], 512);                         // Block length
+       put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+                                               /* Max logical block */
+       put_unaligned_be32(512, &buf[4]);       /* Block length */
        return 8;
 }
 
@@ -2144,7 +2127,7 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
                dest[0] = 0;            /* Reserved */
        } else {
                /* Absolute sector */
-               put_be32(dest, addr);
+               put_unaligned_be32(addr, dest);
        }
 }
 
@@ -2152,7 +2135,7 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
 {
        struct lun      *curlun = fsg->curlun;
        int             msf = fsg->cmnd[1] & 0x02;
-       u32             lba = get_be32(&fsg->cmnd[2]);
+       u32             lba = get_unaligned_be32(&fsg->cmnd[2]);
        u8              *buf = (u8 *) bh->buf;
 
        if ((fsg->cmnd[1] & ~0x02) != 0) {              /* Mask away MSF */
@@ -2252,10 +2235,13 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
                        buf[2] = 0x04;  // Write cache enable,
                                        // Read cache not disabled
                                        // No cache retention priorities
-                       put_be16(&buf[4], 0xffff);  // Don't disable prefetch
-                                       // Minimum prefetch = 0
-                       put_be16(&buf[8], 0xffff);  // Maximum prefetch
-                       put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling
+                       put_unaligned_be16(0xffff, &buf[4]);
+                                       /* Don't disable prefetch */
+                                       /* Minimum prefetch = 0 */
+                       put_unaligned_be16(0xffff, &buf[8]);
+                                       /* Maximum prefetch */
+                       put_unaligned_be16(0xffff, &buf[10]);
+                                       /* Maximum prefetch ceiling */
                }
                buf += 12;
        }
@@ -2272,7 +2258,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
        if (mscmnd == SC_MODE_SENSE_6)
                buf0[0] = len - 1;
        else
-               put_be16(buf0, len - 2);
+               put_unaligned_be16(len - 2, buf0);
        return len;
 }
 
@@ -2360,9 +2346,10 @@ static int do_read_format_capacities(struct fsg_dev *fsg,
        buf[3] = 8;             // Only the Current/Maximum Capacity Descriptor
        buf += 4;
 
-       put_be32(&buf[0], curlun->num_sectors);         // Number of blocks
-       put_be32(&buf[4], 512);                         // Block length
-       buf[4] = 0x02;                                  // Current capacity
+       put_unaligned_be32(curlun->num_sectors, &buf[0]);
+                                               /* Number of blocks */
+       put_unaligned_be32(512, &buf[4]);       /* Block length */
+       buf[4] = 0x02;                          /* Current capacity */
        return 12;
 }
 
@@ -2882,7 +2869,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_MODE_SELECT_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
                                (1<<1) | (3<<7), 0,
                                "MODE SELECT(10)")) == 0)
@@ -2898,7 +2885,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_MODE_SENSE_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (1<<1) | (1<<2) | (3<<7), 0,
                                "MODE SENSE(10)")) == 0)
@@ -2923,7 +2910,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be16(&fsg->cmnd[7]) << 9;
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (1<<1) | (0xf<<2) | (3<<7), 1,
                                "READ(10)")) == 0)
@@ -2931,7 +2919,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_12:
-               fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be32(&fsg->cmnd[6]) << 9;
                if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
                                (1<<1) | (0xf<<2) | (0xf<<6), 1,
                                "READ(12)")) == 0)
@@ -2949,7 +2938,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
        case SC_READ_HEADER:
                if (!mod_data.cdrom)
                        goto unknown_cmnd;
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (3<<7) | (0x1f<<1), 1,
                                "READ HEADER")) == 0)
@@ -2959,7 +2948,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
        case SC_READ_TOC:
                if (!mod_data.cdrom)
                        goto unknown_cmnd;
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (7<<6) | (1<<1), 1,
                                "READ TOC")) == 0)
@@ -2967,7 +2956,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_READ_FORMAT_CAPACITIES:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
+               fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
                if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
                                (3<<7), 1,
                                "READ FORMAT CAPACITIES")) == 0)
@@ -3025,7 +3014,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_WRITE_10:
-               fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be16(&fsg->cmnd[7]) << 9;
                if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
                                (1<<1) | (0xf<<2) | (3<<7), 1,
                                "WRITE(10)")) == 0)
@@ -3033,7 +3023,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
                break;
 
        case SC_WRITE_12:
-               fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
+               fsg->data_size_from_cmnd =
+                               get_unaligned_be32(&fsg->cmnd[6]) << 9;
                if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
                                (1<<1) | (0xf<<2) | (0xf<<6), 1,
                                "WRITE(12)")) == 0)
diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mx3_udc.c
new file mode 100644 (file)
index 0000000..4bc2bf3
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * Description:
+ * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
+ * driver to function correctly on these systems.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+
+static struct clk *mxc_ahb_clk;
+static struct clk *mxc_usb_clk;
+
+int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata;
+       unsigned long freq;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+
+       mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
+       if (IS_ERR(mxc_ahb_clk))
+               return PTR_ERR(mxc_ahb_clk);
+
+       ret = clk_enable(mxc_ahb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
+               goto eenahb;
+       }
+
+       /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
+       mxc_usb_clk = clk_get(&pdev->dev, "usb");
+       if (IS_ERR(mxc_usb_clk)) {
+               dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
+               ret = PTR_ERR(mxc_usb_clk);
+               goto egusb;
+       }
+
+       freq = clk_get_rate(mxc_usb_clk);
+       if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
+           (freq < 59999000 || freq > 60001000)) {
+               dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
+               goto eclkrate;
+       }
+
+       ret = clk_enable(mxc_usb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
+               goto eenusb;
+       }
+
+       return 0;
+
+eenusb:
+eclkrate:
+       clk_put(mxc_usb_clk);
+       mxc_usb_clk = NULL;
+egusb:
+       clk_disable(mxc_ahb_clk);
+eenahb:
+       clk_put(mxc_ahb_clk);
+       return ret;
+}
+
+void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+       /* ULPI transceivers don't need usbpll */
+       if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
+               clk_disable(mxc_usb_clk);
+               clk_put(mxc_usb_clk);
+               mxc_usb_clk = NULL;
+       }
+}
+
+void fsl_udc_clk_release(void)
+{
+       if (mxc_usb_clk) {
+               clk_disable(mxc_usb_clk);
+               clk_put(mxc_usb_clk);
+       }
+       clk_disable(mxc_ahb_clk);
+       clk_put(mxc_ahb_clk);
+}
similarity index 99%
rename from drivers/usb/gadget/fsl_usb2_udc.c
rename to drivers/usb/gadget/fsl_udc_core.c
index 9d7b95d4e3d2e523b7792f6b412b39bf6958b914..42a74b8a0bb82e3dcc0ab0becf156e44db9c32a1 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
+#include <linux/delay.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -57,7 +58,9 @@ static const char driver_name[] = "fsl-usb2-udc";
 static const char driver_desc[] = DRIVER_DESC;
 
 static struct usb_dr_device *dr_regs;
+#ifndef CONFIG_ARCH_MXC
 static struct usb_sys_interface *usb_sys_regs;
+#endif
 
 /* it is initialized in probe()  */
 static struct fsl_udc *udc_controller = NULL;
@@ -174,10 +177,34 @@ static void nuke(struct fsl_ep *ep, int status)
 
 static int dr_controller_setup(struct fsl_udc *udc)
 {
-       unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+       unsigned int tmp, portctrl;
+#ifndef CONFIG_ARCH_MXC
+       unsigned int ctrl;
+#endif
        unsigned long timeout;
 #define FSL_UDC_RESET_TIMEOUT 1000
 
+       /* Config PHY interface */
+       portctrl = fsl_readl(&dr_regs->portsc1);
+       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+       switch (udc->phy_mode) {
+       case FSL_USB2_PHY_ULPI:
+               portctrl |= PORTSCX_PTS_ULPI;
+               break;
+       case FSL_USB2_PHY_UTMI_WIDE:
+               portctrl |= PORTSCX_PTW_16BIT;
+               /* fall through */
+       case FSL_USB2_PHY_UTMI:
+               portctrl |= PORTSCX_PTS_UTMI;
+               break;
+       case FSL_USB2_PHY_SERIAL:
+               portctrl |= PORTSCX_PTS_FSLS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       fsl_writel(portctrl, &dr_regs->portsc1);
+
        /* Stop and reset the usb controller */
        tmp = fsl_readl(&dr_regs->usbcmd);
        tmp &= ~USB_CMD_RUN_STOP;
@@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
                udc->ep_qh, (int)tmp,
                fsl_readl(&dr_regs->endpointlistaddr));
 
-       /* Config PHY interface */
-       portctrl = fsl_readl(&dr_regs->portsc1);
-       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
-       switch (udc->phy_mode) {
-       case FSL_USB2_PHY_ULPI:
-               portctrl |= PORTSCX_PTS_ULPI;
-               break;
-       case FSL_USB2_PHY_UTMI_WIDE:
-               portctrl |= PORTSCX_PTW_16BIT;
-               /* fall through */
-       case FSL_USB2_PHY_UTMI:
-               portctrl |= PORTSCX_PTS_UTMI;
-               break;
-       case FSL_USB2_PHY_SERIAL:
-               portctrl |= PORTSCX_PTS_FSLS;
-               break;
-       default:
-               return -EINVAL;
-       }
-       fsl_writel(portctrl, &dr_regs->portsc1);
-
        /* Config control enable i/o output, cpu endian register */
+#ifndef CONFIG_ARCH_MXC
        ctrl = __raw_readl(&usb_sys_regs->control);
        ctrl |= USB_CTRL_IOENB;
        __raw_writel(ctrl, &usb_sys_regs->control);
+#endif
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
        /* Turn on cache snooping hardware, since some PowerPC platforms
@@ -2043,6 +2051,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
        size -= t;
        next += t;
 
+#ifndef CONFIG_ARCH_MXC
        tmp_reg = usb_sys_regs->snoop1;
        t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
        size -= t;
@@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
                        tmp_reg);
        size -= t;
        next += t;
+#endif
 
        /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
        ep = &udc->eps[0];
@@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_kfree;
        }
 
-       dr_regs = ioremap(res->start, res->end - res->start + 1);
+       dr_regs = ioremap(res->start, resource_size(res));
        if (!dr_regs) {
                ret = -ENOMEM;
                goto err_release_mem_region;
        }
 
+#ifndef CONFIG_ARCH_MXC
        usb_sys_regs = (struct usb_sys_interface *)
                        ((u32)dr_regs + USB_DR_SYS_OFFSET);
+#endif
+
+       /* Initialize USB clocks */
+       ret = fsl_udc_clk_init(pdev);
+       if (ret < 0)
+               goto err_iounmap_noclk;
 
        /* Read Device Controller Capability Parameters register */
        dccparams = fsl_readl(&dr_regs->dccparams);
@@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
         * leave usbintr reg untouched */
        dr_controller_setup(udc_controller);
 
+       fsl_udc_clk_finalize(pdev);
+
        /* Setup gadget structure */
        udc_controller->gadget.ops = &fsl_gadget_ops;
        udc_controller->gadget.is_dualspeed = 1;
@@ -2362,6 +2381,8 @@ err_unregister:
 err_free_irq:
        free_irq(udc_controller->irq, udc_controller);
 err_iounmap:
+       fsl_udc_clk_release();
+err_iounmap_noclk:
        iounmap(dr_regs);
 err_release_mem_region:
        release_mem_region(res->start, res->end - res->start + 1);
@@ -2384,6 +2405,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
                return -ENODEV;
        udc_controller->done = &done;
 
+       fsl_udc_clk_release();
+
        /* DR has been stopped in usb_gadget_unregister_driver() */
        remove_proc_file();
 
index e63ef12645f5524582a65059875f404f438a6554..20aeceed48c712e1db819b0047f970787923205f 100644 (file)
@@ -563,4 +563,22 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
                                        * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
 #define get_pipe_by_ep(EP)     (ep_index(EP) * 2 + ep_is_in(EP))
 
+struct platform_device;
+#ifdef CONFIG_ARCH_MXC
+int fsl_udc_clk_init(struct platform_device *pdev);
+void fsl_udc_clk_finalize(struct platform_device *pdev);
+void fsl_udc_clk_release(void);
+#else
+static inline int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       return 0;
+}
+static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+}
+static inline void fsl_udc_clk_release(void)
+{
+}
+#endif
+
 #endif
index ec6d439a2aa588c0872e86335ff22079f6463f6c..8e0e9a0b736479a77aa59a8e73bce9a2c28aa512 100644 (file)
 #define gadget_is_musbhdrc(g)  0
 #endif
 
+#ifdef CONFIG_USB_GADGET_LANGWELL
+#define gadget_is_langwell(g)  (!strcmp("langwell_udc", (g)->name))
+#else
+#define gadget_is_langwell(g)  0
+#endif
+
 /* from Montavista kernel (?) */
 #ifdef CONFIG_USB_GADGET_MPC8272
 #define gadget_is_mpc8272(g)   !strcmp("mpc8272_udc", (g)->name)
@@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
                return 0x22;
        else if (gadget_is_ci13xxx(gadget))
                return 0x23;
+       else if (gadget_is_langwell(gadget))
+               return 0x24;
        return -ENOENT;
 }
 
index de010c939dbbca672c5f992f85471aab5d0b6dec..112bb40a427cd4a217ed69af61d73effc8863959 100644 (file)
@@ -110,10 +110,10 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
                return -EINVAL;
        if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
                return -ESHUTDOWN;
-       if (ep->num != (desc->bEndpointAddress & 0x0f))
+       if (ep->num != usb_endpoint_num(desc))
                return -EINVAL;
 
-       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       switch (usb_endpoint_type(desc)) {
        case USB_ENDPOINT_XFER_BULK:
        case USB_ENDPOINT_XFER_INT:
                break;
@@ -142,7 +142,7 @@ goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        /* ep1/ep2 dma direction is chosen early; it works in the other
         * direction, with pio.  be cautious with out-dma.
         */
-       ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
+       ep->is_in = usb_endpoint_dir_in(desc);
        if (ep->is_in) {
                mode |= 1;
                ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
index 168658b4b4e26d2bc7b2614cf5b8cb891a3614e5..c52a681f376cba495303fe843e9cd0839a15a234 100644 (file)
@@ -415,6 +415,13 @@ static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req)
        u8      *buf;
        int     length, count, temp;
 
+       if (unlikely(__raw_readl(imx_ep->imx_usb->base +
+                                USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) {
+               D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n",
+                       __func__, imx_ep->ep.name);
+               return -1;
+       }
+
        buf = req->req.buf + req->req.actual;
        prefetch(buf);
 
@@ -734,9 +741,12 @@ static struct usb_request *imx_ep_alloc_request
 {
        struct imx_request *req;
 
+       if (!usb_ep)
+               return NULL;
+
        req = kzalloc(sizeof *req, gfp_flags);
-       if (!req || !usb_ep)
-               return 0;
+       if (!req)
+               return NULL;
 
        INIT_LIST_HEAD(&req->queue);
        req->in_use = 0;
index d20937f28a1988209557a356f061900ec2706d35..7d33f50b5874bc683ed42db17aa87c8b59544035 100644 (file)
@@ -384,9 +384,8 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                return value;
 
        /* halt any endpoint by doing a "wrong direction" i/o call */
-       if (data->desc.bEndpointAddress & USB_DIR_IN) {
-               if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               == USB_ENDPOINT_XFER_ISOC)
+       if (usb_endpoint_dir_in(&data->desc)) {
+               if (usb_endpoint_xfer_isoc(&data->desc))
                        return -EINVAL;
                DBG (data->dev, "%s halt\n", data->name);
                spin_lock_irq (&data->dev->lock);
@@ -428,9 +427,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                return value;
 
        /* halt any endpoint by doing a "wrong direction" i/o call */
-       if (!(data->desc.bEndpointAddress & USB_DIR_IN)) {
-               if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               == USB_ENDPOINT_XFER_ISOC)
+       if (!usb_endpoint_dir_in(&data->desc)) {
+               if (usb_endpoint_xfer_isoc(&data->desc))
                        return -EINVAL;
                DBG (data->dev, "%s halt\n", data->name);
                spin_lock_irq (&data->dev->lock);
@@ -691,7 +689,7 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
        struct ep_data          *epdata = iocb->ki_filp->private_data;
        char                    *buf;
 
-       if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
+       if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
                return -EINVAL;
 
        buf = kmalloc(iocb->ki_left, GFP_KERNEL);
@@ -711,7 +709,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
        size_t                  len = 0;
        int                     i = 0;
 
-       if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
+       if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
                return -EINVAL;
 
        buf = kmalloc(iocb->ki_left, GFP_KERNEL);
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
new file mode 100644 (file)
index 0000000..6829d59
--- /dev/null
@@ -0,0 +1,3373 @@
+/*
+ * Intel Langwell USB Device Controller driver
+ * Copyright (C) 2008-2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+/* #undef      DEBUG */
+/* #undef      VERBOSE */
+
+#if defined(CONFIG_USB_LANGWELL_OTG)
+#define        OTG_TRANSCEIVER
+#endif
+
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "langwell_udc.h"
+
+
+#define        DRIVER_DESC             "Intel Langwell USB Device Controller driver"
+#define        DRIVER_VERSION          "16 May 2009"
+
+static const char driver_name[] = "langwell_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+
+/* controller device global variable */
+static struct langwell_udc     *the_controller;
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor
+langwell_ep0_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     0,
+       .bmAttributes =         USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize =       EP0_MAX_PKT_SIZE,
+};
+
+
+/*-------------------------------------------------------------------------*/
+/* debugging */
+
+#ifdef DEBUG
+#define        DBG(dev, fmt, args...) \
+       pr_debug("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+#else
+#define        DBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* DEBUG */
+
+
+#ifdef VERBOSE
+#define        VDBG DBG
+#else
+#define        VDBG(dev, fmt, args...) \
+       do { } while (0)
+#endif /* VERBOSE */
+
+
+#define        ERROR(dev, fmt, args...) \
+       pr_err("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        WARNING(dev, fmt, args...) \
+       pr_warning("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+#define        INFO(dev, fmt, args...) \
+       pr_info("%s %s: " fmt , driver_name, \
+                       pci_name(dev->pdev), ## args)
+
+
+#ifdef VERBOSE
+static inline void print_all_registers(struct langwell_udc *dev)
+{
+       int     i;
+
+       /* Capability Registers */
+       printk(KERN_DEBUG "Capability Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       CAP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_cap_regs));
+       printk(KERN_DEBUG "caplength=0x%02x\n",
+                       readb(&dev->cap_regs->caplength));
+       printk(KERN_DEBUG "hciversion=0x%04x\n",
+                       readw(&dev->cap_regs->hciversion));
+       printk(KERN_DEBUG "hcsparams=0x%08x\n",
+                       readl(&dev->cap_regs->hcsparams));
+       printk(KERN_DEBUG "hccparams=0x%08x\n",
+                       readl(&dev->cap_regs->hccparams));
+       printk(KERN_DEBUG "dciversion=0x%04x\n",
+                       readw(&dev->cap_regs->dciversion));
+       printk(KERN_DEBUG "dccparams=0x%08x\n",
+                       readl(&dev->cap_regs->dccparams));
+
+       /* Operational Registers */
+       printk(KERN_DEBUG "Operational Registers (offset: "
+                       "0x%04x, length: 0x%08x)\n",
+                       OP_REG_OFFSET,
+                       (u32)sizeof(struct langwell_op_regs));
+       printk(KERN_DEBUG "extsts=0x%08x\n",
+                       readl(&dev->op_regs->extsts));
+       printk(KERN_DEBUG "extintr=0x%08x\n",
+                       readl(&dev->op_regs->extintr));
+       printk(KERN_DEBUG "usbcmd=0x%08x\n",
+                       readl(&dev->op_regs->usbcmd));
+       printk(KERN_DEBUG "usbsts=0x%08x\n",
+                       readl(&dev->op_regs->usbsts));
+       printk(KERN_DEBUG "usbintr=0x%08x\n",
+                       readl(&dev->op_regs->usbintr));
+       printk(KERN_DEBUG "frindex=0x%08x\n",
+                       readl(&dev->op_regs->frindex));
+       printk(KERN_DEBUG "ctrldssegment=0x%08x\n",
+                       readl(&dev->op_regs->ctrldssegment));
+       printk(KERN_DEBUG "deviceaddr=0x%08x\n",
+                       readl(&dev->op_regs->deviceaddr));
+       printk(KERN_DEBUG "endpointlistaddr=0x%08x\n",
+                       readl(&dev->op_regs->endpointlistaddr));
+       printk(KERN_DEBUG "ttctrl=0x%08x\n",
+                       readl(&dev->op_regs->ttctrl));
+       printk(KERN_DEBUG "burstsize=0x%08x\n",
+                       readl(&dev->op_regs->burstsize));
+       printk(KERN_DEBUG "txfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txfilltuning));
+       printk(KERN_DEBUG "txttfilltuning=0x%08x\n",
+                       readl(&dev->op_regs->txttfilltuning));
+       printk(KERN_DEBUG "ic_usb=0x%08x\n",
+                       readl(&dev->op_regs->ic_usb));
+       printk(KERN_DEBUG "ulpi_viewport=0x%08x\n",
+                       readl(&dev->op_regs->ulpi_viewport));
+       printk(KERN_DEBUG "configflag=0x%08x\n",
+                       readl(&dev->op_regs->configflag));
+       printk(KERN_DEBUG "portsc1=0x%08x\n",
+                       readl(&dev->op_regs->portsc1));
+       printk(KERN_DEBUG "devlc=0x%08x\n",
+                       readl(&dev->op_regs->devlc));
+       printk(KERN_DEBUG "otgsc=0x%08x\n",
+                       readl(&dev->op_regs->otgsc));
+       printk(KERN_DEBUG "usbmode=0x%08x\n",
+                       readl(&dev->op_regs->usbmode));
+       printk(KERN_DEBUG "endptnak=0x%08x\n",
+                       readl(&dev->op_regs->endptnak));
+       printk(KERN_DEBUG "endptnaken=0x%08x\n",
+                       readl(&dev->op_regs->endptnaken));
+       printk(KERN_DEBUG "endptsetupstat=0x%08x\n",
+                       readl(&dev->op_regs->endptsetupstat));
+       printk(KERN_DEBUG "endptprime=0x%08x\n",
+                       readl(&dev->op_regs->endptprime));
+       printk(KERN_DEBUG "endptflush=0x%08x\n",
+                       readl(&dev->op_regs->endptflush));
+       printk(KERN_DEBUG "endptstat=0x%08x\n",
+                       readl(&dev->op_regs->endptstat));
+       printk(KERN_DEBUG "endptcomplete=0x%08x\n",
+                       readl(&dev->op_regs->endptcomplete));
+
+       for (i = 0; i < dev->ep_max / 2; i++) {
+               printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n",
+                               i, readl(&dev->op_regs->endptctrl[i]));
+       }
+}
+#endif /* VERBOSE */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define        DIR_STRING(bAddress)    (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+#define is_in(ep)      (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \
+                       USB_DIR_IN) : ((ep)->desc->bEndpointAddress \
+                       & USB_DIR_IN) == USB_DIR_IN)
+
+
+#ifdef 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 "int";
+       };
+
+       return "control";
+}
+#endif
+
+
+/* configure endpoint control registers */
+static void ep_reset(struct langwell_ep *ep, unsigned char ep_num,
+               unsigned char is_in, unsigned char ep_type)
+{
+       struct langwell_udc     *dev;
+       u32                     endptctrl;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in) {    /* TX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_TXR;
+               endptctrl |= EPCTRL_TXE;
+               endptctrl |= ep_type << EPCTRL_TXT_SHIFT;
+       } else {        /* RX */
+               if (ep_num)
+                       endptctrl |= EPCTRL_RXR;
+               endptctrl |= EPCTRL_RXE;
+               endptctrl |= ep_type << EPCTRL_RXT_SHIFT;
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* reset ep0 dQH and endptctrl */
+static void ep0_reset(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* ep0 in and out */
+       for (i = 0; i < 2; i++) {
+               ep = &dev->ep[i];
+               ep->dev = dev;
+
+               /* ep0 dQH */
+               ep->dqh = &dev->ep_dqh[i];
+
+               /* configure ep0 endpoint capabilities in dQH */
+               ep->dqh->dqh_ios = 1;
+               ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE;
+
+               /* FIXME: enable ep0-in HW zero length termination select */
+               if (is_in(ep))
+                       ep->dqh->dqh_zlt = 0;
+               ep->dqh->dqh_mult = 0;
+
+               /* configure ep0 control registers */
+               ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoints operations */
+
+/* configure endpoint, making it usable */
+static int langwell_ep_enable(struct usb_ep *_ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct langwell_udc     *dev;
+       struct langwell_ep      *ep;
+       u16                     max = 0;
+       unsigned long           flags;
+       int                     retval = 0;
+       unsigned char           zlt, ios = 0, mult = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !desc || ep->desc
+                       || desc->bDescriptorType != USB_DT_ENDPOINT)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       max = le16_to_cpu(desc->wMaxPacketSize);
+
+       /*
+        * disable HW zero length termination select
+        * driver handles zero length packet through req->req.zero
+        */
+       zlt = 1;
+
+       /*
+        * sanity check type, direction, address, and then
+        * initialize the endpoint capabilities fields in dQH
+        */
+       switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+       case USB_ENDPOINT_XFER_CONTROL:
+               ios = 1;
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               if ((dev->gadget.speed == USB_SPEED_HIGH
+                                       && max != 512)
+                               || (dev->gadget.speed == USB_SPEED_FULL
+                                       && max > 64)) {
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 64)
+                               break;
+               default:
+                       if (max <= 8)
+                               break;
+                       goto done;
+               }
+               break;
+       case USB_ENDPOINT_XFER_ISOC:
+               if (strstr(ep->ep.name, "-bulk")
+                               || strstr(ep->ep.name, "-int"))
+                       goto done;
+
+               switch (dev->gadget.speed) {
+               case USB_SPEED_HIGH:
+                       if (max <= 1024)
+                               break;
+               case USB_SPEED_FULL:
+                       if (max <= 1023)
+                               break;
+               default:
+                       goto done;
+               }
+               /*
+                * FIXME:
+                * calculate transactions needed for high bandwidth iso
+                */
+               mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+               max = max & 0x8ff;      /* bit 0~10 */
+               /* 3 transactions at most */
+               if (mult > 3)
+                       goto done;
+               break;
+       default:
+               goto done;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* configure endpoint capabilities in dQH */
+       ep->dqh->dqh_ios = ios;
+       ep->dqh->dqh_mpl = cpu_to_le16(max);
+       ep->dqh->dqh_zlt = zlt;
+       ep->dqh->dqh_mult = mult;
+
+       ep->ep.maxpacket = max;
+       ep->desc = desc;
+       ep->stopped = 0;
+       ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+       /* ep_type */
+       ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+       /* configure endpoint control registers */
+       ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type);
+
+       DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n",
+                       _ep->name,
+                       ep->ep_num,
+                       DIR_STRING(desc->bEndpointAddress),
+                       type_string(desc->bmAttributes),
+                       max);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* retire a request */
+static void done(struct langwell_ep *ep, struct langwell_request *req,
+               int status)
+{
+       struct langwell_udc     *dev = ep->dev;
+       unsigned                stopped = ep->stopped;
+       struct langwell_dtd     *curr_dtd, *next_dtd;
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* remove the req from ep->queue */
+       list_del_init(&req->queue);
+
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+       else
+               status = req->req.status;
+
+       /* free dTD for the request */
+       next_dtd = req->head;
+       for (i = 0; i < req->dtd_count; i++) {
+               curr_dtd = next_dtd;
+               if (i != req->dtd_count - 1)
+                       next_dtd = curr_dtd->next_dtd_virt;
+               dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma);
+       }
+
+       if (req->mapped) {
+               dma_unmap_single(&dev->pdev->dev, req->req.dma, req->req.length,
+                       is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+               req->req.dma = DMA_ADDR_INVALID;
+               req->mapped = 0;
+       } else
+               dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma,
+                               req->req.length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+       if (status != -ESHUTDOWN)
+               DBG(dev, "complete %s, req %p, stat %d, len %u/%u\n",
+                       ep->ep.name, &req->req, status,
+                       req->req.actual, req->req.length);
+
+       /* don't modify queue heads during completion callback */
+       ep->stopped = 1;
+
+       spin_unlock(&dev->lock);
+       /* complete routine from gadget driver */
+       if (req->req.complete)
+               req->req.complete(&ep->ep, &req->req);
+
+       spin_lock(&dev->lock);
+       ep->stopped = stopped;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+static void langwell_ep_fifo_flush(struct usb_ep *_ep);
+
+/* delete all endpoint requests, called with spinlock held */
+static void nuke(struct langwell_ep *ep, int status)
+{
+       /* called with spinlock held */
+       ep->stopped = 1;
+
+       /* endpoint fifo flush */
+       if (&ep->ep && ep->desc)
+               langwell_ep_fifo_flush(&ep->ep);
+
+       while (!list_empty(&ep->queue)) {
+               struct langwell_request *req = NULL;
+               req = list_entry(ep->queue.next, struct langwell_request,
+                               queue);
+               done(ep, req, status);
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint is no longer usable */
+static int langwell_ep_disable(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       unsigned long           flags;
+       struct langwell_udc     *dev;
+       int                     ep_num;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* disable endpoint control register */
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* nuke all pending requests (does flush) */
+       nuke(ep, -ESHUTDOWN);
+
+       ep->desc = NULL;
+       ep->stopped = 1;
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       DBG(dev, "disabled %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+
+       return 0;
+}
+
+
+/* allocate a request object to use with this endpoint */
+static struct usb_request *langwell_alloc_request(struct usb_ep *_ep,
+               gfp_t gfp_flags)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       if (!_ep)
+               return NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+       if (!req)
+               return NULL;
+
+       req->req.dma = DMA_ADDR_INVALID;
+       INIT_LIST_HEAD(&req->queue);
+
+       VDBG(dev, "alloc request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return &req->req;
+}
+
+
+/* free a request object */
+static void langwell_free_request(struct usb_ep *_ep,
+               struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req = NULL;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !_req)
+               return;
+
+       req = container_of(_req, struct langwell_request, req);
+       WARN_ON(!list_empty(&req->queue));
+
+       if (_req)
+               kfree(req);
+
+       VDBG(dev, "free request for %s\n", _ep->name);
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* queue dTD and PRIME endpoint */
+static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req)
+{
+       u32                     bit_mask, usbcmd, endptstat, dtd_dma;
+       u8                      dtd_status;
+       int                     i;
+       struct langwell_dqh     *dqh;
+       struct langwell_udc     *dev;
+
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       i = ep->ep_num * 2 + is_in(ep);
+       dqh = &dev->ep_dqh[i];
+
+       if (ep->ep_num)
+               VDBG(dev, "%s\n", ep->name);
+       else
+               /* ep0 */
+               VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out");
+
+       VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i]));
+
+       bit_mask = is_in(ep) ?
+               (1 << (ep->ep_num + 16)) : (1 << (ep->ep_num));
+
+       VDBG(dev, "bit_mask = 0x%08x\n", bit_mask);
+
+       /* check if the pipe is empty */
+       if (!(list_empty(&ep->queue))) {
+               /* add dTD to the end of linked list */
+               struct langwell_request *lastreq;
+               lastreq = list_entry(ep->queue.prev,
+                               struct langwell_request, queue);
+
+               lastreq->tail->dtd_next =
+                       cpu_to_le32(req->head->dtd_dma & DTD_NEXT_MASK);
+
+               /* read prime bit, if 1 goto out */
+               if (readl(&dev->op_regs->endptprime) & bit_mask)
+                       goto out;
+
+               do {
+                       /* set ATDTW bit in USBCMD */
+                       usbcmd = readl(&dev->op_regs->usbcmd);
+                       writel(usbcmd | CMD_ATDTW, &dev->op_regs->usbcmd);
+
+                       /* read correct status bit */
+                       endptstat = readl(&dev->op_regs->endptstat) & bit_mask;
+
+               } while (!(readl(&dev->op_regs->usbcmd) & CMD_ATDTW));
+
+               /* write ATDTW bit to 0 */
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               writel(usbcmd & ~CMD_ATDTW, &dev->op_regs->usbcmd);
+
+               if (endptstat)
+                       goto out;
+       }
+
+       /* write dQH next pointer and terminate bit to 0 */
+       dtd_dma = req->head->dtd_dma & DTD_NEXT_MASK;
+       dqh->dtd_next = cpu_to_le32(dtd_dma);
+
+       /* clear active and halt bit */
+       dtd_status = (u8) ~(DTD_STS_ACTIVE | DTD_STS_HALTED);
+       dqh->dtd_status &= dtd_status;
+       VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status);
+
+       /* write 1 to endptprime register to PRIME endpoint */
+       bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num);
+       VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask);
+       writel(bit_mask, &dev->op_regs->endptprime);
+out:
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* fill in the dTD structure to build a transfer descriptor */
+static struct langwell_dtd *build_dtd(struct langwell_request *req,
+               unsigned *length, dma_addr_t *dma, int *is_last)
+{
+       u32                      buf_ptr;
+       struct langwell_dtd     *dtd;
+       struct langwell_udc     *dev;
+       int                     i;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* the maximum transfer length, up to 16k bytes */
+       *length = min(req->req.length - req->req.actual,
+                       (unsigned)DTD_MAX_TRANSFER_LENGTH);
+
+       /* create dTD dma_pool resource */
+       dtd = dma_pool_alloc(dev->dtd_pool, GFP_KERNEL, dma);
+       if (dtd == NULL)
+               return dtd;
+       dtd->dtd_dma = *dma;
+
+       /* initialize buffer page pointers */
+       buf_ptr = (u32)(req->req.dma + req->req.actual);
+       for (i = 0; i < 5; i++)
+               dtd->dtd_buf[i] = cpu_to_le32(buf_ptr + i * PAGE_SIZE);
+
+       req->req.actual += *length;
+
+       /* fill in total bytes with transfer size */
+       dtd->dtd_total = cpu_to_le16(*length);
+       VDBG(dev, "dtd->dtd_total = %d\n", dtd->dtd_total);
+
+       /* set is_last flag if req->req.zero is set or not */
+       if (req->req.zero) {
+               if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+                       *is_last = 1;
+               else
+                       *is_last = 0;
+       } else if (req->req.length == req->req.actual) {
+               *is_last = 1;
+       } else
+               *is_last = 0;
+
+       if (*is_last == 0)
+               VDBG(dev, "multi-dtd request!\n");
+
+       /* set interrupt on complete bit for the last dTD */
+       if (*is_last && !req->req.no_interrupt)
+               dtd->dtd_ioc = 1;
+
+       /* set multiplier override 0 for non-ISO and non-TX endpoint */
+       dtd->dtd_multo = 0;
+
+       /* set the active bit of status field to 1 */
+       dtd->dtd_status = DTD_STS_ACTIVE;
+       VDBG(dev, "dtd->dtd_status = 0x%02x\n", dtd->dtd_status);
+
+       VDBG(dev, "length = %d, dma addr= 0x%08x\n", *length, (int)*dma);
+       VDBG(dev, "<--- %s()\n", __func__);
+       return dtd;
+}
+
+
+/* generate dTD linked list for a request */
+static int req_to_dtd(struct langwell_request *req)
+{
+       unsigned                count;
+       int                     is_last, is_first = 1;
+       struct langwell_dtd     *dtd, *last_dtd = NULL;
+       struct langwell_udc     *dev;
+       dma_addr_t              dma;
+
+       dev = req->ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+       do {
+               dtd = build_dtd(req, &count, &dma, &is_last);
+               if (dtd == NULL)
+                       return -ENOMEM;
+
+               if (is_first) {
+                       is_first = 0;
+                       req->head = dtd;
+               } else {
+                       last_dtd->dtd_next = cpu_to_le32(dma);
+                       last_dtd->next_dtd_virt = dtd;
+               }
+               last_dtd = dtd;
+               req->dtd_count++;
+       } while (!is_last);
+
+       /* set terminate bit to 1 for the last dTD */
+       dtd->dtd_next = DTD_TERM;
+
+       req->tail = dtd;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* queue (submits) an I/O requests to an endpoint */
+static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+               gfp_t gfp_flags)
+{
+       struct langwell_request *req;
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     is_iso = 0, zlflag = 0;
+
+       /* always require a cpu-view buffer */
+       req = container_of(_req, struct langwell_request, req);
+       ep = container_of(_ep, struct langwell_ep, ep);
+
+       if (!_req || !_req->complete || !_req->buf
+                       || !list_empty(&req->queue)) {
+               return -EINVAL;
+       }
+
+       if (unlikely(!_ep || !ep->desc))
+               return -EINVAL;
+
+       dev = ep->dev;
+       req->ep = ep;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+               if (req->req.length > ep->ep.maxpacket)
+                       return -EMSGSIZE;
+               is_iso = 1;
+       }
+
+       if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
+               return -ESHUTDOWN;
+
+       /* set up dma mapping in case the caller didn't */
+       if (_req->dma == DMA_ADDR_INVALID) {
+               /* WORKAROUND: WARN_ON(size == 0) */
+               if (_req->length == 0) {
+                       VDBG(dev, "req->length: 0->1\n");
+                       zlflag = 1;
+                       _req->length++;
+               }
+
+               _req->dma = dma_map_single(&dev->pdev->dev,
+                               _req->buf, _req->length,
+                               is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               if (zlflag && (_req->length == 1)) {
+                       VDBG(dev, "req->length: 1->0\n");
+                       zlflag = 0;
+                       _req->length = 0;
+               }
+
+               req->mapped = 1;
+               VDBG(dev, "req->mapped = 1\n");
+       } else {
+               dma_sync_single_for_device(&dev->pdev->dev,
+                               _req->dma, _req->length,
+                               is_in(ep) ?  DMA_TO_DEVICE : DMA_FROM_DEVICE);
+               req->mapped = 0;
+               VDBG(dev, "req->mapped = 0\n");
+       }
+
+       DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
+                       _ep->name,
+                       _req, _req->length, _req->buf, _req->dma);
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+       req->dtd_count = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /* build and put dTDs to endpoint queue */
+       if (!req_to_dtd(req)) {
+               queue_dtd(ep, req);
+       } else {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -ENOMEM;
+       }
+
+       /* update ep0 state */
+       if (ep->ep_num == 0)
+               dev->ep0_state = DATA_STATE_XMIT;
+
+       if (likely(req != NULL)) {
+               list_add_tail(&req->queue, &ep->queue);
+               VDBG(dev, "list_add_tail() \n");
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* dequeue (cancels, unlinks) an I/O request from an endpoint */
+static int langwell_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       struct langwell_request *req;
+       unsigned long           flags;
+       int                     stopped, ep_num, retval = 0;
+       u32                     endptctrl;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc || !_req)
+               return -EINVAL;
+
+       if (!dev->driver)
+               return -ESHUTDOWN;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       stopped = ep->stopped;
+
+       /* quiesce dma while we patch the queue */
+       ep->stopped = 1;
+       ep_num = ep->ep_num;
+
+       /* disable endpoint control register */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl &= ~EPCTRL_TXE;
+       else
+               endptctrl &= ~EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       /* make sure it's still queued on this endpoint */
+       list_for_each_entry(req, &ep->queue, queue) {
+               if (&req->req == _req)
+                       break;
+       }
+
+       if (&req->req != _req) {
+               retval = -EINVAL;
+               goto done;
+       }
+
+       /* queue head may be partially complete. */
+       if (ep->queue.next == &req->queue) {
+               DBG(dev, "unlink (%s) dma\n", _ep->name);
+               _req->status = -ECONNRESET;
+               langwell_ep_fifo_flush(&ep->ep);
+
+               /* not the last request in endpoint queue */
+               if (likely(ep->queue.next == &req->queue)) {
+                       struct langwell_dqh     *dqh;
+                       struct langwell_request *next_req;
+
+                       dqh = ep->dqh;
+                       next_req = list_entry(req->queue.next,
+                                       struct langwell_request, queue);
+
+                       /* point the dQH to the first dTD of next request */
+                       writel((u32) next_req->head, &dqh->dqh_current);
+               }
+       } else {
+               struct langwell_request *prev_req;
+
+               prev_req = list_entry(req->queue.prev,
+                               struct langwell_request, queue);
+               writel(readl(&req->tail->dtd_next),
+                               &prev_req->tail->dtd_next);
+       }
+
+       done(ep, req, -ECONNRESET);
+
+done:
+       /* enable endpoint again */
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+       if (is_in(ep))
+               endptctrl |= EPCTRL_TXE;
+       else
+               endptctrl |= EPCTRL_RXE;
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       ep->stopped = stopped;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint set/clear halt */
+static void ep_set_halt(struct langwell_ep *ep, int value)
+{
+       u32                     endptctrl = 0;
+       int                     ep_num;
+       struct langwell_udc     *dev = ep->dev;
+       VDBG(dev, "---> %s()\n", __func__);
+
+       ep_num = ep->ep_num;
+       endptctrl = readl(&dev->op_regs->endptctrl[ep_num]);
+
+       /* value: 1 - set halt, 0 - clear halt */
+       if (value) {
+               /* set the stall bit */
+               if (is_in(ep))
+                       endptctrl |= EPCTRL_TXS;
+               else
+                       endptctrl |= EPCTRL_RXS;
+       } else {
+               /* clear the stall bit and reset data toggle */
+               if (is_in(ep)) {
+                       endptctrl &= ~EPCTRL_TXS;
+                       endptctrl |= EPCTRL_TXR;
+               } else {
+                       endptctrl &= ~EPCTRL_RXS;
+                       endptctrl |= EPCTRL_RXR;
+               }
+       }
+
+       writel(endptctrl, &dev->op_regs->endptctrl[ep_num]);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* set the endpoint halt feature */
+static int langwell_ep_set_halt(struct usb_ep *_ep, int value)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       int                     retval = 0;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                       == USB_ENDPOINT_XFER_ISOC)
+               return  -EOPNOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       /*
+        * attempt to halt IN ep will fail if any transfer requests
+        * are still queue
+        */
+       if (!list_empty(&ep->queue) && is_in(ep) && value) {
+               /* IN endpoint FIFO holds bytes */
+               DBG(dev, "%s FIFO holds bytes\n", _ep->name);
+               retval = -EAGAIN;
+               goto done;
+       }
+
+       /* endpoint set/clear halt */
+       if (ep->ep_num) {
+               ep_set_halt(ep, value);
+       } else { /* endpoint 0 */
+               dev->ep0_state = WAIT_FOR_SETUP;
+               dev->ep0_dir = USB_DIR_OUT;
+       }
+done:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       DBG(dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* set the halt feature and ignores clear requests */
+static int langwell_ep_set_wedge(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc)
+               return -EINVAL;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return usb_ep_set_halt(_ep);
+}
+
+
+/* flush contents of a fifo */
+static void langwell_ep_fifo_flush(struct usb_ep *_ep)
+{
+       struct langwell_ep      *ep;
+       struct langwell_udc     *dev;
+       u32                     flush_bit;
+       unsigned long           timeout;
+
+       ep = container_of(_ep, struct langwell_ep, ep);
+       dev = ep->dev;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (!_ep || !ep->desc) {
+               VDBG(dev, "ep or ep->desc is NULL\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return;
+       }
+
+       VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out");
+
+       /* flush endpoint buffer */
+       if (ep->ep_num == 0)
+               flush_bit = (1 << 16) | 1;
+       else if (is_in(ep))
+               flush_bit = 1 << (ep->ep_num + 16);     /* TX */
+       else
+               flush_bit = 1 << ep->ep_num;            /* RX */
+
+       /* wait until flush complete */
+       timeout = jiffies + FLUSH_TIMEOUT;
+       do {
+               writel(flush_bit, &dev->op_regs->endptflush);
+               while (readl(&dev->op_regs->endptflush)) {
+                       if (time_after(jiffies, timeout)) {
+                               ERROR(dev, "ep flush timeout\n");
+                               goto done;
+                       }
+                       cpu_relax();
+               }
+       } while (readl(&dev->op_regs->endptstat) & flush_bit);
+done:
+       VDBG(dev, "<--- %s()\n", __func__);
+}
+
+
+/* endpoints operations structure */
+static const struct usb_ep_ops langwell_ep_ops = {
+
+       /* configure endpoint, making it usable */
+       .enable         = langwell_ep_enable,
+
+       /* endpoint is no longer usable */
+       .disable        = langwell_ep_disable,
+
+       /* allocate a request object to use with this endpoint */
+       .alloc_request  = langwell_alloc_request,
+
+       /* free a request object */
+       .free_request   = langwell_free_request,
+
+       /* queue (submits) an I/O requests to an endpoint */
+       .queue          = langwell_ep_queue,
+
+       /* dequeue (cancels, unlinks) an I/O request from an endpoint */
+       .dequeue        = langwell_ep_dequeue,
+
+       /* set the endpoint halt feature */
+       .set_halt       = langwell_ep_set_halt,
+
+       /* set the halt feature and ignores clear requests */
+       .set_wedge      = langwell_ep_set_wedge,
+
+       /* flush contents of a fifo */
+       .fifo_flush     = langwell_ep_fifo_flush,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller usb_gadget_ops structure */
+
+/* returns the current frame number */
+static int langwell_get_frame(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u16                     retval;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       retval = readl(&dev->op_regs->frindex) & FRINDEX_MASK;
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return retval;
+}
+
+
+/* tries to wake up the host connected to this gadget */
+static int langwell_wakeup(struct usb_gadget *_gadget)
+{
+       struct langwell_udc     *dev;
+       u32                     portsc1, devlc;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return 0;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* Remote Wakeup feature not enabled by host */
+       if (!dev->remote_wakeup)
+               return -ENOTSUPP;
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       portsc1 = readl(&dev->op_regs->portsc1);
+       if (!(portsc1 & PORTS_SUSP)) {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return 0;
+       }
+
+       /* LPM L1 to L0, remote wakeup */
+       if (dev->lpm && dev->lpm_state == LPM_L1) {
+               portsc1 |= PORTS_SLP;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* force port resume */
+       if (dev->usb_state == USB_STATE_SUSPENDED) {
+               portsc1 |= PORTS_FPR;
+               writel(portsc1, &dev->op_regs->portsc1);
+       }
+
+       /* exit PHY low power suspend */
+       devlc = readl(&dev->op_regs->devlc);
+       VDBG(dev, "devlc = 0x%08x\n", devlc);
+       devlc &= ~LPM_PHCD;
+       writel(devlc, &dev->op_regs->devlc);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* notify controller that VBUS is powered or not */
+static int langwell_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+       struct langwell_udc     *dev;
+       unsigned long           flags;
+       u32                     usbcmd;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       VDBG(dev, "VBUS status: %s\n", is_active ? "on" : "off");
+
+       dev->vbus_active = (is_active != 0);
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* constrain controller's VBUS power usage */
+static int langwell_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+       struct langwell_udc     *dev;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+       VDBG(dev, "---> %s()\n", __func__);
+
+       if (dev->transceiver) {
+               VDBG(dev, "otg_set_power\n");
+               VDBG(dev, "<--- %s()\n", __func__);
+               return otg_set_power(dev->transceiver, mA);
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return -ENOTSUPP;
+}
+
+
+/* D+ pullup, software-controlled connect/disconnect to USB host */
+static int langwell_pullup(struct usb_gadget *_gadget, int is_on)
+{
+       struct langwell_udc     *dev;
+       u32                     usbcmd;
+       unsigned long           flags;
+
+       if (!_gadget)
+               return -ENODEV;
+
+       dev = container_of(_gadget, struct langwell_udc, gadget);
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->softconnected = (is_on != 0);
+
+       if (dev->driver && dev->softconnected && dev->vbus_active) {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd |= CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       } else {
+               usbcmd = readl(&dev->op_regs->usbcmd);
+               usbcmd &= ~CMD_RUNSTOP;
+               writel(usbcmd, &dev->op_regs->usbcmd);
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops langwell_ops = {
+
+       /* returns the current frame number */
+       .get_frame      = langwell_get_frame,
+
+       /* tries to wake up the host connected to this gadget */
+       .wakeup         = langwell_wakeup,
+
+       /* set the device selfpowered feature, always selfpowered */
+       /* .set_selfpowered = langwell_set_selfpowered, */
+
+       /* notify controller that VBUS is powered or not */
+       .vbus_session   = langwell_vbus_session,
+
+       /* constrain controller's VBUS power usage */
+       .vbus_draw      = langwell_vbus_draw,
+
+       /* D+ pullup, software-controlled connect/disconnect to USB host */
+       .pullup         = langwell_pullup,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* device controller operations */
+
+/* reset device controller */
+static int langwell_udc_reset(struct langwell_udc *dev)
+{
+       u32             usbcmd, usbmode, devlc, endpointlistaddr;
+       unsigned long   timeout;
+
+       if (!dev)
+               return -EINVAL;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* reset device controller */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RST;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       /* wait for reset to complete */
+       timeout = jiffies + RESET_TIMEOUT;
+       while (readl(&dev->op_regs->usbcmd) & CMD_RST) {
+               if (time_after(jiffies, timeout)) {
+                       ERROR(dev, "device reset timeout\n");
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+       }
+
+       /* set controller to device mode */
+       usbmode = readl(&dev->op_regs->usbmode);
+       usbmode |= MODE_DEVICE;
+
+       /* turn setup lockout off, require setup tripwire in usbcmd */
+       usbmode |= MODE_SLOM;
+
+       writel(usbmode, &dev->op_regs->usbmode);
+       usbmode = readl(&dev->op_regs->usbmode);
+       VDBG(dev, "usbmode=0x%08x\n", usbmode);
+
+       /* Write-Clear setup status */
+       writel(0, &dev->op_regs->usbsts);
+
+       /* if support USB LPM, ACK all LPM token */
+       if (dev->lpm) {
+               devlc = readl(&dev->op_regs->devlc);
+               devlc &= ~LPM_STL;      /* don't STALL LPM token */
+               devlc &= ~LPM_NYT_ACK;  /* ACK LPM token */
+               writel(devlc, &dev->op_regs->devlc);
+       }
+
+       /* fill endpointlistaddr register */
+       endpointlistaddr = dev->ep_dqh_dma;
+       endpointlistaddr &= ENDPOINTLISTADDR_MASK;
+       writel(endpointlistaddr, &dev->op_regs->endpointlistaddr);
+
+       VDBG(dev, "dQH base (vir: %p, phy: 0x%08x), endpointlistaddr=0x%08x\n",
+                       dev->ep_dqh, endpointlistaddr,
+                       readl(&dev->op_regs->endpointlistaddr));
+       DBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* reinitialize device controller endpoints */
+static int eps_reinit(struct langwell_udc *dev)
+{
+       struct langwell_ep      *ep;
+       char                    name[14];
+       int                     i;
+
+       VDBG(dev, "---> %s()\n", __func__);
+
+       /* initialize ep0 */
+       ep = &dev->ep[0];
+       ep->dev = dev;
+       strncpy(ep->name, "ep0", sizeof(ep->name));
+       ep->ep.name = ep->name;
+       ep->ep.ops = &langwell_ep_ops;
+       ep->stopped = 0;
+       ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+       ep->ep_num = 0;
+       ep->desc = &langwell_ep0_desc;
+       INIT_LIST_HEAD(&ep->queue);
+
+       ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+       /* initialize other endpoints */
+       for (i = 2; i < dev->ep_max; i++) {
+               ep = &dev->ep[i];
+               if (i % 2)
+                       snprintf(name, sizeof(name), "ep%din", i / 2);
+               else
+                       snprintf(name, sizeof(name), "ep%dout", i / 2);
+               ep->dev = dev;
+               strncpy(ep->name, name, sizeof(ep->name));
+               ep->ep.name = ep->name;
+
+               ep->ep.ops = &langwell_ep_ops;
+               ep->stopped = 0;
+               ep->ep.maxpacket = (unsigned short) ~0;
+               ep->ep_num = i / 2;
+
+               INIT_LIST_HEAD(&ep->queue);
+               list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+               ep->dqh = &dev->ep_dqh[i];
+       }
+
+       VDBG(dev, "<--- %s()\n", __func__);
+       return 0;
+}
+
+
+/* enable interrupt and set controller to run state */
+static void langwell_udc_start(struct langwell_udc *dev)
+{
+       u32     usbintr, usbcmd;
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* enable interrupts */
+       usbintr = INTR_ULPIE    /* ULPI */
+               | INTR_SLE      /* suspend */
+               /* | INTR_SRE   SOF received */
+               | INTR_URE      /* USB reset */
+               | INTR_AAE      /* async advance */
+               | INTR_SEE      /* system error */
+               | INTR_FRE      /* frame list rollover */
+               | INTR_PCE      /* port change detect */
+               | INTR_UEE      /* USB error interrupt */
+               | INTR_UE;      /* USB interrupt */
+       writel(usbintr, &dev->op_regs->usbintr);
+
+       /* clear stopped bit */
+       dev->stopped = 0;
+
+       /* set controller to run */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd |= CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* disable interrupt and set controller to stop state */
+static void langwell_udc_stop(struct langwell_udc *dev)
+{
+       u32     usbcmd;
+
+       DBG(dev, "---> %s()\n", __func__);
+
+       /* disable all interrupts */
+       writel(0, &dev->op_regs->usbintr);
+
+       /* set stopped bit */
+       dev->stopped = 1;
+
+       /* set controller to stop state */
+       usbcmd = readl(&dev->op_regs->usbcmd);
+       usbcmd &= ~CMD_RUNSTOP;
+       writel(usbcmd, &dev->op_regs->usbcmd);
+
+       DBG(dev, "<--- %s()\n", __func__);
+       return;
+}
+
+
+/* stop all USB activities */
+static void stop_activity(struct langwell_udc *dev,
+               struct usb_gadget_driver *driver)
+{
+       struct langwell_ep      *ep;
+       DBG(dev, "---> %s()\n", __func__);
+
+       nuke(&dev->ep[0], -ESHUTDOWN);
+
+       list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+               nuke(ep, -ESHUTDOWN);
+       }
+
+       /* report disconnect; the driver is already quiesced */
+       if (driver) {
+               spin_unlock(&dev->lock);
+               driver->disconnect(&dev->gadget);
+               spin_lock(&dev->lock);
+       }
+
+       DBG(dev, "<--- %s()\n", __func_