Browse Source

Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (129 commits)
  [PATCH] USB Storage: fix Rio Karma eject support build error
  USB: Airprime driver improvements to allow full speed EvDO transfers
  USB: remove OTG build warning
  USB: EHCI update VIA workaround
  USB: force root hub resume after power loss
  USB: ohci_usb can oops on shutdown
  USB: Dealias -110 code (more complete)
  USB: Remove unneeded void * casts in core files
  USB: u132-hcd: host controller driver for ELAN U132 adapter
  USB: ftdi-elan: client driver for ELAN Uxxx adapters
  usb serial: support Alcor Micro Corp. USB 2.0 TO RS-232 through pl2303 driver
  USB: Moschip 7840 USB-Serial Driver
  USB: add PlayStation 2 Trance Vibrator driver
  USB: Add ADU support for Ontrak ADU devices
  aircable: fix printk format warnings
  Add AIRcable USB Bluetooth Dongle Driver
  cypress_m8: implement graceful failure handling
  cypress_m8: improve control endpoint error handling
  cypress_m8: use usb_fill_int_urb where appropriate
  cypress_m8: use appropriate URB polling interval
  ...
Linus Torvalds 19 years ago
parent
commit
1f9bd4c96a
100 changed files with 7937 additions and 1670 deletions
  1. 62 61
      Documentation/DocBook/usb.tmpl
  2. 3 0
      Documentation/devices.txt
  3. 4 7
      Documentation/usb/error-codes.txt
  4. 5 0
      Documentation/usb/usb-serial.txt
  5. 1 14
      arch/arm/mach-pxa/corgi.c
  6. 1 1
      arch/arm/plat-omap/usb.c
  7. 25 13
      drivers/block/ub.c
  8. 1 1
      drivers/i2c/chips/isp1301_omap.c
  9. 1 1
      drivers/isdn/gigaset/bas-gigaset.c
  10. 3 3
      drivers/isdn/hisax/hfc_usb.h
  11. 0 1
      drivers/media/dvb/dvb-usb/dvb-usb-urb.c
  12. 1 1
      drivers/media/dvb/ttusb-dec/ttusb_dec.c
  13. 4 3
      drivers/media/video/ov511.c
  14. 1 1
      drivers/media/video/pwc/pwc-if.c
  15. 3 4
      drivers/media/video/w9968cf.c
  16. 7 11
      drivers/net/irda/irda-usb.c
  17. 2 2
      drivers/net/wireless/zd1201.c
  18. 1 0
      drivers/usb/Kconfig
  19. 8 4
      drivers/usb/Makefile
  20. 34 40
      drivers/usb/atm/ueagle-atm.c
  21. 6 9
      drivers/usb/class/usblp.c
  22. 1 1
      drivers/usb/core/Makefile
  23. 2 2
      drivers/usb/core/buffer.c
  24. 3 1
      drivers/usb/core/config.c
  25. 3 3
      drivers/usb/core/devices.c
  26. 38 28
      drivers/usb/core/devio.c
  27. 934 77
      drivers/usb/core/driver.c
  28. 22 8
      drivers/usb/core/endpoint.c
  29. 1 1
      drivers/usb/core/file.c
  30. 208 0
      drivers/usb/core/generic.c
  31. 16 0
      drivers/usb/core/hcd-pci.c
  32. 79 165
      drivers/usb/core/hcd.c
  33. 23 37
      drivers/usb/core/hcd.h
  34. 197 358
      drivers/usb/core/hub.c
  35. 2 1
      drivers/usb/core/hub.h
  36. 3 3
      drivers/usb/core/inode.c
  37. 98 50
      drivers/usb/core/message.c
  38. 3 0
      drivers/usb/core/notify.c
  39. 45 15
      drivers/usb/core/sysfs.c
  40. 6 9
      drivers/usb/core/urb.c
  41. 198 335
      drivers/usb/core/usb.c
  42. 61 15
      drivers/usb/core/usb.h
  43. 15 1
      drivers/usb/gadget/Kconfig
  44. 2 0
      drivers/usb/gadget/Makefile
  45. 1 1
      drivers/usb/gadget/at91_udc.c
  46. 2 6
      drivers/usb/gadget/dummy_hcd.c
  47. 6 3
      drivers/usb/gadget/ether.c
  48. 1337 0
      drivers/usb/gadget/gmidi.c
  49. 48 12
      drivers/usb/gadget/inode.c
  50. 88 68
      drivers/usb/gadget/net2280.c
  51. 2 2
      drivers/usb/gadget/omap_udc.c
  52. 68 2
      drivers/usb/gadget/pxa2xx_udc.c
  53. 8 16
      drivers/usb/gadget/pxa2xx_udc.h
  54. 3 0
      drivers/usb/gadget/serial.c
  55. 29 0
      drivers/usb/host/Kconfig
  56. 1 0
      drivers/usb/host/Makefile
  57. 2 0
      drivers/usb/host/ehci-au1xxx.c
  58. 15 16
      drivers/usb/host/ehci-dbg.c
  59. 2 0
      drivers/usb/host/ehci-fsl.c
  60. 58 37
      drivers/usb/host/ehci-hcd.c
  61. 7 7
      drivers/usb/host/ehci-hub.c
  62. 7 7
      drivers/usb/host/ehci-mem.c
  63. 3 1
      drivers/usb/host/ehci-pci.c
  64. 12 14
      drivers/usb/host/ehci-q.c
  65. 13 13
      drivers/usb/host/ehci-sched.c
  66. 33 26
      drivers/usb/host/ehci.h
  67. 1 1
      drivers/usb/host/isp116x-hcd.c
  68. 1 1
      drivers/usb/host/isp116x.h
  69. 5 2
      drivers/usb/host/ohci-at91.c
  70. 3 4
      drivers/usb/host/ohci-au1xxx.c
  71. 12 6
      drivers/usb/host/ohci-dbg.c
  72. 3 0
      drivers/usb/host/ohci-ep93xx.c
  73. 45 19
      drivers/usb/host/ohci-hcd.c
  74. 46 24
      drivers/usb/host/ohci-hub.c
  75. 3 4
      drivers/usb/host/ohci-lh7a404.c
  76. 0 1
      drivers/usb/host/ohci-mem.c
  77. 77 43
      drivers/usb/host/ohci-omap.c
  78. 7 1
      drivers/usb/host/ohci-pci.c
  79. 476 0
      drivers/usb/host/ohci-pnx4008.c
  80. 3 0
      drivers/usb/host/ohci-ppc-soc.c
  81. 3 0
      drivers/usb/host/ohci-pxa27x.c
  82. 4 1
      drivers/usb/host/ohci-s3c2410.c
  83. 1 4
      drivers/usb/host/ohci-sa1111.c
  84. 1 3
      drivers/usb/host/ohci.h
  85. 2 2
      drivers/usb/host/sl811-hcd.c
  86. 3295 0
      drivers/usb/host/u132-hcd.c
  87. 2 2
      drivers/usb/host/uhci-debug.c
  88. 9 3
      drivers/usb/host/uhci-hub.c
  89. 2 2
      drivers/usb/image/mdc800.c
  90. 26 4
      drivers/usb/input/Kconfig
  91. 2 0
      drivers/usb/input/Makefile
  92. 1 4
      drivers/usb/input/acecad.c
  93. 1 4
      drivers/usb/input/appletouch.c
  94. 2 6
      drivers/usb/input/ati_remote.c
  95. 21 3
      drivers/usb/input/hid-core.c
  96. 1 1
      drivers/usb/input/hiddev.c
  97. 1 1
      drivers/usb/input/itmtouch.c
  98. 1 2
      drivers/usb/input/keyspan_remote.c
  99. 1 1
      drivers/usb/input/mtouchusb.c
  100. 1 3
      drivers/usb/input/powermate.c

+ 62 - 61
Documentation/DocBook/usb.tmpl

@@ -43,59 +43,52 @@
 
 
     <para>A Universal Serial Bus (USB) is used to connect a host,
     <para>A Universal Serial Bus (USB) is used to connect a host,
     such as a PC or workstation, to a number of peripheral
     such as a PC or workstation, to a number of peripheral
-    devices.  USB uses a tree structure, with the host at the
+    devices.  USB uses a tree structure, with the host as the
     root (the system's master), hubs as interior nodes, and
     root (the system's master), hubs as interior nodes, and
-    peripheral devices as leaves (and slaves).
+    peripherals as leaves (and slaves).
     Modern PCs support several such trees of USB devices, usually
     Modern PCs support several such trees of USB devices, usually
     one USB 2.0 tree (480 Mbit/sec each) with
     one USB 2.0 tree (480 Mbit/sec each) with
     a few USB 1.1 trees (12 Mbit/sec each) that are used when you
     a few USB 1.1 trees (12 Mbit/sec each) that are used when you
     connect a USB 1.1 device directly to the machine's "root hub".
     connect a USB 1.1 device directly to the machine's "root hub".
     </para>
     </para>
 
 
-    <para>That master/slave asymmetry was designed in part for
-    ease of use.  It is not physically possible to assemble
-    (legal) USB cables incorrectly:  all upstream "to-the-host"
-    connectors are the rectangular type, matching the sockets on
-    root hubs, and the downstream type are the squarish type
-    (or they are built in to the peripheral).
-    Software doesn't need to deal with distributed autoconfiguration
-    since the pre-designated master node manages all that.
-    At the electrical level, bus protocol overhead is reduced by
-    eliminating arbitration and moving scheduling into host software.
+    <para>That master/slave asymmetry was designed-in for a number of
+    reasons, one being ease of use.  It is not physically possible to
+    assemble (legal) USB cables incorrectly:  all upstream "to the host"
+    connectors are the rectangular type (matching the sockets on
+    root hubs), and all downstream connectors are the squarish type
+    (or they are built into the peripheral).
+    Also, the host software doesn't need to deal with distributed
+    auto-configuration since the pre-designated master node manages all that.
+    And finally, at the electrical level, bus protocol overhead is reduced by
+    eliminating arbitration and moving scheduling into the host software.
     </para>
     </para>
 
 
-    <para>USB 1.0 was announced in January 1996, and was revised
+    <para>USB 1.0 was announced in January 1996 and was revised
     as USB 1.1 (with improvements in hub specification and
     as USB 1.1 (with improvements in hub specification and
     support for interrupt-out transfers) in September 1998.
     support for interrupt-out transfers) in September 1998.
-    USB 2.0 was released in April 2000, including high speed
-    transfers and transaction translating hubs (used for USB 1.1
+    USB 2.0 was released in April 2000, adding high-speed
+    transfers and transaction-translating hubs (used for USB 1.1
     and 1.0 backward compatibility).
     and 1.0 backward compatibility).
     </para>
     </para>
 
 
-    <para>USB support was added to Linux early in the 2.2 kernel series
-    shortly before the 2.3 development forked off.  Updates
-    from 2.3 were regularly folded back into 2.2 releases, bringing
-    new features such as <filename>/sbin/hotplug</filename> support,
-    more drivers, and more robustness.
-    The 2.5 kernel series continued such improvements, and also
-    worked on USB 2.0 support,
-    higher performance,
-    better consistency between host controller drivers,
-    API simplification (to make bugs less likely),
-    and providing internal "kerneldoc" documentation.
+    <para>Kernel developers added USB support to Linux early in the 2.2 kernel
+    series, shortly before 2.3 development forked.  Updates from 2.3 were
+    regularly folded back into 2.2 releases, which improved reliability and
+    brought <filename>/sbin/hotplug</filename> support as well more drivers.
+    Such improvements were continued in the 2.5 kernel series, where they added
+    USB 2.0 support, improved performance, and made the host controller drivers
+    (HCDs) more consistent.  They also simplified the API (to make bugs less
+    likely) and added internal "kerneldoc" documentation.
     </para>
     </para>
 
 
     <para>Linux can run inside USB devices as well as on
     <para>Linux can run inside USB devices as well as on
     the hosts that control the devices.
     the hosts that control the devices.
-    Because the Linux 2.x USB support evolved to support mass market
-    platforms such as Apple Macintosh or PC-compatible systems,
-    it didn't address design concerns for those types of USB systems.
-    So it can't be used inside mass-market PDAs, or other peripherals.
-    USB device drivers running inside those Linux peripherals
+    But USB device drivers running inside those peripherals
     don't do the same things as the ones running inside hosts,
     don't do the same things as the ones running inside hosts,
-    and so they've been given a different name:
-    they're called <emphasis>gadget drivers</emphasis>.
-    This document does not present gadget drivers.
+    so they've been given a different name:
+    <emphasis>gadget drivers</emphasis>.
+    This document does not cover gadget drivers.
     </para>
     </para>
 
 
     </chapter>
     </chapter>
@@ -103,17 +96,14 @@
 <chapter id="host">
 <chapter id="host">
     <title>USB Host-Side API Model</title>
     <title>USB Host-Side API Model</title>
 
 
-    <para>Within the kernel,
-    host-side drivers for USB devices talk to the "usbcore" APIs.
-    There are two types of public "usbcore" APIs, targetted at two different
-    layers of USB driver.  Those are
-    <emphasis>general purpose</emphasis> drivers, exposed through
-    driver frameworks such as block, character, or network devices;
-    and drivers that are <emphasis>part of the core</emphasis>,
-    which are involved in managing a USB bus.
-    Such core drivers include the <emphasis>hub</emphasis> driver,
-    which manages trees of USB devices, and several different kinds
-    of <emphasis>host controller driver (HCD)</emphasis>,
+    <para>Host-side drivers for USB devices talk to the "usbcore" APIs.
+    There are two.  One is intended for
+    <emphasis>general-purpose</emphasis> drivers (exposed through
+    driver frameworks), and the other is for drivers that are
+    <emphasis>part of the core</emphasis>.
+    Such core drivers include the <emphasis>hub</emphasis> driver
+    (which manages trees of USB devices) and several different kinds
+    of <emphasis>host controller drivers</emphasis>,
     which control individual busses.
     which control individual busses.
     </para>
     </para>
 
 
@@ -122,21 +112,21 @@
      
      
     <itemizedlist>
     <itemizedlist>
 
 
-	<listitem><para>USB supports four kinds of data transfer
-	(control, bulk, interrupt, and isochronous).  Two transfer
-	types use bandwidth as it's available (control and bulk),
-	while the other two types of transfer (interrupt and isochronous)
+	<listitem><para>USB supports four kinds of data transfers
+	(control, bulk, interrupt, and isochronous).  Two of them (control
+	and bulk) use bandwidth as it's available,
+	while the other two (interrupt and isochronous)
 	are scheduled to provide guaranteed bandwidth.
 	are scheduled to provide guaranteed bandwidth.
 	</para></listitem>
 	</para></listitem>
 
 
 	<listitem><para>The device description model includes one or more
 	<listitem><para>The device description model includes one or more
 	"configurations" per device, only one of which is active at a time.
 	"configurations" per device, only one of which is active at a time.
-	Devices that are capable of high speed operation must also support
-	full speed configurations, along with a way to ask about the
-	"other speed" configurations that might be used.
+	Devices that are capable of high-speed operation must also support
+	full-speed configurations, along with a way to ask about the
+	"other speed" configurations which might be used.
 	</para></listitem>
 	</para></listitem>
 
 
-	<listitem><para>Configurations have one or more "interface", each
+	<listitem><para>Configurations have one or more "interfaces", each
 	of which may have "alternate settings".  Interfaces may be
 	of which may have "alternate settings".  Interfaces may be
 	standardized by USB "Class" specifications, or may be specific to
 	standardized by USB "Class" specifications, or may be specific to
 	a vendor or device.</para>
 	a vendor or device.</para>
@@ -162,7 +152,7 @@
 	</para></listitem>
 	</para></listitem>
 
 
 	<listitem><para>The Linux USB API supports synchronous calls for
 	<listitem><para>The Linux USB API supports synchronous calls for
-	control and bulk messaging.
+	control and bulk messages.
 	It also supports asynchnous calls for all kinds of data transfer,
 	It also supports asynchnous calls for all kinds of data transfer,
 	using request structures called "URBs" (USB Request Blocks).
 	using request structures called "URBs" (USB Request Blocks).
 	</para></listitem>
 	</para></listitem>
@@ -463,14 +453,25 @@
 	    file in your Linux kernel sources.
 	    file in your Linux kernel sources.
 	    </para>
 	    </para>
 
 
-	    <para>Otherwise the main use for this file from programs
-	    is to poll() it to get notifications of usb devices
-	    as they're plugged or unplugged.
-	    To see what changed, you'd need to read the file and
-	    compare "before" and "after" contents, scan the filesystem,
-	    or see its hotplug event.
+	    <para>This file, in combination with the poll() system call, can
+	    also be used to detect when devices are added or removed:
+<programlisting>int fd;
+struct pollfd pfd;
+
+fd = open("/proc/bus/usb/devices", O_RDONLY);
+pfd = { fd, POLLIN, 0 };
+for (;;) {
+	/* The first time through, this call will return immediately. */
+	poll(&amp;pfd, 1, -1);
+
+	/* To see what's changed, compare the file's previous and current
+	   contents or scan the filesystem.  (Scanning is more precise.) */
+}</programlisting>
+	    Note that this behavior is intended to be used for informational
+	    and debug purposes.  It would be more appropriate to use programs
+	    such as udev or HAL to initialize a device or start a user-mode
+	    helper program, for instance.
 	    </para>
 	    </para>
-
 	</sect1>
 	</sect1>
 
 
 	<sect1>
 	<sect1>

+ 3 - 0
Documentation/devices.txt

@@ -2543,6 +2543,9 @@ Your cooperation is appreciated.
 		 64 = /dev/usb/rio500	Diamond Rio 500
 		 64 = /dev/usb/rio500	Diamond Rio 500
 		 65 = /dev/usb/usblcd	USBLCD Interface (info@usblcd.de)
 		 65 = /dev/usb/usblcd	USBLCD Interface (info@usblcd.de)
 		 66 = /dev/usb/cpad0	Synaptics cPad (mouse/LCD)
 		 66 = /dev/usb/cpad0	Synaptics cPad (mouse/LCD)
+		 67 = /dev/usb/adutux0	1st Ontrak ADU device
+		    ...
+		 76 = /dev/usb/adutux10	10th Ontrak ADU device
 		 96 = /dev/usb/hiddev0	1st USB HID device
 		 96 = /dev/usb/hiddev0	1st USB HID device
 		    ...
 		    ...
 		111 = /dev/usb/hiddev15	16th USB HID device
 		111 = /dev/usb/hiddev15	16th USB HID device

+ 4 - 7
Documentation/usb/error-codes.txt

@@ -98,13 +98,13 @@ one or more packets could finish before an error stops further endpoint I/O.
 			error, a failure to respond (often caused by
 			error, a failure to respond (often caused by
 			device disconnect), or some other fault.
 			device disconnect), or some other fault.
 
 
--ETIMEDOUT (**)		No response packet received within the prescribed
+-ETIME (**)		No response packet received within the prescribed
 			bus turn-around time.  This error may instead be
 			bus turn-around time.  This error may instead be
 			reported as -EPROTO or -EILSEQ.
 			reported as -EPROTO or -EILSEQ.
 
 
-			Note that the synchronous USB message functions
-			also use this code to indicate timeout expired
-			before the transfer completed.
+-ETIMEDOUT		Synchronous USB message functions use this code
+			to indicate timeout expired before the transfer
+			completed, and no other error was reported by HC.
 
 
 -EPIPE (**)		Endpoint stalled.  For non-control endpoints,
 -EPIPE (**)		Endpoint stalled.  For non-control endpoints,
 			reset this status with usb_clear_halt().
 			reset this status with usb_clear_halt().
@@ -163,6 +163,3 @@ usb_get_*/usb_set_*():
 usb_control_msg():
 usb_control_msg():
 usb_bulk_msg():
 usb_bulk_msg():
 -ETIMEDOUT		Timeout expired before the transfer completed.
 -ETIMEDOUT		Timeout expired before the transfer completed.
-			In the future this code may change to -ETIME,
-			whose definition is a closer match to this sort
-			of error.

+ 5 - 0
Documentation/usb/usb-serial.txt

@@ -433,6 +433,11 @@ Options supported:
   See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
   See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
   information on this driver.
   information on this driver.
 
 
+AIRcable USB Dongle Bluetooth driver
+  If there is the cdc_acm driver loaded in the system, you will find that the
+  cdc_acm claims the device before AIRcable can. This is simply corrected
+  by unloading both modules and then loading the aircable module before
+  cdc_acm module
 
 
 Generic Serial driver
 Generic Serial driver
 
 

+ 1 - 14
arch/arm/mach-pxa/corgi.c

@@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = {
 /*
 /*
  * USB Device Controller
  * USB Device Controller
  */
  */
-static void corgi_udc_command(int cmd)
-{
-	switch(cmd)	{
-	case PXA2XX_UDC_CMD_CONNECT:
-		GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-		break;
-	case PXA2XX_UDC_CMD_DISCONNECT:
-		GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-		break;
-	}
-}
-
 static struct pxa2xx_udc_mach_info udc_info __initdata = {
 static struct pxa2xx_udc_mach_info udc_info __initdata = {
 	/* no connect GPIO; corgi can't tell connection status */
 	/* no connect GPIO; corgi can't tell connection status */
-	.udc_command		= corgi_udc_command,
+	.gpio_pullup		= CORGI_GPIO_USB_PULLUP,
 };
 };
 
 
 
 
@@ -350,7 +338,6 @@ static void __init corgi_init(void)
 	corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
 	corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
 
 
 	pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
 	pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
-	pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
 	pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
 	pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
 
 
  	pxa_set_udc_info(&udc_info);
  	pxa_set_udc_info(&udc_info);

+ 1 - 1
arch/arm/plat-omap/usb.c

@@ -26,7 +26,7 @@
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 
 
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/irq.h>

+ 25 - 13
drivers/block/ub.c

@@ -358,7 +358,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
 static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
 static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
     struct ub_scsi_cmd *cmd, struct ub_request *urq);
     struct ub_scsi_cmd *cmd, struct ub_request *urq);
 static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, int uptodate);
+static void ub_end_rq(struct request *rq, unsigned int status);
 static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
 static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
     struct ub_request *urq, struct ub_scsi_cmd *cmd);
     struct ub_request *urq, struct ub_scsi_cmd *cmd);
 static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
 static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
@@ -639,9 +639,15 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
 	struct ub_request *urq;
 	struct ub_request *urq;
 	int n_elem;
 	int n_elem;
 
 
-	if (atomic_read(&sc->poison) || lun->changed) {
+	if (atomic_read(&sc->poison)) {
+		blkdev_dequeue_request(rq);
+		ub_end_rq(rq, DID_NO_CONNECT << 16);
+		return 0;
+	}
+
+	if (lun->changed && !blk_pc_request(rq)) {
 		blkdev_dequeue_request(rq);
 		blkdev_dequeue_request(rq);
-		ub_end_rq(rq, 0);
+		ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -693,7 +699,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
 
 
 drop:
 drop:
 	ub_put_cmd(lun, cmd);
 	ub_put_cmd(lun, cmd);
-	ub_end_rq(rq, 0);
+	ub_end_rq(rq, DID_ERROR << 16);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -761,47 +767,53 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
 	struct ub_lun *lun = cmd->lun;
 	struct ub_lun *lun = cmd->lun;
 	struct ub_request *urq = cmd->back;
 	struct ub_request *urq = cmd->back;
 	struct request *rq;
 	struct request *rq;
-	int uptodate;
+	unsigned int scsi_status;
 
 
 	rq = urq->rq;
 	rq = urq->rq;
 
 
 	if (cmd->error == 0) {
 	if (cmd->error == 0) {
-		uptodate = 1;
-
 		if (blk_pc_request(rq)) {
 		if (blk_pc_request(rq)) {
 			if (cmd->act_len >= rq->data_len)
 			if (cmd->act_len >= rq->data_len)
 				rq->data_len = 0;
 				rq->data_len = 0;
 			else
 			else
 				rq->data_len -= cmd->act_len;
 				rq->data_len -= cmd->act_len;
 		}
 		}
+		scsi_status = 0;
 	} else {
 	} else {
-		uptodate = 0;
-
 		if (blk_pc_request(rq)) {
 		if (blk_pc_request(rq)) {
 			/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
 			/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
 			memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
 			memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
 			rq->sense_len = UB_SENSE_SIZE;
 			rq->sense_len = UB_SENSE_SIZE;
 			if (sc->top_sense[0] != 0)
 			if (sc->top_sense[0] != 0)
-				rq->errors = SAM_STAT_CHECK_CONDITION;
+				scsi_status = SAM_STAT_CHECK_CONDITION;
 			else
 			else
-				rq->errors = DID_ERROR << 16;
+				scsi_status = DID_ERROR << 16;
 		} else {
 		} else {
 			if (cmd->error == -EIO) {
 			if (cmd->error == -EIO) {
 				if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
 				if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
 					return;
 					return;
 			}
 			}
+			scsi_status = SAM_STAT_CHECK_CONDITION;
 		}
 		}
 	}
 	}
 
 
 	urq->rq = NULL;
 	urq->rq = NULL;
 
 
 	ub_put_cmd(lun, cmd);
 	ub_put_cmd(lun, cmd);
-	ub_end_rq(rq, uptodate);
+	ub_end_rq(rq, scsi_status);
 	blk_start_queue(lun->disk->queue);
 	blk_start_queue(lun->disk->queue);
 }
 }
 
 
-static void ub_end_rq(struct request *rq, int uptodate)
+static void ub_end_rq(struct request *rq, unsigned int scsi_status)
 {
 {
+	int uptodate;
+
+	if (scsi_status == 0) {
+		uptodate = 1;
+	} else {
+		uptodate = 0;
+		rq->errors = scsi_status;
+	}
 	end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
 	end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
 	end_that_request_last(rq, uptodate);
 	end_that_request_last(rq, uptodate);
 }
 }

+ 1 - 1
drivers/i2c/chips/isp1301_omap.c

@@ -30,7 +30,7 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 
 

+ 1 - 1
drivers/isdn/gigaset/bas-gigaset.c

@@ -192,7 +192,7 @@ static char *get_usb_statmsg(int status)
 		return "bit stuffing error, timeout, or unknown USB error";
 		return "bit stuffing error, timeout, or unknown USB error";
 	case -EILSEQ:
 	case -EILSEQ:
 		return "CRC mismatch, timeout, or unknown USB error";
 		return "CRC mismatch, timeout, or unknown USB error";
-	case -ETIMEDOUT:
+	case -ETIME:
 		return "timed out";
 		return "timed out";
 	case -EPIPE:
 	case -EPIPE:
 		return "endpoint stalled";
 		return "endpoint stalled";

+ 3 - 3
drivers/isdn/hisax/hfc_usb.h

@@ -137,11 +137,11 @@ static struct hfcusb_symbolic_list urb_errlist[] = {
 	{-ENXIO, "URB already queued"},
 	{-ENXIO, "URB already queued"},
 	{-EFBIG, "Too much ISO frames requested"},
 	{-EFBIG, "Too much ISO frames requested"},
 	{-ENOSR, "Buffer error (overrun)"},
 	{-ENOSR, "Buffer error (overrun)"},
-	{-EPIPE, "Specified endpoint is stalled (device not responding)"},
+	{-EPIPE, "Specified endpoint is stalled"},
 	{-EOVERFLOW, "Babble (bad cable?)"},
 	{-EOVERFLOW, "Babble (bad cable?)"},
 	{-EPROTO, "Bit-stuff error (bad cable?)"},
 	{-EPROTO, "Bit-stuff error (bad cable?)"},
-	{-EILSEQ, "CRC/Timeout"},
-	{-ETIMEDOUT, "NAK (device does not respond)"},
+	{-EILSEQ, "CRC or missing token"},
+	{-ETIME, "Device did not respond"},
 	{-ESHUTDOWN, "Device unplugged"},
 	{-ESHUTDOWN, "Device unplugged"},
 	{-1, NULL}
 	{-1, NULL}
 };
 };

+ 0 - 1
drivers/media/dvb/dvb-usb/dvb-usb-urb.c

@@ -80,7 +80,6 @@ static void dvb_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
 
 
 	switch (urb->status) {
 	switch (urb->status) {
 		case 0:         /* success */
 		case 0:         /* success */
-		case -ETIMEDOUT:    /* NAK */
 			break;
 			break;
 		case -ECONNRESET:   /* kill */
 		case -ECONNRESET:   /* kill */
 		case -ENOENT:
 		case -ENOENT:

+ 1 - 1
drivers/media/dvb/ttusb-dec/ttusb_dec.c

@@ -215,7 +215,7 @@ static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
 		case -ECONNRESET:
 		case -ECONNRESET:
 		case -ENOENT:
 		case -ENOENT:
 		case -ESHUTDOWN:
 		case -ESHUTDOWN:
-		case -ETIMEDOUT:
+		case -ETIME:
 			/* this urb is dead, cleanup */
 			/* this urb is dead, cleanup */
 			dprintk("%s:urb shutting down with status: %d\n",
 			dprintk("%s:urb shutting down with status: %d\n",
 					__FUNCTION__, urb->status);
 					__FUNCTION__, urb->status);

+ 4 - 3
drivers/media/video/ov511.c

@@ -301,10 +301,11 @@ static struct symbolic_list senlist[] = {
 static struct symbolic_list urb_errlist[] = {
 static struct symbolic_list urb_errlist[] = {
 	{ -ENOSR,	"Buffer error (overrun)" },
 	{ -ENOSR,	"Buffer error (overrun)" },
 	{ -EPIPE,	"Stalled (device not responding)" },
 	{ -EPIPE,	"Stalled (device not responding)" },
-	{ -EOVERFLOW,	"Babble (bad cable?)" },
+	{ -EOVERFLOW,	"Babble (device sends too much data)" },
 	{ -EPROTO,	"Bit-stuff error (bad cable?)" },
 	{ -EPROTO,	"Bit-stuff error (bad cable?)" },
-	{ -EILSEQ,	"CRC/Timeout" },
-	{ -ETIMEDOUT,	"NAK (device does not respond)" },
+	{ -EILSEQ,	"CRC/Timeout (bad cable?)" },
+	{ -ETIME,	"Device does not respond to token" },
+	{ -ETIMEDOUT,	"Device does not respond to command" },
 	{ -1, NULL }
 	{ -1, NULL }
 };
 };
 
 

+ 1 - 1
drivers/media/video/pwc/pwc-if.c

@@ -711,7 +711,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
 			case -EOVERFLOW:	errmsg = "Babble (bad cable?)"; break;
 			case -EOVERFLOW:	errmsg = "Babble (bad cable?)"; break;
 			case -EPROTO:		errmsg = "Bit-stuff error (bad cable?)"; break;
 			case -EPROTO:		errmsg = "Bit-stuff error (bad cable?)"; break;
 			case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)"; break;
 			case -EILSEQ:		errmsg = "CRC/Timeout (could be anything)"; break;
-			case -ETIMEDOUT:	errmsg = "NAK (device does not respond)"; break;
+			case -ETIME:		errmsg = "Device does not respond"; break;
 		}
 		}
 		PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
 		PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
 		/* Give up after a number of contiguous errors on the USB bus.
 		/* Give up after a number of contiguous errors on the USB bus.

+ 3 - 4
drivers/media/video/w9968cf.c

@@ -586,15 +586,14 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
 	{ -EFBIG,     "Too much ISO frames requested" },
 	{ -EFBIG,     "Too much ISO frames requested" },
 	{ -ENOSR,     "Buffer error (overrun)" },
 	{ -ENOSR,     "Buffer error (overrun)" },
 	{ -EPIPE,     "Specified endpoint is stalled (device not responding)"},
 	{ -EPIPE,     "Specified endpoint is stalled (device not responding)"},
-	{ -EOVERFLOW, "Babble (bad cable?)" },
+	{ -EOVERFLOW, "Babble (too much data)" },
 	{ -EPROTO,    "Bit-stuff error (bad cable?)" },
 	{ -EPROTO,    "Bit-stuff error (bad cable?)" },
 	{ -EILSEQ,    "CRC/Timeout" },
 	{ -EILSEQ,    "CRC/Timeout" },
-	{ -ETIMEDOUT, "NAK (device does not respond)" },
+	{ -ETIME,     "Device does not respond to token" },
+	{ -ETIMEDOUT, "Device does not respond to command" },
 	{ -1, NULL }
 	{ -1, NULL }
 };
 };
 
 
-
-
 /****************************************************************************
 /****************************************************************************
  * Memory management functions                                              *
  * Memory management functions                                              *
  ****************************************************************************/
  ****************************************************************************/

+ 7 - 11
drivers/net/irda/irda-usb.c

@@ -671,10 +671,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
 			 * Jean II */
 			 * Jean II */
 			done = 1;
 			done = 1;
 			break;
 			break;
-		case -ECONNABORTED:		/* -103 */
-		case -ECONNRESET:		/* -104 */
-		case -ETIMEDOUT:		/* -110 */
-		case -ENOENT:			/* -2 (urb unlinked by us)  */
+		case -ECONNRESET:
+		case -ENOENT:			/* urb unlinked by us */
 		default:			/* ??? - Play safe */
 		default:			/* ??? - Play safe */
 			urb->status = 0;
 			urb->status = 0;
 			netif_wake_queue(self->netdev);
 			netif_wake_queue(self->netdev);
@@ -712,10 +710,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
 			 * Jean II */
 			 * Jean II */
 			done = 1;
 			done = 1;
 			break;
 			break;
-		case -ECONNABORTED:		/* -103 */
-		case -ECONNRESET:		/* -104 */
-		case -ETIMEDOUT:		/* -110 */
-		case -ENOENT:			/* -2 (urb unlinked by us)  */
+		case -ECONNRESET:
+		case -ENOENT:			/* urb unlinked by us */
 		default:			/* ??? - Play safe */
 		default:			/* ??? - Play safe */
 			if(skb != NULL) {
 			if(skb != NULL) {
 				dev_kfree_skb_any(skb);
 				dev_kfree_skb_any(skb);
@@ -845,14 +841,14 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs)
 			self->stats.rx_crc_errors++;	
 			self->stats.rx_crc_errors++;	
 			/* Also precursor to a hot-unplug on UHCI. */
 			/* Also precursor to a hot-unplug on UHCI. */
 			/* Fallthrough... */
 			/* Fallthrough... */
-		case -ECONNRESET:		/* -104 */
+		case -ECONNRESET:
 			/* Random error, if I remember correctly */
 			/* Random error, if I remember correctly */
 			/* uhci_cleanup_unlink() is going to kill the Rx
 			/* uhci_cleanup_unlink() is going to kill the Rx
 			 * URB just after we return. No problem, at this
 			 * URB just after we return. No problem, at this
 			 * point the URB will be idle ;-) - Jean II */
 			 * point the URB will be idle ;-) - Jean II */
-		case -ESHUTDOWN:		/* -108 */
+		case -ESHUTDOWN:
 			/* That's usually a hot-unplug. Submit will fail... */
 			/* That's usually a hot-unplug. Submit will fail... */
-		case -ETIMEDOUT:		/* -110 */
+		case -ETIME:
 			/* Usually precursor to a hot-unplug on OHCI. */
 			/* Usually precursor to a hot-unplug on OHCI. */
 		default:
 		default:
 			self->stats.rx_errors++;
 			self->stats.rx_errors++;

+ 2 - 2
drivers/net/wireless/zd1201.c

@@ -119,7 +119,7 @@ static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
 	switch(urb->status) {
 	switch(urb->status) {
 		case -EILSEQ:
 		case -EILSEQ:
 		case -ENODEV:
 		case -ENODEV:
-		case -ETIMEDOUT:
+		case -ETIME:
 		case -ENOENT:
 		case -ENOENT:
 		case -EPIPE:
 		case -EPIPE:
 		case -EOVERFLOW:
 		case -EOVERFLOW:
@@ -201,7 +201,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
 	switch(urb->status) {
 	switch(urb->status) {
 		case -EILSEQ:
 		case -EILSEQ:
 		case -ENODEV:
 		case -ENODEV:
-		case -ETIMEDOUT:
+		case -ETIME:
 		case -ENOENT:
 		case -ENOENT:
 		case -EPIPE:
 		case -EPIPE:
 		case -EOVERFLOW:
 		case -EOVERFLOW:

+ 1 - 0
drivers/usb/Kconfig

@@ -25,6 +25,7 @@ config USB_ARCH_HAS_OHCI
 	default y if PXA27x
 	default y if PXA27x
 	default y if ARCH_EP93XX
 	default y if ARCH_EP93XX
 	default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
 	default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
+	default y if ARCH_PNX4008
 	# PPC:
 	# PPC:
 	default y if STB03xxx
 	default y if STB03xxx
 	default y if PPC_MPC52xx
 	default y if PPC_MPC52xx

+ 8 - 4
drivers/usb/Makefile

@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD)	+= host/
 obj-$(CONFIG_USB_OHCI_HCD)	+= host/
 obj-$(CONFIG_USB_OHCI_HCD)	+= host/
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
+obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_ETRAX_USB_HOST)	+= host/
 obj-$(CONFIG_ETRAX_USB_HOST)	+= host/
 obj-$(CONFIG_USB_OHCI_AT91)	+= host/
 obj-$(CONFIG_USB_OHCI_AT91)	+= host/
 
 
@@ -23,6 +24,7 @@ obj-$(CONFIG_USB_PRINTER)	+= class/
 obj-$(CONFIG_USB_STORAGE)	+= storage/
 obj-$(CONFIG_USB_STORAGE)	+= storage/
 obj-$(CONFIG_USB)		+= storage/
 obj-$(CONFIG_USB)		+= storage/
 
 
+obj-$(CONFIG_USB_ACECAD)	+= input/
 obj-$(CONFIG_USB_AIPTEK)	+= input/
 obj-$(CONFIG_USB_AIPTEK)	+= input/
 obj-$(CONFIG_USB_ATI_REMOTE)	+= input/
 obj-$(CONFIG_USB_ATI_REMOTE)	+= input/
 obj-$(CONFIG_USB_HID)		+= input/
 obj-$(CONFIG_USB_HID)		+= input/
@@ -31,8 +33,8 @@ obj-$(CONFIG_USB_KBTAB)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
-obj-$(CONFIG_USB_ACECAD)	+= input/
 obj-$(CONFIG_USB_XPAD)		+= input/
 obj-$(CONFIG_USB_XPAD)		+= input/
 
 
 obj-$(CONFIG_USB_CATC)		+= net/
 obj-$(CONFIG_USB_CATC)		+= net/
@@ -47,22 +49,24 @@ obj-$(CONFIG_USB_MICROTEK)	+= image/
 
 
 obj-$(CONFIG_USB_SERIAL)	+= serial/
 obj-$(CONFIG_USB_SERIAL)	+= serial/
 
 
+obj-$(CONFIG_USB_ADUTUX)	+= misc/
+obj-$(CONFIG_USB_APPLEDISPLAY)	+= misc/
 obj-$(CONFIG_USB_AUERSWALD)	+= misc/
 obj-$(CONFIG_USB_AUERSWALD)	+= misc/
 obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
 obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
 obj-$(CONFIG_USB_CYTHERM)	+= misc/
 obj-$(CONFIG_USB_CYTHERM)	+= misc/
 obj-$(CONFIG_USB_EMI26)		+= misc/
 obj-$(CONFIG_USB_EMI26)		+= misc/
 obj-$(CONFIG_USB_EMI62)		+= misc/
 obj-$(CONFIG_USB_EMI62)		+= misc/
+obj-$(CONFIG_USB_FTDI_ELAN)	+= misc/
 obj-$(CONFIG_USB_IDMOUSE)	+= misc/
 obj-$(CONFIG_USB_IDMOUSE)	+= misc/
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_LD)		+= misc/
 obj-$(CONFIG_USB_LD)		+= misc/
 obj-$(CONFIG_USB_LED)		+= misc/
 obj-$(CONFIG_USB_LED)		+= misc/
 obj-$(CONFIG_USB_LEGOTOWER)	+= misc/
 obj-$(CONFIG_USB_LEGOTOWER)	+= misc/
+obj-$(CONFIG_USB_PHIDGETSERVO)	+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
+obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
 obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
-obj-$(CONFIG_USB_PHIDGETSERVO)	+= misc/
-obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
-obj-$(CONFIG_USB_APPLEDISPLAY)	+= misc/
 
 
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= atm/

+ 34 - 40
drivers/usb/atm/ueagle-atm.c

@@ -1621,26 +1621,32 @@ static int claim_interface(struct usb_device *usb_dev,
 	return ret;
 	return ret;
 }
 }
 
 
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static struct attribute *attrs[] = {
+	&dev_attr_stat_status.attr,
+	&dev_attr_stat_mflags.attr,
+	&dev_attr_stat_human_status.attr,
+	&dev_attr_stat_delin.attr,
+	&dev_attr_stat_vidcpe.attr,
+	&dev_attr_stat_usrate.attr,
+	&dev_attr_stat_dsrate.attr,
+	&dev_attr_stat_usattenuation.attr,
+	&dev_attr_stat_dsattenuation.attr,
+	&dev_attr_stat_usmargin.attr,
+	&dev_attr_stat_dsmargin.attr,
+	&dev_attr_stat_txflow.attr,
+	&dev_attr_stat_rxflow.attr,
+	&dev_attr_stat_uscorr.attr,
+	&dev_attr_stat_dscorr.attr,
+	&dev_attr_stat_usunc.attr,
+	&dev_attr_stat_dsunc.attr,
+};
+static struct attribute_group attr_grp = {
+	.attrs = attrs,
+};
+
+static int create_fs_entries(struct usb_interface *intf)
 {
 {
-	/* sysfs interface */
-	device_create_file(&intf->dev, &dev_attr_stat_status);
-	device_create_file(&intf->dev, &dev_attr_stat_mflags);
-	device_create_file(&intf->dev, &dev_attr_stat_human_status);
-	device_create_file(&intf->dev, &dev_attr_stat_delin);
-	device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
-	device_create_file(&intf->dev, &dev_attr_stat_usrate);
-	device_create_file(&intf->dev, &dev_attr_stat_dsrate);
-	device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
-	device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
-	device_create_file(&intf->dev, &dev_attr_stat_usmargin);
-	device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
-	device_create_file(&intf->dev, &dev_attr_stat_txflow);
-	device_create_file(&intf->dev, &dev_attr_stat_rxflow);
-	device_create_file(&intf->dev, &dev_attr_stat_uscorr);
-	device_create_file(&intf->dev, &dev_attr_stat_dscorr);
-	device_create_file(&intf->dev, &dev_attr_stat_usunc);
-	device_create_file(&intf->dev, &dev_attr_stat_dsunc);
+	return sysfs_create_group(&intf->dev.kobj, &attr_grp);
 }
 }
 
 
 static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
 static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
@@ -1708,37 +1714,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
 		return ret;
 		return ret;
 	}
 	}
 
 
-	create_fs_entries(sc, intf);
+	ret = create_fs_entries(intf);
+	if (ret) {
+		uea_stop(sc);
+		kfree(sc);
+		return ret;
+	}
 	return 0;
 	return 0;
 }
 }
 
 
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static void destroy_fs_entries(struct usb_interface *intf)
 {
 {
-	/* sysfs interface */
-	device_remove_file(&intf->dev, &dev_attr_stat_status);
-	device_remove_file(&intf->dev, &dev_attr_stat_mflags);
-	device_remove_file(&intf->dev, &dev_attr_stat_human_status);
-	device_remove_file(&intf->dev, &dev_attr_stat_delin);
-	device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
-	device_remove_file(&intf->dev, &dev_attr_stat_usrate);
-	device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
-	device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
-	device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
-	device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
-	device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
-	device_remove_file(&intf->dev, &dev_attr_stat_txflow);
-	device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
-	device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
-	device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
-	device_remove_file(&intf->dev, &dev_attr_stat_usunc);
-	device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+	sysfs_remove_group(&intf->dev.kobj, &attr_grp);
 }
 }
 
 
 static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
 {
 	struct uea_softc *sc = usbatm->driver_data;
 	struct uea_softc *sc = usbatm->driver_data;
 
 
-	destroy_fs_entries(sc, intf);
+	destroy_fs_entries(intf);
 	uea_stop(sc);
 	uea_stop(sc);
 	kfree(sc);
 	kfree(sc);
 }
 }

+ 6 - 9
drivers/usb/class/usblp.c

@@ -813,7 +813,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct file_operations usblp_fops = {
+static const struct file_operations usblp_fops = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.read =		usblp_read,
 	.read =		usblp_read,
 	.write =	usblp_write,
 	.write =	usblp_write,
@@ -927,7 +927,9 @@ static int usblp_probe(struct usb_interface *intf,
 
 
 	/* Retrieve and store the device ID string. */
 	/* Retrieve and store the device ID string. */
 	usblp_cache_device_id_string(usblp);
 	usblp_cache_device_id_string(usblp);
-	device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+	retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+	if (retval)
+		goto abort_intfdata;
 
 
 #ifdef DEBUG
 #ifdef DEBUG
 	usblp_check_status(usblp, 0);
 	usblp_check_status(usblp, 0);
@@ -1021,18 +1023,13 @@ static int usblp_select_alts(struct usblp *usblp)
 		for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
 		for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
 			epd = &ifd->endpoint[e].desc;
 			epd = &ifd->endpoint[e].desc;
 
 
-			if ((epd->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)!=
-			    USB_ENDPOINT_XFER_BULK)
-				continue;
-
-			if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) {
+			if (usb_endpoint_is_bulk_out(epd))
 				if (!epwrite)
 				if (!epwrite)
 					epwrite = epd;
 					epwrite = epd;
 
 
-			} else {
+			if (usb_endpoint_is_bulk_in(epd))
 				if (!epread)
 				if (!epread)
 					epread = epd;
 					epread = epd;
-			}
 		}
 		}
 
 
 		/* Ignore buggy hardware without the right endpoints. */
 		/* Ignore buggy hardware without the right endpoints. */

+ 1 - 1
drivers/usb/core/Makefile

@@ -4,7 +4,7 @@
 
 
 usbcore-objs	:= usb.o hub.o hcd.o urb.o message.o driver.o \
 usbcore-objs	:= usb.o hub.o hcd.o urb.o message.o driver.o \
 			config.o file.o buffer.o sysfs.o endpoint.o \
 			config.o file.o buffer.o sysfs.o endpoint.o \
-			devio.o notify.o
+			devio.o notify.o generic.o
 
 
 ifeq ($(CONFIG_PCI),y)
 ifeq ($(CONFIG_PCI),y)
 	usbcore-objs	+= hcd-pci.o
 	usbcore-objs	+= hcd-pci.o

+ 2 - 2
drivers/usb/core/buffer.c

@@ -104,7 +104,7 @@ void *hcd_buffer_alloc (
 	dma_addr_t		*dma
 	dma_addr_t		*dma
 )
 )
 {
 {
-	struct usb_hcd		*hcd = bus->hcpriv;
+	struct usb_hcd		*hcd = bus_to_hcd(bus);
 	int 			i;
 	int 			i;
 
 
 	/* some USB hosts just use PIO */
 	/* some USB hosts just use PIO */
@@ -127,7 +127,7 @@ void hcd_buffer_free (
 	dma_addr_t		dma
 	dma_addr_t		dma
 )
 )
 {
 {
-	struct usb_hcd		*hcd = bus->hcpriv;
+	struct usb_hcd		*hcd = bus_to_hcd(bus);
 	int 			i;
 	int 			i;
 
 
 	if (!addr)
 	if (!addr)

+ 3 - 1
drivers/usb/core/config.c

@@ -475,7 +475,9 @@ int usb_get_configuration(struct usb_device *dev)
 		if (result < 0) {
 		if (result < 0) {
 			dev_err(ddev, "unable to read config index %d "
 			dev_err(ddev, "unable to read config index %d "
 			    "descriptor/%s\n", cfgno, "start");
 			    "descriptor/%s\n", cfgno, "start");
-			goto err;
+			dev_err(ddev, "chopping to %d config(s)\n", cfgno);
+			dev->descriptor.bNumConfigurations = cfgno;
+			break;
 		} else if (result < 4) {
 		} else if (result < 4) {
 			dev_err(ddev, "config index %d descriptor too short "
 			dev_err(ddev, "config index %d descriptor too short "
 			    "(expected %i, got %i)\n", cfgno,
 			    "(expected %i, got %i)\n", cfgno,

+ 3 - 3
drivers/usb/core/devices.c

@@ -593,7 +593,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
 /* Kernel lock for "lastev" protection */
 /* Kernel lock for "lastev" protection */
 static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
 static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
 {
 {
-	struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+	struct usb_device_status *st = file->private_data;
 	unsigned int mask = 0;
 	unsigned int mask = 0;
 
 
 	lock_kernel();
 	lock_kernel();
@@ -603,7 +603,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
 			unlock_kernel();
 			unlock_kernel();
 			return POLLIN;
 			return POLLIN;
 		}
 		}
-		
+
 		/* we may have dropped BKL - need to check for having lost the race */
 		/* we may have dropped BKL - need to check for having lost the race */
 		if (file->private_data) {
 		if (file->private_data) {
 			kfree(st);
 			kfree(st);
@@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
 	return ret;
 	return ret;
 }
 }
 
 
-struct file_operations usbfs_devices_fops = {
+const struct file_operations usbfs_devices_fops = {
 	.llseek =	usb_device_lseek,
 	.llseek =	usb_device_lseek,
 	.read =		usb_device_read,
 	.read =		usb_device_read,
 	.poll =		usb_device_poll,
 	.poll =		usb_device_poll,

+ 38 - 28
drivers/usb/core/devio.c

@@ -59,6 +59,9 @@
 #define USB_DEVICE_MAX			USB_MAXBUS * 128
 #define USB_DEVICE_MAX			USB_MAXBUS * 128
 static struct class *usb_device_class;
 static struct class *usb_device_class;
 
 
+/* Mutual exclusion for removal, open, and release */
+DEFINE_MUTEX(usbfs_mutex);
+
 struct async {
 struct async {
 	struct list_head asynclist;
 	struct list_head asynclist;
 	struct dev_state *ps;
 	struct dev_state *ps;
@@ -87,9 +90,10 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
 
 
 #define	MAX_USBFS_BUFFER_SIZE	16384
 #define	MAX_USBFS_BUFFER_SIZE	16384
 
 
-static inline int connected (struct usb_device *dev)
+static inline int connected (struct dev_state *ps)
 {
 {
-	return dev->state != USB_STATE_NOTATTACHED;
+	return (!list_empty(&ps->list) &&
+			ps->dev->state != USB_STATE_NOTATTACHED);
 }
 }
 
 
 static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
 static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
@@ -118,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
 
 
 static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 {
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	struct usb_device *dev = ps->dev;
 	ssize_t ret = 0;
 	ssize_t ret = 0;
 	unsigned len;
 	unsigned len;
@@ -127,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
 
 
 	pos = *ppos;
 	pos = *ppos;
 	usb_lock_device(dev);
 	usb_lock_device(dev);
-	if (!connected(dev)) {
+	if (!connected(ps)) {
 		ret = -ENODEV;
 		ret = -ENODEV;
 		goto err;
 		goto err;
 	} else if (pos < 0) {
 	} else if (pos < 0) {
@@ -301,7 +305,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
 
 
 static void async_completed(struct urb *urb, struct pt_regs *regs)
 static void async_completed(struct urb *urb, struct pt_regs *regs)
 {
 {
-        struct async *as = (struct async *)urb->context;
+        struct async *as = urb->context;
         struct dev_state *ps = as->ps;
         struct dev_state *ps = as->ps;
 	struct siginfo sinfo;
 	struct siginfo sinfo;
 
 
@@ -541,25 +545,25 @@ static int usbdev_open(struct inode *inode, struct file *file)
 	struct dev_state *ps;
 	struct dev_state *ps;
 	int ret;
 	int ret;
 
 
-	/* 
-	 * no locking necessary here, as chrdev_open has the kernel lock
-	 * (still acquire the kernel lock for safety)
-	 */
+	/* Protect against simultaneous removal or release */
+	mutex_lock(&usbfs_mutex);
+
 	ret = -ENOMEM;
 	ret = -ENOMEM;
 	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
 	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
-		goto out_nolock;
+		goto out;
 
 
-	lock_kernel();
 	ret = -ENOENT;
 	ret = -ENOENT;
 	/* check if we are called from a real node or usbfs */
 	/* check if we are called from a real node or usbfs */
 	if (imajor(inode) == USB_DEVICE_MAJOR)
 	if (imajor(inode) == USB_DEVICE_MAJOR)
 		dev = usbdev_lookup_minor(iminor(inode));
 		dev = usbdev_lookup_minor(iminor(inode));
 	if (!dev)
 	if (!dev)
 		dev = inode->i_private;
 		dev = inode->i_private;
-	if (!dev) {
-		kfree(ps);
+	if (!dev)
 		goto out;
 		goto out;
-	}
+	ret = usb_autoresume_device(dev, 1);
+	if (ret)
+		goto out;
+
 	usb_get_dev(dev);
 	usb_get_dev(dev);
 	ret = 0;
 	ret = 0;
 	ps->dev = dev;
 	ps->dev = dev;
@@ -579,30 +583,36 @@ static int usbdev_open(struct inode *inode, struct file *file)
 	list_add_tail(&ps->list, &dev->filelist);
 	list_add_tail(&ps->list, &dev->filelist);
 	file->private_data = ps;
 	file->private_data = ps;
  out:
  out:
-	unlock_kernel();
- out_nolock:
-        return ret;
+	if (ret)
+		kfree(ps);
+	mutex_unlock(&usbfs_mutex);
+	return ret;
 }
 }
 
 
 static int usbdev_release(struct inode *inode, struct file *file)
 static int usbdev_release(struct inode *inode, struct file *file)
 {
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	struct usb_device *dev = ps->dev;
 	unsigned int ifnum;
 	unsigned int ifnum;
 
 
 	usb_lock_device(dev);
 	usb_lock_device(dev);
+
+	/* Protect against simultaneous open */
+	mutex_lock(&usbfs_mutex);
 	list_del_init(&ps->list);
 	list_del_init(&ps->list);
+	mutex_unlock(&usbfs_mutex);
+
 	for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
 	for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
 			ifnum++) {
 			ifnum++) {
 		if (test_bit(ifnum, &ps->ifclaimed))
 		if (test_bit(ifnum, &ps->ifclaimed))
 			releaseintf(ps, ifnum);
 			releaseintf(ps, ifnum);
 	}
 	}
 	destroy_all_async(ps);
 	destroy_all_async(ps);
+	usb_autosuspend_device(dev, 1);
 	usb_unlock_device(dev);
 	usb_unlock_device(dev);
 	usb_put_dev(dev);
 	usb_put_dev(dev);
-	ps->dev = NULL;
 	kfree(ps);
 	kfree(ps);
-        return 0;
+	return 0;
 }
 }
 
 
 static int proc_control(struct dev_state *ps, void __user *arg)
 static int proc_control(struct dev_state *ps, void __user *arg)
@@ -1322,7 +1332,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
 		}
 		}
 	}
 	}
 
 
-	if (!connected(ps->dev)) {
+	if (!connected(ps)) {
 		kfree(buf);
 		kfree(buf);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
@@ -1349,7 +1359,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
 	/* let kernel drivers try to (re)bind to the interface */
 	/* let kernel drivers try to (re)bind to the interface */
 	case USBDEVFS_CONNECT:
 	case USBDEVFS_CONNECT:
 		usb_unlock_device(ps->dev);
 		usb_unlock_device(ps->dev);
-		bus_rescan_devices(intf->dev.bus);
+		retval = bus_rescan_devices(intf->dev.bus);
 		usb_lock_device(ps->dev);
 		usb_lock_device(ps->dev);
 		break;
 		break;
 
 
@@ -1413,7 +1423,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
  */
  */
 static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	struct usb_device *dev = ps->dev;
 	void __user *p = (void __user *)arg;
 	void __user *p = (void __user *)arg;
 	int ret = -ENOTTY;
 	int ret = -ENOTTY;
@@ -1421,7 +1431,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 	if (!(file->f_mode & FMODE_WRITE))
 	if (!(file->f_mode & FMODE_WRITE))
 		return -EPERM;
 		return -EPERM;
 	usb_lock_device(dev);
 	usb_lock_device(dev);
-	if (!connected(dev)) {
+	if (!connected(ps)) {
 		usb_unlock_device(dev);
 		usb_unlock_device(dev);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
@@ -1556,18 +1566,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 /* No kernel lock - fine */
 /* No kernel lock - fine */
 static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
 static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
 {
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
-        unsigned int mask = 0;
+	struct dev_state *ps = file->private_data;
+	unsigned int mask = 0;
 
 
 	poll_wait(file, &ps->wait, wait);
 	poll_wait(file, &ps->wait, wait);
 	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
 	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
 		mask |= POLLOUT | POLLWRNORM;
 		mask |= POLLOUT | POLLWRNORM;
-	if (!connected(ps->dev))
+	if (!connected(ps))
 		mask |= POLLERR | POLLHUP;
 		mask |= POLLERR | POLLHUP;
 	return mask;
 	return mask;
 }
 }
 
 
-struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbfs_device_file_operations = {
 	.llseek =	usbdev_lseek,
 	.llseek =	usbdev_lseek,
 	.read =		usbdev_read,
 	.read =		usbdev_read,
 	.poll =		usbdev_poll,
 	.poll =		usbdev_poll,

+ 934 - 77
drivers/usb/core/driver.c

@@ -17,7 +17,8 @@
  *
  *
  * NOTE! This is not actually a driver at all, rather this is
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
  * just a collection of helper routines that implement the
- * generic USB things that the real drivers can use..
+ * matching, probing, releasing, suspending and resuming for
+ * real drivers.
  *
  *
  */
  */
 
 
@@ -34,38 +35,6 @@ struct usb_dynid {
 	struct usb_device_id id;
 	struct usb_device_id id;
 };
 };
 
 
-
-static int generic_probe(struct device *dev)
-{
-	return 0;
-}
-static int generic_remove(struct device *dev)
-{
-	struct usb_device *udev = to_usb_device(dev);
-
-	/* if this is only an unbind, not a physical disconnect, then
-	 * unconfigure the device */
-	if (udev->state == USB_STATE_CONFIGURED)
-		usb_set_configuration(udev, 0);
-
-	/* in case the call failed or the device was suspended */
-	if (udev->state >= USB_STATE_CONFIGURED)
-		usb_disable_device(udev, 0);
-	return 0;
-}
-
-struct device_driver usb_generic_driver = {
-	.owner = THIS_MODULE,
-	.name =	"usb",
-	.bus = &usb_bus_type,
-	.probe = generic_probe,
-	.remove = generic_remove,
-};
-
-/* Fun hack to determine if the struct device is a
- * usb device or a usb interface. */
-int usb_generic_driver_data;
-
 #ifdef CONFIG_HOTPLUG
 #ifdef CONFIG_HOTPLUG
 
 
 /*
 /*
@@ -80,6 +49,7 @@ static ssize_t store_new_id(struct device_driver *driver,
 	u32 idVendor = 0;
 	u32 idVendor = 0;
 	u32 idProduct = 0;
 	u32 idProduct = 0;
 	int fields = 0;
 	int fields = 0;
+	int retval = 0;
 
 
 	fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
 	fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
 	if (fields < 2)
 	if (fields < 2)
@@ -99,10 +69,12 @@ static ssize_t store_new_id(struct device_driver *driver,
 	spin_unlock(&usb_drv->dynids.lock);
 	spin_unlock(&usb_drv->dynids.lock);
 
 
 	if (get_driver(driver)) {
 	if (get_driver(driver)) {
-		driver_attach(driver);
+		retval = driver_attach(driver);
 		put_driver(driver);
 		put_driver(driver);
 	}
 	}
 
 
+	if (retval)
+		return retval;
 	return count;
 	return count;
 }
 }
 static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
 static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -115,7 +87,7 @@ static int usb_create_newid_file(struct usb_driver *usb_drv)
 		goto exit;
 		goto exit;
 
 
 	if (usb_drv->probe != NULL)
 	if (usb_drv->probe != NULL)
-		error = sysfs_create_file(&usb_drv->driver.kobj,
+		error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
 					  &driver_attr_new_id.attr);
 					  &driver_attr_new_id.attr);
 exit:
 exit:
 	return error;
 	return error;
@@ -127,7 +99,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
 		return;
 		return;
 
 
 	if (usb_drv->probe != NULL)
 	if (usb_drv->probe != NULL)
-		sysfs_remove_file(&usb_drv->driver.kobj,
+		sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
 				  &driver_attr_new_id.attr);
 				  &driver_attr_new_id.attr);
 }
 }
 
 
@@ -174,21 +146,57 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
 }
 }
 
 
 
 
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
+static int usb_probe_device(struct device *dev)
+{
+	struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+	struct usb_device *udev;
+	int error = -ENODEV;
+
+	dev_dbg(dev, "%s\n", __FUNCTION__);
+
+	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
+	 * unless the driver suports autosuspend.
+	 */
+	udev->pm_usage_cnt = !(udriver->supports_autosuspend);
+
+	error = udriver->probe(udev);
+	return error;
+}
+
+/* called from driver core with dev locked */
+static int usb_unbind_device(struct device *dev)
+{
+	struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+
+	udriver->disconnect(to_usb_device(dev));
+	return 0;
+}
+
+
+/* called from driver core with dev locked */
 static int usb_probe_interface(struct device *dev)
 static int usb_probe_interface(struct device *dev)
 {
 {
-	struct usb_interface * intf = to_usb_interface(dev);
-	struct usb_driver * driver = to_usb_driver(dev->driver);
+	struct usb_driver *driver = to_usb_driver(dev->driver);
+	struct usb_interface *intf;
+	struct usb_device *udev;
 	const struct usb_device_id *id;
 	const struct usb_device_id *id;
 	int error = -ENODEV;
 	int error = -ENODEV;
 
 
 	dev_dbg(dev, "%s\n", __FUNCTION__);
 	dev_dbg(dev, "%s\n", __FUNCTION__);
 
 
-	if (!driver->probe)
+	if (is_usb_device(dev))		/* Sanity check */
 		return error;
 		return error;
-	/* FIXME we'd much prefer to just resume it ... */
-	if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
-		return -EHOSTUNREACH;
+
+	intf = to_usb_interface(dev);
+	udev = interface_to_usbdev(intf);
 
 
 	id = usb_match_id(intf, driver->id_table);
 	id = usb_match_id(intf, driver->id_table);
 	if (!id)
 	if (!id)
@@ -196,48 +204,165 @@ static int usb_probe_interface(struct device *dev)
 	if (id) {
 	if (id) {
 		dev_dbg(dev, "%s - got id\n", __FUNCTION__);
 		dev_dbg(dev, "%s - got id\n", __FUNCTION__);
 
 
+		error = usb_autoresume_device(udev, 1);
+		if (error)
+			return error;
+
 		/* Interface "power state" doesn't correspond to any hardware
 		/* Interface "power state" doesn't correspond to any hardware
 		 * state whatsoever.  We use it to record when it's bound to
 		 * state whatsoever.  We use it to record when it's bound to
 		 * a driver that may start I/0:  it's not frozen/quiesced.
 		 * a driver that may start I/0:  it's not frozen/quiesced.
 		 */
 		 */
 		mark_active(intf);
 		mark_active(intf);
 		intf->condition = USB_INTERFACE_BINDING;
 		intf->condition = USB_INTERFACE_BINDING;
+
+		/* The interface should always appear to be in use
+		 * unless the driver suports autosuspend.
+		 */
+		intf->pm_usage_cnt = !(driver->supports_autosuspend);
+
 		error = driver->probe(intf, id);
 		error = driver->probe(intf, id);
 		if (error) {
 		if (error) {
 			mark_quiesced(intf);
 			mark_quiesced(intf);
+			intf->needs_remote_wakeup = 0;
 			intf->condition = USB_INTERFACE_UNBOUND;
 			intf->condition = USB_INTERFACE_UNBOUND;
 		} else
 		} else
 			intf->condition = USB_INTERFACE_BOUND;
 			intf->condition = USB_INTERFACE_BOUND;
+
+		usb_autosuspend_device(udev, 1);
 	}
 	}
 
 
 	return error;
 	return error;
 }
 }
 
 
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
 static int usb_unbind_interface(struct device *dev)
 static int usb_unbind_interface(struct device *dev)
 {
 {
+	struct usb_driver *driver = to_usb_driver(dev->driver);
 	struct usb_interface *intf = to_usb_interface(dev);
 	struct usb_interface *intf = to_usb_interface(dev);
-	struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+	struct usb_device *udev;
+	int error;
 
 
 	intf->condition = USB_INTERFACE_UNBINDING;
 	intf->condition = USB_INTERFACE_UNBINDING;
 
 
+	/* Autoresume for set_interface call below */
+	udev = interface_to_usbdev(intf);
+	error = usb_autoresume_device(udev, 1);
+
 	/* release all urbs for this interface */
 	/* release all urbs for this interface */
 	usb_disable_interface(interface_to_usbdev(intf), intf);
 	usb_disable_interface(interface_to_usbdev(intf), intf);
 
 
-	if (driver && driver->disconnect)
-		driver->disconnect(intf);
+	driver->disconnect(intf);
 
 
 	/* reset other interface state */
 	/* reset other interface state */
 	usb_set_interface(interface_to_usbdev(intf),
 	usb_set_interface(interface_to_usbdev(intf),
 			intf->altsetting[0].desc.bInterfaceNumber,
 			intf->altsetting[0].desc.bInterfaceNumber,
 			0);
 			0);
 	usb_set_intfdata(intf, NULL);
 	usb_set_intfdata(intf, NULL);
+
 	intf->condition = USB_INTERFACE_UNBOUND;
 	intf->condition = USB_INTERFACE_UNBOUND;
 	mark_quiesced(intf);
 	mark_quiesced(intf);
+	intf->needs_remote_wakeup = 0;
+
+	if (!error)
+		usb_autosuspend_device(udev, 1);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound; must be in the
+ *	usb device's active configuration
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method.
+ *
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+int usb_driver_claim_interface(struct usb_driver *driver,
+				struct usb_interface *iface, void* priv)
+{
+	struct device *dev = &iface->dev;
+	struct usb_device *udev = interface_to_usbdev(iface);
+	int retval = 0;
+
+	if (dev->driver)
+		return -EBUSY;
+
+	dev->driver = &driver->drvwrap.driver;
+	usb_set_intfdata(iface, priv);
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	iface->condition = USB_INTERFACE_BOUND;
+	mark_active(iface);
+	iface->pm_usage_cnt = !(driver->supports_autosuspend);
+	mutex_unlock(&udev->pm_mutex);
+
+	/* if interface was already added, bind now; else let
+	 * the future device_add() bind it, bypassing probe()
+	 */
+	if (device_is_registered(dev))
+		retval = device_bind_driver(dev);
+
+	return retval;
+}
+EXPORT_SYMBOL(usb_driver_claim_interface);
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called.  In typical cases this
+ * also causes the driver disconnect() method to be called.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock.  So driver disconnect() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+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)
+		return;
+
+	/* don't release from within disconnect() */
+	if (iface->condition != USB_INTERFACE_BOUND)
+		return;
+
+	/* don't release if the interface hasn't been added yet */
+	if (device_is_registered(dev)) {
+		iface->condition = USB_INTERFACE_UNBINDING;
+		device_release_driver(dev);
+	}
+
+	dev->driver = NULL;
+	usb_set_intfdata(iface, NULL);
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	iface->condition = USB_INTERFACE_UNBOUND;
+	mark_quiesced(iface);
+	iface->needs_remote_wakeup = 0;
+	mutex_unlock(&udev->pm_mutex);
+}
+EXPORT_SYMBOL(usb_driver_release_interface);
+
 /* returns 0 if no match, 1 if match */
 /* returns 0 if no match, 1 if match */
 static int usb_match_one_id(struct usb_interface *interface,
 static int usb_match_one_id(struct usb_interface *interface,
 			    const struct usb_device_id *id)
 			    const struct usb_device_id *id)
@@ -380,36 +505,224 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface,
 EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
 EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
 
 
 int usb_device_match(struct device *dev, struct device_driver *drv)
 int usb_device_match(struct device *dev, struct device_driver *drv)
+{
+	/* devices and interfaces are handled separately */
+	if (is_usb_device(dev)) {
+
+		/* interface drivers never match devices */
+		if (!is_usb_device_driver(drv))
+			return 0;
+
+		/* TODO: Add real matching code */
+		return 1;
+
+	} else {
+		struct usb_interface *intf;
+		struct usb_driver *usb_drv;
+		const struct usb_device_id *id;
+
+		/* device drivers never match interfaces */
+		if (is_usb_device_driver(drv))
+			return 0;
+
+		intf = to_usb_interface(dev);
+		usb_drv = to_usb_driver(drv);
+
+		id = usb_match_id(intf, usb_drv->id_table);
+		if (id)
+			return 1;
+
+		id = usb_match_dynamic_id(intf, usb_drv);
+		if (id)
+			return 1;
+	}
+
+	return 0;
+}
+
+#ifdef	CONFIG_HOTPLUG
+
+/*
+ * This sends an uevent to userspace, typically helping to load driver
+ * or other modules, configure the device, and more.  Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_uevent(struct device *dev, char **envp, int num_envp,
+		      char *buffer, int buffer_size)
 {
 {
 	struct usb_interface *intf;
 	struct usb_interface *intf;
-	struct usb_driver *usb_drv;
-	const struct usb_device_id *id;
+	struct usb_device *usb_dev;
+	struct usb_host_interface *alt;
+	int i = 0;
+	int length = 0;
 
 
-	/* check for generic driver, which we don't match any device with */
-	if (drv == &usb_generic_driver)
-		return 0;
+	if (!dev)
+		return -ENODEV;
 
 
-	intf = to_usb_interface(dev);
-	usb_drv = to_usb_driver(drv);
+	/* driver is often null here; dev_dbg() would oops */
+	pr_debug ("usb %s: uevent\n", dev->bus_id);
 
 
-	id = usb_match_id(intf, usb_drv->id_table);
-	if (id)
-		return 1;
+	if (is_usb_device(dev)) {
+		usb_dev = to_usb_device(dev);
+		alt = NULL;
+	} else {
+		intf = to_usb_interface(dev);
+		usb_dev = interface_to_usbdev(intf);
+		alt = intf->cur_altsetting;
+	}
+
+	if (usb_dev->devnum < 0) {
+		pr_debug ("usb %s: already deleted?\n", dev->bus_id);
+		return -ENODEV;
+	}
+	if (!usb_dev->bus) {
+		pr_debug ("usb %s: bus removed?\n", dev->bus_id);
+		return -ENODEV;
+	}
+
+#ifdef	CONFIG_USB_DEVICEFS
+	/* If this is available, userspace programs can directly read
+	 * all the device descriptors we don't tell them about.  Or
+	 * even act as usermode drivers.
+	 *
+	 * FIXME reduce hardwired intelligence here
+	 */
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "DEVICE=/proc/bus/usb/%03d/%03d",
+			   usb_dev->bus->busnum, usb_dev->devnum))
+		return -ENOMEM;
+#endif
+
+	/* per-device configurations are common */
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "PRODUCT=%x/%x/%x",
+			   le16_to_cpu(usb_dev->descriptor.idVendor),
+			   le16_to_cpu(usb_dev->descriptor.idProduct),
+			   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+		return -ENOMEM;
+
+	/* class-based driver binding models */
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "TYPE=%d/%d/%d",
+			   usb_dev->descriptor.bDeviceClass,
+			   usb_dev->descriptor.bDeviceSubClass,
+			   usb_dev->descriptor.bDeviceProtocol))
+		return -ENOMEM;
+
+	if (!is_usb_device(dev)) {
+
+		if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "INTERFACE=%d/%d/%d",
+			   alt->desc.bInterfaceClass,
+			   alt->desc.bInterfaceSubClass,
+			   alt->desc.bInterfaceProtocol))
+			return -ENOMEM;
+
+		if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+			   le16_to_cpu(usb_dev->descriptor.idVendor),
+			   le16_to_cpu(usb_dev->descriptor.idProduct),
+			   le16_to_cpu(usb_dev->descriptor.bcdDevice),
+			   usb_dev->descriptor.bDeviceClass,
+			   usb_dev->descriptor.bDeviceSubClass,
+			   usb_dev->descriptor.bDeviceProtocol,
+			   alt->desc.bInterfaceClass,
+			   alt->desc.bInterfaceSubClass,
+			   alt->desc.bInterfaceProtocol))
+			return -ENOMEM;
+	}
+
+	envp[i] = NULL;
 
 
-	id = usb_match_dynamic_id(intf, usb_drv);
-	if (id)
-		return 1;
 	return 0;
 	return 0;
 }
 }
 
 
+#else
+
+static int usb_uevent(struct device *dev, char **envp,
+			int num_envp, char *buffer, int buffer_size)
+{
+	return -ENODEV;
+}
+
+#endif	/* CONFIG_HOTPLUG */
+
+/**
+ * usb_register_device_driver - register a USB device (not interface) driver
+ * @new_udriver: USB operations for the device driver
+ * @owner: module owner of this driver.
+ *
+ * Registers a USB device driver with the USB core.  The list of
+ * unattached devices will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ */
+int usb_register_device_driver(struct usb_device_driver *new_udriver,
+		struct module *owner)
+{
+	int retval = 0;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	new_udriver->drvwrap.for_devices = 1;
+	new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
+	new_udriver->drvwrap.driver.bus = &usb_bus_type;
+	new_udriver->drvwrap.driver.probe = usb_probe_device;
+	new_udriver->drvwrap.driver.remove = usb_unbind_device;
+	new_udriver->drvwrap.driver.owner = owner;
+
+	retval = driver_register(&new_udriver->drvwrap.driver);
+
+	if (!retval) {
+		pr_info("%s: registered new device driver %s\n",
+			usbcore_name, new_udriver->name);
+		usbfs_update_special();
+	} else {
+		printk(KERN_ERR "%s: error %d registering device "
+			"	driver %s\n",
+			usbcore_name, retval, new_udriver->name);
+	}
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(usb_register_device_driver);
+
+/**
+ * usb_deregister_device_driver - unregister a USB device (not interface) driver
+ * @udriver: USB operations of the device driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ */
+void usb_deregister_device_driver(struct usb_device_driver *udriver)
+{
+	pr_info("%s: deregistering device driver %s\n",
+			usbcore_name, udriver->name);
+
+	driver_unregister(&udriver->drvwrap.driver);
+	usbfs_update_special();
+}
+EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
+
 /**
 /**
- * usb_register_driver - register a USB driver
- * @new_driver: USB operations for the driver
+ * usb_register_driver - register a USB interface driver
+ * @new_driver: USB operations for the interface driver
  * @owner: module owner of this driver.
  * @owner: module owner of this driver.
  *
  *
- * Registers a USB driver with the USB core.  The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
+ * Registers a USB interface driver with the USB core.  The list of
+ * unattached interfaces will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized interfaces.
  * Returns a negative error code on failure and 0 on success.
  * Returns a negative error code on failure and 0 on success.
  *
  *
  * NOTE: if you want your driver to use the USB major number, you must call
  * NOTE: if you want your driver to use the USB major number, you must call
@@ -423,23 +736,25 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
 	if (usb_disabled())
 	if (usb_disabled())
 		return -ENODEV;
 		return -ENODEV;
 
 
-	new_driver->driver.name = (char *)new_driver->name;
-	new_driver->driver.bus = &usb_bus_type;
-	new_driver->driver.probe = usb_probe_interface;
-	new_driver->driver.remove = usb_unbind_interface;
-	new_driver->driver.owner = owner;
+	new_driver->drvwrap.for_devices = 0;
+	new_driver->drvwrap.driver.name = (char *) new_driver->name;
+	new_driver->drvwrap.driver.bus = &usb_bus_type;
+	new_driver->drvwrap.driver.probe = usb_probe_interface;
+	new_driver->drvwrap.driver.remove = usb_unbind_interface;
+	new_driver->drvwrap.driver.owner = owner;
 	spin_lock_init(&new_driver->dynids.lock);
 	spin_lock_init(&new_driver->dynids.lock);
 	INIT_LIST_HEAD(&new_driver->dynids.list);
 	INIT_LIST_HEAD(&new_driver->dynids.list);
 
 
-	retval = driver_register(&new_driver->driver);
+	retval = driver_register(&new_driver->drvwrap.driver);
 
 
 	if (!retval) {
 	if (!retval) {
-		pr_info("%s: registered new driver %s\n",
+		pr_info("%s: registered new interface driver %s\n",
 			usbcore_name, new_driver->name);
 			usbcore_name, new_driver->name);
 		usbfs_update_special();
 		usbfs_update_special();
 		usb_create_newid_file(new_driver);
 		usb_create_newid_file(new_driver);
 	} else {
 	} else {
-		printk(KERN_ERR "%s: error %d registering driver %s\n",
+		printk(KERN_ERR "%s: error %d registering interface "
+			"	driver %s\n",
 			usbcore_name, retval, new_driver->name);
 			usbcore_name, retval, new_driver->name);
 	}
 	}
 
 
@@ -448,8 +763,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
 EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
 EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
 
 
 /**
 /**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
+ * usb_deregister - unregister a USB interface driver
+ * @driver: USB operations of the interface driver to unregister
  * Context: must be able to sleep
  * Context: must be able to sleep
  *
  *
  * Unlinks the specified driver from the internal USB driver list.
  * Unlinks the specified driver from the internal USB driver list.
@@ -460,12 +775,554 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
  */
  */
 void usb_deregister(struct usb_driver *driver)
 void usb_deregister(struct usb_driver *driver)
 {
 {
-	pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+	pr_info("%s: deregistering interface driver %s\n",
+			usbcore_name, driver->name);
 
 
 	usb_remove_newid_file(driver);
 	usb_remove_newid_file(driver);
 	usb_free_dynids(driver);
 	usb_free_dynids(driver);
-	driver_unregister(&driver->driver);
+	driver_unregister(&driver->drvwrap.driver);
 
 
 	usbfs_update_special();
 	usbfs_update_special();
 }
 }
 EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
 EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
+
+#ifdef CONFIG_PM
+
+/* Caller has locked udev->pm_mutex */
+static int suspend_device(struct usb_device *udev, pm_message_t msg)
+{
+	struct usb_device_driver	*udriver;
+	int				status = 0;
+
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state == USB_STATE_SUSPENDED)
+		goto done;
+
+	/* For devices that don't have a driver, we do a standard suspend. */
+	if (udev->dev.driver == NULL) {
+		udev->do_remote_wakeup = 0;
+		status = usb_port_suspend(udev);
+		goto done;
+	}
+
+	udriver = to_usb_device_driver(udev->dev.driver);
+	status = udriver->suspend(udev, msg);
+
+done:
+	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	if (status == 0)
+		udev->dev.power.power_state.event = msg.event;
+	return status;
+}
+
+/* Caller has locked udev->pm_mutex */
+static int resume_device(struct usb_device *udev)
+{
+	struct usb_device_driver	*udriver;
+	int				status = 0;
+
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			udev->state != USB_STATE_SUSPENDED)
+		goto done;
+
+	/* Can't resume it if it doesn't have a driver. */
+	if (udev->dev.driver == NULL) {
+		status = -ENOTCONN;
+		goto done;
+	}
+
+	udriver = to_usb_device_driver(udev->dev.driver);
+	status = udriver->resume(udev);
+
+done:
+	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	if (status == 0)
+		udev->dev.power.power_state.event = PM_EVENT_ON;
+	return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
+{
+	struct usb_driver	*driver;
+	int			status = 0;
+
+	/* with no hardware, USB interfaces only use FREEZE and ON states */
+	if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+			!is_active(intf))
+		goto done;
+
+	if (intf->condition == USB_INTERFACE_UNBOUND)	/* This can't happen */
+		goto done;
+	driver = to_usb_driver(intf->dev.driver);
+
+	if (driver->suspend && driver->resume) {
+		status = driver->suspend(intf, msg);
+		if (status == 0)
+			mark_quiesced(intf);
+		else if (!interface_to_usbdev(intf)->auto_pm)
+			dev_err(&intf->dev, "%s error %d\n",
+					"suspend", status);
+	} else {
+		// FIXME else if there's no suspend method, disconnect...
+		// Not possible if auto_pm is set...
+		dev_warn(&intf->dev, "no suspend for driver %s?\n",
+				driver->name);
+		mark_quiesced(intf);
+	}
+
+done:
+	// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+	if (status == 0)
+		intf->dev.power.power_state.event = msg.event;
+	return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int resume_interface(struct usb_interface *intf)
+{
+	struct usb_driver	*driver;
+	int			status = 0;
+
+	if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+			is_active(intf))
+		goto done;
+
+	/* Don't let autoresume interfere with unbinding */
+	if (intf->condition == USB_INTERFACE_UNBINDING)
+		goto done;
+
+	/* Can't resume it if it doesn't have a driver. */
+	if (intf->condition == USB_INTERFACE_UNBOUND) {
+		status = -ENOTCONN;
+		goto done;
+	}
+	driver = to_usb_driver(intf->dev.driver);
+
+	if (driver->resume) {
+		status = driver->resume(intf);
+		if (status)
+			dev_err(&intf->dev, "%s error %d\n",
+					"resume", status);
+		else
+			mark_active(intf);
+	} else {
+		dev_warn(&intf->dev, "no resume for driver %s?\n",
+				driver->name);
+		mark_active(intf);
+	}
+
+done:
+	// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+	if (status == 0)
+		intf->dev.power.power_state.event = PM_EVENT_ON;
+	return status;
+}
+
+/**
+ * usb_suspend_both - suspend a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This is the central routine for suspending USB devices.  It calls the
+ * suspend methods for all the interface drivers in @udev and then calls
+ * the suspend method for @udev itself.  If an error occurs at any stage,
+ * all the interfaces which were suspended are resumed so that they remain
+ * in the same state as the device.
+ *
+ * If an autosuspend is in progress (@udev->auto_pm is set), the routine
+ * checks first to make sure that neither the device itself or any of its
+ * active interfaces is in use (pm_usage_cnt is greater than 0).  If they
+ * are, the autosuspend fails.
+ *
+ * If the suspend succeeds, the routine recursively queues an autosuspend
+ * request for @udev's parent device, thereby propagating the change up
+ * the device tree.  If all of the parent's children are now suspended,
+ * the parent will autosuspend in turn.
+ *
+ * The suspend method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex.  Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autosuspend requests generated by a child device or
+ * interface driver may not be.  Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations.  However, drivers
+ * must be prepared to handle suspend calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * autosuspends) while holding @udev's device lock (preventing outside
+ * suspends).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+{
+	int			status = 0;
+	int			i = 0;
+	struct usb_interface	*intf;
+	struct usb_device	*parent = udev->parent;
+
+	cancel_delayed_work(&udev->autosuspend);
+	if (udev->state == USB_STATE_NOTATTACHED)
+		return 0;
+	if (udev->state == USB_STATE_SUSPENDED)
+		return 0;
+
+	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+
+	/* For autosuspend, fail fast if anything is in use.
+	 * Also fail if any interfaces require remote wakeup but it
+	 * isn't available. */
+	if (udev->auto_pm) {
+		if (udev->pm_usage_cnt > 0)
+			return -EBUSY;
+		if (udev->actconfig) {
+			for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+				intf = udev->actconfig->interface[i];
+				if (!is_active(intf))
+					continue;
+				if (intf->pm_usage_cnt > 0)
+					return -EBUSY;
+				if (intf->needs_remote_wakeup &&
+						!udev->do_remote_wakeup) {
+					dev_dbg(&udev->dev,
+	"remote wakeup needed for autosuspend\n");
+					return -EOPNOTSUPP;
+				}
+			}
+			i = 0;
+		}
+	}
+
+	/* Suspend all the interfaces and then udev itself */
+	if (udev->actconfig) {
+		for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+			intf = udev->actconfig->interface[i];
+			status = suspend_interface(intf, msg);
+			if (status != 0)
+				break;
+		}
+	}
+	if (status == 0)
+		status = suspend_device(udev, msg);
+
+	/* If the suspend failed, resume interfaces that did get suspended */
+	if (status != 0) {
+		while (--i >= 0) {
+			intf = udev->actconfig->interface[i];
+			resume_interface(intf);
+		}
+
+	/* If the suspend succeeded, propagate it up the tree */
+	} else if (parent)
+		usb_autosuspend_device(parent, 0);
+
+	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	return status;
+}
+
+/**
+ * usb_resume_both - resume a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This is the central routine for resuming USB devices.  It calls the
+ * the resume method for @udev and then calls the resume methods for all
+ * the interface drivers in @udev.
+ *
+ * Before starting the resume, the routine calls itself recursively for
+ * the parent device of @udev, thereby propagating the change up the device
+ * tree and assuring that @udev will be able to resume.  If the parent is
+ * unable to resume successfully, the routine fails.
+ *
+ * The resume method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex.  Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autoresume requests generated by a child device or
+ * interface driver may not be.  Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations.  However, drivers
+ * must be prepared to handle resume calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * other autoresumes) while holding @udev's device lock (preventing outside
+ * resumes).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_resume_both(struct usb_device *udev)
+{
+	int			status = 0;
+	int			i;
+	struct usb_interface	*intf;
+	struct usb_device	*parent = udev->parent;
+
+	cancel_delayed_work(&udev->autosuspend);
+	if (udev->state == USB_STATE_NOTATTACHED)
+		return -ENODEV;
+
+	/* Propagate the resume up the tree, if necessary */
+	if (udev->state == USB_STATE_SUSPENDED) {
+		if (parent) {
+			mutex_lock_nested(&parent->pm_mutex, parent->level);
+			parent->auto_pm = 1;
+			status = usb_resume_both(parent);
+		} else {
+
+			/* We can't progagate beyond the USB subsystem,
+			 * so if a root hub's controller is suspended
+			 * then we're stuck. */
+			if (udev->dev.parent->power.power_state.event !=
+					PM_EVENT_ON)
+				status = -EHOSTUNREACH;
+		}
+		if (status == 0)
+			status = resume_device(udev);
+		if (parent)
+			mutex_unlock(&parent->pm_mutex);
+	} else {
+
+		/* Needed only for setting udev->dev.power.power_state.event
+		 * and for possible debugging message. */
+		status = resume_device(udev);
+	}
+
+	/* Now the parent won't suspend until we are finished */
+
+	if (status == 0 && udev->actconfig) {
+		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+			intf = udev->actconfig->interface[i];
+			resume_interface(intf);
+		}
+	}
+
+	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	return status;
+}
+
+#ifdef CONFIG_USB_SUSPEND
+
+/**
+ * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
+ * @udev - the usb_device to autosuspend
+ * @dec_usage_cnt - flag to decrement @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem is finished using
+ * @udev and wants to allow it to autosuspend.  Examples would be when
+ * @udev's device file in usbfs is closed or after a configuration change.
+ *
+ * @dec_usage_cnt should be 1 if the subsystem previously incremented
+ * @udev's usage counter (such as by passing 1 to usb_autoresume_device);
+ * otherwise it should be 0.
+ *
+ * If the usage counter for @udev or any of its active interfaces is greater
+ * than 0, the autosuspend request will not be queued.  (If an interface
+ * driver does not support autosuspend then its usage counter is permanently
+ * positive.)  Likewise, if an interface driver requires remote-wakeup
+ * capability during autosuspend but remote wakeup is disabled, the
+ * autosuspend will fail.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt)
+{
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	udev->pm_usage_cnt -= dec_usage_cnt;
+	if (udev->pm_usage_cnt <= 0)
+		schedule_delayed_work(&udev->autosuspend,
+				USB_AUTOSUSPEND_DELAY);
+	mutex_unlock(&udev->pm_mutex);
+	// dev_dbg(&udev->dev, "%s: cnt %d\n",
+	//		__FUNCTION__, udev->pm_usage_cnt);
+}
+
+/**
+ * usb_autoresume_device - immediately autoresume a USB device and its interfaces
+ * @udev - the usb_device to autoresume
+ * @inc_usage_cnt - flag to increment @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem wants to use @udev
+ * and needs to guarantee that it is not suspended.  In addition, the
+ * caller can prevent @udev from being autosuspended subsequently.  (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * Examples would be when @udev's device file in usbfs is opened (autosuspend
+ * should be prevented until the file is closed) or when a remote-wakeup
+ * request is received (later autosuspends should not be prevented).
+ *
+ * @inc_usage_cnt should be 1 to increment @udev's usage counter and prevent
+ * autosuspends.  This prevention will persist until the usage counter is
+ * decremented again (such as by passing 1 to usb_autosuspend_device).
+ * Otherwise @inc_usage_cnt should be 0 to leave the usage counter unchanged.
+ * Regardless, if the autoresume fails then the usage counter is not
+ * incremented.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary (and attempting it might cause deadlock).
+ *
+ * This routine can run only in process context.
+ */
+int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
+{
+	int	status;
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	udev->pm_usage_cnt += inc_usage_cnt;
+	udev->auto_pm = 1;
+	status = usb_resume_both(udev);
+	if (status != 0)
+		udev->pm_usage_cnt -= inc_usage_cnt;
+	mutex_unlock(&udev->pm_mutex);
+	// dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
+	//		__FUNCTION__, status, udev->pm_usage_cnt);
+	return status;
+}
+
+/**
+ * usb_autopm_put_interface - decrement a USB interface's PM-usage counter
+ * @intf - the usb_interface whose counter should be decremented
+ *
+ * This routine should be called by an interface driver when it is
+ * finished using @intf and wants to allow it to autosuspend.  A typical
+ * example would be a character-device driver when its device file is
+ * closed.
+ *
+ * The routine decrements @intf's usage counter.  When the counter reaches
+ * 0, a delayed autosuspend request for @intf's device is queued.  When
+ * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all
+ * the other usage counters for the sibling interfaces and @intf's
+ * usb_device, the device and all its interfaces will be autosuspended.
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver.  The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface.  The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * If the driver has set @intf->needs_remote_wakeup then autosuspend will
+ * take place only if the device's remote-wakeup facility is enabled.
+ *
+ * Suspend method calls queued by this routine can arrive at any time
+ * while @intf is resumed and its usage counter is equal to 0.  They are
+ * not protected by the usb_device's lock but only by its pm_mutex.
+ * Drivers must provide their own synchronization.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autopm_put_interface(struct usb_interface *intf)
+{
+	struct usb_device	*udev = interface_to_usbdev(intf);
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	if (intf->condition != USB_INTERFACE_UNBOUND) {
+		if (--intf->pm_usage_cnt <= 0)
+			schedule_delayed_work(&udev->autosuspend,
+					USB_AUTOSUSPEND_DELAY);
+	}
+	mutex_unlock(&udev->pm_mutex);
+	// dev_dbg(&intf->dev, "%s: cnt %d\n",
+	//		__FUNCTION__, intf->pm_usage_cnt);
+}
+EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
+
+/**
+ * usb_autopm_get_interface - increment a USB interface's PM-usage counter
+ * @intf - the usb_interface whose counter should be incremented
+ *
+ * This routine should be called by an interface driver when it wants to
+ * use @intf and needs to guarantee that it is not suspended.  In addition,
+ * the routine prevents @intf from being autosuspended subsequently.  (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * This prevention will persist until usb_autopm_put_interface() is called
+ * or @intf is unbound.  A typical example would be a character-device
+ * driver when its device file is opened.
+ *
+ * The routine increments @intf's usage counter.  So long as the counter
+ * is greater than 0, autosuspend will not be allowed for @intf or its
+ * usb_device.  When the driver is finished using @intf it should call
+ * usb_autopm_put_interface() to decrement the usage counter and queue
+ * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver.  The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface.  The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * Resume method calls generated by this routine can arrive at any time
+ * while @intf is suspended.  They are not protected by the usb_device's
+ * lock but only by its pm_mutex.  Drivers must provide their own
+ * synchronization.
+ *
+ * This routine can run only in process context.
+ */
+int usb_autopm_get_interface(struct usb_interface *intf)
+{
+	struct usb_device	*udev = interface_to_usbdev(intf);
+	int			status;
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	if (intf->condition == USB_INTERFACE_UNBOUND)
+		status = -ENODEV;
+	else {
+		++intf->pm_usage_cnt;
+		udev->auto_pm = 1;
+		status = usb_resume_both(udev);
+		if (status != 0)
+			--intf->pm_usage_cnt;
+	}
+	mutex_unlock(&udev->pm_mutex);
+	// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
+	//		__FUNCTION__, status, intf->pm_usage_cnt);
+	return status;
+}
+EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
+
+#endif /* CONFIG_USB_SUSPEND */
+
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+	int	status;
+
+	if (is_usb_device(dev)) {
+		struct usb_device *udev = to_usb_device(dev);
+
+		mutex_lock_nested(&udev->pm_mutex, udev->level);
+		udev->auto_pm = 0;
+		status = usb_suspend_both(udev, message);
+		mutex_unlock(&udev->pm_mutex);
+	} else
+		status = 0;
+	return status;
+}
+
+static int usb_resume(struct device *dev)
+{
+	int	status;
+
+	if (is_usb_device(dev)) {
+		struct usb_device *udev = to_usb_device(dev);
+
+		mutex_lock_nested(&udev->pm_mutex, udev->level);
+		udev->auto_pm = 0;
+		status = usb_resume_both(udev);
+		mutex_unlock(&udev->pm_mutex);
+
+		/* Rebind drivers that had no suspend method? */
+	} else
+		status = 0;
+	return status;
+}
+
+#endif /* CONFIG_PM */
+
+struct bus_type usb_bus_type = {
+	.name =		"usb",
+	.match =	usb_device_match,
+	.uevent =	usb_uevent,
+#ifdef CONFIG_PM
+	.suspend =	usb_suspend,
+	.resume =	usb_resume,
+#endif
+};

+ 22 - 8
drivers/usb/core/endpoint.c

@@ -207,9 +207,9 @@ static void ep_device_release(struct device *dev)
 	kfree(ep_dev);
 	kfree(ep_dev);
 }
 }
 
 
-void usb_create_ep_files(struct device *parent,
-			 struct usb_host_endpoint *endpoint,
-			 struct usb_device *udev)
+int usb_create_ep_files(struct device *parent,
+			struct usb_host_endpoint *endpoint,
+			struct usb_device *udev)
 {
 {
 	char name[8];
 	char name[8];
 	struct ep_device *ep_dev;
 	struct ep_device *ep_dev;
@@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent,
 	retval = device_register(&ep_dev->dev);
 	retval = device_register(&ep_dev->dev);
 	if (retval)
 	if (retval)
 		goto error;
 		goto error;
-	sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+	retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+	if (retval)
+		goto error_group;
 
 
 	endpoint->ep_dev = ep_dev;
 	endpoint->ep_dev = ep_dev;
 
 
 	/* create the symlink to the old-style "ep_XX" directory */
 	/* create the symlink to the old-style "ep_XX" directory */
 	sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
 	sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
-	sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name);
-
+	retval = sysfs_create_link(&parent->kobj,
+				   &endpoint->ep_dev->dev.kobj, name);
+	if (retval)
+		goto error_link;
 exit:
 exit:
-	return;
+	return retval;
+
+error_link:
+	sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+
+error_group:
+	device_unregister(&ep_dev->dev);
+	endpoint->ep_dev = NULL;
+	destroy_endpoint_class();
+	return retval;
 error:
 error:
 	kfree(ep_dev);
 	kfree(ep_dev);
-	return;
+	destroy_endpoint_class();
+	return retval;
 }
 }
 
 
 void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
 void usb_remove_ep_files(struct usb_host_endpoint *endpoint)

+ 1 - 1
drivers/usb/core/file.c

@@ -55,7 +55,7 @@ static int usb_open(struct inode * inode, struct file * file)
 	return err;
 	return err;
 }
 }
 
 
-static struct file_operations usb_fops = {
+static const struct file_operations usb_fops = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.open =		usb_open,
 	.open =		usb_open,
 };
 };

+ 208 - 0
drivers/usb/core/generic.c

@@ -0,0 +1,208 @@
+/*
+ * drivers/usb/generic.c - generic driver for USB devices (not interfaces)
+ *
+ * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * based on drivers/usb/usb.c which had the following copyrights:
+ *	(C) Copyright Linus Torvalds 1999
+ *	(C) Copyright Johannes Erdfelt 1999-2001
+ *	(C) Copyright Andreas Gal 1999
+ *	(C) Copyright Gregory P. Smith 1999
+ *	(C) Copyright Deti Fliegl 1999 (new USB architecture)
+ *	(C) Copyright Randy Dunlap 2000
+ *	(C) Copyright David Brownell 2000-2004
+ *	(C) Copyright Yggdrasil Computing, Inc. 2000
+ *		(usb_device_id matching changes by Adam J. Richter)
+ *	(C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/usb.h>
+#include "usb.h"
+
+static inline const char *plural(int n)
+{
+	return (n == 1 ? "" : "s");
+}
+
+static int choose_configuration(struct usb_device *udev)
+{
+	int i;
+	int num_configs;
+	int insufficient_power = 0;
+	struct usb_host_config *c, *best;
+
+	best = NULL;
+	c = udev->config;
+	num_configs = udev->descriptor.bNumConfigurations;
+	for (i = 0; i < num_configs; (i++, c++)) {
+		struct usb_interface_descriptor	*desc = NULL;
+
+		/* It's possible that a config has no interfaces! */
+		if (c->desc.bNumInterfaces > 0)
+			desc = &c->intf_cache[0]->altsetting->desc;
+
+		/*
+		 * HP's USB bus-powered keyboard has only one configuration
+		 * and it claims to be self-powered; other devices may have
+		 * similar errors in their descriptors.  If the next test
+		 * were allowed to execute, such configurations would always
+		 * be rejected and the devices would not work as expected.
+		 * In the meantime, we run the risk of selecting a config
+		 * that requires external power at a time when that power
+		 * isn't available.  It seems to be the lesser of two evils.
+		 *
+		 * Bugzilla #6448 reports a device that appears to crash
+		 * when it receives a GET_DEVICE_STATUS request!  We don't
+		 * have any other way to tell whether a device is self-powered,
+		 * but since we don't use that information anywhere but here,
+		 * the call has been removed.
+		 *
+		 * Maybe the GET_DEVICE_STATUS call and the test below can
+		 * be reinstated when device firmwares become more reliable.
+		 * Don't hold your breath.
+		 */
+#if 0
+		/* Rule out self-powered configs for a bus-powered device */
+		if (bus_powered && (c->desc.bmAttributes &
+					USB_CONFIG_ATT_SELFPOWER))
+			continue;
+#endif
+
+		/*
+		 * The next test may not be as effective as it should be.
+		 * Some hubs have errors in their descriptor, claiming
+		 * to be self-powered when they are really bus-powered.
+		 * We will overestimate the amount of current such hubs
+		 * make available for each port.
+		 *
+		 * This is a fairly benign sort of failure.  It won't
+		 * cause us to reject configurations that we should have
+		 * accepted.
+		 */
+
+		/* Rule out configs that draw too much bus current */
+		if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+			insufficient_power++;
+			continue;
+		}
+
+		/* If the first config's first interface is COMM/2/0xff
+		 * (MSFT RNDIS), rule it out unless Linux has host-side
+		 * RNDIS support. */
+		if (i == 0 && desc
+				&& desc->bInterfaceClass == USB_CLASS_COMM
+				&& desc->bInterfaceSubClass == 2
+				&& desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS_HOST
+			continue;
+#else
+			best = c;
+#endif
+		}
+
+		/* From the remaining configs, choose the first one whose
+		 * first interface is for a non-vendor-specific class.
+		 * Reason: Linux is more likely to have a class driver
+		 * than a vendor-specific driver. */
+		else if (udev->descriptor.bDeviceClass !=
+						USB_CLASS_VENDOR_SPEC &&
+				(!desc || desc->bInterfaceClass !=
+						USB_CLASS_VENDOR_SPEC)) {
+			best = c;
+			break;
+		}
+
+		/* If all the remaining configs are vendor-specific,
+		 * choose the first one. */
+		else if (!best)
+			best = c;
+	}
+
+	if (insufficient_power > 0)
+		dev_info(&udev->dev, "rejected %d configuration%s "
+			"due to insufficient available bus power\n",
+			insufficient_power, plural(insufficient_power));
+
+	if (best) {
+		i = best->desc.bConfigurationValue;
+		dev_info(&udev->dev,
+			"configuration #%d chosen from %d choice%s\n",
+			i, num_configs, plural(num_configs));
+	} else {
+		i = -1;
+		dev_warn(&udev->dev,
+			"no configuration chosen from %d choice%s\n",
+			num_configs, plural(num_configs));
+	}
+	return i;
+}
+
+static int generic_probe(struct usb_device *udev)
+{
+	int err, c;
+
+	/* put device-specific files into sysfs */
+	usb_create_sysfs_dev_files(udev);
+
+	/* Choose and set the configuration.  This registers the interfaces
+	 * with the driver core and lets interface drivers bind to them.
+	 */
+	c = choose_configuration(udev);
+	if (c >= 0) {
+		err = usb_set_configuration(udev, c);
+		if (err) {
+			dev_err(&udev->dev, "can't set config #%d, error %d\n",
+					c, err);
+			/* This need not be fatal.  The user can try to
+			 * set other configurations. */
+		}
+	}
+
+	/* USB device state == configured ... usable */
+	usb_notify_add_device(udev);
+
+	return 0;
+}
+
+static void generic_disconnect(struct usb_device *udev)
+{
+	usb_notify_remove_device(udev);
+
+	/* if this is only an unbind, not a physical disconnect, then
+	 * unconfigure the device */
+	if (udev->actconfig)
+		usb_set_configuration(udev, 0);
+
+	usb_remove_sysfs_dev_files(udev);
+}
+
+#ifdef	CONFIG_PM
+
+static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+{
+	/* USB devices enter SUSPEND state through their hubs, but can be
+	 * marked for FREEZE as soon as their children are already idled.
+	 * But those semantics are useless, so we equate the two (sigh).
+	 */
+	return usb_port_suspend(udev);
+}
+
+static int generic_resume(struct usb_device *udev)
+{
+	return usb_port_resume(udev);
+}
+
+#endif	/* CONFIG_PM */
+
+struct usb_device_driver usb_generic_driver = {
+	.name =	"usb",
+	.probe = generic_probe,
+	.disconnect = generic_disconnect,
+#ifdef	CONFIG_PM
+	.suspend = generic_suspend,
+	.resume = generic_resume,
+#endif
+	.supports_autosuspend = 1,
+};

+ 16 - 0
drivers/usb/core/hcd-pci.c

@@ -413,4 +413,20 @@ EXPORT_SYMBOL (usb_hcd_pci_resume);
 
 
 #endif	/* CONFIG_PM */
 #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)
+{
+	struct usb_hcd		*hcd;
+
+	hcd = pci_get_drvdata(dev);
+	if (!hcd)
+		return;
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_shutdown);
 
 

+ 79 - 165
drivers/usb/core/hcd.c

@@ -36,6 +36,7 @@
 #include <linux/mutex.h>
 #include <linux/mutex.h>
 #include <asm/irq.h>
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
+#include <linux/platform_device.h>
 
 
 #include <linux/usb.h>
 #include <linux/usb.h>
 
 
@@ -632,31 +633,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-/* Asynchronous unlinks of root-hub control URBs are legal, but they
- * don't do anything.  Status URB unlinks must be made in process context
- * with interrupts enabled.
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
  */
  */
 static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
 {
-	if (usb_pipeendpoint(urb->pipe) == 0) {	/* Control URB */
-		if (in_interrupt())
-			return 0;		/* nothing to do */
-
-		spin_lock_irq(&urb->lock);	/* from usb_kill_urb */
-		++urb->reject;
-		spin_unlock_irq(&urb->lock);
-
-		wait_event(usb_kill_urb_queue,
-				atomic_read(&urb->use_count) == 0);
+	unsigned long	flags;
 
 
-		spin_lock_irq(&urb->lock);
-		--urb->reject;
-		spin_unlock_irq(&urb->lock);
+	if (usb_pipeendpoint(urb->pipe) == 0) {	/* Control URB */
+		;	/* Do nothing */
 
 
 	} else {				/* Status URB */
 	} else {				/* Status URB */
 		if (!hcd->uses_new_polling)
 		if (!hcd->uses_new_polling)
-			del_timer_sync (&hcd->rh_timer);
-		local_irq_disable ();
+			del_timer (&hcd->rh_timer);
+		local_irq_save (flags);
 		spin_lock (&hcd_root_hub_lock);
 		spin_lock (&hcd_root_hub_lock);
 		if (urb == hcd->status_urb) {
 		if (urb == hcd->status_urb) {
 			hcd->status_urb = NULL;
 			hcd->status_urb = NULL;
@@ -666,7 +656,7 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 		spin_unlock (&hcd_root_hub_lock);
 		spin_unlock (&hcd_root_hub_lock);
 		if (urb)
 		if (urb)
 			usb_hcd_giveback_urb (hcd, urb, NULL);
 			usb_hcd_giveback_urb (hcd, urb, NULL);
-		local_irq_enable ();
+		local_irq_restore (flags);
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -674,31 +664,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get(struct usb_bus *bus)
-{
-	if (bus)
-		kref_get(&bus->kref);
-	return bus;
-}
-
-static void usb_host_release(struct kref *kref)
-{
-	struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
-
-	if (bus->release)
-		bus->release(bus);
-}
-
-/* exported only within usbcore */
-void usb_bus_put(struct usb_bus *bus)
-{
-	if (bus)
-		kref_put(&bus->kref, usb_host_release);
-}
-
-/*-------------------------------------------------------------------------*/
-
 static struct class *usb_host_class;
 static struct class *usb_host_class;
 
 
 int usb_host_init(void)
 int usb_host_init(void)
@@ -730,39 +695,12 @@ static void usb_bus_init (struct usb_bus *bus)
 	bus->devnum_next = 1;
 	bus->devnum_next = 1;
 
 
 	bus->root_hub = NULL;
 	bus->root_hub = NULL;
-	bus->hcpriv = NULL;
 	bus->busnum = -1;
 	bus->busnum = -1;
 	bus->bandwidth_allocated = 0;
 	bus->bandwidth_allocated = 0;
 	bus->bandwidth_int_reqs  = 0;
 	bus->bandwidth_int_reqs  = 0;
 	bus->bandwidth_isoc_reqs = 0;
 	bus->bandwidth_isoc_reqs = 0;
 
 
 	INIT_LIST_HEAD (&bus->bus_list);
 	INIT_LIST_HEAD (&bus->bus_list);
-
-	kref_init(&bus->kref);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified 
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
-	struct usb_bus *bus;
-
-	bus = kzalloc (sizeof *bus, GFP_KERNEL);
-	if (!bus)
-		return NULL;
-	usb_bus_init (bus);
-	bus->op = op;
-	return bus;
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
@@ -1112,10 +1050,10 @@ static void urb_unlink (struct urb *urb)
  * expects usb_submit_urb() to have sanity checked and conditioned all
  * expects usb_submit_urb() to have sanity checked and conditioned all
  * inputs in the urb
  * inputs in the urb
  */
  */
-static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 {
 {
 	int			status;
 	int			status;
-	struct usb_hcd		*hcd = urb->dev->bus->hcpriv;
+	struct usb_hcd		*hcd = bus_to_hcd(urb->dev->bus);
 	struct usb_host_endpoint *ep;
 	struct usb_host_endpoint *ep;
 	unsigned long		flags;
 	unsigned long		flags;
 
 
@@ -1186,7 +1124,7 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 	/* lower level hcd code should use *_dma exclusively,
 	/* 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.
 	 */
 	 */
-	if (hcd->self.controller->dma_mask) {
+	if (hcd->self.uses_dma) {
 		if (usb_pipecontrol (urb->pipe)
 		if (usb_pipecontrol (urb->pipe)
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			urb->setup_dma = dma_map_single (
 			urb->setup_dma = dma_map_single (
@@ -1221,9 +1159,10 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /* called in any context */
 /* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
+int usb_hcd_get_frame_number (struct usb_device *udev)
 {
 {
-	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;
+	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);
+
 	if (!HC_IS_RUNNING (hcd->state))
 	if (!HC_IS_RUNNING (hcd->state))
 		return -ESHUTDOWN;
 		return -ESHUTDOWN;
 	return hcd->driver->get_frame_number (hcd);
 	return hcd->driver->get_frame_number (hcd);
@@ -1263,7 +1202,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
  * caller guarantees urb won't be recycled till both unlink()
  * caller guarantees urb won't be recycled till both unlink()
  * and the urb's completion function return
  * and the urb's completion function return
  */
  */
-static int hcd_unlink_urb (struct urb *urb, int status)
+int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
 {
 	struct usb_host_endpoint	*ep;
 	struct usb_host_endpoint	*ep;
 	struct usb_hcd			*hcd = NULL;
 	struct usb_hcd			*hcd = NULL;
@@ -1296,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
 	spin_lock (&hcd_data_lock);
 	spin_lock (&hcd_data_lock);
 
 
 	sys = &urb->dev->dev;
 	sys = &urb->dev->dev;
-	hcd = urb->dev->bus->hcpriv;
+	hcd = bus_to_hcd(urb->dev->bus);
 	if (hcd == NULL) {
 	if (hcd == NULL) {
 		retval = -ENODEV;
 		retval = -ENODEV;
 		goto done;
 		goto done;
@@ -1354,41 +1293,33 @@ static int hcd_unlink_urb (struct urb *urb, int status)
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /* disables the endpoint: cancels any pending urbs, then synchronizes with
 /* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. use for
  * set_configuration, set_interface, driver removal, physical disconnect.
  * set_configuration, set_interface, driver removal, physical disconnect.
  *
  *
  * example:  a qh stored in ep->hcpriv, holding state related to endpoint
  * example:  a qh stored in ep->hcpriv, holding state related to endpoint
  * type, maxpacket size, toggle, halt status, and scheduling.
  * type, maxpacket size, toggle, halt status, and scheduling.
  */
  */
-static void
-hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+void usb_hcd_endpoint_disable (struct usb_device *udev,
+		struct usb_host_endpoint *ep)
 {
 {
 	struct usb_hcd		*hcd;
 	struct usb_hcd		*hcd;
 	struct urb		*urb;
 	struct urb		*urb;
 
 
-	hcd = udev->bus->hcpriv;
+	hcd = bus_to_hcd(udev->bus);
 
 
 	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
 	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
 			udev->state != USB_STATE_NOTATTACHED);
 			udev->state != USB_STATE_NOTATTACHED);
 
 
 	local_irq_disable ();
 	local_irq_disable ();
 
 
-	/* FIXME move most of this into message.c as part of its
-	 * endpoint disable logic
-	 */
-
 	/* ep is already gone from udev->ep_{in,out}[]; no more submits */
 	/* ep is already gone from udev->ep_{in,out}[]; no more submits */
 rescan:
 rescan:
 	spin_lock (&hcd_data_lock);
 	spin_lock (&hcd_data_lock);
 	list_for_each_entry (urb, &ep->urb_list, urb_list) {
 	list_for_each_entry (urb, &ep->urb_list, urb_list) {
 		int	tmp;
 		int	tmp;
 
 
-		/* another cpu may be in hcd, spinning on hcd_data_lock
-		 * to giveback() this urb.  the races here should be
-		 * small, but a full fix needs a new "can't submit"
-		 * urb state.
-		 * FIXME urb->reject should allow that...
-		 */
+		/* the urb may already have been unlinked */
 		if (urb->status != -EINPROGRESS)
 		if (urb->status != -EINPROGRESS)
 			continue;
 			continue;
 		usb_get_urb (urb);
 		usb_get_urb (urb);
@@ -1430,6 +1361,30 @@ hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
 	might_sleep ();
 	might_sleep ();
 	if (hcd->driver->endpoint_disable)
 	if (hcd->driver->endpoint_disable)
 		hcd->driver->endpoint_disable (hcd, ep);
 		hcd->driver->endpoint_disable (hcd, ep);
+
+	/* Wait until the endpoint queue is completely empty.  Most HCDs
+	 * will have done this already in their endpoint_disable method,
+	 * but some might not.  And there could be root-hub control URBs
+	 * still pending since they aren't affected by the HCDs'
+	 * endpoint_disable methods.
+	 */
+	while (!list_empty (&ep->urb_list)) {
+		spin_lock_irq (&hcd_data_lock);
+
+		/* The list may have changed while we acquired the spinlock */
+		urb = NULL;
+		if (!list_empty (&ep->urb_list)) {
+			urb = list_entry (ep->urb_list.prev, struct urb,
+					urb_list);
+			usb_get_urb (urb);
+		}
+		spin_unlock_irq (&hcd_data_lock);
+
+		if (urb) {
+			usb_kill_urb (urb);
+			usb_put_urb (urb);
+		}
+	}
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
@@ -1476,50 +1431,6 @@ int hcd_bus_resume (struct usb_bus *bus)
 	return status;
 	return status;
 }
 }
 
 
-/*
- * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
- * @hcd: host controller for this root hub
- *
- * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
- * that the HCD's root hub polling is deactivated; and that the root's hub
- * driver is suspended.  HCDs may call this to autosuspend when their root
- * hub's downstream ports are all inactive:  unpowered, disconnected,
- * disabled, or suspended.
- *
- * The HCD will autoresume on device connect change detection (using SRP
- * or a D+/D- pullup).  The HCD also autoresumes on remote wakeup signaling
- * from any ports that are suspended (if that is enabled).  In most cases,
- * overcurrent signaling (on powered ports) will also start autoresume.
- *
- * Always called with IRQs blocked.
- */
-void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
-{
-	struct urb	*urb;
-
-	spin_lock (&hcd_root_hub_lock);
-	usb_suspend_root_hub (hcd->self.root_hub);
-
-	/* force status urb to complete/unlink while suspended */
-	if (hcd->status_urb) {
-		urb = hcd->status_urb;
-		urb->status = -ECONNRESET;
-		urb->hcpriv = NULL;
-		urb->actual_length = 0;
-
-		del_timer (&hcd->rh_timer);
-		hcd->poll_pending = 0;
-		hcd->status_urb = NULL;
-	} else
-		urb = NULL;
-	spin_unlock (&hcd_root_hub_lock);
-	hcd->state = HC_STATE_SUSPENDED;
-
-	if (urb)
-		usb_hcd_giveback_urb (hcd, urb, NULL);
-}
-EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
-
 /**
 /**
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
  * @hcd: host controller for this root hub
  * @hcd: host controller for this root hub
@@ -1583,20 +1494,6 @@ EXPORT_SYMBOL (usb_bus_start_enum);
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-/*
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- */
-static struct usb_operations usb_hcd_operations = {
-	.get_frame_number =	hcd_get_frame_number,
-	.submit_urb =		hcd_submit_urb,
-	.unlink_urb =		hcd_unlink_urb,
-	.buffer_alloc =		hcd_buffer_alloc,
-	.buffer_free =		hcd_buffer_free,
-	.disable =		hcd_endpoint_disable,
-};
-
-/*-------------------------------------------------------------------------*/
-
 /**
 /**
  * usb_hcd_giveback_urb - return URB from HCD to device driver
  * usb_hcd_giveback_urb - return URB from HCD to device driver
  * @hcd: host controller returning the URB
  * @hcd: host controller returning the URB
@@ -1617,8 +1514,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
 	at_root_hub = (urb->dev == hcd->self.root_hub);
 	at_root_hub = (urb->dev == hcd->self.root_hub);
 	urb_unlink (urb);
 	urb_unlink (urb);
 
 
-	/* lower level hcd code should use *_dma exclusively */
-	if (hcd->self.controller->dma_mask && !at_root_hub) {
+	/* lower level hcd code should use *_dma exclusively if the
+	 * host controller does DMA */
+	if (hcd->self.uses_dma && !at_root_hub) {
 		if (usb_pipecontrol (urb->pipe)
 		if (usb_pipecontrol (urb->pipe)
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			dma_unmap_single (hcd->self.controller, urb->setup_dma,
 			dma_unmap_single (hcd->self.controller, urb->setup_dma,
@@ -1704,14 +1602,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static void hcd_release (struct usb_bus *bus)
-{
-	struct usb_hcd *hcd;
-
-	hcd = container_of(bus, struct usb_hcd, self);
-	kfree(hcd);
-}
-
 /**
 /**
  * usb_create_hcd - create and initialize an HCD structure
  * usb_create_hcd - create and initialize an HCD structure
  * @driver: HC driver that will use this hcd
  * @driver: HC driver that will use this hcd
@@ -1736,13 +1626,12 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 		return NULL;
 		return NULL;
 	}
 	}
 	dev_set_drvdata(dev, hcd);
 	dev_set_drvdata(dev, hcd);
+	kref_init(&hcd->kref);
 
 
 	usb_bus_init(&hcd->self);
 	usb_bus_init(&hcd->self);
-	hcd->self.op = &usb_hcd_operations;
-	hcd->self.hcpriv = hcd;
-	hcd->self.release = &hcd_release;
 	hcd->self.controller = dev;
 	hcd->self.controller = dev;
 	hcd->self.bus_name = bus_name;
 	hcd->self.bus_name = bus_name;
+	hcd->self.uses_dma = (dev->dma_mask != NULL);
 
 
 	init_timer(&hcd->rh_timer);
 	init_timer(&hcd->rh_timer);
 	hcd->rh_timer.function = rh_timer_func;
 	hcd->rh_timer.function = rh_timer_func;
@@ -1756,10 +1645,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 }
 }
 EXPORT_SYMBOL (usb_create_hcd);
 EXPORT_SYMBOL (usb_create_hcd);
 
 
+static void hcd_release (struct kref *kref)
+{
+	struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+	kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+	if (hcd)
+		kref_get (&hcd->kref);
+	return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
 void usb_put_hcd (struct usb_hcd *hcd)
 void usb_put_hcd (struct usb_hcd *hcd)
 {
 {
-	dev_set_drvdata(hcd->self.controller, NULL);
-	usb_bus_put(&hcd->self);
+	if (hcd)
+		kref_put (&hcd->kref, hcd_release);
 }
 }
 EXPORT_SYMBOL (usb_put_hcd);
 EXPORT_SYMBOL (usb_put_hcd);
 
 
@@ -1915,6 +1819,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 }
 }
 EXPORT_SYMBOL (usb_remove_hcd);
 EXPORT_SYMBOL (usb_remove_hcd);
 
 
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 #if defined(CONFIG_USB_MON)
 #if defined(CONFIG_USB_MON)

+ 23 - 37
drivers/usb/core/hcd.h

@@ -55,12 +55,13 @@
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-struct usb_hcd {	/* usb_bus.hcpriv points to this */
+struct usb_hcd {
 
 
 	/*
 	/*
 	 * housekeeping
 	 * housekeeping
 	 */
 	 */
 	struct usb_bus		self;		/* hcd is-a bus */
 	struct usb_bus		self;		/* hcd is-a bus */
+	struct kref		kref;		/* reference counter */
 
 
 	const char		*product_desc;	/* product/vendor string */
 	const char		*product_desc;	/* product/vendor string */
 	char			irq_descr[24];	/* driver + bus # */
 	char			irq_descr[24];	/* driver + bus # */
@@ -85,6 +86,7 @@ struct usb_hcd {	/* usb_bus.hcpriv points to this */
 	unsigned		uses_new_polling:1;
 	unsigned		uses_new_polling:1;
 	unsigned		poll_rh:1;	/* poll for rh status? */
 	unsigned		poll_rh:1;	/* poll for rh status? */
 	unsigned		poll_pending:1;	/* status has changed? */
 	unsigned		poll_pending:1;	/* status has changed? */
+	unsigned		wireless:1;	/* Wireless USB HCD */
 
 
 	int			irq;		/* irq allocated */
 	int			irq;		/* irq allocated */
 	void __iomem		*regs;		/* device memory/io */
 	void __iomem		*regs;		/* device memory/io */
@@ -128,8 +130,10 @@ static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
 	return &hcd->self;
 	return &hcd->self;
 }
 }
 
 
-
-// urb.hcpriv is really hardware-specific
+static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus)
+{
+	return container_of(bus, struct usb_hcd, self);
+}
 
 
 struct hcd_timeout {	/* timeouts we allocate */
 struct hcd_timeout {	/* timeouts we allocate */
 	struct list_head	timeout_list;
 	struct list_head	timeout_list;
@@ -138,28 +142,6 @@ struct hcd_timeout {	/* timeouts we allocate */
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-/*
- * FIXME usb_operations should vanish or become hc_driver,
- * when usb_bus and usb_hcd become the same thing.
- */
-
-struct usb_operations {
-	int (*get_frame_number) (struct usb_device *usb_dev);
-	int (*submit_urb) (struct urb *urb, gfp_t mem_flags);
-	int (*unlink_urb) (struct urb *urb, int status);
-
-	/* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
-	void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
-			gfp_t mem_flags,
-			dma_addr_t *dma);
-	void (*buffer_free)(struct usb_bus *bus, size_t size,
-			void *addr, dma_addr_t dma);
-
-	void (*disable)(struct usb_device *udev,
-			struct usb_host_endpoint *ep);
-};
-
-/* each driver provides one of these, and hardware init support */
 
 
 struct pt_regs;
 struct pt_regs;
 
 
@@ -192,6 +174,9 @@ struct hc_driver {
 	/* cleanly make HCD stop writing memory and doing I/O */
 	/* cleanly make HCD stop writing memory and doing I/O */
 	void	(*stop) (struct usb_hcd *hcd);
 	void	(*stop) (struct usb_hcd *hcd);
 
 
+	/* shutdown HCD */
+	void	(*shutdown) (struct usb_hcd *hcd);
+
 	/* return current frame number */
 	/* return current frame number */
 	int	(*get_frame_number) (struct usb_hcd *hcd);
 	int	(*get_frame_number) (struct usb_hcd *hcd);
 
 
@@ -218,15 +203,25 @@ struct hc_driver {
 		/* Needed only if port-change IRQs are level-triggered */
 		/* Needed only if port-change IRQs are level-triggered */
 };
 };
 
 
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
+extern int usb_hcd_unlink_urb (struct urb *urb, int status);
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb,
+		struct pt_regs *regs);
+extern void usb_hcd_endpoint_disable (struct usb_device *udev,
+		struct usb_host_endpoint *ep);
+extern int usb_hcd_get_frame_number (struct usb_device *udev);
 
 
 extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 		struct device *dev, char *bus_name);
 		struct device *dev, char *bus_name);
+extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd);
 extern void usb_put_hcd (struct usb_hcd *hcd);
 extern void usb_put_hcd (struct usb_hcd *hcd);
 extern int usb_add_hcd(struct usb_hcd *hcd,
 extern int usb_add_hcd(struct usb_hcd *hcd,
 		unsigned int irqnum, unsigned long irqflags);
 		unsigned int irqnum, unsigned long irqflags);
 extern void usb_remove_hcd(struct usb_hcd *hcd);
 extern void usb_remove_hcd(struct usb_hcd *hcd);
 
 
+struct platform_device;
+extern void usb_hcd_platform_shutdown(struct platform_device* dev);
+
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
 struct pci_dev;
 struct pci_dev;
 struct pci_device_id;
 struct pci_device_id;
@@ -239,6 +234,8 @@ extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
 extern int usb_hcd_pci_resume (struct pci_dev *dev);
 extern int usb_hcd_pci_resume (struct pci_dev *dev);
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
 
 
+extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
+
 #endif /* CONFIG_PCI */
 #endif /* CONFIG_PCI */
 
 
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
@@ -352,8 +349,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
-
 extern void usb_set_device_state(struct usb_device *udev,
 extern void usb_set_device_state(struct usb_device *udev,
 		enum usb_device_state new_state);
 		enum usb_device_state new_state);
 
 
@@ -365,9 +360,6 @@ extern struct list_head usb_bus_list;
 extern struct mutex usb_bus_list_lock;
 extern struct mutex usb_bus_list_lock;
 extern wait_queue_head_t usb_kill_urb_queue;
 extern wait_queue_head_t usb_kill_urb_queue;
 
 
-extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
-extern void usb_bus_put (struct usb_bus *bus);
-
 extern void usb_enable_root_hub_irq (struct usb_bus *bus);
 extern void usb_enable_root_hub_irq (struct usb_bus *bus);
 
 
 extern int usb_find_interface_driver (struct usb_device *dev,
 extern int usb_find_interface_driver (struct usb_device *dev,
@@ -376,17 +368,11 @@ extern int usb_find_interface_driver (struct usb_device *dev,
 #define usb_endpoint_out(ep_dir)	(!((ep_dir) & USB_DIR_IN))
 #define usb_endpoint_out(ep_dir)	(!((ep_dir) & USB_DIR_IN))
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
-extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
 extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
 extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
 extern void usb_root_hub_lost_power (struct usb_device *rhdev);
 extern void usb_root_hub_lost_power (struct usb_device *rhdev);
 extern int hcd_bus_suspend (struct usb_bus *bus);
 extern int hcd_bus_suspend (struct usb_bus *bus);
 extern int hcd_bus_resume (struct usb_bus *bus);
 extern int hcd_bus_resume (struct usb_bus *bus);
 #else
 #else
-static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd)
-{
-	return;
-}
-
 static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
 static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
 {
 {
 	return;
 	return;

+ 197 - 358
drivers/usb/core/hub.c

@@ -293,7 +293,7 @@ void usb_kick_khubd(struct usb_device *hdev)
 /* completion function, fires on port status changes and various faults */
 /* completion function, fires on port status changes and various faults */
 static void hub_irq(struct urb *urb, struct pt_regs *regs)
 static void hub_irq(struct urb *urb, struct pt_regs *regs)
 {
 {
-	struct usb_hub *hub = (struct usb_hub *)urb->context;
+	struct usb_hub *hub = urb->context;
 	int status;
 	int status;
 	int i;
 	int i;
 	unsigned long bits;
 	unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
 			goto resubmit;
 			goto resubmit;
 		hub->error = urb->status;
 		hub->error = urb->status;
 		/* FALL THROUGH */
 		/* FALL THROUGH */
-	
+
 	/* let khubd handle things */
 	/* let khubd handle things */
 	case 0:			/* we got data:  port status changed */
 	case 0:			/* we got data:  port status changed */
 		bits = 0;
 		bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
 	msleep(max(pgood_delay, (unsigned) 100));
 	msleep(max(pgood_delay, (unsigned) 100));
 }
 }
 
 
-static inline void __hub_quiesce(struct usb_hub *hub)
+static void hub_quiesce(struct usb_hub *hub)
 {
 {
 	/* (nonblocking) khubd and related activity won't re-trigger */
 	/* (nonblocking) khubd and related activity won't re-trigger */
 	hub->quiescing = 1;
 	hub->quiescing = 1;
 	hub->activating = 0;
 	hub->activating = 0;
 	hub->resume_root_hub = 0;
 	hub->resume_root_hub = 0;
-}
 
 
-static void hub_quiesce(struct usb_hub *hub)
-{
 	/* (blocking) stop khubd and related activity */
 	/* (blocking) stop khubd and related activity */
-	__hub_quiesce(hub);
 	usb_kill_urb(hub->urb);
 	usb_kill_urb(hub->urb);
 	if (hub->has_indicators)
 	if (hub->has_indicators)
 		cancel_delayed_work(&hub->leds);
 		cancel_delayed_work(&hub->leds);
@@ -868,13 +864,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
 
 	endpoint = &desc->endpoint[0].desc;
 	endpoint = &desc->endpoint[0].desc;
 
 
-	/* Output endpoint? Curiouser and curiouser.. */
-	if (!(endpoint->bEndpointAddress & USB_DIR_IN))
-		goto descriptor_error;
-
-	/* If it's not an interrupt endpoint, we'd better punt! */
-	if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-			!= USB_ENDPOINT_XFER_INT)
+	/* If it's not an interrupt in endpoint, we'd better punt! */
+	if (!usb_endpoint_is_int_in(endpoint))
 		goto descriptor_error;
 		goto descriptor_error;
 
 
 	/* We found a hub */
 	/* We found a hub */
@@ -1022,26 +1013,29 @@ void usb_set_device_state(struct usb_device *udev,
 	if (udev->state == USB_STATE_NOTATTACHED)
 	if (udev->state == USB_STATE_NOTATTACHED)
 		;	/* do nothing */
 		;	/* do nothing */
 	else if (new_state != USB_STATE_NOTATTACHED) {
 	else if (new_state != USB_STATE_NOTATTACHED) {
-		udev->state = new_state;
 
 
 		/* root hub wakeup capabilities are managed out-of-band
 		/* root hub wakeup capabilities are managed out-of-band
 		 * and may involve silicon errata ... ignore them here.
 		 * and may involve silicon errata ... ignore them here.
 		 */
 		 */
 		if (udev->parent) {
 		if (udev->parent) {
-			if (new_state == USB_STATE_CONFIGURED)
+			if (udev->state == USB_STATE_SUSPENDED
+					|| new_state == USB_STATE_SUSPENDED)
+				;	/* No change to wakeup settings */
+			else if (new_state == USB_STATE_CONFIGURED)
 				device_init_wakeup(&udev->dev,
 				device_init_wakeup(&udev->dev,
 					(udev->actconfig->desc.bmAttributes
 					(udev->actconfig->desc.bmAttributes
 					 & USB_CONFIG_ATT_WAKEUP));
 					 & USB_CONFIG_ATT_WAKEUP));
-			else if (new_state != USB_STATE_SUSPENDED)
+			else
 				device_init_wakeup(&udev->dev, 0);
 				device_init_wakeup(&udev->dev, 0);
 		}
 		}
+		udev->state = new_state;
 	} else
 	} else
 		recursively_mark_NOTATTACHED(udev);
 		recursively_mark_NOTATTACHED(udev);
 	spin_unlock_irqrestore(&device_state_lock, flags);
 	spin_unlock_irqrestore(&device_state_lock, flags);
 }
 }
 
 
 
 
-#ifdef CONFIG_PM
+#ifdef	CONFIG_PM
 
 
 /**
 /**
  * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
  * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
@@ -1059,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
 	unsigned long flags;
 	unsigned long flags;
 
 
 	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
 	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+	/* Make sure no potential wakeup events get lost,
+	 * by forcing the root hub to be resumed.
+	 */
+	rhdev->dev.power.prev_state.event = PM_EVENT_ON;
+
 	spin_lock_irqsave(&device_state_lock, flags);
 	spin_lock_irqsave(&device_state_lock, flags);
 	hub = hdev_to_hub(rhdev);
 	hub = hdev_to_hub(rhdev);
 	for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
 	for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1072,7 +1072,7 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
 }
 }
 EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
 
-#endif
+#endif	/* CONFIG_PM */
 
 
 static void choose_address(struct usb_device *udev)
 static void choose_address(struct usb_device *udev)
 {
 {
@@ -1148,144 +1148,28 @@ void usb_disconnect(struct usb_device **pdev)
 	 * cleaning up all state associated with the current configuration
 	 * cleaning up all state associated with the current configuration
 	 * so that the hardware is now fully quiesced.
 	 * so that the hardware is now fully quiesced.
 	 */
 	 */
+	dev_dbg (&udev->dev, "unregistering device\n");
 	usb_disable_device(udev, 0);
 	usb_disable_device(udev, 0);
 
 
-	usb_notify_remove_device(udev);
+	usb_unlock_device(udev);
 
 
-	/* Free the device number, remove the /proc/bus/usb entry and
-	 * the sysfs attributes, and delete the parent's children[]
+	/* Unregister the device.  The device driver is responsible
+	 * for removing the device files from usbfs and sysfs and for
+	 * de-configuring the device.
+	 */
+	device_del(&udev->dev);
+
+	/* Free the device number and delete the parent's children[]
 	 * (or root_hub) pointer.
 	 * (or root_hub) pointer.
 	 */
 	 */
-	dev_dbg (&udev->dev, "unregistering device\n");
 	release_address(udev);
 	release_address(udev);
-	usb_remove_sysfs_dev_files(udev);
 
 
 	/* Avoid races with recursively_mark_NOTATTACHED() */
 	/* Avoid races with recursively_mark_NOTATTACHED() */
 	spin_lock_irq(&device_state_lock);
 	spin_lock_irq(&device_state_lock);
 	*pdev = NULL;
 	*pdev = NULL;
 	spin_unlock_irq(&device_state_lock);
 	spin_unlock_irq(&device_state_lock);
 
 
-	usb_unlock_device(udev);
-
-	device_unregister(&udev->dev);
-}
-
-static inline const char *plural(int n)
-{
-	return (n == 1 ? "" : "s");
-}
-
-static int choose_configuration(struct usb_device *udev)
-{
-	int i;
-	int num_configs;
-	int insufficient_power = 0;
-	struct usb_host_config *c, *best;
-
-	best = NULL;
-	c = udev->config;
-	num_configs = udev->descriptor.bNumConfigurations;
-	for (i = 0; i < num_configs; (i++, c++)) {
-		struct usb_interface_descriptor	*desc = NULL;
-
-		/* It's possible that a config has no interfaces! */
-		if (c->desc.bNumInterfaces > 0)
-			desc = &c->intf_cache[0]->altsetting->desc;
-
-		/*
-		 * HP's USB bus-powered keyboard has only one configuration
-		 * and it claims to be self-powered; other devices may have
-		 * similar errors in their descriptors.  If the next test
-		 * were allowed to execute, such configurations would always
-		 * be rejected and the devices would not work as expected.
-		 * In the meantime, we run the risk of selecting a config
-		 * that requires external power at a time when that power
-		 * isn't available.  It seems to be the lesser of two evils.
-		 *
-		 * Bugzilla #6448 reports a device that appears to crash
-		 * when it receives a GET_DEVICE_STATUS request!  We don't
-		 * have any other way to tell whether a device is self-powered,
-		 * but since we don't use that information anywhere but here,
-		 * the call has been removed.
-		 *
-		 * Maybe the GET_DEVICE_STATUS call and the test below can
-		 * be reinstated when device firmwares become more reliable.
-		 * Don't hold your breath.
-		 */
-#if 0
-		/* Rule out self-powered configs for a bus-powered device */
-		if (bus_powered && (c->desc.bmAttributes &
-					USB_CONFIG_ATT_SELFPOWER))
-			continue;
-#endif
-
-		/*
-		 * The next test may not be as effective as it should be.
-		 * Some hubs have errors in their descriptor, claiming
-		 * to be self-powered when they are really bus-powered.
-		 * We will overestimate the amount of current such hubs
-		 * make available for each port.
-		 *
-		 * This is a fairly benign sort of failure.  It won't
-		 * cause us to reject configurations that we should have
-		 * accepted.
-		 */
-
-		/* Rule out configs that draw too much bus current */
-		if (c->desc.bMaxPower * 2 > udev->bus_mA) {
-			insufficient_power++;
-			continue;
-		}
-
-		/* If the first config's first interface is COMM/2/0xff
-		 * (MSFT RNDIS), rule it out unless Linux has host-side
-		 * RNDIS support. */
-		if (i == 0 && desc
-				&& desc->bInterfaceClass == USB_CLASS_COMM
-				&& desc->bInterfaceSubClass == 2
-				&& desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS_HOST
-			continue;
-#else
-			best = c;
-#endif
-		}
-
-		/* From the remaining configs, choose the first one whose
-		 * first interface is for a non-vendor-specific class.
-		 * Reason: Linux is more likely to have a class driver
-		 * than a vendor-specific driver. */
-		else if (udev->descriptor.bDeviceClass !=
-						USB_CLASS_VENDOR_SPEC &&
-				(!desc || desc->bInterfaceClass !=
-						USB_CLASS_VENDOR_SPEC)) {
-			best = c;
-			break;
-		}
-
-		/* If all the remaining configs are vendor-specific,
-		 * choose the first one. */
-		else if (!best)
-			best = c;
-	}
-
-	if (insufficient_power > 0)
-		dev_info(&udev->dev, "rejected %d configuration%s "
-			"due to insufficient available bus power\n",
-			insufficient_power, plural(insufficient_power));
-
-	if (best) {
-		i = best->desc.bConfigurationValue;
-		dev_info(&udev->dev,
-			"configuration #%d chosen from %d choice%s\n",
-			i, num_configs, plural(num_configs));
-	} else {
-		i = -1;
-		dev_warn(&udev->dev,
-			"no configuration chosen from %d choice%s\n",
-			num_configs, plural(num_configs));
-	}
-	return i;
+	put_device(&udev->dev);
 }
 }
 
 
 #ifdef DEBUG
 #ifdef DEBUG
@@ -1328,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
 int usb_new_device(struct usb_device *udev)
 int usb_new_device(struct usb_device *udev)
 {
 {
 	int err;
 	int err;
-	int c;
 
 
 	err = usb_get_configuration(udev);
 	err = usb_get_configuration(udev);
 	if (err < 0) {
 	if (err < 0) {
@@ -1371,8 +1254,7 @@ int usb_new_device(struct usb_device *udev)
 					USB_DT_OTG, (void **) &desc) == 0) {
 					USB_DT_OTG, (void **) &desc) == 0) {
 			if (desc->bmAttributes & USB_OTG_HNP) {
 			if (desc->bmAttributes & USB_OTG_HNP) {
 				unsigned		port1 = udev->portnum;
 				unsigned		port1 = udev->portnum;
-				struct usb_device	*root = udev->parent;
-				
+
 				dev_info(&udev->dev,
 				dev_info(&udev->dev,
 					"Dual-Role OTG device on %sHNP port\n",
 					"Dual-Role OTG device on %sHNP port\n",
 					(port1 == bus->otg_port)
 					(port1 == bus->otg_port)
@@ -1407,9 +1289,9 @@ int usb_new_device(struct usb_device *udev)
 		 * (Includes HNP test device.)
 		 * (Includes HNP test device.)
 		 */
 		 */
 		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
 		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-			static int __usb_suspend_device(struct usb_device *,
+			static int __usb_port_suspend(struct usb_device *,
 						int port1);
 						int port1);
-			err = __usb_suspend_device(udev, udev->bus->otg_port);
+			err = __usb_port_suspend(udev, udev->bus->otg_port);
 			if (err < 0)
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
 		}
@@ -1418,34 +1300,15 @@ int usb_new_device(struct usb_device *udev)
 	}
 	}
 #endif
 #endif
 
 
-	/* put device-specific files into sysfs */
+	/* Register the device.  The device driver is responsible
+	 * for adding the device files to usbfs and sysfs and for
+	 * configuring the device.
+	 */
 	err = device_add (&udev->dev);
 	err = device_add (&udev->dev);
 	if (err) {
 	if (err) {
 		dev_err(&udev->dev, "can't device_add, error %d\n", err);
 		dev_err(&udev->dev, "can't device_add, error %d\n", err);
 		goto fail;
 		goto fail;
 	}
 	}
-	usb_create_sysfs_dev_files (udev);
-
-	usb_lock_device(udev);
-
-	/* choose and set the configuration. that registers the interfaces
-	 * with the driver core, and lets usb device drivers bind to them.
-	 */
-	c = choose_configuration(udev);
-	if (c >= 0) {
-		err = usb_set_configuration(udev, c);
-		if (err) {
-			dev_err(&udev->dev, "can't set config #%d, error %d\n",
-					c, err);
-			/* This need not be fatal.  The user can try to
-			 * set other configurations. */
-		}
-	}
-
-	/* USB device state == configured ... usable */
-	usb_notify_add_device(udev);
-
-	usb_unlock_device(udev);
 
 
 	return 0;
 	return 0;
 
 
@@ -1472,6 +1335,18 @@ static int hub_port_status(struct usb_hub *hub, int port1,
 	return ret;
 	return ret;
 }
 }
 
 
+
+/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
+static unsigned hub_is_wusb(struct usb_hub *hub)
+{
+	struct usb_hcd *hcd;
+	if (hub->hdev->parent != NULL)  /* not a root hub? */
+		return 0;
+	hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
+	return hcd->wireless;
+}
+
+
 #define PORT_RESET_TRIES	5
 #define PORT_RESET_TRIES	5
 #define SET_ADDRESS_TRIES	2
 #define SET_ADDRESS_TRIES	2
 #define GET_DESCRIPTOR_TRIES	2
 #define GET_DESCRIPTOR_TRIES	2
@@ -1512,7 +1387,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 		/* if we`ve finished resetting, then break out of the loop */
 		/* if we`ve finished resetting, then break out of the loop */
 		if (!(portstatus & USB_PORT_STAT_RESET) &&
 		if (!(portstatus & USB_PORT_STAT_RESET) &&
 		    (portstatus & USB_PORT_STAT_ENABLE)) {
 		    (portstatus & USB_PORT_STAT_ENABLE)) {
-			if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+			if (hub_is_wusb(hub))
+				udev->speed = USB_SPEED_VARIABLE;
+			else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
 				udev->speed = USB_SPEED_HIGH;
 				udev->speed = USB_SPEED_HIGH;
 			else if (portstatus & USB_PORT_STAT_LOW_SPEED)
 			else if (portstatus & USB_PORT_STAT_LOW_SPEED)
 				udev->speed = USB_SPEED_LOW;
 				udev->speed = USB_SPEED_LOW;
@@ -1607,6 +1484,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
  	kick_khubd(hub);
  	kick_khubd(hub);
 }
 }
 
 
+#ifdef	CONFIG_PM
 
 
 #ifdef	CONFIG_USB_SUSPEND
 #ifdef	CONFIG_USB_SUSPEND
 
 
@@ -1633,7 +1511,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
 	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
 	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
 	 * we don't explicitly enable it here.
 	 * we don't explicitly enable it here.
 	 */
 	 */
-	if (device_may_wakeup(&udev->dev)) {
+	if (udev->do_remote_wakeup) {
 		status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 		status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
 				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
 				USB_DEVICE_REMOTE_WAKEUP, 0,
 				USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1659,7 +1537,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
 				USB_CTRL_SET_TIMEOUT);
 				USB_CTRL_SET_TIMEOUT);
 	} else {
 	} else {
 		/* device has up to 10 msec to fully suspend */
 		/* device has up to 10 msec to fully suspend */
-		dev_dbg(&udev->dev, "usb suspend\n");
+		dev_dbg(&udev->dev, "usb %ssuspend\n",
+				udev->auto_pm ? "auto-" : "");
 		usb_set_device_state(udev, USB_STATE_SUSPENDED);
 		usb_set_device_state(udev, USB_STATE_SUSPENDED);
 		msleep(10);
 		msleep(10);
 	}
 	}
@@ -1684,7 +1563,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * the root hub for their bus goes into global suspend ... so we don't
  * the root hub for their bus goes into global suspend ... so we don't
  * (falsely) update the device power state to say it suspended.
  * (falsely) update the device power state to say it suspended.
  */
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1)
+static int __usb_port_suspend (struct usb_device *udev, int port1)
 {
 {
 	int	status = 0;
 	int	status = 0;
 
 
@@ -1692,49 +1571,29 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
 	if (port1 < 0)
 	if (port1 < 0)
 		return port1;
 		return port1;
 
 
-	if (udev->state == USB_STATE_SUSPENDED
-			|| udev->state == USB_STATE_NOTATTACHED) {
-		return 0;
-	}
-
-	/* all interfaces must already be suspended */
-	if (udev->actconfig) {
-		int	i;
-
-		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-			struct usb_interface	*intf;
-
-			intf = udev->actconfig->interface[i];
-			if (is_active(intf)) {
-				dev_dbg(&intf->dev, "nyet suspended\n");
-				return -EBUSY;
-			}
-		}
-	}
-
-	/* we only change a device's upstream USB link.
-	 * root hubs have no upstream USB link.
+	/* we change the device's upstream USB link,
+	 * but root hubs have no upstream USB link.
 	 */
 	 */
 	if (udev->parent)
 	if (udev->parent)
 		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
 		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
 				udev);
 				udev);
-
-	if (status == 0)
-		udev->dev.power.power_state = PMSG_SUSPEND;
+	else {
+		dev_dbg(&udev->dev, "usb %ssuspend\n",
+				udev->auto_pm ? "auto-" : "");
+		usb_set_device_state(udev, USB_STATE_SUSPENDED);
+	}
 	return status;
 	return status;
 }
 }
 
 
-#endif
-
 /*
 /*
- * usb_suspend_device - suspend a usb device
+ * usb_port_suspend - suspend a usb device's upstream port
  * @udev: device that's no longer in active use
  * @udev: device that's no longer in active use
  * Context: must be able to sleep; device not locked; pm locks held
  * Context: must be able to sleep; device not locked; pm locks held
  *
  *
  * Suspends a USB device that isn't in active use, conserving power.
  * Suspends a USB device that isn't in active use, conserving power.
  * Devices may wake out of a suspend, if anything important happens,
  * Devices may wake out of a suspend, if anything important happens,
  * using the remote wakeup mechanism.  They may also be taken out of
  * using the remote wakeup mechanism.  They may also be taken out of
- * suspend by the host, using usb_resume_device().  It's also routine
+ * suspend by the host, using usb_port_resume().  It's also routine
  * to disconnect devices while they are suspended.
  * to disconnect devices while they are suspended.
  *
  *
  * This only affects the USB hardware for a device; its interfaces
  * This only affects the USB hardware for a device; its interfaces
@@ -1746,17 +1605,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
  *
  *
  * Returns 0 on success, else negative errno.
  * Returns 0 on success, else negative errno.
  */
  */
-int usb_suspend_device(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
 {
 {
-#ifdef	CONFIG_USB_SUSPEND
-	if (udev->state == USB_STATE_NOTATTACHED)
-		return -ENODEV;
-	return __usb_suspend_device(udev, udev->portnum);
-#else
-	/* NOTE:  udev->state unchanged, it's not lying ... */
-	udev->dev.power.power_state = PMSG_SUSPEND;
-	return 0;
-#endif
+	return __usb_port_suspend(udev, udev->portnum);
 }
 }
 
 
 /*
 /*
@@ -1767,7 +1618,7 @@ int usb_suspend_device(struct usb_device *udev)
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
  * in the tree that's rooted at this device.
  */
  */
-static int finish_device_resume(struct usb_device *udev)
+static int finish_port_resume(struct usb_device *udev)
 {
 {
 	int	status;
 	int	status;
 	u16	devstatus;
 	u16	devstatus;
@@ -1783,7 +1634,6 @@ static int finish_device_resume(struct usb_device *udev)
 	usb_set_device_state(udev, udev->actconfig
 	usb_set_device_state(udev, udev->actconfig
 			? USB_STATE_CONFIGURED
 			? USB_STATE_CONFIGURED
 			: USB_STATE_ADDRESS);
 			: USB_STATE_ADDRESS);
-	udev->dev.power.power_state = PMSG_ON;
 
 
  	/* 10.5.4.5 says be sure devices in the tree are still there.
  	/* 10.5.4.5 says be sure devices in the tree are still there.
  	 * For now let's assume the device didn't go crazy on resume,
  	 * For now let's assume the device didn't go crazy on resume,
@@ -1798,9 +1648,6 @@ static int finish_device_resume(struct usb_device *udev)
 			"gone after usb resume? status %d\n",
 			"gone after usb resume? status %d\n",
 			status);
 			status);
 	else if (udev->actconfig) {
 	else if (udev->actconfig) {
-		unsigned	i;
-		int		(*resume)(struct device *);
-
 		le16_to_cpus(&devstatus);
 		le16_to_cpus(&devstatus);
 		if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
 		if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
 				&& udev->parent) {
 				&& udev->parent) {
@@ -1811,24 +1658,9 @@ static int finish_device_resume(struct usb_device *udev)
 					USB_DEVICE_REMOTE_WAKEUP, 0,
 					USB_DEVICE_REMOTE_WAKEUP, 0,
 					NULL, 0,
 					NULL, 0,
 					USB_CTRL_SET_TIMEOUT);
 					USB_CTRL_SET_TIMEOUT);
-			if (status) {
+			if (status)
 				dev_dbg(&udev->dev, "disable remote "
 				dev_dbg(&udev->dev, "disable remote "
 					"wakeup, status %d\n", status);
 					"wakeup, status %d\n", status);
-				status = 0;
-			}
-		}
-
-		/* resume interface drivers; if this is a hub, it
-		 * may have a child resume event to deal with soon
-		 */
-		resume = udev->dev.bus->resume;
-		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-			struct device *dev =
-					&udev->actconfig->interface[i]->dev;
-
-			down(&dev->sem);
-			(void) resume(dev);
-			up(&dev->sem);
 		}
 		}
 		status = 0;
 		status = 0;
 
 
@@ -1839,8 +1671,6 @@ static int finish_device_resume(struct usb_device *udev)
 	return status;
 	return status;
 }
 }
 
 
-#ifdef	CONFIG_USB_SUSPEND
-
 static int
 static int
 hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 {
 {
@@ -1848,6 +1678,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 
 
 	// dev_dbg(hub->intfdev, "resume port %d\n", port1);
 	// dev_dbg(hub->intfdev, "resume port %d\n", port1);
 
 
+	set_bit(port1, hub->busy_bits);
+
 	/* see 7.1.7.7; affects power usage, but not budgeting */
 	/* see 7.1.7.7; affects power usage, but not budgeting */
 	status = clear_port_feature(hub->hdev,
 	status = clear_port_feature(hub->hdev,
 			port1, USB_PORT_FEAT_SUSPEND);
 			port1, USB_PORT_FEAT_SUSPEND);
@@ -1861,7 +1693,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 
 
 		/* drive resume for at least 20 msec */
 		/* drive resume for at least 20 msec */
 		if (udev)
 		if (udev)
-			dev_dbg(&udev->dev, "RESUME\n");
+			dev_dbg(&udev->dev, "usb %sresume\n",
+					udev->auto_pm ? "auto-" : "");
 		msleep(25);
 		msleep(25);
 
 
 #define LIVE_FLAGS	( USB_PORT_STAT_POWER \
 #define LIVE_FLAGS	( USB_PORT_STAT_POWER \
@@ -1891,19 +1724,21 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
 			/* TRSMRCY = 10 msec */
 			/* TRSMRCY = 10 msec */
 			msleep(10);
 			msleep(10);
 			if (udev)
 			if (udev)
-				status = finish_device_resume(udev);
+				status = finish_port_resume(udev);
 		}
 		}
 	}
 	}
 	if (status < 0)
 	if (status < 0)
 		hub_port_logical_disconnect(hub, port1);
 		hub_port_logical_disconnect(hub, port1);
 
 
+	clear_bit(port1, hub->busy_bits);
+	if (!hub->hdev->parent && !hub->busy_bits[0])
+		usb_enable_root_hub_irq(hub->hdev->bus);
+
 	return status;
 	return status;
 }
 }
 
 
-#endif
-
 /*
 /*
- * usb_resume_device - re-activate a suspended usb device
+ * usb_port_resume - re-activate a suspended usb device's upstream port
  * @udev: device to re-activate
  * @udev: device to re-activate
  * Context: must be able to sleep; device not locked; pm locks held
  * Context: must be able to sleep; device not locked; pm locks held
  *
  *
@@ -1915,36 +1750,24 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
  *
  *
  * Returns 0 on success, else negative errno.
  * Returns 0 on success, else negative errno.
  */
  */
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
 {
 {
 	int	status;
 	int	status;
 
 
-	if (udev->state == USB_STATE_NOTATTACHED)
-		return -ENODEV;
-
-	/* selective resume of one downstream hub-to-device port */
+	/* we change the device's upstream USB link,
+	 * but root hubs have no upstream USB link.
+	 */
 	if (udev->parent) {
 	if (udev->parent) {
-#ifdef	CONFIG_USB_SUSPEND
-		if (udev->state == USB_STATE_SUSPENDED) {
-			// NOTE swsusp may bork us, device state being wrong...
-			// NOTE this fails if parent is also suspended...
-			status = hub_port_resume(hdev_to_hub(udev->parent),
-					udev->portnum, udev);
-		} else
-#endif
-			status = 0;
-	} else
-		status = finish_device_resume(udev);
-	if (status < 0)
-		dev_dbg(&udev->dev, "can't resume, status %d\n",
-			status);
-
-	/* rebind drivers that had no suspend() */
-	if (status == 0) {
-		usb_unlock_device(udev);
-		bus_rescan_devices(&usb_bus_type);
-		usb_lock_device(udev);
+		// NOTE this fails if parent is also suspended...
+		status = hub_port_resume(hdev_to_hub(udev->parent),
+				udev->portnum, udev);
+	} else {
+		dev_dbg(&udev->dev, "usb %sresume\n",
+				udev->auto_pm ? "auto-" : "");
+		status = finish_port_resume(udev);
 	}
 	}
+	if (status < 0)
+		dev_dbg(&udev->dev, "can't resume, status %d\n", status);
 	return status;
 	return status;
 }
 }
 
 
@@ -1952,23 +1775,60 @@ static int remote_wakeup(struct usb_device *udev)
 {
 {
 	int	status = 0;
 	int	status = 0;
 
 
-#ifdef	CONFIG_USB_SUSPEND
+	/* All this just to avoid sending a port-resume message
+	 * to the parent hub! */
 
 
-	/* don't repeat RESUME sequence if this device
-	 * was already woken up by some other task
-	 */
 	usb_lock_device(udev);
 	usb_lock_device(udev);
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
 	if (udev->state == USB_STATE_SUSPENDED) {
 	if (udev->state == USB_STATE_SUSPENDED) {
-		dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
 		/* TRSMRCY = 10 msec */
 		/* TRSMRCY = 10 msec */
 		msleep(10);
 		msleep(10);
-		status = finish_device_resume(udev);
+		status = finish_port_resume(udev);
+		if (status == 0)
+			udev->dev.power.power_state.event = PM_EVENT_ON;
 	}
 	}
+	mutex_unlock(&udev->pm_mutex);
+
+	if (status == 0)
+		usb_autoresume_device(udev, 0);
 	usb_unlock_device(udev);
 	usb_unlock_device(udev);
-#endif
 	return status;
 	return status;
 }
 }
 
 
+#else	/* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
+
+int usb_port_suspend(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline int
+finish_port_resume(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+	return 0;
+}
+
+int usb_port_resume(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline int remote_wakeup(struct usb_device *udev)
+{
+	return 0;
+}
+
+#endif
+
 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 {
 {
 	struct usb_hub		*hub = usb_get_intfdata (intf);
 	struct usb_hub		*hub = usb_get_intfdata (intf);
@@ -1980,13 +1840,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 		struct usb_device	*udev;
 		struct usb_device	*udev;
 
 
 		udev = hdev->children [port1-1];
 		udev = hdev->children [port1-1];
-		if (udev && (udev->dev.power.power_state.event
-					== PM_EVENT_ON
+		if (udev && msg.event == PM_EVENT_SUSPEND &&
 #ifdef	CONFIG_USB_SUSPEND
 #ifdef	CONFIG_USB_SUSPEND
-				|| udev->state != USB_STATE_SUSPENDED
+				udev->state != USB_STATE_SUSPENDED
+#else
+				udev->dev.power.power_state.event
+					== PM_EVENT_ON
 #endif
 #endif
-				)) {
-			dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+				) {
+			if (!hdev->auto_pm)
+				dev_dbg(&intf->dev, "port %d nyet suspended\n",
+						port1);
 			return -EBUSY;
 			return -EBUSY;
 		}
 		}
 	}
 	}
@@ -2035,66 +1899,22 @@ static int hub_resume(struct usb_interface *intf)
 		}
 		}
 	}
 	}
 
 
+	/* tell khubd to look for changes on this hub */
 	hub_activate(hub);
 	hub_activate(hub);
-
-	/* REVISIT:  this recursion probably shouldn't exist.  Remove
-	 * this code sometime, after retesting with different root and
-	 * external hubs.
-	 */
-#ifdef	CONFIG_USB_SUSPEND
-	{
-	unsigned		port1;
-
-	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-		struct usb_device	*udev;
-		u16			portstat, portchange;
-
-		udev = hdev->children [port1-1];
-		status = hub_port_status(hub, port1, &portstat, &portchange);
-		if (status == 0) {
-			if (portchange & USB_PORT_STAT_C_SUSPEND) {
-				clear_port_feature(hdev, port1,
-					USB_PORT_FEAT_C_SUSPEND);
-				portchange &= ~USB_PORT_STAT_C_SUSPEND;
-			}
-
-			/* let khubd handle disconnects etc */
-			if (portchange)
-				continue;
-		}
-
-		if (!udev || status < 0)
-			continue;
-		usb_lock_device(udev);
-		if (portstat & USB_PORT_STAT_SUSPEND)
-			status = hub_port_resume(hub, port1, udev);
-		else {
-			status = finish_device_resume(udev);
-			if (status < 0) {
-				dev_dbg(&intf->dev, "resume port %d --> %d\n",
-					port1, status);
-				hub_port_logical_disconnect(hub, port1);
-			}
-		}
-		usb_unlock_device(udev);
-	}
-	}
-#endif
 	return 0;
 	return 0;
 }
 }
 
 
-void usb_suspend_root_hub(struct usb_device *hdev)
-{
-	struct usb_hub *hub = hdev_to_hub(hdev);
+#else	/* CONFIG_PM */
 
 
-	/* This also makes any led blinker stop retriggering.  We're called
-	 * from irq, so the blinker might still be scheduled.  Caller promises
-	 * that the root hub status URB will be canceled.
-	 */
-	__hub_quiesce(hub);
-	mark_quiesced(to_usb_interface(hub->intfdev));
+static inline int remote_wakeup(struct usb_device *udev)
+{
+	return 0;
 }
 }
 
 
+#define hub_suspend NULL
+#define hub_resume NULL
+#endif
+
 void usb_resume_root_hub(struct usb_device *hdev)
 void usb_resume_root_hub(struct usb_device *hdev)
 {
 {
 	struct usb_hub *hub = hdev_to_hub(hdev);
 	struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2214,6 +2034,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 	int			i, j, retval;
 	int			i, j, retval;
 	unsigned		delay = HUB_SHORT_RESET_TIME;
 	unsigned		delay = HUB_SHORT_RESET_TIME;
 	enum usb_device_speed	oldspeed = udev->speed;
 	enum usb_device_speed	oldspeed = udev->speed;
+	char 			*speed, *type;
 
 
 	/* root hub ports have a slightly longer reset period
 	/* root hub ports have a slightly longer reset period
 	 * (from USB 2.0 spec, section 7.1.7.5)
 	 * (from USB 2.0 spec, section 7.1.7.5)
@@ -2246,8 +2067,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
   
   
 	/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
 	/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
 	 * it's fixed size except for full speed devices.
 	 * it's fixed size except for full speed devices.
+	 * For Wireless USB devices, ep0 max packet is always 512 (tho
+	 * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
 	 */
 	 */
 	switch (udev->speed) {
 	switch (udev->speed) {
+	case USB_SPEED_VARIABLE:	/* fixed at 512 */
+		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
+		break;
 	case USB_SPEED_HIGH:		/* fixed at 64 */
 	case USB_SPEED_HIGH:		/* fixed at 64 */
 		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
 		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
 		break;
 		break;
@@ -2265,17 +2091,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 		goto fail;
 		goto fail;
 	}
 	}
  
  
+	type = "";
+	switch (udev->speed) {
+	case USB_SPEED_LOW:	speed = "low";	break;
+	case USB_SPEED_FULL:	speed = "full";	break;
+	case USB_SPEED_HIGH:	speed = "high";	break;
+	case USB_SPEED_VARIABLE:
+				speed = "variable";
+				type = "Wireless ";
+				break;
+	default: 		speed = "?";	break;
+	}
 	dev_info (&udev->dev,
 	dev_info (&udev->dev,
-			"%s %s speed USB device using %s and address %d\n",
-			(udev->config) ? "reset" : "new",
-			({ char *speed; switch (udev->speed) {
-			case USB_SPEED_LOW:	speed = "low";	break;
-			case USB_SPEED_FULL:	speed = "full";	break;
-			case USB_SPEED_HIGH:	speed = "high";	break;
-			default: 		speed = "?";	break;
-			}; speed;}),
-			udev->bus->controller->driver->name,
-			udev->devnum);
+		  "%s %s speed %sUSB device using %s and address %d\n",
+		  (udev->config) ? "reset" : "new", speed, type,
+		  udev->bus->controller->driver->name, udev->devnum);
 
 
 	/* Set up TT records, if needed  */
 	/* Set up TT records, if needed  */
 	if (hdev->tt) {
 	if (hdev->tt) {
@@ -2317,6 +2147,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 			 * down tremendously by NAKing the unexpectedly
 			 * down tremendously by NAKing the unexpectedly
 			 * early status stage.  Also, retry on all errors;
 			 * early status stage.  Also, retry on all errors;
 			 * some devices are flakey.
 			 * some devices are flakey.
+			 * 255 is for WUSB devices, we actually need to use 512.
+			 * WUSB1.0[4.8.1].
 			 */
 			 */
 			for (j = 0; j < 3; ++j) {
 			for (j = 0; j < 3; ++j) {
 				buf->bMaxPacketSize0 = 0;
 				buf->bMaxPacketSize0 = 0;
@@ -2326,7 +2158,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 					buf, GET_DESCRIPTOR_BUFSIZE,
 					buf, GET_DESCRIPTOR_BUFSIZE,
 					(i ? USB_CTRL_GET_TIMEOUT : 1000));
 					(i ? USB_CTRL_GET_TIMEOUT : 1000));
 				switch (buf->bMaxPacketSize0) {
 				switch (buf->bMaxPacketSize0) {
-				case 8: case 16: case 32: case 64:
+				case 8: case 16: case 32: case 64: case 255:
 					if (buf->bDescriptorType ==
 					if (buf->bDescriptorType ==
 							USB_DT_DEVICE) {
 							USB_DT_DEVICE) {
 						r = 0;
 						r = 0;
@@ -2400,7 +2232,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 	if (retval)
 	if (retval)
 		goto fail;
 		goto fail;
 
 
-	i = udev->descriptor.bMaxPacketSize0;
+	i = udev->descriptor.bMaxPacketSize0 == 0xff?
+	    512 : udev->descriptor.bMaxPacketSize0;
 	if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
 	if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
 		if (udev->speed != USB_SPEED_FULL ||
 		if (udev->speed != USB_SPEED_FULL ||
 				!(i == 8 || i == 16 || i == 32 || i == 64)) {
 				!(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2585,6 +2418,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 		usb_set_device_state(udev, USB_STATE_POWERED);
 		usb_set_device_state(udev, USB_STATE_POWERED);
 		udev->speed = USB_SPEED_UNKNOWN;
 		udev->speed = USB_SPEED_UNKNOWN;
  		udev->bus_mA = hub->mA_per_port;
  		udev->bus_mA = hub->mA_per_port;
+		udev->level = hdev->level + 1;
 
 
 		/* set the address */
 		/* set the address */
 		choose_address(udev);
 		choose_address(udev);
@@ -2736,17 +2570,6 @@ static void hub_events(void)
 		usb_get_intf(intf);
 		usb_get_intf(intf);
 		spin_unlock_irq(&hub_event_lock);
 		spin_unlock_irq(&hub_event_lock);
 
 
-		/* Is this is a root hub wanting to reactivate the downstream
-		 * ports?  If so, be sure the interface resumes even if its
-		 * stub "device" node was never suspended.
-		 */
-		if (i) {
-			dpm_runtime_resume(&hdev->dev);
-			dpm_runtime_resume(&intf->dev);
-			usb_put_intf(intf);
-			continue;
-		}
-
 		/* Lock the device, then check to see if we were
 		/* Lock the device, then check to see if we were
 		 * disconnected while waiting for the lock to succeed. */
 		 * disconnected while waiting for the lock to succeed. */
 		if (locktree(hdev) < 0) {
 		if (locktree(hdev) < 0) {
@@ -2763,6 +2586,13 @@ static void hub_events(void)
 			goto loop;
 			goto loop;
 		}
 		}
 
 
+		/* Is this is a root hub wanting to reactivate the downstream
+		 * ports?  If so, be sure the interface resumes even if its
+		 * stub "device" node was never suspended.
+		 */
+		if (i)
+			usb_autoresume_device(hdev, 0);
+
 		/* If this is an inactive or suspended hub, do nothing */
 		/* If this is an inactive or suspended hub, do nothing */
 		if (hub->quiescing)
 		if (hub->quiescing)
 			goto loop;
 			goto loop;
@@ -2900,7 +2730,7 @@ static void hub_events(void)
 
 
 		/* If this is a root hub, tell the HCD it's okay to
 		/* If this is a root hub, tell the HCD it's okay to
 		 * re-enable port-change interrupts now. */
 		 * re-enable port-change interrupts now. */
-		if (!hdev->parent)
+		if (!hdev->parent && !hub->busy_bits[0])
 			usb_enable_root_hub_irq(hdev->bus);
 			usb_enable_root_hub_irq(hdev->bus);
 
 
 loop:
 loop:
@@ -3075,6 +2905,9 @@ int usb_reset_device(struct usb_device *udev)
 			break;
 			break;
 	}
 	}
 	clear_bit(port1, parent_hub->busy_bits);
 	clear_bit(port1, parent_hub->busy_bits);
+	if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+		usb_enable_root_hub_irq(parent_hdev->bus);
+
 	if (ret < 0)
 	if (ret < 0)
 		goto re_enumerate;
 		goto re_enumerate;
  
  
@@ -3128,6 +2961,7 @@ int usb_reset_device(struct usb_device *udev)
 	hub_port_logical_disconnect(parent_hub, port1);
 	hub_port_logical_disconnect(parent_hub, port1);
 	return -ENODEV;
 	return -ENODEV;
 }
 }
+EXPORT_SYMBOL(usb_reset_device);
 
 
 /**
 /**
  * usb_reset_composite_device - warn interface drivers and perform a USB port reset
  * usb_reset_composite_device - warn interface drivers and perform a USB port reset
@@ -3163,6 +2997,9 @@ int usb_reset_composite_device(struct usb_device *udev,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	/* Prevent autosuspend during the reset */
+	usb_autoresume_device(udev, 1);
+
 	if (iface && iface->condition != USB_INTERFACE_BINDING)
 	if (iface && iface->condition != USB_INTERFACE_BINDING)
 		iface = NULL;
 		iface = NULL;
 
 
@@ -3204,5 +3041,7 @@ int usb_reset_composite_device(struct usb_device *udev,
 		}
 		}
 	}
 	}
 
 
+	usb_autosuspend_device(udev, 1);
 	return ret;
 	return ret;
 }
 }
+EXPORT_SYMBOL(usb_reset_composite_device);

+ 2 - 1
drivers/usb/core/hub.h

@@ -212,7 +212,8 @@ struct usb_hub {
 	unsigned long		event_bits[1];	/* status change bitmask */
 	unsigned long		event_bits[1];	/* status change bitmask */
 	unsigned long		change_bits[1];	/* ports with logical connect
 	unsigned long		change_bits[1];	/* ports with logical connect
 							status change */
 							status change */
-	unsigned long		busy_bits[1];	/* ports being reset */
+	unsigned long		busy_bits[1];	/* ports being reset or
+							resumed */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
 #error event_bits[] is too short!
 #error event_bits[] is too short!
 #endif
 #endif

+ 3 - 3
drivers/usb/core/inode.c

@@ -44,7 +44,7 @@
 #include "hcd.h"
 #include "hcd.h"
 
 
 static struct super_operations usbfs_ops;
 static struct super_operations usbfs_ops;
-static struct file_operations default_file_operations;
+static const struct file_operations default_file_operations;
 static struct vfsmount *usbfs_mount;
 static struct vfsmount *usbfs_mount;
 static int usbfs_mount_count;	/* = 0 */
 static int usbfs_mount_count;	/* = 0 */
 static int ignore_mount = 0;
 static int ignore_mount = 0;
@@ -407,7 +407,7 @@ static int default_open (struct inode *inode, struct file *file)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct file_operations default_file_operations = {
+static const struct file_operations default_file_operations = {
 	.read =		default_read_file,
 	.read =		default_read_file,
 	.write =	default_write_file,
 	.write =	default_write_file,
 	.open =		default_open,
 	.open =		default_open,
@@ -494,7 +494,7 @@ static int fs_create_by_name (const char *name, mode_t mode,
 
 
 static struct dentry *fs_create_file (const char *name, mode_t mode,
 static struct dentry *fs_create_file (const char *name, mode_t mode,
 				      struct dentry *parent, void *data,
 				      struct dentry *parent, void *data,
-				      struct file_operations *fops,
+				      const struct file_operations *fops,
 				      uid_t uid, gid_t gid)
 				      uid_t uid, gid_t gid)
 {
 {
 	struct dentry *dentry;
 	struct dentry *dentry;

+ 98 - 50
drivers/usb/core/message.c

@@ -23,59 +23,44 @@ static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
 }
 }
 
 
 
 
-static void timeout_kill(unsigned long data)
-{
-	struct urb	*urb = (struct urb *) data;
-
-	usb_unlink_urb(urb);
-}
-
-// Starts urb and waits for completion or timeout
-// note that this call is NOT interruptible, while
-// many device driver i/o requests should be interruptible
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+/*
+ * Starts urb and waits for completion or timeout. Note that this call
+ * is NOT interruptible. Many device driver i/o requests should be
+ * interruptible and therefore these drivers should implement their
+ * own interruptible routines.
+ */
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
 { 
 { 
-	struct completion	done;
-	struct timer_list	timer;
-	int			status;
+	struct completion done;
+	unsigned long expire;
+	int status;
 
 
 	init_completion(&done); 	
 	init_completion(&done); 	
 	urb->context = &done;
 	urb->context = &done;
 	urb->actual_length = 0;
 	urb->actual_length = 0;
 	status = usb_submit_urb(urb, GFP_NOIO);
 	status = usb_submit_urb(urb, GFP_NOIO);
-
-	if (status == 0) {
-		if (timeout > 0) {
-			init_timer(&timer);
-			timer.expires = jiffies + msecs_to_jiffies(timeout);
-			timer.data = (unsigned long)urb;
-			timer.function = timeout_kill;
-			/* grr.  timeout _should_ include submit delays. */
-			add_timer(&timer);
-		}
-		wait_for_completion(&done);
+	if (unlikely(status))
+		goto out;
+
+	expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+	if (!wait_for_completion_timeout(&done, expire)) {
+
+		dev_dbg(&urb->dev->dev,
+			"%s timed out on ep%d%s len=%d/%d\n",
+			current->comm,
+			usb_pipeendpoint(urb->pipe),
+			usb_pipein(urb->pipe) ? "in" : "out",
+			urb->actual_length,
+			urb->transfer_buffer_length);
+
+		usb_kill_urb(urb);
+		status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
+	} else
 		status = urb->status;
 		status = urb->status;
-		/* note:  HCDs return ETIMEDOUT for other reasons too */
-		if (status == -ECONNRESET) {
-			dev_dbg(&urb->dev->dev,
-				"%s timed out on ep%d%s len=%d/%d\n",
-				current->comm,
-				usb_pipeendpoint(urb->pipe),
-				usb_pipein(urb->pipe) ? "in" : "out",
-				urb->actual_length,
-				urb->transfer_buffer_length
-				);
-			if (urb->actual_length > 0)
-				status = 0;
-			else
-				status = -ETIMEDOUT;
-		}
-		if (timeout > 0)
-			del_timer_sync(&timer);
-	}
-
+out:
 	if (actual_length)
 	if (actual_length)
 		*actual_length = urb->actual_length;
 		*actual_length = urb->actual_length;
+
 	usb_free_urb(urb);
 	usb_free_urb(urb);
 	return status;
 	return status;
 }
 }
@@ -263,7 +248,7 @@ static void sg_clean (struct usb_sg_request *io)
 
 
 static void sg_complete (struct urb *urb, struct pt_regs *regs)
 static void sg_complete (struct urb *urb, struct pt_regs *regs)
 {
 {
-	struct usb_sg_request	*io = (struct usb_sg_request *) urb->context;
+	struct usb_sg_request	*io = urb->context;
 
 
 	spin_lock (&io->lock);
 	spin_lock (&io->lock);
 
 
@@ -999,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
 		ep = dev->ep_in[epnum];
 		ep = dev->ep_in[epnum];
 		dev->ep_in[epnum] = NULL;
 		dev->ep_in[epnum] = NULL;
 	}
 	}
-	if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
-		dev->bus->op->disable(dev, ep);
+	if (ep && dev->bus)
+		usb_hcd_endpoint_disable(dev, ep);
 }
 }
 
 
 /**
 /**
@@ -1381,9 +1366,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
 	if (cp && configuration == 0)
 	if (cp && configuration == 0)
 		dev_warn(&dev->dev, "config 0 descriptor??\n");
 		dev_warn(&dev->dev, "config 0 descriptor??\n");
 
 
-	if (dev->state == USB_STATE_SUSPENDED)
-		return -EHOSTUNREACH;
-
 	/* Allocate memory for new interfaces before doing anything else,
 	/* Allocate memory for new interfaces before doing anything else,
 	 * so that if we run out then nothing will have changed. */
 	 * so that if we run out then nothing will have changed. */
 	n = nintf = 0;
 	n = nintf = 0;
@@ -1418,6 +1400,11 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
 					configuration, -i);
 					configuration, -i);
 	}
 	}
 
 
+	/* Wake up the device so we can send it the Set-Config request */
+	ret = usb_autoresume_device(dev, 1);
+	if (ret)
+		goto free_interfaces;
+
 	/* if it's already configured, clear out old state first.
 	/* if it's already configured, clear out old state first.
 	 * getting rid of old interfaces means unbinding their drivers.
 	 * getting rid of old interfaces means unbinding their drivers.
 	 */
 	 */
@@ -1437,6 +1424,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
 	dev->actconfig = cp;
 	dev->actconfig = cp;
 	if (!cp) {
 	if (!cp) {
 		usb_set_device_state(dev, USB_STATE_ADDRESS);
 		usb_set_device_state(dev, USB_STATE_ADDRESS);
+		usb_autosuspend_device(dev, 1);
 		goto free_interfaces;
 		goto free_interfaces;
 	}
 	}
 	usb_set_device_state(dev, USB_STATE_CONFIGURED);
 	usb_set_device_state(dev, USB_STATE_CONFIGURED);
@@ -1505,8 +1493,68 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
 		usb_create_sysfs_intf_files (intf);
 		usb_create_sysfs_intf_files (intf);
 	}
 	}
 
 
+	usb_autosuspend_device(dev, 1);
+	return 0;
+}
+
+struct set_config_request {
+	struct usb_device	*udev;
+	int			config;
+	struct work_struct	work;
+};
+
+/* Worker routine for usb_driver_set_configuration() */
+static void driver_set_config_work(void *_req)
+{
+	struct set_config_request *req = _req;
+
+	usb_lock_device(req->udev);
+	usb_set_configuration(req->udev, req->config);
+	usb_unlock_device(req->udev);
+	usb_put_dev(req->udev);
+	kfree(req);
+}
+
+/**
+ * usb_driver_set_configuration - Provide a way for drivers to change device configurations
+ * @udev: the device whose configuration is being updated
+ * @config: the configuration being chosen.
+ * Context: In process context, must be able to sleep
+ *
+ * Device interface drivers are not allowed to change device configurations.
+ * This is because changing configurations will destroy the interface the
+ * driver is bound to and create new ones; it would be like a floppy-disk
+ * driver telling the computer to replace the floppy-disk drive with a
+ * tape drive!
+ *
+ * Still, in certain specialized circumstances the need may arise.  This
+ * routine gets around the normal restrictions by using a work thread to
+ * submit the change-config request.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_driver_set_configuration(struct usb_device *udev, int config)
+{
+	struct set_config_request *req;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+	req->udev = udev;
+	req->config = config;
+	INIT_WORK(&req->work, driver_set_config_work, req);
+
+	usb_get_dev(udev);
+	if (!schedule_work(&req->work)) {
+		usb_put_dev(udev);
+		kfree(req);
+		return -EINVAL;
+	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 
 
 // synchronous request completion model
 // synchronous request completion model
 EXPORT_SYMBOL(usb_control_msg);
 EXPORT_SYMBOL(usb_control_msg);

+ 3 - 0
drivers/usb/core/notify.c

@@ -50,8 +50,11 @@ void usb_notify_add_device(struct usb_device *udev)
 
 
 void usb_notify_remove_device(struct usb_device *udev)
 void usb_notify_remove_device(struct usb_device *udev)
 {
 {
+	/* Protect against simultaneous usbfs open */
+	mutex_lock(&usbfs_mutex);
 	blocking_notifier_call_chain(&usb_notifier_list,
 	blocking_notifier_call_chain(&usb_notifier_list,
 			USB_DEVICE_REMOVE, udev);
 			USB_DEVICE_REMOVE, udev);
+	mutex_unlock(&usbfs_mutex);
 }
 }
 
 
 void usb_notify_add_bus(struct usb_bus *ubus)
 void usb_notify_add_bus(struct usb_bus *ubus)

+ 45 - 15
drivers/usb/core/sysfs.c

@@ -60,7 +60,7 @@ static ssize_t
 set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
 set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
 		const char *buf, size_t count)
 		const char *buf, size_t count)
 {
 {
-	struct usb_device	*udev = udev = to_usb_device (dev);
+	struct usb_device	*udev = to_usb_device (dev);
 	int			config, value;
 	int			config, value;
 
 
 	if (sscanf (buf, "%u", &config) != 1 || config > 255)
 	if (sscanf (buf, "%u", &config) != 1 || config > 255)
@@ -186,6 +186,7 @@ usb_descriptor_attr (bMaxPacketSize0, "%d\n")
 
 
 static struct attribute *dev_attrs[] = {
 static struct attribute *dev_attrs[] = {
 	/* current configuration's attributes */
 	/* current configuration's attributes */
+	&dev_attr_configuration.attr,
 	&dev_attr_bNumInterfaces.attr,
 	&dev_attr_bNumInterfaces.attr,
 	&dev_attr_bConfigurationValue.attr,
 	&dev_attr_bConfigurationValue.attr,
 	&dev_attr_bmAttributes.attr,
 	&dev_attr_bmAttributes.attr,
@@ -209,20 +210,40 @@ static struct attribute_group dev_attr_grp = {
 	.attrs = dev_attrs,
 	.attrs = dev_attrs,
 };
 };
 
 
-void usb_create_sysfs_dev_files (struct usb_device *udev)
+int usb_create_sysfs_dev_files(struct usb_device *udev)
 {
 {
 	struct device *dev = &udev->dev;
 	struct device *dev = &udev->dev;
+	int retval;
 
 
-	sysfs_create_group(&dev->kobj, &dev_attr_grp);
+	retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
+	if (retval)
+		return retval;
 
 
-	if (udev->manufacturer)
-		device_create_file (dev, &dev_attr_manufacturer);
-	if (udev->product)
-		device_create_file (dev, &dev_attr_product);
-	if (udev->serial)
-		device_create_file (dev, &dev_attr_serial);
-	device_create_file (dev, &dev_attr_configuration);
-	usb_create_ep_files(dev, &udev->ep0, udev);
+	if (udev->manufacturer) {
+		retval = device_create_file (dev, &dev_attr_manufacturer);
+		if (retval)
+			goto error;
+	}
+	if (udev->product) {
+		retval = device_create_file (dev, &dev_attr_product);
+		if (retval)
+			goto error;
+	}
+	if (udev->serial) {
+		retval = device_create_file (dev, &dev_attr_serial);
+		if (retval)
+			goto error;
+	}
+	retval = usb_create_ep_files(dev, &udev->ep0, udev);
+	if (retval)
+		goto error;
+	return 0;
+error:
+	usb_remove_ep_files(&udev->ep0);
+	device_remove_file(dev, &dev_attr_manufacturer);
+	device_remove_file(dev, &dev_attr_product);
+	device_remove_file(dev, &dev_attr_serial);
+	return retval;
 }
 }
 
 
 void usb_remove_sysfs_dev_files (struct usb_device *udev)
 void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -238,7 +259,6 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
 		device_remove_file(dev, &dev_attr_product);
 		device_remove_file(dev, &dev_attr_product);
 	if (udev->serial)
 	if (udev->serial)
 		device_remove_file(dev, &dev_attr_serial);
 		device_remove_file(dev, &dev_attr_serial);
-	device_remove_file (dev, &dev_attr_configuration);
 }
 }
 
 
 /* Interface fields */
 /* Interface fields */
@@ -340,18 +360,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
 		usb_remove_ep_files(&iface_desc->endpoint[i]);
 		usb_remove_ep_files(&iface_desc->endpoint[i]);
 }
 }
 
 
-void usb_create_sysfs_intf_files (struct usb_interface *intf)
+int usb_create_sysfs_intf_files(struct usb_interface *intf)
 {
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct usb_host_interface *alt = intf->cur_altsetting;
 	struct usb_host_interface *alt = intf->cur_altsetting;
+	int retval;
 
 
-	sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+	retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+	if (retval)
+		goto error;
 
 
 	if (alt->string == NULL)
 	if (alt->string == NULL)
 		alt->string = usb_cache_string(udev, alt->desc.iInterface);
 		alt->string = usb_cache_string(udev, alt->desc.iInterface);
 	if (alt->string)
 	if (alt->string)
-		device_create_file(&intf->dev, &dev_attr_interface);
+		retval = device_create_file(&intf->dev, &dev_attr_interface);
 	usb_create_intf_ep_files(intf, udev);
 	usb_create_intf_ep_files(intf, udev);
+	return 0;
+error:
+	if (alt->string)
+		device_remove_file(&intf->dev, &dev_attr_interface);
+	sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+	usb_remove_intf_ep_files(intf);
+	return retval;
 }
 }
 
 
 void usb_remove_sysfs_intf_files (struct usb_interface *intf)
 void usb_remove_sysfs_intf_files (struct usb_interface *intf)

+ 6 - 9
drivers/usb/core/urb.c

@@ -57,7 +57,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
 {
 {
 	struct urb *urb;
 	struct urb *urb;
 
 
-	urb = (struct urb *)kmalloc(sizeof(struct urb) + 
+	urb = kmalloc(sizeof(struct urb) +
 		iso_packets * sizeof(struct usb_iso_packet_descriptor),
 		iso_packets * sizeof(struct usb_iso_packet_descriptor),
 		mem_flags);
 		mem_flags);
 	if (!urb) {
 	if (!urb) {
@@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 {
 {
 	int			pipe, temp, max;
 	int			pipe, temp, max;
 	struct usb_device	*dev;
 	struct usb_device	*dev;
-	struct usb_operations	*op;
 	int			is_out;
 	int			is_out;
 
 
 	if (!urb || urb->hcpriv || !urb->complete)
 	if (!urb || urb->hcpriv || !urb->complete)
@@ -233,8 +232,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 	if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
 	if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
 			|| dev->state == USB_STATE_SUSPENDED)
 			|| dev->state == USB_STATE_SUSPENDED)
 		return -EHOSTUNREACH;
 		return -EHOSTUNREACH;
-	if (!(op = dev->bus->op) || !op->submit_urb)
-		return -ENODEV;
 
 
 	urb->status = -EINPROGRESS;
 	urb->status = -EINPROGRESS;
 	urb->actual_length = 0;
 	urb->actual_length = 0;
@@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 		urb->interval = temp;
 		urb->interval = temp;
 	}
 	}
 
 
-	return op->submit_urb (urb, mem_flags);
+	return usb_hcd_submit_urb (urb, mem_flags);
 }
 }
 
 
 /*-------------------------------------------------------------------*/
 /*-------------------------------------------------------------------*/
@@ -440,9 +437,9 @@ int usb_unlink_urb(struct urb *urb)
 {
 {
 	if (!urb)
 	if (!urb)
 		return -EINVAL;
 		return -EINVAL;
-	if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+	if (!(urb->dev && urb->dev->bus))
 		return -ENODEV;
 		return -ENODEV;
-	return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+	return usb_hcd_unlink_urb(urb, -ECONNRESET);
 }
 }
 
 
 /**
 /**
@@ -468,13 +465,13 @@ int usb_unlink_urb(struct urb *urb)
 void usb_kill_urb(struct urb *urb)
 void usb_kill_urb(struct urb *urb)
 {
 {
 	might_sleep();
 	might_sleep();
-	if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+	if (!(urb && urb->dev && urb->dev->bus))
 		return;
 		return;
 	spin_lock_irq(&urb->lock);
 	spin_lock_irq(&urb->lock);
 	++urb->reject;
 	++urb->reject;
 	spin_unlock_irq(&urb->lock);
 	spin_unlock_irq(&urb->lock);
 
 
-	urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+	usb_hcd_unlink_urb(urb, -ENOENT);
 	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
 	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
 
 
 	spin_lock_irq(&urb->lock);
 	spin_lock_irq(&urb->lock);

+ 198 - 335
drivers/usb/core/usb.c

@@ -67,7 +67,8 @@ static int nousb;	/* Disable USB when built into kernel image */
  * Don't call this function unless you are bound to one of the interfaces
  * Don't call this function unless you are bound to one of the interfaces
  * on this device or you have locked the device!
  * on this device or you have locked the device!
  */
  */
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+				      unsigned ifnum)
 {
 {
 	struct usb_host_config *config = dev->actconfig;
 	struct usb_host_config *config = dev->actconfig;
 	int i;
 	int i;
@@ -100,8 +101,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
  * Don't call this function unless you are bound to the intf interface
  * Don't call this function unless you are bound to the intf interface
  * or you have locked the device!
  * or you have locked the device!
  */
  */
-struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
-		unsigned int altnum)
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf,
+						    unsigned int altnum)
 {
 {
 	int i;
 	int i;
 
 
@@ -112,87 +113,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
 	return NULL;
 	return NULL;
 }
 }
 
 
-/**
- * usb_driver_claim_interface - bind a driver to an interface
- * @driver: the driver to be bound
- * @iface: the interface to which it will be bound; must be in the
- *	usb device's active configuration
- * @priv: driver data associated with that interface
- *
- * This is used by usb device drivers that need to claim more than one
- * interface on a device when probing (audio and acm are current examples).
- * No device driver should directly modify internal usb_interface or
- * usb_device structure members.
- *
- * Few drivers should need to use this routine, since the most natural
- * way to bind to an interface is to return the private data from
- * the driver's probe() method.
- *
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver probe() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-int usb_driver_claim_interface(struct usb_driver *driver,
-				struct usb_interface *iface, void* priv)
-{
-	struct device *dev = &iface->dev;
-
-	if (dev->driver)
-		return -EBUSY;
-
-	dev->driver = &driver->driver;
-	usb_set_intfdata(iface, priv);
-	iface->condition = USB_INTERFACE_BOUND;
-	mark_active(iface);
-
-	/* if interface was already added, bind now; else let
-	 * the future device_add() bind it, bypassing probe()
-	 */
-	if (device_is_registered(dev))
-		device_bind_driver(dev);
-
-	return 0;
-}
-
-/**
- * usb_driver_release_interface - unbind a driver from an interface
- * @driver: the driver to be unbound
- * @iface: the interface from which it will be unbound
- *
- * This can be used by drivers to release an interface without waiting
- * for their disconnect() methods to be called.  In typical cases this
- * also causes the driver disconnect() method to be called.
- *
- * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock.  So driver disconnect() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-void usb_driver_release_interface(struct usb_driver *driver,
-					struct usb_interface *iface)
-{
-	struct device *dev = &iface->dev;
-
-	/* this should never happen, don't release something that's not ours */
-	if (!dev->driver || dev->driver != &driver->driver)
-		return;
-
-	/* don't release from within disconnect() */
-	if (iface->condition != USB_INTERFACE_BOUND)
-		return;
-
-	/* don't release if the interface hasn't been added yet */
-	if (device_is_registered(dev)) {
-		iface->condition = USB_INTERFACE_UNBINDING;
-		device_release_driver(dev);
-	}
-
-	dev->driver = NULL;
-	usb_set_intfdata(iface, NULL);
-	iface->condition = USB_INTERFACE_UNBOUND;
-	mark_quiesced(iface);
-}
-
 struct find_interface_arg {
 struct find_interface_arg {
 	int minor;
 	int minor;
 	struct usb_interface *interface;
 	struct usb_interface *interface;
@@ -204,7 +124,7 @@ static int __find_interface(struct device * dev, void * data)
 	struct usb_interface *intf;
 	struct usb_interface *intf;
 
 
 	/* can't look at usb devices, only interfaces */
 	/* can't look at usb devices, only interfaces */
-	if (dev->driver == &usb_generic_driver)
+	if (is_usb_device(dev))
 		return 0;
 		return 0;
 
 
 	intf = to_usb_interface(dev);
 	intf = to_usb_interface(dev);
@@ -227,127 +147,16 @@ static int __find_interface(struct device * dev, void * data)
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 {
 {
 	struct find_interface_arg argb;
 	struct find_interface_arg argb;
+	int retval;
 
 
 	argb.minor = minor;
 	argb.minor = minor;
 	argb.interface = NULL;
 	argb.interface = NULL;
-	driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+	/* eat the error, it will be in argb.interface */
+	retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
+					__find_interface);
 	return argb.interface;
 	return argb.interface;
 }
 }
 
 
-#ifdef	CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more.  Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
- *
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
- */
-static int usb_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size)
-{
-	struct usb_interface *intf;
-	struct usb_device *usb_dev;
-	struct usb_host_interface *alt;
-	int i = 0;
-	int length = 0;
-
-	if (!dev)
-		return -ENODEV;
-
-	/* driver is often null here; dev_dbg() would oops */
-	pr_debug ("usb %s: uevent\n", dev->bus_id);
-
-	/* Must check driver_data here, as on remove driver is always NULL */
-	if ((dev->driver == &usb_generic_driver) || 
-	    (dev->driver_data == &usb_generic_driver_data))
-		return 0;
-
-	intf = to_usb_interface(dev);
-	usb_dev = interface_to_usbdev (intf);
-	alt = intf->cur_altsetting;
-
-	if (usb_dev->devnum < 0) {
-		pr_debug ("usb %s: already deleted?\n", dev->bus_id);
-		return -ENODEV;
-	}
-	if (!usb_dev->bus) {
-		pr_debug ("usb %s: bus removed?\n", dev->bus_id);
-		return -ENODEV;
-	}
-
-#ifdef	CONFIG_USB_DEVICEFS
-	/* If this is available, userspace programs can directly read
-	 * all the device descriptors we don't tell them about.  Or
-	 * even act as usermode drivers.
-	 *
-	 * FIXME reduce hardwired intelligence here
-	 */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "DEVICE=/proc/bus/usb/%03d/%03d",
-			   usb_dev->bus->busnum, usb_dev->devnum))
-		return -ENOMEM;
-#endif
-
-	/* per-device configurations are common */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PRODUCT=%x/%x/%x",
-			   le16_to_cpu(usb_dev->descriptor.idVendor),
-			   le16_to_cpu(usb_dev->descriptor.idProduct),
-			   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
-		return -ENOMEM;
-
-	/* class-based driver binding models */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "TYPE=%d/%d/%d",
-			   usb_dev->descriptor.bDeviceClass,
-			   usb_dev->descriptor.bDeviceSubClass,
-			   usb_dev->descriptor.bDeviceProtocol))
-		return -ENOMEM;
-
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "INTERFACE=%d/%d/%d",
-			   alt->desc.bInterfaceClass,
-			   alt->desc.bInterfaceSubClass,
-			   alt->desc.bInterfaceProtocol))
-		return -ENOMEM;
-
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
-			   le16_to_cpu(usb_dev->descriptor.idVendor),
-			   le16_to_cpu(usb_dev->descriptor.idProduct),
-			   le16_to_cpu(usb_dev->descriptor.bcdDevice),
-			   usb_dev->descriptor.bDeviceClass,
-			   usb_dev->descriptor.bDeviceSubClass,
-			   usb_dev->descriptor.bDeviceProtocol,
-			   alt->desc.bInterfaceClass,
-			   alt->desc.bInterfaceSubClass,
-			   alt->desc.bInterfaceProtocol))
-		return -ENOMEM;
-
-	envp[i] = NULL;
-
-	return 0;
-}
-
-#else
-
-static int usb_uevent(struct device *dev, char **envp,
-			int num_envp, char *buffer, int buffer_size)
-{
-	return -ENODEV;
-}
-
-#endif	/* CONFIG_HOTPLUG */
-
 /**
 /**
  * usb_release_dev - free a usb device structure when all users of it are finished.
  * usb_release_dev - free a usb device structure when all users of it are finished.
  * @dev: device that's been disconnected
  * @dev: device that's been disconnected
@@ -361,14 +170,33 @@ static void usb_release_dev(struct device *dev)
 
 
 	udev = to_usb_device(dev);
 	udev = to_usb_device(dev);
 
 
+#ifdef	CONFIG_PM
+	cancel_delayed_work(&udev->autosuspend);
+	flush_scheduled_work();
+#endif
 	usb_destroy_configuration(udev);
 	usb_destroy_configuration(udev);
-	usb_bus_put(udev->bus);
+	usb_put_hcd(bus_to_hcd(udev->bus));
 	kfree(udev->product);
 	kfree(udev->product);
 	kfree(udev->manufacturer);
 	kfree(udev->manufacturer);
 	kfree(udev->serial);
 	kfree(udev->serial);
 	kfree(udev);
 	kfree(udev);
 }
 }
 
 
+#ifdef	CONFIG_PM
+
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
+{
+	struct usb_device	*udev = _udev;
+
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
+	udev->auto_pm = 1;
+	usb_suspend_both(udev, PMSG_SUSPEND);
+	mutex_unlock(&udev->pm_mutex);
+}
+
+#endif
+
 /**
 /**
  * usb_alloc_dev - usb device constructor (usbcore-internal)
  * usb_alloc_dev - usb device constructor (usbcore-internal)
  * @parent: hub to which device is connected; null to allocate a root hub
  * @parent: hub to which device is connected; null to allocate a root hub
@@ -390,8 +218,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 	if (!dev)
 	if (!dev)
 		return NULL;
 		return NULL;
 
 
-	bus = usb_bus_get(bus);
-	if (!bus) {
+	if (!usb_get_hcd(bus_to_hcd(bus))) {
 		kfree(dev);
 		kfree(dev);
 		return NULL;
 		return NULL;
 	}
 	}
@@ -399,11 +226,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 	device_initialize(&dev->dev);
 	device_initialize(&dev->dev);
 	dev->dev.bus = &usb_bus_type;
 	dev->dev.bus = &usb_bus_type;
 	dev->dev.dma_mask = bus->controller->dma_mask;
 	dev->dev.dma_mask = bus->controller->dma_mask;
-	dev->dev.driver_data = &usb_generic_driver_data;
-	dev->dev.driver = &usb_generic_driver;
 	dev->dev.release = usb_release_dev;
 	dev->dev.release = usb_release_dev;
 	dev->state = USB_STATE_ATTACHED;
 	dev->state = USB_STATE_ATTACHED;
 
 
+	/* This magic assignment distinguishes devices from interfaces */
+	dev->dev.platform_data = &usb_generic_driver;
+
 	INIT_LIST_HEAD(&dev->ep0.urb_list);
 	INIT_LIST_HEAD(&dev->ep0.urb_list);
 	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
 	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
 	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
 	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -444,6 +272,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 	dev->parent = parent;
 	dev->parent = parent;
 	INIT_LIST_HEAD(&dev->filelist);
 	INIT_LIST_HEAD(&dev->filelist);
 
 
+#ifdef	CONFIG_PM
+	mutex_init(&dev->pm_mutex);
+	INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
 	return dev;
 	return dev;
 }
 }
 
 
@@ -549,7 +381,7 @@ void usb_put_intf(struct usb_interface *intf)
  * case the driver already owns the device lock.)
  * case the driver already owns the device lock.)
  */
  */
 int usb_lock_device_for_reset(struct usb_device *udev,
 int usb_lock_device_for_reset(struct usb_device *udev,
-		struct usb_interface *iface)
+			      const struct usb_interface *iface)
 {
 {
 	unsigned long jiffies_expire = jiffies + HZ;
 	unsigned long jiffies_expire = jiffies + HZ;
 
 
@@ -672,7 +504,139 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
  */
  */
 int usb_get_current_frame_number(struct usb_device *dev)
 int usb_get_current_frame_number(struct usb_device *dev)
 {
 {
-	return dev->bus->op->get_frame_number (dev);
+	return usb_hcd_get_frame_number (dev);
+}
+
+/**
+ * usb_endpoint_dir_in - check if the endpoint has IN direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type IN, otherwise it returns false.
+ */
+int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+	return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+	return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+/**
+ * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type bulk, otherwise it returns false.
+ */
+int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
+{
+	return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+	return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		USB_ENDPOINT_XFER_INT);
+}
+
+/**
+ * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type isochronous, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
+{
+	return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+		USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
+ * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd)
+{
+	return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
 }
 }
 
 
 /*-------------------------------------------------------------------*/
 /*-------------------------------------------------------------------*/
@@ -737,9 +701,9 @@ void *usb_buffer_alloc (
 	dma_addr_t *dma
 	dma_addr_t *dma
 )
 )
 {
 {
-	if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+	if (!dev || !dev->bus)
 		return NULL;
 		return NULL;
-	return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+	return hcd_buffer_alloc (dev->bus, size, mem_flags, dma);
 }
 }
 
 
 /**
 /**
@@ -760,9 +724,11 @@ void usb_buffer_free (
 	dma_addr_t dma
 	dma_addr_t dma
 )
 )
 {
 {
-	if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
-	    	return;
-	dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+	if (!dev || !dev->bus)
+		return;
+	if (!addr)
+		return;
+	hcd_buffer_free (dev->bus, size, addr, dma);
 }
 }
 
 
 /**
 /**
@@ -911,8 +877,8 @@ void usb_buffer_unmap (struct urb *urb)
  *
  *
  * Reverse the effect of this call with usb_buffer_unmap_sg().
  * Reverse the effect of this call with usb_buffer_unmap_sg().
  */
  */
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
-		struct scatterlist *sg, int nents)
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+		      struct scatterlist *sg, int nents)
 {
 {
 	struct usb_bus		*bus;
 	struct usb_bus		*bus;
 	struct device		*controller;
 	struct device		*controller;
@@ -946,8 +912,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
  * Use this when you are re-using a scatterlist's data buffers for
  * Use this when you are re-using a scatterlist's data buffers for
  * another USB request.
  * another USB request.
  */
  */
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
-		struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+			   struct scatterlist *sg, int n_hw_ents)
 {
 {
 	struct usb_bus		*bus;
 	struct usb_bus		*bus;
 	struct device		*controller;
 	struct device		*controller;
@@ -972,8 +938,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
  *
  *
  * Reverses the effect of usb_buffer_map_sg().
  * Reverses the effect of usb_buffer_map_sg().
  */
  */
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
-		struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+			 struct scatterlist *sg, int n_hw_ents)
 {
 {
 	struct usb_bus		*bus;
 	struct usb_bus		*bus;
 	struct device		*controller;
 	struct device		*controller;
@@ -988,116 +954,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
 			usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 			usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 }
 
 
-static int verify_suspended(struct device *dev, void *unused)
-{
-	if (dev->driver == NULL)
-		return 0;
-	return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
-}
-
-static int usb_generic_suspend(struct device *dev, pm_message_t message)
-{
-	struct usb_interface	*intf;
-	struct usb_driver	*driver;
-	int			status;
-
-	/* USB devices enter SUSPEND state through their hubs, but can be
-	 * marked for FREEZE as soon as their children are already idled.
-	 * But those semantics are useless, so we equate the two (sigh).
-	 */
-	if (dev->driver == &usb_generic_driver) {
-		if (dev->power.power_state.event == message.event)
-			return 0;
-		/* we need to rule out bogus requests through sysfs */
-		status = device_for_each_child(dev, NULL, verify_suspended);
-		if (status)
-			return status;
- 		return usb_suspend_device (to_usb_device(dev));
-	}
-
-	if ((dev->driver == NULL) ||
-	    (dev->driver_data == &usb_generic_driver_data))
-		return 0;
-
-	intf = to_usb_interface(dev);
-	driver = to_usb_driver(dev->driver);
-
-	/* with no hardware, USB interfaces only use FREEZE and ON states */
-	if (!is_active(intf))
-		return 0;
-
-	if (driver->suspend && driver->resume) {
-		status = driver->suspend(intf, message);
-		if (status)
-			dev_err(dev, "%s error %d\n", "suspend", status);
-		else
-			mark_quiesced(intf);
-	} else {
-		// FIXME else if there's no suspend method, disconnect...
-		dev_warn(dev, "no suspend for driver %s?\n", driver->name);
-		mark_quiesced(intf);
-		status = 0;
-	}
-	return status;
-}
-
-static int usb_generic_resume(struct device *dev)
-{
-	struct usb_interface	*intf;
-	struct usb_driver	*driver;
-	struct usb_device	*udev;
-	int			status;
-
-	if (dev->power.power_state.event == PM_EVENT_ON)
-		return 0;
-
-	/* mark things as "on" immediately, no matter what errors crop up */
-	dev->power.power_state.event = PM_EVENT_ON;
-
-	/* devices resume through their hubs */
-	if (dev->driver == &usb_generic_driver) {
-		udev = to_usb_device(dev);
-		if (udev->state == USB_STATE_NOTATTACHED)
-			return 0;
-		return usb_resume_device (to_usb_device(dev));
-	}
-
-	if ((dev->driver == NULL) ||
-	    (dev->driver_data == &usb_generic_driver_data)) {
-		dev->power.power_state.event = PM_EVENT_FREEZE;
-		return 0;
-	}
-
-	intf = to_usb_interface(dev);
-	driver = to_usb_driver(dev->driver);
-
-	udev = interface_to_usbdev(intf);
-	if (udev->state == USB_STATE_NOTATTACHED)
-		return 0;
-
-	/* if driver was suspended, it has a resume method;
-	 * however, sysfs can wrongly mark things as suspended
-	 * (on the "no suspend method" FIXME path above)
-	 */
-	if (driver->resume) {
-		status = driver->resume(intf);
-		if (status) {
-			dev_err(dev, "%s error %d\n", "resume", status);
-			mark_quiesced(intf);
-		}
-	} else
-		dev_warn(dev, "no resume for driver %s?\n", driver->name);
-	return 0;
-}
-
-struct bus_type usb_bus_type = {
-	.name =		"usb",
-	.match =	usb_device_match,
-	.uevent =	usb_uevent,
-	.suspend =	usb_generic_suspend,
-	.resume =	usb_generic_resume,
-};
-
 /* format to disable USB on kernel command line is: nousb */
 /* format to disable USB on kernel command line is: nousb */
 __module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
 __module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
 
 
@@ -1141,7 +997,7 @@ static int __init usb_init(void)
 	retval = usb_hub_init();
 	retval = usb_hub_init();
 	if (retval)
 	if (retval)
 		goto hub_init_failed;
 		goto hub_init_failed;
-	retval = driver_register(&usb_generic_driver);
+	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
 	if (!retval)
 	if (!retval)
 		goto out;
 		goto out;
 
 
@@ -1171,7 +1027,7 @@ static void __exit usb_exit(void)
 	if (nousb)
 	if (nousb)
 		return;
 		return;
 
 
-	driver_unregister(&usb_generic_driver);
+	usb_deregister_device_driver(&usb_generic_driver);
 	usb_major_cleanup();
 	usb_major_cleanup();
 	usbfs_cleanup();
 	usbfs_cleanup();
 	usb_deregister(&usbfs_driver);
 	usb_deregister(&usbfs_driver);
@@ -1201,20 +1057,27 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
 
 EXPORT_SYMBOL(usb_lock_device_for_reset);
 EXPORT_SYMBOL(usb_lock_device_for_reset);
 
 
-EXPORT_SYMBOL(usb_driver_claim_interface);
-EXPORT_SYMBOL(usb_driver_release_interface);
 EXPORT_SYMBOL(usb_find_interface);
 EXPORT_SYMBOL(usb_find_interface);
 EXPORT_SYMBOL(usb_ifnum_to_if);
 EXPORT_SYMBOL(usb_ifnum_to_if);
 EXPORT_SYMBOL(usb_altnum_to_altsetting);
 EXPORT_SYMBOL(usb_altnum_to_altsetting);
 
 
-EXPORT_SYMBOL(usb_reset_device);
-EXPORT_SYMBOL(usb_reset_composite_device);
-
 EXPORT_SYMBOL(__usb_get_extra_descriptor);
 EXPORT_SYMBOL(__usb_get_extra_descriptor);
 
 
 EXPORT_SYMBOL(usb_find_device);
 EXPORT_SYMBOL(usb_find_device);
 EXPORT_SYMBOL(usb_get_current_frame_number);
 EXPORT_SYMBOL(usb_get_current_frame_number);
 
 
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out);
+
 EXPORT_SYMBOL (usb_buffer_alloc);
 EXPORT_SYMBOL (usb_buffer_alloc);
 EXPORT_SYMBOL (usb_buffer_free);
 EXPORT_SYMBOL (usb_buffer_free);
 
 

+ 61 - 15
drivers/usb/core/usb.h

@@ -1,10 +1,10 @@
 /* Functions local to drivers/usb/core/ */
 /* Functions local to drivers/usb/core/ */
 
 
-extern void usb_create_sysfs_dev_files (struct usb_device *dev);
+extern int usb_create_sysfs_dev_files (struct usb_device *dev);
 extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
 extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
-extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
+extern int usb_create_sysfs_intf_files (struct usb_interface *intf);
 extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
 extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
-extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
+extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
 				struct usb_device *udev);
 				struct usb_device *udev);
 extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
 extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
 
 
@@ -20,7 +20,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 
 
 extern void usb_kick_khubd(struct usb_device *dev);
 extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_suspend_root_hub(struct usb_device *hdev);
 extern void usb_resume_root_hub(struct usb_device *dev);
 extern void usb_resume_root_hub(struct usb_device *dev);
 
 
 extern int  usb_hub_init(void);
 extern int  usb_hub_init(void);
@@ -30,28 +29,74 @@ extern void usb_major_cleanup(void);
 extern int usb_host_init(void);
 extern int usb_host_init(void);
 extern void usb_host_cleanup(void);
 extern void usb_host_cleanup(void);
 
 
-extern int usb_suspend_device(struct usb_device *dev);
-extern int usb_resume_device(struct usb_device *dev);
+#ifdef	CONFIG_PM
 
 
-extern struct device_driver usb_generic_driver;
-extern int usb_generic_driver_data;
-extern int usb_device_match(struct device *dev, struct device_driver *drv);
+extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
+extern int usb_resume_both(struct usb_device *udev);
+extern int usb_port_suspend(struct usb_device *dev);
+extern int usb_port_resume(struct usb_device *dev);
+
+#else
+
+#define usb_suspend_both(udev, msg)	0
+static inline int usb_resume_both(struct usb_device *udev)
+{
+	return 0;
+}
+#define usb_port_suspend(dev)		0
+#define usb_port_resume(dev)		0
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
+#define USB_AUTOSUSPEND_DELAY	(HZ*2)
+
+extern void usb_autosuspend_device(struct usb_device *udev, int dec_busy_cnt);
+extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt);
+
+#else
+
+#define usb_autosuspend_device(udev, dec_busy_cnt)	do {} while (0)
+#define usb_autoresume_device(udev, inc_busy_cnt)	0
+
+#endif
+
+extern struct bus_type usb_bus_type;
+extern struct usb_device_driver usb_generic_driver;
+
+/* Here's how we tell apart devices and interfaces.  Luckily there's
+ * no such thing as a platform USB device, so we can steal the use
+ * of the platform_data field. */
+
+static inline int is_usb_device(const struct device *dev)
+{
+	return dev->platform_data == &usb_generic_driver;
+}
+
+/* Do the same for device drivers and interface drivers. */
+
+static inline int is_usb_device_driver(struct device_driver *drv)
+{
+	return container_of(drv, struct usbdrv_wrap, driver)->
+			for_devices;
+}
 
 
 /* Interfaces and their "power state" are owned by usbcore */
 /* Interfaces and their "power state" are owned by usbcore */
 
 
 static inline void mark_active(struct usb_interface *f)
 static inline void mark_active(struct usb_interface *f)
 {
 {
-	f->dev.power.power_state.event = PM_EVENT_ON;
+	f->is_active = 1;
 }
 }
 
 
 static inline void mark_quiesced(struct usb_interface *f)
 static inline void mark_quiesced(struct usb_interface *f)
 {
 {
-	f->dev.power.power_state.event = PM_EVENT_FREEZE;
+	f->is_active = 0;
 }
 }
 
 
-static inline int is_active(struct usb_interface *f)
+static inline int is_active(const struct usb_interface *f)
 {
 {
-	return f->dev.power.power_state.event == PM_EVENT_ON;
+	return f->is_active;
 }
 }
 
 
 
 
@@ -59,9 +104,10 @@ static inline int is_active(struct usb_interface *f)
 extern const char *usbcore_name;
 extern const char *usbcore_name;
 
 
 /* usbfs stuff */
 /* usbfs stuff */
+extern struct mutex usbfs_mutex;
 extern struct usb_driver usbfs_driver;
 extern struct usb_driver usbfs_driver;
-extern struct file_operations usbfs_devices_fops;
-extern struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbfs_devices_fops;
+extern const struct file_operations usbfs_device_file_operations;
 extern void usbfs_conn_disc_event(void);
 extern void usbfs_conn_disc_event(void);
 
 
 extern int usbdev_init(void);
 extern int usbdev_init(void);

+ 15 - 1
drivers/usb/gadget/Kconfig

@@ -26,7 +26,7 @@ config USB_GADGET
 	   you need a low level bus controller driver, and some software
 	   you need a low level bus controller driver, and some software
 	   talking to it.  Peripheral controllers are often discrete silicon,
 	   talking to it.  Peripheral controllers are often discrete silicon,
 	   or are integrated with the CPU in a microcontroller.  The more
 	   or are integrated with the CPU in a microcontroller.  The more
-	   familiar host side controllers have names like like "EHCI", "OHCI",
+	   familiar host side controllers have names like "EHCI", "OHCI",
 	   or "UHCI", and are usually integrated into southbridges on PC
 	   or "UHCI", and are usually integrated into southbridges on PC
 	   motherboards.
 	   motherboards.
 
 
@@ -404,6 +404,20 @@ config USB_G_SERIAL
 	  which includes instructions and a "driver info file" needed to
 	  which includes instructions and a "driver info file" needed to
 	  make MS-Windows work with this driver.
 	  make MS-Windows work with this driver.
 
 
+config USB_MIDI_GADGET
+	tristate "MIDI Gadget (EXPERIMENTAL)"
+	depends on SND && EXPERIMENTAL
+	select SND_RAWMIDI
+	help
+	  The MIDI Gadget acts as a USB Audio device, with one MIDI
+	  input and one MIDI output. These MIDI jacks appear as
+	  a sound "card" in the ALSA sound system. Other MIDI
+	  connections can then be made on the gadget system, using
+	  ALSA's aconnect utility etc.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "g_midi".
+
 
 
 # put drivers that need isochronous transfer support (for audio
 # put drivers that need isochronous transfer support (for audio
 # or video class gadget drivers), or specific hardware, here.
 # or video class gadget drivers), or specific hardware, here.

+ 2 - 0
drivers/usb/gadget/Makefile

@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 g_zero-objs			:= zero.o usbstring.o config.o epautoconf.o
 g_zero-objs			:= zero.o usbstring.o config.o epautoconf.o
 g_ether-objs			:= ether.o usbstring.o config.o epautoconf.o
 g_ether-objs			:= ether.o usbstring.o config.o epautoconf.o
 g_serial-objs			:= serial.o usbstring.o config.o epautoconf.o
 g_serial-objs			:= serial.o usbstring.o config.o epautoconf.o
+g_midi-objs			:= gmidi.o usbstring.o config.o epautoconf.o
 gadgetfs-objs			:= inode.o
 gadgetfs-objs			:= inode.o
 g_file_storage-objs		:= file_storage.o usbstring.o config.o \
 g_file_storage-objs		:= file_storage.o usbstring.o config.o \
 					epautoconf.o
 					epautoconf.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_USB_ETH)		+= g_ether.o
 obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
 obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 obj-$(CONFIG_USB_G_SERIAL)	+= g_serial.o
 obj-$(CONFIG_USB_G_SERIAL)	+= g_serial.o
+obj-$(CONFIG_USB_MIDI_GADGET)	+= g_midi.o
 
 

+ 1 - 1
drivers/usb/gadget/at91_udc.c

@@ -247,7 +247,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
 	return single_open(file, proc_udc_show, PDE(inode)->data);
 	return single_open(file, proc_udc_show, PDE(inode)->data);
 }
 }
 
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_udc_open,
 	.open		= proc_udc_open,
 	.read		= seq_read,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
 	.llseek		= seq_lseek,

+ 2 - 6
drivers/usb/gadget/dummy_hcd.c

@@ -889,11 +889,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
 static void
 static void
 dummy_gadget_release (struct device *dev)
 dummy_gadget_release (struct device *dev)
 {
 {
-#if 0		/* usb_bus_put isn't EXPORTed! */
 	struct dummy	*dum = gadget_dev_to_dummy (dev);
 	struct dummy	*dum = gadget_dev_to_dummy (dev);
 
 
-	usb_bus_put (&dummy_to_hcd (dum)->self);
-#endif
+	usb_put_hcd (dummy_to_hcd (dum));
 }
 }
 
 
 static int dummy_udc_probe (struct platform_device *pdev)
 static int dummy_udc_probe (struct platform_device *pdev)
@@ -915,9 +913,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
 	if (rc < 0)
 	if (rc < 0)
 		return rc;
 		return rc;
 
 
-#if 0		/* usb_bus_get isn't EXPORTed! */
-	usb_bus_get (&dummy_to_hcd (dum)->self);
-#endif
+	usb_get_hcd (dummy_to_hcd (dum));
 
 
 	platform_set_drvdata (pdev, dum);
 	platform_set_drvdata (pdev, dum);
 	device_create_file (&dum->gadget.dev, &dev_attr_function);
 	device_create_file (&dum->gadget.dev, &dev_attr_function);

+ 6 - 3
drivers/usb/gadget/ether.c

@@ -262,7 +262,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
 #define DEV_CONFIG_CDC
 #define DEV_CONFIG_CDC
 #endif
 #endif
 
 
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
 #define DEV_CONFIG_CDC
 #define DEV_CONFIG_CDC
 #endif
 #endif
 
 
@@ -2014,7 +2014,7 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
 static int rndis_control_ack (struct net_device *net)
 static int rndis_control_ack (struct net_device *net)
 {
 {
 	struct eth_dev          *dev = netdev_priv(net);
 	struct eth_dev          *dev = netdev_priv(net);
-	u32                     length;
+	int                     length;
 	struct usb_request      *resp = dev->stat_req;
 	struct usb_request      *resp = dev->stat_req;
 
 
 	/* in case RNDIS calls this after disconnect */
 	/* in case RNDIS calls this after disconnect */
@@ -2230,6 +2230,9 @@ eth_bind (struct usb_gadget *gadget)
 	if (gadget_is_pxa (gadget)) {
 	if (gadget_is_pxa (gadget)) {
 		/* pxa doesn't support altsettings */
 		/* pxa doesn't support altsettings */
 		cdc = 0;
 		cdc = 0;
+	} else if (gadget_is_musbhdrc(gadget)) {
+		/* reduce tx dma overhead by avoiding special cases */
+		zlp = 0;
 	} else if (gadget_is_sh(gadget)) {
 	} else if (gadget_is_sh(gadget)) {
 		/* sh doesn't support multiple interfaces or configs */
 		/* sh doesn't support multiple interfaces or configs */
 		cdc = 0;
 		cdc = 0;
@@ -2564,7 +2567,7 @@ static struct usb_gadget_driver eth_driver = {
 
 
 	.function	= (char *) driver_desc,
 	.function	= (char *) driver_desc,
 	.bind		= eth_bind,
 	.bind		= eth_bind,
-	.unbind		= __exit_p(eth_unbind),
+	.unbind		= eth_unbind,
 
 
 	.setup		= eth_setup,
 	.setup		= eth_setup,
 	.disconnect	= eth_disconnect,
 	.disconnect	= eth_disconnect,

+ 1337 - 0
drivers/usb/gadget/gmidi.c

@@ -0,0 +1,1337 @@
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "gadget_chips.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+#define DRIVER_VERSION "25 Jul 2006"
+
+static const char shortname[] = "g_midi";
+static const char longname[] = "MIDI Gadget";
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both.  These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+
+/*
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
+ */
+static const char *EP_IN_NAME;
+static const char *EP_OUT_NAME;
+
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 256
+
+
+/* This is a gadget, and the IN/OUT naming is from the host's perspective.
+   USB -> OUT endpoint -> rawmidi
+   USB <- IN endpoint  <- rawmidi */
+struct gmidi_in_port {
+	struct gmidi_device* dev;
+	int active;
+	uint8_t cable;		/* cable number << 4 */
+	uint8_t state;
+#define STATE_UNKNOWN	0
+#define STATE_1PARAM	1
+#define STATE_2PARAM_1	2
+#define STATE_2PARAM_2	3
+#define STATE_SYSEX_0	4
+#define STATE_SYSEX_1	5
+#define STATE_SYSEX_2	6
+	uint8_t data[2];
+};
+
+struct gmidi_device {
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+	struct usb_request	*req;		/* for control responses */
+	u8			config;
+	struct usb_ep		*in_ep, *out_ep;
+	struct snd_card 	*card;
+	struct snd_rawmidi	*rmidi;
+	struct snd_rawmidi_substream *in_substream;
+	struct snd_rawmidi_substream *out_substream;
+
+	/* For the moment we only support one port in
+	   each direction, but in_port is kept as a
+	   separate struct so we can have more later. */
+	struct gmidi_in_port	in_port;
+	unsigned long		out_triggered;
+	struct tasklet_struct	tasklet;
+};
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
+
+
+#define xprintk(d,level,fmt,args...) \
+	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG	DBG
+#else
+#define VDBG(dev,fmt,args...) \
+	do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+
+static unsigned buflen = 256;
+static unsigned qlen = 32;
+
+module_param(buflen, uint, S_IRUGO);
+module_param(qlen, uint, S_IRUGO);
+
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM	0x17b3		/* Grey Innovation */
+#define DRIVER_PRODUCT_NUM	0x0004		/* Linux-USB "MIDI Gadget" */
+
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER	25
+#define STRING_PRODUCT		42
+#define STRING_SERIAL		101
+#define STRING_MIDI_GADGET	250
+
+/* We only have the one configuration, it's number 1. */
+#define	GMIDI_CONFIG		1
+
+/* We have two interfaces- AudioControl and MIDIStreaming */
+#define GMIDI_AC_INTERFACE	0
+#define GMIDI_MS_INTERFACE	1
+#define GMIDI_NUM_INTERFACES	2
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+
+/* B.1  Device Descriptor */
+static struct usb_device_descriptor device_desc = {
+	.bLength =		USB_DT_DEVICE_SIZE,
+	.bDescriptorType =	USB_DT_DEVICE,
+	.bcdUSB =		__constant_cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+	.idVendor =		__constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+	.idProduct =		__constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+	.iManufacturer =	STRING_MANUFACTURER,
+	.iProduct =		STRING_PRODUCT,
+	.bNumConfigurations =	1,
+};
+
+/* B.2  Configuration Descriptor */
+static struct usb_config_descriptor config_desc = {
+	.bLength =		USB_DT_CONFIG_SIZE,
+	.bDescriptorType =	USB_DT_CONFIG,
+	/* compute wTotalLength on the fly */
+	.bNumInterfaces =	GMIDI_NUM_INTERFACES,
+	.bConfigurationValue =	GMIDI_CONFIG,
+	.iConfiguration =	STRING_MIDI_GADGET,
+	/*
+	 * FIXME: When embedding this driver in a device,
+	 * these need to be set to reflect the actual
+	 * power properties of the device. Is it selfpowered?
+	 */
+	.bmAttributes =		USB_CONFIG_ATT_ONE,
+	.bMaxPower =		1,
+};
+
+/* B.3.1  Standard AC Interface Descriptor */
+static const struct usb_interface_descriptor ac_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bInterfaceNumber =	GMIDI_AC_INTERFACE,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
+	.iInterface =		STRING_MIDI_GADGET,
+};
+
+/* B.3.2  Class-Specific AC Interface Descriptor */
+static const struct usb_ac_header_descriptor_1 ac_header_desc = {
+	.bLength =		USB_DT_AC_HEADER_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_HEADER,
+	.bcdADC =		__constant_cpu_to_le16(0x0100),
+	.wTotalLength =		USB_DT_AC_HEADER_SIZE(1),
+	.bInCollection =	1,
+	.baInterfaceNr = {
+		[0] =		GMIDI_MS_INTERFACE,
+	}
+};
+
+/* B.4.1  Standard MS Interface Descriptor */
+static const struct usb_interface_descriptor ms_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bInterfaceNumber =	GMIDI_MS_INTERFACE,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_AUDIO,
+	.bInterfaceSubClass =	USB_SUBCLASS_MIDISTREAMING,
+	.iInterface =		STRING_MIDI_GADGET,
+};
+
+/* B.4.2  Class-Specific MS Interface Descriptor */
+static const struct usb_ms_header_descriptor ms_header_desc = {
+	.bLength =		USB_DT_MS_HEADER_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_HEADER,
+	.bcdMSC =		__constant_cpu_to_le16(0x0100),
+	.wTotalLength =		USB_DT_MS_HEADER_SIZE
+				+ 2*USB_DT_MIDI_IN_SIZE
+				+ 2*USB_DT_MIDI_OUT_SIZE(1),
+};
+
+#define JACK_IN_EMB	1
+#define JACK_IN_EXT	2
+#define JACK_OUT_EMB	3
+#define JACK_OUT_EXT	4
+
+/* B.4.3  MIDI IN Jack Descriptors */
+static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
+	.bLength =		USB_DT_MIDI_IN_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_MIDI_IN_JACK,
+	.bJackType =		USB_MS_EMBEDDED,
+	.bJackID =		JACK_IN_EMB,
+};
+
+static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = {
+	.bLength =		USB_DT_MIDI_IN_SIZE,
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_MIDI_IN_JACK,
+	.bJackType =		USB_MS_EXTERNAL,
+	.bJackID =		JACK_IN_EXT,
+};
+
+/* B.4.4  MIDI OUT Jack Descriptors */
+static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = {
+	.bLength =		USB_DT_MIDI_OUT_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_MIDI_OUT_JACK,
+	.bJackType =		USB_MS_EMBEDDED,
+	.bJackID =		JACK_OUT_EMB,
+	.bNrInputPins =		1,
+	.pins = {
+		[0] = {
+			.baSourceID =	JACK_IN_EXT,
+			.baSourcePin =	1,
+		}
+	}
+};
+
+static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = {
+	.bLength =		USB_DT_MIDI_OUT_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype =	USB_MS_MIDI_OUT_JACK,
+	.bJackType =		USB_MS_EXTERNAL,
+	.bJackID =		JACK_OUT_EXT,
+	.bNrInputPins =		1,
+	.pins = {
+		[0] = {
+			.baSourceID =	JACK_IN_EMB,
+			.baSourcePin =	1,
+		}
+	}
+};
+
+/* B.5.1  Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2  Class-specific MS Bulk OUT Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = {
+	.bLength =		USB_DT_MS_ENDPOINT_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	USB_MS_GENERAL,
+	.bNumEmbMIDIJack =	1,
+	.baAssocJackID = {
+		[0] =		JACK_IN_EMB,
+	}
+};
+
+/* B.6.1  Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_AUDIO_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2  Class-specific MS Bulk IN Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = {
+	.bLength =		USB_DT_MS_ENDPOINT_SIZE(1),
+	.bDescriptorType =	USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype =	USB_MS_GENERAL,
+	.bNumEmbMIDIJack =	1,
+	.baAssocJackID = {
+		[0] =		JACK_OUT_EMB,
+	}
+};
+
+static const struct usb_descriptor_header *gmidi_function [] = {
+	(struct usb_descriptor_header *)&ac_interface_desc,
+	(struct usb_descriptor_header *)&ac_header_desc,
+	(struct usb_descriptor_header *)&ms_interface_desc,
+
+	(struct usb_descriptor_header *)&ms_header_desc,
+	(struct usb_descriptor_header *)&jack_in_emb_desc,
+	(struct usb_descriptor_header *)&jack_in_ext_desc,
+	(struct usb_descriptor_header *)&jack_out_emb_desc,
+	(struct usb_descriptor_header *)&jack_out_ext_desc,
+	/* If you add more jacks, update ms_header_desc.wTotalLength */
+
+	(struct usb_descriptor_header *)&bulk_out_desc,
+	(struct usb_descriptor_header *)&ms_out_desc,
+	(struct usb_descriptor_header *)&bulk_in_desc,
+	(struct usb_descriptor_header *)&ms_in_desc,
+	NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = "MIDI Gadget";
+static char serial_number[20];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+	{ STRING_MANUFACTURER, manufacturer, },
+	{ STRING_PRODUCT, product_desc, },
+	{ STRING_SERIAL, serial_number, },
+	{ STRING_MIDI_GADGET, longname, },
+	{  }			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+	.language	= 0x0409,	/* en-us */
+	.strings	= strings,
+};
+
+static int config_buf(struct usb_gadget *gadget,
+		u8 *buf, u8 type, unsigned index)
+{
+	int len;
+
+	/* only one configuration */
+	if (index != 0) {
+		return -EINVAL;
+	}
+	len = usb_gadget_config_buf(&config_desc,
+			buf, USB_BUFSIZ, gmidi_function);
+	if (len < 0) {
+		return len;
+	}
+	((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+	return len;
+}
+
+static struct usb_request* alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (req) {
+		req->length = length;
+		req->buf = kmalloc(length, GFP_ATOMIC);
+		if (!req->buf) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+	return req;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static const uint8_t gmidi_cin_length[] = {
+	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void gmidi_read_data(struct usb_ep *ep, int cable,
+				   uint8_t* data, int length)
+{
+	struct gmidi_device *dev = ep->driver_data;
+	/* cable is ignored, because for now we only have one. */
+
+	if (!dev->out_substream) {
+		/* Nobody is listening - throw it on the floor. */
+		return;
+	}
+	if (!test_bit(dev->out_substream->number, &dev->out_triggered)) {
+		return;
+	}
+	snd_rawmidi_receive(dev->out_substream, data, length);
+}
+
+static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned i;
+	u8 *buf = req->buf;
+
+	for (i = 0; i + 3 < req->actual; i += 4) {
+		if (buf[i] != 0) {
+			int cable = buf[i] >> 4;
+			int length = gmidi_cin_length[buf[i] & 0x0f];
+			gmidi_read_data(ep, cable, &buf[i + 1], length);
+		}
+	}
+}
+
+static void gmidi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct gmidi_device *dev = ep->driver_data;
+	int status = req->status;
+
+	switch (status) {
+	case 0: 			/* normal completion */
+		if (ep == dev->out_ep) {
+			/* we received stuff.
+			   req is queued again, below */
+			gmidi_handle_out_data(ep, req);
+		} else if (ep == dev->in_ep) {
+			/* our transmit completed.
+			   see if there's more to go.
+			   gmidi_transmit eats req, don't queue it again. */
+			gmidi_transmit(dev, req);
+			return;
+		}
+		break;
+
+	/* this endpoint is normally active while we're configured */
+	case -ECONNABORTED: 		/* hardware forced ep reset */
+	case -ECONNRESET:		/* request dequeued */
+	case -ESHUTDOWN:		/* disconnect from host */
+		VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
+				req->actual, req->length);
+		if (ep == dev->out_ep) {
+			gmidi_handle_out_data(ep, req);
+		}
+		free_ep_req(ep, req);
+		return;
+
+	case -EOVERFLOW:		/* buffer overrun on read means that
+					 * we didn't provide a big enough
+					 * buffer.
+					 */
+	default:
+		DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
+				status, req->actual, req->length);
+		break;
+	case -EREMOTEIO:		/* short read */
+		break;
+	}
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		ERROR(dev, "kill %s:  resubmit %d bytes --> %d\n",
+				ep->name, req->length, status);
+		usb_ep_set_halt(ep);
+		/* FIXME recover later ... somehow */
+	}
+}
+
+static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
+{
+	int err = 0;
+	struct usb_request *req;
+	struct usb_ep* ep;
+	unsigned i;
+
+	err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
+	if (err) {
+		ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
+		goto fail;
+	}
+	dev->in_ep->driver_data = dev;
+
+	err = usb_ep_enable(dev->out_ep, &bulk_out_desc);
+	if (err) {
+		ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
+		goto fail;
+	}
+	dev->out_ep->driver_data = dev;
+
+	/* allocate a bunch of read buffers and queue them all at once. */
+	ep = dev->out_ep;
+	for (i = 0; i < qlen && err == 0; i++) {
+		req = alloc_ep_req(ep, buflen);
+		if (req) {
+			req->complete = gmidi_complete;
+			err = usb_ep_queue(ep, req, GFP_ATOMIC);
+			if (err) {
+				DBG(dev, "%s queue req: %d\n", ep->name, err);
+			}
+		} else {
+			err = -ENOMEM;
+		}
+	}
+fail:
+	/* caller is responsible for cleanup on error */
+	return err;
+}
+
+
+static void gmidi_reset_config(struct gmidi_device *dev)
+{
+	if (dev->config == 0) {
+		return;
+	}
+
+	DBG(dev, "reset config\n");
+
+	/* just disable endpoints, forcing completion of pending i/o.
+	 * all our completion handlers free their requests in this case.
+	 */
+	usb_ep_disable(dev->in_ep);
+	usb_ep_disable(dev->out_ep);
+	dev->config = 0;
+}
+
+/* change our operational config.  this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
+{
+	int result = 0;
+	struct usb_gadget *gadget = dev->gadget;
+
+#if 0
+	/* FIXME */
+	/* Hacking this bit out fixes a bug where on receipt of two
+	   USB_REQ_SET_CONFIGURATION messages, we end up with no
+	   buffered OUT requests waiting for data. This is clearly
+	   hiding a bug elsewhere, because if the config didn't
+	   change then we really shouldn't do anything. */
+	/* Having said that, when we do "change" from config 1
+	   to config 1, we at least gmidi_reset_config() which
+	   clears out any requests on endpoints, so it's not like
+	   we leak or anything. */
+	if (number == dev->config) {
+		return 0;
+	}
+#endif
+
+	if (gadget_is_sa1100(gadget) && dev->config) {
+		/* tx fifo is full, but we can't clear it...*/
+		INFO(dev, "can't change configurations\n");
+		return -ESPIPE;
+	}
+	gmidi_reset_config(dev);
+
+	switch (number) {
+	case GMIDI_CONFIG:
+		result = set_gmidi_config(dev, gfp_flags);
+		break;
+	default:
+		result = -EINVAL;
+		/* FALL THROUGH */
+	case 0:
+		return result;
+	}
+
+	if (!result && (!dev->in_ep || !dev->out_ep)) {
+		result = -ENODEV;
+	}
+	if (result) {
+		gmidi_reset_config(dev);
+	} else {
+		char *speed;
+
+		switch (gadget->speed) {
+		case USB_SPEED_LOW:	speed = "low"; break;
+		case USB_SPEED_FULL:	speed = "full"; break;
+		case USB_SPEED_HIGH:	speed = "high"; break;
+		default: 		speed = "?"; break;
+		}
+
+		dev->config = number;
+		INFO(dev, "%s speed\n", speed);
+	}
+	return result;
+}
+
+
+static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length) {
+		DBG((struct gmidi_device *) ep->driver_data,
+				"setup complete --> %d, %d/%d\n",
+				req->status, req->actual, req->length);
+	}
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status).  It's all
+ * housekeeping for the gadget function we're implementing.  Most of
+ * the work is in config-specific setup.
+ */
+static int gmidi_setup(struct usb_gadget *gadget,
+			const struct usb_ctrlrequest *ctrl)
+{
+	struct gmidi_device *dev = get_gadget_data(gadget);
+	struct usb_request *req = dev->req;
+	int value = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	/* usually this stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will reconfigure hardware.
+	 */
+	req->zero = 0;
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != USB_DIR_IN) {
+			goto unknown;
+		}
+		switch (w_value >> 8) {
+
+		case USB_DT_DEVICE:
+			value = min(w_length, (u16) sizeof(device_desc));
+			memcpy(req->buf, &device_desc, value);
+			break;
+		case USB_DT_CONFIG:
+			value = config_buf(gadget, req->buf,
+					w_value >> 8,
+					w_value & 0xff);
+			if (value >= 0) {
+				value = min(w_length, (u16)value);
+			}
+			break;
+
+		case USB_DT_STRING:
+			/* wIndex == language code.
+			 * this driver only handles one language, you can
+			 * add string tables for other languages, using
+			 * any UTF-8 characters
+			 */
+			value = usb_gadget_get_string(&stringtab,
+					w_value & 0xff, req->buf);
+			if (value >= 0) {
+				value = min(w_length, (u16)value);
+			}
+			break;
+		}
+		break;
+
+	/* currently two configs, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0) {
+			goto unknown;
+		}
+		if (gadget->a_hnp_support) {
+			DBG(dev, "HNP available\n");
+		} else if (gadget->a_alt_hnp_support) {
+			DBG(dev, "HNP needs a different root port\n");
+		} else {
+			VDBG(dev, "HNP inactive\n");
+		}
+		spin_lock(&dev->lock);
+		value = gmidi_set_config(dev, w_value, GFP_ATOMIC);
+		spin_unlock(&dev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN) {
+			goto unknown;
+		}
+		*(u8 *)req->buf = dev->config;
+		value = min(w_length, (u16)1);
+		break;
+
+	/* until we add altsetting support, or other interfaces,
+	 * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
+	 * and already killed pending endpoint I/O.
+	 */
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
+			goto unknown;
+		}
+		spin_lock(&dev->lock);
+		if (dev->config && w_index < GMIDI_NUM_INTERFACES
+			&& w_value == 0)
+		{
+			u8 config = dev->config;
+
+			/* resets interface configuration, forgets about
+			 * previous transaction state (queued bufs, etc)
+			 * and re-inits endpoint state (toggle etc)
+			 * no response queued, just zero status == success.
+			 * if we had more than one interface we couldn't
+			 * use this "reset the config" shortcut.
+			 */
+			gmidi_reset_config(dev);
+			gmidi_set_config(dev, config, GFP_ATOMIC);
+			value = 0;
+		}
+		spin_unlock(&dev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
+			goto unknown;
+		}
+		if (!dev->config) {
+			break;
+		}
+		if (w_index >= GMIDI_NUM_INTERFACES) {
+			value = -EDOM;
+			break;
+		}
+		*(u8 *)req->buf = 0;
+		value = min(w_length, (u16)1);
+		break;
+
+	default:
+unknown:
+		VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG(dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			gmidi_setup_complete(gadget->ep0, req);
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void gmidi_disconnect(struct usb_gadget *gadget)
+{
+	struct gmidi_device *dev = get_gadget_data(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	gmidi_reset_config(dev);
+
+	/* a more significant application might have some non-usb
+	 * activities to quiesce here, saving resources like power
+	 * or pushing the notification up a network stack.
+	 */
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* next we may get setup() calls to enumerate new connections;
+	 * or an unbind() during shutdown (including removing module).
+	 */
+}
+
+static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
+{
+	struct gmidi_device *dev = get_gadget_data(gadget);
+	struct snd_card* card;
+
+	DBG(dev, "unbind\n");
+
+	card = dev->card;
+	dev->card = NULL;
+	if (card) {
+		snd_card_free(card);
+	}
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		dev->req->length = USB_BUFSIZ;
+		free_ep_req(gadget->ep0, dev->req);
+	}
+	kfree(dev);
+	set_gadget_data(gadget, NULL);
+}
+
+static int gmidi_snd_free(struct snd_device *device)
+{
+	return 0;
+}
+
+static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
+					uint8_t p1, uint8_t p2, uint8_t p3)
+{
+	unsigned length = req->length;
+
+	uint8_t* buf = (uint8_t*)req->buf + length;
+	buf[0] = p0;
+	buf[1] = p1;
+	buf[2] = p2;
+	buf[3] = p3;
+	req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void gmidi_transmit_byte(struct usb_request* req,
+				struct gmidi_in_port* port, uint8_t b)
+{
+	uint8_t p0 = port->cable;
+
+	if (b >= 0xf8) {
+		gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+	} else if (b >= 0xf0) {
+		switch (b) {
+		case 0xf0:
+			port->data[0] = b;
+			port->state = STATE_SYSEX_1;
+			break;
+		case 0xf1:
+		case 0xf3:
+			port->data[0] = b;
+			port->state = STATE_1PARAM;
+			break;
+		case 0xf2:
+			port->data[0] = b;
+			port->state = STATE_2PARAM_1;
+			break;
+		case 0xf4:
+		case 0xf5:
+			port->state = STATE_UNKNOWN;
+			break;
+		case 0xf6:
+			gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+			port->state = STATE_UNKNOWN;
+			break;
+		case 0xf7:
+			switch (port->state) {
+			case STATE_SYSEX_0:
+				gmidi_transmit_packet(req,
+					p0 | 0x05, 0xf7, 0, 0);
+				break;
+			case STATE_SYSEX_1:
+				gmidi_transmit_packet(req,
+					p0 | 0x06, port->data[0], 0xf7, 0);
+				break;
+			case STATE_SYSEX_2:
+				gmidi_transmit_packet(req,
+					p0 | 0x07, port->data[0],
+					port->data[1], 0xf7);
+				break;
+			}
+			port->state = STATE_UNKNOWN;
+			break;
+		}
+	} else if (b >= 0x80) {
+		port->data[0] = b;
+		if (b >= 0xc0 && b <= 0xdf)
+			port->state = STATE_1PARAM;
+		else
+			port->state = STATE_2PARAM_1;
+	} else { /* b < 0x80 */
+		switch (port->state) {
+		case STATE_1PARAM:
+			if (port->data[0] < 0xf0) {
+				p0 |= port->data[0] >> 4;
+			} else {
+				p0 |= 0x02;
+				port->state = STATE_UNKNOWN;
+			}
+			gmidi_transmit_packet(req, p0, port->data[0], b, 0);
+			break;
+		case STATE_2PARAM_1:
+			port->data[1] = b;
+			port->state = STATE_2PARAM_2;
+			break;
+		case STATE_2PARAM_2:
+			if (port->data[0] < 0xf0) {
+				p0 |= port->data[0] >> 4;
+				port->state = STATE_2PARAM_1;
+			} else {
+				p0 |= 0x03;
+				port->state = STATE_UNKNOWN;
+			}
+			gmidi_transmit_packet(req,
+				p0, port->data[0], port->data[1], b);
+			break;
+		case STATE_SYSEX_0:
+			port->data[0] = b;
+			port->state = STATE_SYSEX_1;
+			break;
+		case STATE_SYSEX_1:
+			port->data[1] = b;
+			port->state = STATE_SYSEX_2;
+			break;
+		case STATE_SYSEX_2:
+			gmidi_transmit_packet(req,
+				p0 | 0x04, port->data[0], port->data[1], b);
+			port->state = STATE_SYSEX_0;
+			break;
+		}
+	}
+}
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
+{
+	struct usb_ep* ep = dev->in_ep;
+	struct gmidi_in_port* port = &dev->in_port;
+
+	if (!ep) {
+		return;
+	}
+	if (!req) {
+		req = alloc_ep_req(ep, buflen);
+	}
+	if (!req) {
+		ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n");
+		return;
+	}
+	req->length = 0;
+	req->complete = gmidi_complete;
+
+	if (port->active) {
+		while (req->length + 3 < buflen) {
+			uint8_t b;
+			if (snd_rawmidi_transmit(dev->in_substream, &b, 1)
+				!= 1)
+			{
+				port->active = 0;
+				break;
+			}
+			gmidi_transmit_byte(req, port, b);
+		}
+	}
+	if (req->length > 0) {
+		usb_ep_queue(ep, req, GFP_ATOMIC);
+	} else {
+		free_ep_req(ep, req);
+	}
+}
+
+static void gmidi_in_tasklet(unsigned long data)
+{
+	struct gmidi_device* dev = (struct gmidi_device*)data;
+
+	gmidi_transmit(dev, NULL);
+}
+
+static int gmidi_in_open(struct snd_rawmidi_substream *substream)
+{
+	struct gmidi_device* dev = substream->rmidi->private_data;
+
+	VDBG(dev, "gmidi_in_open\n");
+	dev->in_substream = substream;
+	dev->in_port.state = STATE_UNKNOWN;
+	return 0;
+}
+
+static int gmidi_in_close(struct snd_rawmidi_substream *substream)
+{
+	VDBG(dev, "gmidi_in_close\n");
+	return 0;
+}
+
+static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct gmidi_device* dev = substream->rmidi->private_data;
+
+	VDBG(dev, "gmidi_in_trigger %d\n", up);
+	dev->in_port.active = up;
+	if (up) {
+		tasklet_hi_schedule(&dev->tasklet);
+	}
+}
+
+static int gmidi_out_open(struct snd_rawmidi_substream *substream)
+{
+	struct gmidi_device* dev = substream->rmidi->private_data;
+
+	VDBG(dev, "gmidi_out_open\n");
+	dev->out_substream = substream;
+	return 0;
+}
+
+static int gmidi_out_close(struct snd_rawmidi_substream *substream)
+{
+	VDBG(dev, "gmidi_out_close\n");
+	return 0;
+}
+
+static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct gmidi_device* dev = substream->rmidi->private_data;
+
+	VDBG(dev, "gmidi_out_trigger %d\n", up);
+	if (up) {
+		set_bit(substream->number, &dev->out_triggered);
+	} else {
+		clear_bit(substream->number, &dev->out_triggered);
+	}
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+	.open = gmidi_in_open,
+	.close = gmidi_in_close,
+	.trigger = gmidi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+	.open = gmidi_out_open,
+	.close = gmidi_out_close,
+	.trigger = gmidi_out_trigger
+};
+
+/* register as a sound "card" */
+static int gmidi_register_card(struct gmidi_device *dev)
+{
+	struct snd_card *card;
+	struct snd_rawmidi *rmidi;
+	int err;
+	int out_ports = 1;
+	int in_ports = 1;
+	static struct snd_device_ops ops = {
+		.dev_free = gmidi_snd_free,
+	};
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (!card) {
+		ERROR(dev, "snd_card_new failed\n");
+		err = -ENOMEM;
+		goto fail;
+	}
+	dev->card = card;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops);
+	if (err < 0) {
+		ERROR(dev, "snd_device_new failed: error %d\n", err);
+		goto fail;
+	}
+
+	strcpy(card->driver, longname);
+	strcpy(card->longname, longname);
+	strcpy(card->shortname, shortname);
+
+	/* Set up rawmidi */
+	dev->in_port.dev = dev;
+	dev->in_port.active = 0;
+	snd_component_add(card, "MIDI");
+	err = snd_rawmidi_new(card, "USB MIDI Gadget", 0,
+			      out_ports, in_ports, &rmidi);
+	if (err < 0) {
+		ERROR(dev, "snd_rawmidi_new failed: error %d\n", err);
+		goto fail;
+	}
+	dev->rmidi = rmidi;
+	strcpy(rmidi->name, card->shortname);
+	rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+			    SNDRV_RAWMIDI_INFO_INPUT |
+			    SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = dev;
+
+	/* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+	   It's an upside-down world being a gadget. */
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+	snd_card_set_dev(card, &dev->gadget->dev);
+
+	/* register it - we're ready to go */
+	err = snd_card_register(card);
+	if (err < 0) {
+		ERROR(dev, "snd_card_register failed\n");
+		goto fail;
+	}
+
+	VDBG(dev, "gmidi_register_card finished ok\n");
+	return 0;
+
+fail:
+	if (dev->card) {
+		snd_card_free(dev->card);
+		dev->card = NULL;
+	}
+	return err;
+}
+
+/*
+ * Creates an output endpoint, and initializes output ports.
+ */
+static int __devinit gmidi_bind(struct usb_gadget *gadget)
+{
+	struct gmidi_device *dev;
+	struct usb_ep *in_ep, *out_ep;
+	int gcnum, err = 0;
+
+	/* support optional vendor/distro customization */
+	if (idVendor) {
+		if (!idProduct) {
+			printk(KERN_ERR "idVendor needs idProduct!\n");
+			return -ENODEV;
+		}
+		device_desc.idVendor = cpu_to_le16(idVendor);
+		device_desc.idProduct = cpu_to_le16(idProduct);
+		if (bcdDevice) {
+			device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+		}
+	}
+	if (iManufacturer) {
+		strlcpy(manufacturer, iManufacturer, sizeof(manufacturer));
+	} else {
+		snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+			system_utsname.sysname, system_utsname.release,
+			gadget->name);
+	}
+	if (iProduct) {
+		strlcpy(product_desc, iProduct, sizeof(product_desc));
+	}
+	if (iSerialNumber) {
+		device_desc.iSerialNumber = STRING_SERIAL,
+		strlcpy(serial_number, iSerialNumber, sizeof(serial_number));
+	}
+
+	/* Bulk-only drivers like this one SHOULD be able to
+	 * autoconfigure on any sane usb controller driver,
+	 * but there may also be important quirks to address.
+	 */
+	usb_ep_autoconfig_reset(gadget);
+	in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+	if (!in_ep) {
+autoconf_fail:
+		printk(KERN_ERR "%s: can't autoconfigure on %s\n",
+			shortname, gadget->name);
+		return -ENODEV;
+	}
+	EP_IN_NAME = in_ep->name;
+	in_ep->driver_data = in_ep;	/* claim */
+
+	out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+	if (!out_ep) {
+		goto autoconf_fail;
+	}
+	EP_OUT_NAME = out_ep->name;
+	out_ep->driver_data = out_ep;	/* claim */
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0) {
+		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+	} else {
+		/* gmidi is so simple (no altsettings) that
+		 * it SHOULD NOT have problems with bulk-capable hardware.
+		 * so warn about unrecognized controllers, don't panic.
+		 */
+		printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+			shortname, gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+	}
+
+
+	/* ok, we made sense of the hardware ... */
+	dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
+	if (!dev) {
+		return -ENOMEM;
+	}
+	spin_lock_init(&dev->lock);
+	dev->gadget = gadget;
+	dev->in_ep = in_ep;
+	dev->out_ep = out_ep;
+	set_gadget_data(gadget, dev);
+	tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!dev->req) {
+		err = -ENOMEM;
+		goto fail;
+	}
+	dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ,
+				&dev->req->dma, GFP_KERNEL);
+	if (!dev->req->buf) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	dev->req->complete = gmidi_setup_complete;
+
+	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+	gadget->ep0->driver_data = dev;
+
+	INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+	INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
+		EP_OUT_NAME, EP_IN_NAME);
+
+	/* register as an ALSA sound card */
+	err = gmidi_register_card(dev);
+	if (err < 0) {
+		goto fail;
+	}
+
+	VDBG(dev, "gmidi_bind finished ok\n");
+	return 0;
+
+fail:
+	gmidi_unbind(gadget);
+	return err;
+}
+
+
+static void gmidi_suspend(struct usb_gadget *gadget)
+{
+	struct gmidi_device *dev = get_gadget_data(gadget);
+
+	if (gadget->speed == USB_SPEED_UNKNOWN) {
+		return;
+	}
+
+	DBG(dev, "suspend\n");
+}
+
+static void gmidi_resume(struct usb_gadget *gadget)
+{
+	struct gmidi_device *dev = get_gadget_data(gadget);
+
+	DBG(dev, "resume\n");
+}
+
+
+static struct usb_gadget_driver gmidi_driver = {
+	.speed		= USB_SPEED_FULL,
+	.function	= (char *)longname,
+	.bind		= gmidi_bind,
+	.unbind		= __exit_p(gmidi_unbind),
+
+	.setup		= gmidi_setup,
+	.disconnect	= gmidi_disconnect,
+
+	.suspend	= gmidi_suspend,
+	.resume		= gmidi_resume,
+
+	.driver 	= {
+		.name		= (char *)shortname,
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init gmidi_init(void)
+{
+	return usb_gadget_register_driver(&gmidi_driver);
+}
+module_init(gmidi_init);
+
+static void __exit gmidi_cleanup(void)
+{
+	usb_gadget_unregister_driver(&gmidi_driver);
+}
+module_exit(gmidi_cleanup);
+

+ 48 - 12
drivers/usb/gadget/inode.c

@@ -32,6 +32,7 @@
 #include <linux/compiler.h>
 #include <linux/compiler.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/poll.h>
 
 
 #include <linux/device.h>
 #include <linux/device.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
@@ -222,7 +223,6 @@ static void put_ep (struct ep_data *data)
 	/* needs no more cleanup */
 	/* needs no more cleanup */
 	BUG_ON (!list_empty (&data->epfiles));
 	BUG_ON (!list_empty (&data->epfiles));
 	BUG_ON (waitqueue_active (&data->wait));
 	BUG_ON (waitqueue_active (&data->wait));
-	BUG_ON (down_trylock (&data->lock) != 0);
 	kfree (data);
 	kfree (data);
 }
 }
 
 
@@ -477,6 +477,10 @@ static int
 ep_release (struct inode *inode, struct file *fd)
 ep_release (struct inode *inode, struct file *fd)
 {
 {
 	struct ep_data		*data = fd->private_data;
 	struct ep_data		*data = fd->private_data;
+	int value;
+
+	if ((value = down_interruptible(&data->lock)) < 0)
+		return value;
 
 
 	/* clean up if this can be reopened */
 	/* clean up if this can be reopened */
 	if (data->state != STATE_EP_UNBOUND) {
 	if (data->state != STATE_EP_UNBOUND) {
@@ -485,6 +489,7 @@ ep_release (struct inode *inode, struct file *fd)
 		data->hs_desc.bDescriptorType = 0;
 		data->hs_desc.bDescriptorType = 0;
 		usb_ep_disable(data->ep);
 		usb_ep_disable(data->ep);
 	}
 	}
+	up (&data->lock);
 	put_ep (data);
 	put_ep (data);
 	return 0;
 	return 0;
 }
 }
@@ -709,7 +714,7 @@ ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
 /*----------------------------------------------------------------------*/
 /*----------------------------------------------------------------------*/
 
 
 /* used after endpoint configuration */
 /* used after endpoint configuration */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.llseek =	no_llseek,
 
 
@@ -741,7 +746,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 	struct ep_data		*data = fd->private_data;
 	struct ep_data		*data = fd->private_data;
 	struct usb_ep		*ep;
 	struct usb_ep		*ep;
 	u32			tag;
 	u32			tag;
-	int			value;
+	int			value, length = len;
 
 
 	if ((value = down_interruptible (&data->lock)) < 0)
 	if ((value = down_interruptible (&data->lock)) < 0)
 		return value;
 		return value;
@@ -792,7 +797,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 			goto fail0;
 			goto fail0;
 		}
 		}
 	}
 	}
-	value = len;
 
 
 	spin_lock_irq (&data->dev->lock);
 	spin_lock_irq (&data->dev->lock);
 	if (data->dev->state == STATE_DEV_UNBOUND) {
 	if (data->dev->state == STATE_DEV_UNBOUND) {
@@ -822,8 +826,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 				data->name);
 				data->name);
 		data->state = STATE_EP_DEFER_ENABLE;
 		data->state = STATE_EP_DEFER_ENABLE;
 	}
 	}
-	if (value == 0)
+	if (value == 0) {
 		fd->f_op = &ep_io_operations;
 		fd->f_op = &ep_io_operations;
+		value = length;
+	}
 gone:
 gone:
 	spin_unlock_irq (&data->dev->lock);
 	spin_unlock_irq (&data->dev->lock);
 	if (value < 0) {
 	if (value < 0) {
@@ -867,7 +873,7 @@ ep_open (struct inode *inode, struct file *fd)
 }
 }
 
 
 /* used before endpoint configuration */
 /* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.llseek =	no_llseek,
 
 
@@ -1009,7 +1015,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 			else {
 			else {
 				len = min (len, (size_t)dev->req->actual);
 				len = min (len, (size_t)dev->req->actual);
 // FIXME don't call this with the spinlock held ...
 // FIXME don't call this with the spinlock held ...
-				if (copy_to_user (buf, &dev->req->buf, len))
+				if (copy_to_user (buf, dev->req->buf, len))
 					retval = -EFAULT;
 					retval = -EFAULT;
 				clean_req (dev->gadget->ep0, dev->req);
 				clean_req (dev->gadget->ep0, dev->req);
 				/* NOTE userspace can't yet choose to stall */
 				/* NOTE userspace can't yet choose to stall */
@@ -1229,6 +1235,35 @@ dev_release (struct inode *inode, struct file *fd)
 	return 0;
 	return 0;
 }
 }
 
 
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+       struct dev_data         *dev = fd->private_data;
+       int                     mask = 0;
+
+       poll_wait(fd, &dev->wait, wait);
+
+       spin_lock_irq (&dev->lock);
+
+       /* report fd mode change before acting on it */
+       if (dev->setup_abort) {
+               dev->setup_abort = 0;
+               mask = POLLHUP;
+               goto out;
+       }
+
+       if (dev->state == STATE_SETUP) {
+               if (dev->setup_in || dev->setup_can_stall)
+                       mask = POLLOUT;
+       } else {
+               if (dev->ev_next != 0)
+                       mask = POLLIN;
+       }
+out:
+       spin_unlock_irq(&dev->lock);
+       return mask;
+}
+
 static int dev_ioctl (struct inode *inode, struct file *fd,
 static int dev_ioctl (struct inode *inode, struct file *fd,
 		unsigned code, unsigned long value)
 		unsigned code, unsigned long value)
 {
 {
@@ -1241,14 +1276,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
 }
 }
 
 
 /* used after device configuration */
 /* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.llseek =	no_llseek,
 
 
 	.read =		ep0_read,
 	.read =		ep0_read,
 	.write =	ep0_write,
 	.write =	ep0_write,
 	.fasync =	ep0_fasync,
 	.fasync =	ep0_fasync,
-	// .poll =	ep0_poll,
+	.poll =		ep0_poll,
 	.ioctl =	dev_ioctl,
 	.ioctl =	dev_ioctl,
 	.release =	dev_release,
 	.release =	dev_release,
 };
 };
@@ -1696,16 +1731,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
 {
 {
 	struct dev_data		*dev = get_gadget_data (gadget);
 	struct dev_data		*dev = get_gadget_data (gadget);
 
 
+	spin_lock (&dev->lock);
 	if (dev->state == STATE_UNCONNECTED) {
 	if (dev->state == STATE_UNCONNECTED) {
 		DBG (dev, "already unconnected\n");
 		DBG (dev, "already unconnected\n");
-		return;
+		goto exit;
 	}
 	}
 	dev->state = STATE_UNCONNECTED;
 	dev->state = STATE_UNCONNECTED;
 
 
 	INFO (dev, "disconnected\n");
 	INFO (dev, "disconnected\n");
-	spin_lock (&dev->lock);
 	next_event (dev, GADGETFS_DISCONNECT);
 	next_event (dev, GADGETFS_DISCONNECT);
 	ep0_readable (dev);
 	ep0_readable (dev);
+exit:
 	spin_unlock (&dev->lock);
 	spin_unlock (&dev->lock);
 }
 }
 
 
@@ -1922,7 +1958,7 @@ dev_open (struct inode *inode, struct file *fd)
 	return value;
 	return value;
 }
 }
 
 
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 	.llseek =	no_llseek,
 
 

+ 88 - 68
drivers/usb/gadget/net2280.c

@@ -2,7 +2,7 @@
  * Driver for the PLX NET2280 USB device controller.
  * Driver for the PLX NET2280 USB device controller.
  * Specs and errata are available from <http://www.plxtech.com>.
  * Specs and errata are available from <http://www.plxtech.com>.
  *
  *
- * PLX Technology Inc. (formerly NetChip Technology) supported the 
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
  * development of this driver.
  * development of this driver.
  *
  *
  *
  *
@@ -26,7 +26,8 @@
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003-2005 PLX Technology, Inc.
  * Copyright (C) 2003-2005 PLX Technology, Inc.
  *
  *
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ *	with 2282 chip
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
 static const char driver_desc [] = DRIVER_DESC;
 static const char driver_desc [] = DRIVER_DESC;
 
 
 static const char ep0name [] = "ep0";
 static const char ep0name [] = "ep0";
-static const char *ep_name [] = {
+static const char *const ep_name [] = {
 	ep0name,
 	ep0name,
 	"ep-a", "ep-b", "ep-c", "ep-d",
 	"ep-a", "ep-b", "ep-c", "ep-d",
 	"ep-e", "ep-f",
 	"ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	if (!ep->is_in)
 	if (!ep->is_in)
 		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
 		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
 	else if (dev->pdev->device != 0x2280) {
 	else if (dev->pdev->device != 0x2280) {
-		/* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
+		/* Added for 2282, Don't use nak packets on an in endpoint,
+		 * this was ignored on 2280
+		 */
 		writel ((1 << CLEAR_NAK_OUT_PACKETS)
 		writel ((1 << CLEAR_NAK_OUT_PACKETS)
 			| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
 			| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
 	}
 	}
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
 	return -ETIMEDOUT;
 	return -ETIMEDOUT;
 }
 }
 
 
-static struct usb_ep_ops net2280_ep_ops;
+static const struct usb_ep_ops net2280_ep_ops;
 
 
 static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
 static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
 {
 {
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-#undef USE_KMALLOC
-
-/* many common platforms have dma-coherent caches, which means that it's
- * safe to use kmalloc() memory for all i/o buffers without using any
- * cache flushing calls.  (unless you're trying to share cache lines
- * between dma and non-dma activities, which is a slow idea in any case.)
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
  *
  *
- * other platforms need more care, with 2.5 having a moderately general
- * solution (which falls down for allocations smaller than one page)
- * that improves significantly on the 2.4 PCI allocators by removing
- * the restriction that memory never be freed in_interrupt().
+ * NOTE: the dma_*_coherent() API calls suck.  Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled:  alloc is safe, free is not.
+ * We currently work around (b), but not (a).
  */
  */
-#if	defined(CONFIG_X86)
-#define USE_KMALLOC
-
-#elif	defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
-#define USE_KMALLOC
 
 
-#elif	defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
-#define USE_KMALLOC
-
-/* FIXME there are other cases, including an x86-64 one ...  */
-#endif
-
-/* allocating buffers this way eliminates dma mapping overhead, which
- * on some platforms will mean eliminating a per-io buffer copy.  with
- * some kinds of system caches, further tweaks may still be needed.
- */
 static void *
 static void *
 net2280_alloc_buffer (
 net2280_alloc_buffer (
 	struct usb_ep		*_ep,
 	struct usb_ep		*_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
 		return NULL;
 		return NULL;
 	*dma = DMA_ADDR_INVALID;
 	*dma = DMA_ADDR_INVALID;
 
 
-#if	defined(USE_KMALLOC)
-	retval = kmalloc(bytes, gfp_flags);
-	if (retval)
-		*dma = virt_to_phys(retval);
-#else
-	if (ep->dma) {
-		/* the main problem with this call is that it wastes memory
-		 * on typical 1/N page allocations: it allocates 1-N pages.
-		 */
-#warning Using dma_alloc_coherent even with buffers smaller than a page.
+	if (ep->dma)
 		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
 		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
 				bytes, dma, gfp_flags);
 				bytes, dma, gfp_flags);
-	} else
+	else
 		retval = kmalloc(bytes, gfp_flags);
 		retval = kmalloc(bytes, gfp_flags);
-#endif
 	return retval;
 	return retval;
 }
 }
 
 
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+	struct list_head	list;
+	struct device		*dev;
+	unsigned		bytes;
+	dma_addr_t		dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+	spin_lock_irq(&buflock);
+	while (!list_empty(&buffers)) {
+		struct free_record	*buf;
+
+		buf = list_entry(buffers.next, struct free_record, list);
+		list_del(&buf->list);
+		spin_unlock_irq(&buflock);
+
+		dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+		spin_lock_irq(&buflock);
+	}
+	spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
 static void
 static void
 net2280_free_buffer (
 net2280_free_buffer (
 	struct usb_ep *_ep,
 	struct usb_ep *_ep,
-	void *buf,
+	void *address,
 	dma_addr_t dma,
 	dma_addr_t dma,
 	unsigned bytes
 	unsigned bytes
 ) {
 ) {
 	/* free memory into the right allocator */
 	/* free memory into the right allocator */
-#ifndef	USE_KMALLOC
 	if (dma != DMA_ADDR_INVALID) {
 	if (dma != DMA_ADDR_INVALID) {
 		struct net2280_ep	*ep;
 		struct net2280_ep	*ep;
+		struct free_record	*buf = address;
+		unsigned long		flags;
 
 
 		ep = container_of(_ep, struct net2280_ep, ep);
 		ep = container_of(_ep, struct net2280_ep, ep);
 		if (!_ep)
 		if (!_ep)
 			return;
 			return;
-		dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
+
+		ep = container_of (_ep, struct net2280_ep, ep);
+		buf->dev = &ep->dev->pdev->dev;
+		buf->bytes = bytes;
+		buf->dma = dma;
+
+		spin_lock_irqsave(&buflock, flags);
+		list_add_tail(&buf->list, &buffers);
+		tasklet_schedule(&deferred_free);
+		spin_unlock_irqrestore(&buflock, flags);
 	} else
 	} else
-#endif
-		kfree (buf);
+		kfree (address);
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
 	 */
 	 */
 	if (ep->is_in)
 	if (ep->is_in)
 		dmacount |= (1 << DMA_DIRECTION);
 		dmacount |= (1 << DMA_DIRECTION);
-	if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280)
+	if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
+			|| ep->dev->pdev->device != 0x2280)
 		dmacount |= (1 << END_OF_CHAIN);
 		dmacount |= (1 << END_OF_CHAIN);
 
 
 	req->valid = valid;
 	req->valid = valid;
@@ -812,7 +825,7 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
 
 
 	/* previous OUT packet might have been short */
 	/* previous OUT packet might have been short */
 	if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
 	if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
-		 		& (1 << NAK_OUT_PACKETS)) != 0) {
+				& (1 << NAK_OUT_PACKETS)) != 0) {
 		writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
 		writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
 			&ep->regs->ep_stat);
 			&ep->regs->ep_stat);
 
 
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
 	(void) readl (&ep->regs->ep_rsp);
 	(void) readl (&ep->regs->ep_rsp);
 }
 }
 
 
-static struct usb_ep_ops net2280_ep_ops = {
+static const struct usb_ep_ops net2280_ep_ops = {
 	.enable		= net2280_enable,
 	.enable		= net2280_enable,
 	.disable	= net2280_disable,
 	.disable	= net2280_disable,
 
 
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
 	}
 	}
 
 
 	/* Indexed Registers */
 	/* Indexed Registers */
-		// none yet 
+		// none yet
 
 
 	/* Statistics */
 	/* Statistics */
 	t = scnprintf (next, size, "\nirqs:  ");
 	t = scnprintf (next, size, "\nirqs:  ");
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
 				({ char *val;
 				({ char *val;
 				 switch (d->bmAttributes & 0x03) {
 				 switch (d->bmAttributes & 0x03) {
 				 case USB_ENDPOINT_XFER_BULK:
 				 case USB_ENDPOINT_XFER_BULK:
-				 	val = "bulk"; break;
+					val = "bulk"; break;
 				 case USB_ENDPOINT_XFER_INT:
 				 case USB_ENDPOINT_XFER_INT:
-				 	val = "intr"; break;
+					val = "intr"; break;
 				 default:
 				 default:
-				 	val = "iso"; break;
+					val = "iso"; break;
 				 }; val; }),
 				 }; val; }),
 				le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
 				le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
 				ep->dma ? "dma" : "pio", ep->fifo_size
 				ep->dma ? "dma" : "pio", ep->fifo_size
@@ -1808,8 +1821,8 @@ extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
  * net2280_set_fifo_mode - change allocation of fifo buffers
  * net2280_set_fifo_mode - change allocation of fifo buffers
  * @gadget: access to the net2280 device that will be updated
  * @gadget: access to the net2280 device that will be updated
  * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
  * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- * 	1 for two 2kB buffers (ep-a and ep-b only);
- * 	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ *	1 for two 2kB buffers (ep-a and ep-b only);
+ *	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
  *
  *
  * returns zero on success, else negative errno.  when this succeeds,
  * returns zero on success, else negative errno.  when this succeeds,
  * the contents of gadget->ep_list may have changed.
  * the contents of gadget->ep_list may have changed.
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
 				req->td->dmacount = 0;
 				req->td->dmacount = 0;
 				t = readl (&ep->regs->ep_avail);
 				t = readl (&ep->regs->ep_avail);
 				dma_done (ep, req, count,
 				dma_done (ep, req, count,
-					(ep->out_overflow || t) ? -EOVERFLOW : 0);
+					(ep->out_overflow || t)
+						? -EOVERFLOW : 0);
 			}
 			}
 
 
 			/* also flush to prevent erratum 0106 trouble */
 			/* also flush to prevent erratum 0106 trouble */
@@ -2411,7 +2425,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
 			, &ep->regs->ep_stat);
 			, &ep->regs->ep_stat);
 		u.raw [0] = readl (&dev->usb->setup0123);
 		u.raw [0] = readl (&dev->usb->setup0123);
 		u.raw [1] = readl (&dev->usb->setup4567);
 		u.raw [1] = readl (&dev->usb->setup4567);
-		
+
 		cpu_to_le32s (&u.raw [0]);
 		cpu_to_le32s (&u.raw [0]);
 		cpu_to_le32s (&u.raw [1]);
 		cpu_to_le32s (&u.raw [1]);
 
 
@@ -2578,14 +2592,16 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
 
 
 	/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
 	/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
 	 * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
 	 * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
-	 * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT 
+	 * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
 	 * only indicates a change in the reset state).
 	 * only indicates a change in the reset state).
 	 */
 	 */
 	if (stat & tmp) {
 	if (stat & tmp) {
 		writel (tmp, &dev->regs->irqstat1);
 		writel (tmp, &dev->regs->irqstat1);
-		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && 
-				((readl (&dev->usb->usbstat) & mask) == 0))
-				|| ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) 
+		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+					&& ((readl (&dev->usb->usbstat) & mask)
+							== 0))
+				|| ((readl (&dev->usb->usbctl)
+					& (1 << VBUS_PIN)) == 0)
 			    ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
 			    ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
 			DEBUG (dev, "disconnect %s\n",
 			DEBUG (dev, "disconnect %s\n",
 					dev->driver->driver.name);
 					dev->driver->driver.name);
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 
 
 	/* now all the pci goodies ... */
 	/* now all the pci goodies ... */
 	if (pci_enable_device (pdev) < 0) {
 	if (pci_enable_device (pdev) < 0) {
-   	        retval = -ENODEV;
+	        retval = -ENODEV;
 		goto done;
 		goto done;
 	}
 	}
 	dev->enabled = 1;
 	dev->enabled = 1;
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 	}
 	dev->region = 1;
 	dev->region = 1;
 
 
+	/* FIXME provide firmware download interface to put
+	 * 8051 code into the chip, e.g. to turn on PCI PM.
+	 */
+
 	base = ioremap_nocache (resource, len);
 	base = ioremap_nocache (resource, len);
 	if (base == NULL) {
 	if (base == NULL) {
 		DEBUG (dev, "can't map memory\n");
 		DEBUG (dev, "can't map memory\n");
@@ -2984,16 +3004,16 @@ static void net2280_shutdown (struct pci_dev *pdev)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static struct pci_device_id pci_ids [] = { {
-	.class = 	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
-	.class_mask = 	~0,
+static const struct pci_device_id pci_ids [] = { {
+	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask =	~0,
 	.vendor =	0x17cc,
 	.vendor =	0x17cc,
 	.device =	0x2280,
 	.device =	0x2280,
 	.subvendor =	PCI_ANY_ID,
 	.subvendor =	PCI_ANY_ID,
 	.subdevice =	PCI_ANY_ID,
 	.subdevice =	PCI_ANY_ID,
 }, {
 }, {
-	.class = 	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
-	.class_mask = 	~0,
+	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask =	~0,
 	.vendor =	0x17cc,
 	.vendor =	0x17cc,
 	.device =	0x2282,
 	.device =	0x2282,
 	.subvendor =	PCI_ANY_ID,
 	.subvendor =	PCI_ANY_ID,

+ 2 - 2
drivers/usb/gadget/omap_udc.c

@@ -40,7 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb_gadget.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
 
 
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
@@ -2437,7 +2437,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
 	return single_open(file, proc_udc_show, NULL);
 	return single_open(file, proc_udc_show, NULL);
 }
 }
 
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_udc_open,
 	.open		= proc_udc_open,
 	.read		= seq_read,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
 	.llseek		= seq_lseek,

+ 68 - 2
drivers/usb/gadget/pxa2xx_udc.c

@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
 static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
 static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
 static void nuke (struct pxa2xx_ep *, int status);
 static void nuke (struct pxa2xx_ep *, int status);
 
 
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_vbus)
+		return pxa_gpio_get(mach->gpio_vbus);
+	if (mach->udc_is_connected)
+		return mach->udc_is_connected();
+	return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_pullup)
+		pxa_gpio_set(mach->gpio_pullup, 0);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_pullup)
+		pxa_gpio_set(mach->gpio_pullup, 1);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
 static void pio_irq_enable(int bEndpointAddress)
 static void pio_irq_enable(int bEndpointAddress)
 {
 {
         bEndpointAddress &= 0xf;
         bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
 
 
 #endif
 #endif
 
 
+static irqreturn_t
+udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct pxa2xx_udc	*dev = _dev;
+	int			vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+	pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+	return IRQ_HANDLED;
+}
+
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
 {
 	struct pxa2xx_udc *dev = &memory;
 	struct pxa2xx_udc *dev = &memory;
-	int retval, out_dma = 1;
+	int retval, out_dma = 1, vbus_irq;
 	u32 chiprev;
 	u32 chiprev;
 
 
 	/* insist on Intel/ARM/XScale */
 	/* insist on Intel/ARM/XScale */
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 	/* other non-static parts of init */
 	/* other non-static parts of init */
 	dev->dev = &pdev->dev;
 	dev->dev = &pdev->dev;
 	dev->mach = pdev->dev.platform_data;
 	dev->mach = pdev->dev.platform_data;
+	if (dev->mach->gpio_vbus) {
+		vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+		pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+				| GPIO_IN);
+		set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+	} else
+		vbus_irq = 0;
+	if (dev->mach->gpio_pullup)
+		pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+				| GPIO_OUT | GPIO_DFLT_LOW);
 
 
 	init_timer(&dev->timer);
 	init_timer(&dev->timer);
 	dev->timer.function = udc_watchdog;
 	dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 		HEX_DISPLAY(dev->stats.irqs);
 		HEX_DISPLAY(dev->stats.irqs);
 		LUB_DISC_BLNK_LED &= 0xff;
 		LUB_DISC_BLNK_LED &= 0xff;
 #endif
 #endif
-	}
+	} else
 #endif
 #endif
+	if (vbus_irq) {
+		retval = request_irq(vbus_irq, udc_vbus_irq,
+				SA_INTERRUPT | SA_SAMPLE_RANDOM,
+				driver_name, dev);
+		if (retval != 0) {
+			printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+				driver_name, vbus_irq, retval);
+			free_irq(IRQ_USB, dev);
+			return -EBUSY;
+		}
+	}
 	create_proc_files();
 	create_proc_files();
 
 
 	return 0;
 	return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
 		free_irq(LUBBOCK_USB_IRQ, dev);
 		free_irq(LUBBOCK_USB_IRQ, dev);
 	}
 	}
 #endif
 #endif
+	if (dev->mach->gpio_vbus)
+		free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
 	platform_set_drvdata(pdev, NULL);
 	platform_set_drvdata(pdev, NULL);
 	the_controller = NULL;
 	the_controller = NULL;
 	return 0;
 	return 0;

+ 8 - 16
drivers/usb/gadget/pxa2xx_udc.h

@@ -177,27 +177,19 @@ struct pxa2xx_udc {
 
 
 static struct pxa2xx_udc *the_controller;
 static struct pxa2xx_udc *the_controller;
 
 
-/* one GPIO should be used to detect VBUS from the host */
-static inline int is_vbus_present(void)
+static inline int pxa_gpio_get(unsigned gpio)
 {
 {
-	if (!the_controller->mach->udc_is_connected)
-		return 1;
-	return the_controller->mach->udc_is_connected();
+	return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
 }
 }
 
 
-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
-static inline void pullup_off(void)
+static inline void pxa_gpio_set(unsigned gpio, int is_on)
 {
 {
-	if (!the_controller->mach->udc_command)
-		return;
-	the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
-}
+	int mask = GPIO_bit(gpio);
 
 
-static inline void pullup_on(void)
-{
-	if (!the_controller->mach->udc_command)
-		return;
-	the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+	if (is_on)
+		GPSR(gpio) = mask;
+	else
+		GPCR(gpio) = mask;
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/

+ 3 - 0
drivers/usb/gadget/serial.c

@@ -1120,12 +1120,15 @@ static int gs_send(struct gs_dev *dev)
 gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
 gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
 			list_del(&req_entry->re_entry);
 			list_del(&req_entry->re_entry);
 			req->length = len;
 			req->length = len;
+			spin_unlock_irqrestore(&dev->dev_lock, flags);
 			if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
 			if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
 				printk(KERN_ERR
 				printk(KERN_ERR
 				"gs_send: cannot queue read request, ret=%d\n",
 				"gs_send: cannot queue read request, ret=%d\n",
 					ret);
 					ret);
+				spin_lock_irqsave(&dev->dev_lock, flags);
 				break;
 				break;
 			}
 			}
+			spin_lock_irqsave(&dev->dev_lock, flags);
 		} else {
 		} else {
 			break;
 			break;
 		}
 		}

+ 29 - 0
drivers/usb/host/Kconfig

@@ -83,6 +83,7 @@ config USB_OHCI_HCD
 	tristate "OHCI HCD support"
 	tristate "OHCI HCD support"
 	depends on USB && USB_ARCH_HAS_OHCI
 	depends on USB && USB_ARCH_HAS_OHCI
 	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
 	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+	select I2C if ARCH_PNX4008
 	---help---
 	---help---
 	  The Open Host Controller Interface (OHCI) is a standard for accessing
 	  The Open Host Controller Interface (OHCI) is a standard for accessing
 	  USB 1.1 host controller hardware.  It does more in hardware than Intel's
 	  USB 1.1 host controller hardware.  It does more in hardware than Intel's
@@ -141,6 +142,34 @@ config USB_UHCI_HCD
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called uhci-hcd.
 	  module will be called uhci-hcd.
 
 
+config USB_U132_HCD
+	tristate "Elan U132 Adapter Host Controller"
+	depends on USB && USB_FTDI_ELAN
+	default M
+	help
+	  The U132 adapter is a USB to CardBus adapter specifically designed
+	  for PC cards that contain an OHCI host controller. Typical PC cards
+	  are the Orange Mobile 3G Option GlobeTrotter Fusion card. The U132
+	  adapter will *NOT* work with PC cards that do not contain an OHCI
+	  controller.
+
+	  For those PC cards that contain multiple OHCI controllers only ther
+	  first one is used.
+
+	  The driver consists of two modules, the "ftdi-elan" module is a
+	  USB client driver that interfaces to the FTDI chip within ELAN's
+	  USB-to-PCMCIA adapter, and this "u132-hcd" module is a USB host
+	  controller driver that talks to the OHCI controller within the
+	  CardBus cards that are inserted in the U132 adapter.
+
+	  This driver has been tested with a CardBus OHCI USB adapter, and
+	  worked with a USB PEN Drive inserted into the first USB port of
+	  the PCCARD. A rather pointless thing to do, but useful for testing.
+
+	  It is safe to say M here.
+
+	  See also <http://www.elandigitalsystems.com/support/ufaq/u132linux.php>
+
 config USB_SL811_HCD
 config USB_SL811_HCD
 	tristate "SL811HS HCD support"
 	tristate "SL811HS HCD support"
 	depends on USB
 	depends on USB

+ 1 - 0
drivers/usb/host/Makefile

@@ -14,4 +14,5 @@ obj-$(CONFIG_USB_OHCI_HCD)	+= ohci-hcd.o
 obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
 obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
 obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
+obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
 obj-$(CONFIG_ETRAX_ARCH_V10)	+= hc_crisv10.o
 obj-$(CONFIG_ETRAX_ARCH_V10)	+= hc_crisv10.o

+ 2 - 0
drivers/usb/host/ehci-au1xxx.c

@@ -200,6 +200,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
 	.reset = ehci_init,
 	.reset = ehci_init,
 	.start = ehci_run,
 	.start = ehci_run,
 	.stop = ehci_stop,
 	.stop = ehci_stop,
+	.shutdown = ehci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -268,6 +269,7 @@ MODULE_ALIAS("au1xxx-ehci");
 static struct platform_driver ehci_hcd_au1xxx_driver = {
 static struct platform_driver ehci_hcd_au1xxx_driver = {
 	.probe = ehci_hcd_au1xxx_drv_probe,
 	.probe = ehci_hcd_au1xxx_drv_probe,
 	.remove = ehci_hcd_au1xxx_drv_remove,
 	.remove = ehci_hcd_au1xxx_drv_remove,
+	.shutdown = usb_hcd_platform_shutdown,
 	/*.suspend      = ehci_hcd_au1xxx_drv_suspend, */
 	/*.suspend      = ehci_hcd_au1xxx_drv_suspend, */
 	/*.resume       = ehci_hcd_au1xxx_drv_resume, */
 	/*.resume       = ehci_hcd_au1xxx_drv_resume, */
 	.driver = {
 	.driver = {

+ 15 - 16
drivers/usb/host/ehci-dbg.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2001-2002 by David Brownell
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -65,7 +65,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
 		for (i = 0; i < HCS_N_PORTS (params); i++) {
 		for (i = 0; i < HCS_N_PORTS (params); i++) {
 			// FIXME MIPS won't readb() ...
 			// FIXME MIPS won't readb() ...
 			byte = readb (&ehci->caps->portroute[(i>>1)]);
 			byte = readb (&ehci->caps->portroute[(i>>1)]);
-			sprintf(tmp, "%d ", 
+			sprintf(tmp, "%d ",
 				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
 				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
 			strcat(buf, tmp);
 			strcat(buf, tmp);
 		}
 		}
@@ -141,12 +141,12 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 }
 }
 
 
 static void __attribute__((__unused__))
 static void __attribute__((__unused__))
-dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd) 
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
 {
 {
 	ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
 	ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
 		label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
 		label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
 	ehci_dbg (ehci,
 	ehci_dbg (ehci,
-		"  trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", 
+		"  trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
 		le32_to_cpu(itd->hw_transaction[0]),
 		le32_to_cpu(itd->hw_transaction[0]),
 		le32_to_cpu(itd->hw_transaction[1]),
 		le32_to_cpu(itd->hw_transaction[1]),
 		le32_to_cpu(itd->hw_transaction[2]),
 		le32_to_cpu(itd->hw_transaction[2]),
@@ -156,7 +156,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
 		le32_to_cpu(itd->hw_transaction[6]),
 		le32_to_cpu(itd->hw_transaction[6]),
 		le32_to_cpu(itd->hw_transaction[7]));
 		le32_to_cpu(itd->hw_transaction[7]));
 	ehci_dbg (ehci,
 	ehci_dbg (ehci,
-		"  buf:   %08x %08x %08x %08x %08x %08x %08x\n", 
+		"  buf:   %08x %08x %08x %08x %08x %08x %08x\n",
 		le32_to_cpu(itd->hw_bufp[0]),
 		le32_to_cpu(itd->hw_bufp[0]),
 		le32_to_cpu(itd->hw_bufp[1]),
 		le32_to_cpu(itd->hw_bufp[1]),
 		le32_to_cpu(itd->hw_bufp[2]),
 		le32_to_cpu(itd->hw_bufp[2]),
@@ -171,12 +171,12 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
 }
 }
 
 
 static void __attribute__((__unused__))
 static void __attribute__((__unused__))
-dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd) 
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
 {
 {
 	ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
 	ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
 		label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
 		label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
 	ehci_dbg (ehci,
 	ehci_dbg (ehci,
-		"  addr %08x sched %04x result %08x buf %08x %08x\n", 
+		"  addr %08x sched %04x result %08x buf %08x %08x\n",
 		le32_to_cpu(sitd->hw_fullspeed_ep),
 		le32_to_cpu(sitd->hw_fullspeed_ep),
 		le32_to_cpu(sitd->hw_uframe),
 		le32_to_cpu(sitd->hw_uframe),
 		le32_to_cpu(sitd->hw_results),
 		le32_to_cpu(sitd->hw_results),
@@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf)
 	*buf = 0;
 	*buf = 0;
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	next = buf;
 	size = PAGE_SIZE;
 	size = PAGE_SIZE;
@@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf)
 	seen_count = 0;
 	seen_count = 0;
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	next = buf;
 	size = PAGE_SIZE;
 	size = PAGE_SIZE;
@@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	static char		label [] = "";
 	static char		label [] = "";
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	next = buf;
 	size = PAGE_SIZE;
 	size = PAGE_SIZE;
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	}
 	}
 
 
 	if (ehci->reclaim) {
 	if (ehci->reclaim) {
-		temp = scnprintf (next, size, "reclaim qh %p%s\n",
-				ehci->reclaim,
-				ehci->reclaim_ready ? " ready" : "");
+		temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
 		size -= temp;
 		size -= temp;
 		next += temp;
 		next += temp;
 	}
 	}
@@ -785,10 +783,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 static inline void create_debug_files (struct ehci_hcd *ehci)
 static inline void create_debug_files (struct ehci_hcd *ehci)
 {
 {
 	struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
 	struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+	int retval;
 
 
-	class_device_create_file(cldev, &class_device_attr_async);
-	class_device_create_file(cldev, &class_device_attr_periodic);
-	class_device_create_file(cldev, &class_device_attr_registers);
+	retval = class_device_create_file(cldev, &class_device_attr_async);
+	retval = class_device_create_file(cldev, &class_device_attr_periodic);
+	retval = class_device_create_file(cldev, &class_device_attr_registers);
 }
 }
 
 
 static inline void remove_debug_files (struct ehci_hcd *ehci)
 static inline void remove_debug_files (struct ehci_hcd *ehci)

+ 2 - 0
drivers/usb/host/ehci-fsl.c

@@ -285,6 +285,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
 	.resume = ehci_bus_resume,
 	.resume = ehci_bus_resume,
 #endif
 #endif
 	.stop = ehci_stop,
 	.stop = ehci_stop,
+	.shutdown = ehci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -329,6 +330,7 @@ MODULE_ALIAS("fsl-ehci");
 static struct platform_driver ehci_fsl_driver = {
 static struct platform_driver ehci_fsl_driver = {
 	.probe = ehci_fsl_drv_probe,
 	.probe = ehci_fsl_drv_probe,
 	.remove = ehci_fsl_drv_remove,
 	.remove = ehci_fsl_drv_remove,
+	.shutdown = usb_hcd_platform_shutdown,
 	.driver = {
 	.driver = {
 		   .name = "fsl-ehci",
 		   .name = "fsl-ehci",
 		   },
 		   },

+ 58 - 37
drivers/usb/host/ehci-hcd.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2000-2004 by David Brownell
  * Copyright (c) 2000-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -70,7 +70,7 @@
  * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
  * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
  *	only scheduling is different, no arbitrary limitations.
  *	only scheduling is different, no arbitrary limitations.
  * 2002-07-25	Sanity check PCI reads, mostly for better cardbus support,
  * 2002-07-25	Sanity check PCI reads, mostly for better cardbus support,
- * 	clean up HC run state handshaking.
+ *	clean up HC run state handshaking.
  * 2002-05-24	Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-24	Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
  *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
@@ -111,7 +111,7 @@ static const char	hcd_name [] = "ehci_hcd";
 #define	EHCI_TUNE_MULT_TT	1
 #define	EHCI_TUNE_MULT_TT	1
 #define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
 #define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
 
 
-#define EHCI_IAA_JIFFIES	(HZ/100)	/* arbitrary; ~10 msec */
+#define EHCI_IAA_MSECS		10		/* arbitrary */
 #define EHCI_IO_JIFFIES		(HZ/10)		/* io watchdog > irq_thresh */
 #define EHCI_IO_JIFFIES		(HZ/10)		/* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_SHRINK_JIFFIES	(HZ/200)	/* async qh unlink delay */
 #define EHCI_SHRINK_JIFFIES	(HZ/200)	/* async qh unlink delay */
@@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
 static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 
 #include "ehci-hub.c"
 #include "ehci-hub.c"
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static void ehci_watchdog (unsigned long param)
+static void ehci_iaa_watchdog (unsigned long param)
 {
 {
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
 	unsigned long		flags;
 	unsigned long		flags;
+	u32			status;
 
 
 	spin_lock_irqsave (&ehci->lock, flags);
 	spin_lock_irqsave (&ehci->lock, flags);
+	WARN_ON(!ehci->reclaim);
 
 
-	/* lost IAA irqs wedge things badly; seen with a vt8235 */
+	/* lost IAA irqs wedge things badly; seen first with a vt8235 */
 	if (ehci->reclaim) {
 	if (ehci->reclaim) {
-		u32		status = readl (&ehci->regs->status);
-
+		status = readl (&ehci->regs->status);
 		if (status & STS_IAA) {
 		if (status & STS_IAA) {
 			ehci_vdbg (ehci, "lost IAA\n");
 			ehci_vdbg (ehci, "lost IAA\n");
 			COUNT (ehci->stats.lost_iaa);
 			COUNT (ehci->stats.lost_iaa);
 			writel (STS_IAA, &ehci->regs->status);
 			writel (STS_IAA, &ehci->regs->status);
-			ehci->reclaim_ready = 1;
+			end_unlink_async (ehci, NULL);
 		}
 		}
 	}
 	}
 
 
- 	/* stop async processing after it's idled a bit */
+	spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+static void ehci_watchdog (unsigned long param)
+{
+	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&ehci->lock, flags);
+
+	/* stop async processing after it's idled a bit */
 	if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
 	if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
- 		start_unlink_async (ehci, ehci->async);
+		start_unlink_async (ehci, ehci->async);
 
 
 	/* ehci could run by timer, without IRQs ... */
 	/* ehci could run by timer, without IRQs ... */
 	ehci_work (ehci, NULL);
 	ehci_work (ehci, NULL);
@@ -292,21 +304,20 @@ static void ehci_watchdog (unsigned long param)
 	spin_unlock_irqrestore (&ehci->lock, flags);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 }
 }
 
 
-/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
  * This forcibly disables dma and IRQs, helping kexec and other cases
  * This forcibly disables dma and IRQs, helping kexec and other cases
  * where the next system software may expect clean state.
  * where the next system software may expect clean state.
  */
  */
-static int
-ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+static void
+ehci_shutdown (struct usb_hcd *hcd)
 {
 {
-	struct ehci_hcd		*ehci;
+	struct ehci_hcd	*ehci;
 
 
-	ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+	ehci = hcd_to_ehci (hcd);
 	(void) ehci_halt (ehci);
 	(void) ehci_halt (ehci);
 
 
 	/* make BIOS/etc use companion controller during reboot */
 	/* make BIOS/etc use companion controller during reboot */
 	writel (0, &ehci->regs->configured_flag);
 	writel (0, &ehci->regs->configured_flag);
-	return 0;
 }
 }
 
 
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -334,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
 static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
 static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
 {
 	timer_action_done (ehci, TIMER_IO_WATCHDOG);
 	timer_action_done (ehci, TIMER_IO_WATCHDOG);
-	if (ehci->reclaim_ready)
-		end_unlink_async (ehci, regs);
 
 
 	/* another CPU may drop ehci->lock during a schedule scan while
 	/* another CPU may drop ehci->lock during a schedule scan while
 	 * it reports urb completions.  this flag guards against bogus
 	 * it reports urb completions.  this flag guards against bogus
@@ -370,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
 
 
 	/* no more interrupts ... */
 	/* no more interrupts ... */
 	del_timer_sync (&ehci->watchdog);
 	del_timer_sync (&ehci->watchdog);
+	del_timer_sync (&ehci->iaa_watchdog);
 
 
 	spin_lock_irq(&ehci->lock);
 	spin_lock_irq(&ehci->lock);
 	if (HC_IS_RUNNING (hcd->state))
 	if (HC_IS_RUNNING (hcd->state))
@@ -381,7 +391,6 @@ static void ehci_stop (struct usb_hcd *hcd)
 
 
 	/* let companion controllers work when we aren't */
 	/* let companion controllers work when we aren't */
 	writel (0, &ehci->regs->configured_flag);
 	writel (0, &ehci->regs->configured_flag);
-	unregister_reboot_notifier (&ehci->reboot_notifier);
 
 
 	remove_debug_files (ehci);
 	remove_debug_files (ehci);
 
 
@@ -417,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
 	ehci->watchdog.function = ehci_watchdog;
 	ehci->watchdog.function = ehci_watchdog;
 	ehci->watchdog.data = (unsigned long) ehci;
 	ehci->watchdog.data = (unsigned long) ehci;
 
 
+	init_timer(&ehci->iaa_watchdog);
+	ehci->iaa_watchdog.function = ehci_iaa_watchdog;
+	ehci->iaa_watchdog.data = (unsigned long) ehci;
+
 	/*
 	/*
 	 * hw default: 1K periodic list heads, one per frame.
 	 * hw default: 1K periodic list heads, one per frame.
 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -427,13 +440,12 @@ static int ehci_init(struct usb_hcd *hcd)
 
 
 	/* controllers may cache some of the periodic schedule ... */
 	/* controllers may cache some of the periodic schedule ... */
 	hcc_params = readl(&ehci->caps->hcc_params);
 	hcc_params = readl(&ehci->caps->hcc_params);
-	if (HCC_ISOC_CACHE(hcc_params)) 	// full frame cache
+	if (HCC_ISOC_CACHE(hcc_params))		// full frame cache
 		ehci->i_thresh = 8;
 		ehci->i_thresh = 8;
 	else					// N microframes cached
 	else					// N microframes cached
 		ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 		ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
 
 	ehci->reclaim = NULL;
 	ehci->reclaim = NULL;
-	ehci->reclaim_ready = 0;
 	ehci->next_uframe = -1;
 	ehci->next_uframe = -1;
 
 
 	/*
 	/*
@@ -483,9 +495,6 @@ static int ehci_init(struct usb_hcd *hcd)
 	}
 	}
 	ehci->command = temp;
 	ehci->command = temp;
 
 
-	ehci->reboot_notifier.notifier_call = ehci_reboot;
-	register_reboot_notifier(&ehci->reboot_notifier);
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -499,7 +508,6 @@ static int ehci_run (struct usb_hcd *hcd)
 
 
 	/* EHCI spec section 4.1 */
 	/* EHCI spec section 4.1 */
 	if ((retval = ehci_reset(ehci)) != 0) {
 	if ((retval = ehci_reset(ehci)) != 0) {
-		unregister_reboot_notifier(&ehci->reboot_notifier);
 		ehci_mem_cleanup(ehci);
 		ehci_mem_cleanup(ehci);
 		return retval;
 		return retval;
 	}
 	}
@@ -611,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
 	/* complete the unlinking of some qh [4.15.2.3] */
 	/* complete the unlinking of some qh [4.15.2.3] */
 	if (status & STS_IAA) {
 	if (status & STS_IAA) {
 		COUNT (ehci->stats.reclaim);
 		COUNT (ehci->stats.reclaim);
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, regs);
 		bh = 1;
 		bh = 1;
 	}
 	}
 
 
@@ -715,10 +723,14 @@ static int ehci_urb_enqueue (
 
 
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 {
-	/* if we need to use IAA and it's busy, defer */
-	if (qh->qh_state == QH_STATE_LINKED
-			&& ehci->reclaim
-			&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+	// BUG_ON(qh->qh_state != QH_STATE_LINKED);
+
+	/* failfast */
+	if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+		end_unlink_async (ehci, NULL);
+
+	/* defer till later if busy */
+	else if (ehci->reclaim) {
 		struct ehci_qh		*last;
 		struct ehci_qh		*last;
 
 
 		for (last = ehci->reclaim;
 		for (last = ehci->reclaim;
@@ -728,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 		qh->qh_state = QH_STATE_UNLINK_WAIT;
 		qh->qh_state = QH_STATE_UNLINK_WAIT;
 		last->reclaim = qh;
 		last->reclaim = qh;
 
 
-	/* bypass IAA if the hc can't care */
-	} else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
-		end_unlink_async (ehci, NULL);
-
-	/* something else might have unlinked the qh by now */
-	if (qh->qh_state == QH_STATE_LINKED)
+	/* start IAA cycle */
+	} else
 		start_unlink_async (ehci, qh);
 		start_unlink_async (ehci, qh);
 }
 }
 
 
@@ -755,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 		qh = (struct ehci_qh *) urb->hcpriv;
 		qh = (struct ehci_qh *) urb->hcpriv;
 		if (!qh)
 		if (!qh)
 			break;
 			break;
-		unlink_async (ehci, qh);
+		switch (qh->qh_state) {
+		case QH_STATE_LINKED:
+		case QH_STATE_COMPLETING:
+			unlink_async (ehci, qh);
+			break;
+		case QH_STATE_UNLINK:
+		case QH_STATE_UNLINK_WAIT:
+			/* already started */
+			break;
+		case QH_STATE_IDLE:
+			WARN_ON(1);
+			break;
+		}
 		break;
 		break;
 
 
 	case PIPE_INTERRUPT:
 	case PIPE_INTERRUPT:
@@ -847,6 +867,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
 		unlink_async (ehci, qh);
 		unlink_async (ehci, qh);
 		/* FALL THROUGH */
 		/* FALL THROUGH */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
+	case QH_STATE_UNLINK_WAIT:
 idle_timeout:
 idle_timeout:
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		schedule_timeout_uninterruptible(1);
 		schedule_timeout_uninterruptible(1);

+ 7 - 7
drivers/usb/host/ehci-hub.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (C) 2001-2004 by David Brownell
  * Copyright (C) 2001-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 	}
 	}
 	ehci->command = readl (&ehci->regs->command);
 	ehci->command = readl (&ehci->regs->command);
 	if (ehci->reclaim)
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, NULL);
 	ehci_work(ehci, NULL);
 	ehci_work(ehci, NULL);
 
 
 	/* suspend any active/unsuspended ports, maybe allow wakeup */
 	/* suspend any active/unsuspended ports, maybe allow wakeup */
@@ -103,10 +103,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
 
 
 	/* re-init operational registers in case we lost power */
 	/* re-init operational registers in case we lost power */
 	if (readl (&ehci->regs->intr_enable) == 0) {
 	if (readl (&ehci->regs->intr_enable) == 0) {
- 		/* at least some APM implementations will try to deliver
+		/* at least some APM implementations will try to deliver
 		 * IRQs right away, so delay them until we're ready.
 		 * IRQs right away, so delay them until we're ready.
- 		 */
- 		intr_enable = 1;
+		 */
+		intr_enable = 1;
 		writel (0, &ehci->regs->segment);
 		writel (0, &ehci->regs->segment);
 		writel (ehci->periodic_dma, &ehci->regs->frame_list);
 		writel (ehci->periodic_dma, &ehci->regs->frame_list);
 		writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
 		writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
@@ -232,7 +232,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
 		buf [1] = 0;
 		buf [1] = 0;
 		retval++;
 		retval++;
 	}
 	}
-	
+
 	/* no hub change reports (bit 0) for now (power, ...) */
 	/* no hub change reports (bit 0) for now (power, ...) */
 
 
 	/* port N changes (bit N)? */
 	/* port N changes (bit N)? */
@@ -304,7 +304,7 @@ ehci_hub_descriptor (
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-#define	PORT_WAKE_BITS 	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+#define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
 
 
 static int ehci_hub_control (
 static int ehci_hub_control (
 	struct usb_hcd	*hcd,
 	struct usb_hcd	*hcd,

+ 7 - 7
drivers/usb/host/ehci-mem.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2001 by David Brownell
  * Copyright (c) 2001 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -25,7 +25,7 @@
  *	- data used only by the HCD ... kmalloc is fine
  *	- data used only by the HCD ... kmalloc is fine
  *	- async and periodic schedules, shared by HC and HCD ... these
  *	- async and periodic schedules, shared by HC and HCD ... these
  *	  need to use dma_pool or dma_alloc_coherent
  *	  need to use dma_pool or dma_alloc_coherent
- *	- driver buffers, read/written by HC ... single shot DMA mapped 
+ *	- driver buffers, read/written by HC ... single shot DMA mapped
  *
  *
  * There's also PCI "register" data, which is memory mapped.
  * There's also PCI "register" data, which is memory mapped.
  * No memory seen by this driver is pageable.
  * No memory seen by this driver is pageable.
@@ -119,7 +119,7 @@ static inline void qh_put (struct ehci_qh *qh)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-/* The queue heads and transfer descriptors are managed from pools tied 
+/* The queue heads and transfer descriptors are managed from pools tied
  * to each of the "per device" structures.
  * to each of the "per device" structures.
  * This is the initialisation and cleanup code.
  * This is the initialisation and cleanup code.
  */
  */
@@ -165,7 +165,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 	int i;
 	int i;
 
 
 	/* QTDs for control/bulk/intr transfers */
 	/* QTDs for control/bulk/intr transfers */
-	ehci->qtd_pool = dma_pool_create ("ehci_qtd", 
+	ehci->qtd_pool = dma_pool_create ("ehci_qtd",
 			ehci_to_hcd(ehci)->self.controller,
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_qtd),
 			sizeof (struct ehci_qtd),
 			32 /* byte alignment (for hw parts) */,
 			32 /* byte alignment (for hw parts) */,
@@ -175,7 +175,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 	}
 	}
 
 
 	/* QHs for control/bulk/intr transfers */
 	/* QHs for control/bulk/intr transfers */
-	ehci->qh_pool = dma_pool_create ("ehci_qh", 
+	ehci->qh_pool = dma_pool_create ("ehci_qh",
 			ehci_to_hcd(ehci)->self.controller,
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_qh),
 			sizeof (struct ehci_qh),
 			32 /* byte alignment (for hw parts) */,
 			32 /* byte alignment (for hw parts) */,
@@ -189,7 +189,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 	}
 	}
 
 
 	/* ITD for high speed ISO transfers */
 	/* ITD for high speed ISO transfers */
-	ehci->itd_pool = dma_pool_create ("ehci_itd", 
+	ehci->itd_pool = dma_pool_create ("ehci_itd",
 			ehci_to_hcd(ehci)->self.controller,
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_itd),
 			sizeof (struct ehci_itd),
 			32 /* byte alignment (for hw parts) */,
 			32 /* byte alignment (for hw parts) */,
@@ -199,7 +199,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
 	}
 	}
 
 
 	/* SITD for full/low speed split ISO transfers */
 	/* SITD for full/low speed split ISO transfers */
-	ehci->sitd_pool = dma_pool_create ("ehci_sitd", 
+	ehci->sitd_pool = dma_pool_create ("ehci_sitd",
 			ehci_to_hcd(ehci)->self.controller,
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_sitd),
 			sizeof (struct ehci_sitd),
 			32 /* byte alignment (for hw parts) */,
 			32 /* byte alignment (for hw parts) */,

+ 3 - 1
drivers/usb/host/ehci-pci.c

@@ -303,7 +303,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
 	/* emptying the schedule aborts any urbs */
 	/* emptying the schedule aborts any urbs */
 	spin_lock_irq(&ehci->lock);
 	spin_lock_irq(&ehci->lock);
 	if (ehci->reclaim)
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, NULL);
 	ehci_work(ehci, NULL);
 	ehci_work(ehci, NULL);
 	spin_unlock_irq(&ehci->lock);
 	spin_unlock_irq(&ehci->lock);
 
 
@@ -338,6 +338,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
 	.resume =		ehci_pci_resume,
 	.resume =		ehci_pci_resume,
 #endif
 #endif
 	.stop =			ehci_stop,
 	.stop =			ehci_stop,
+	.shutdown =		ehci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -384,4 +385,5 @@ static struct pci_driver ehci_pci_driver = {
 	.suspend =	usb_hcd_pci_suspend,
 	.suspend =	usb_hcd_pci_suspend,
 	.resume =	usb_hcd_pci_resume,
 	.resume =	usb_hcd_pci_resume,
 #endif
 #endif
+	.shutdown = 	usb_hcd_pci_shutdown,
 };
 };

+ 12 - 14
drivers/usb/host/ehci-q.c

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (C) 2001-2004 by David Brownell
  * Copyright (C) 2001-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -31,7 +31,7 @@
  * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
  * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
  * interrupts) needs careful scheduling.  Performance improvements can be
  * interrupts) needs careful scheduling.  Performance improvements can be
  * an ongoing challenge.  That's in "ehci-sched.c".
  * an ongoing challenge.  That's in "ehci-sched.c".
- * 
+ *
  * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
  * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
  * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
  * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
  * (b) special fields in qh entries or (c) split iso entries.  TTs will
  * (b) special fields in qh entries or (c) split iso entries.  TTs will
@@ -199,7 +199,7 @@ static void qtd_copy_status (
 				&& ((token & QTD_STS_MMF) != 0
 				&& ((token & QTD_STS_MMF) != 0
 					|| QTD_CERR(token) == 0)
 					|| QTD_CERR(token) == 0)
 				&& (!ehci_is_TDI(ehci)
 				&& (!ehci_is_TDI(ehci)
-                	                || urb->dev->tt->hub !=
+			                || urb->dev->tt->hub !=
 					   ehci_to_hcd(ehci)->self.root_hub)) {
 					   ehci_to_hcd(ehci)->self.root_hub)) {
 #ifdef DEBUG
 #ifdef DEBUG
 			struct usb_device *tt = urb->dev->tt->hub;
 			struct usb_device *tt = urb->dev->tt->hub;
@@ -364,7 +364,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
 			 */
 			 */
 			if (likely (urb->status == -EINPROGRESS))
 			if (likely (urb->status == -EINPROGRESS))
 				continue;
 				continue;
-			
+
 			/* issue status after short control reads */
 			/* issue status after short control reads */
 			if (unlikely (do_status != 0)
 			if (unlikely (do_status != 0)
 					&& QTD_PID (token) == 0 /* OUT */) {
 					&& QTD_PID (token) == 0 /* OUT */) {
@@ -388,7 +388,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
 				wmb ();
 				wmb ();
 			}
 			}
 		}
 		}
- 
+
 		/* remove it from the queue */
 		/* remove it from the queue */
 		spin_lock (&urb->lock);
 		spin_lock (&urb->lock);
 		qtd_copy_status (ehci, urb, qtd->length, token);
 		qtd_copy_status (ehci, urb, qtd->length, token);
@@ -518,7 +518,7 @@ qh_urb_transaction (
 		/* for zero length DATA stages, STATUS is always IN */
 		/* for zero length DATA stages, STATUS is always IN */
 		if (len == 0)
 		if (len == 0)
 			token |= (1 /* "in" */ << 8);
 			token |= (1 /* "in" */ << 8);
-	} 
+	}
 
 
 	/*
 	/*
 	 * data transfer stage:  buffer setup
 	 * data transfer stage:  buffer setup
@@ -759,7 +759,7 @@ qh_make (
 		}
 		}
 		break;
 		break;
 	default:
 	default:
- 		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
 done:
 done:
 		qh_put (qh);
 		qh_put (qh);
 		return NULL;
 		return NULL;
@@ -967,17 +967,16 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 	struct ehci_qh		*qh = ehci->reclaim;
 	struct ehci_qh		*qh = ehci->reclaim;
 	struct ehci_qh		*next;
 	struct ehci_qh		*next;
 
 
-	timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+	iaa_watchdog_done (ehci);
 
 
 	// qh->hw_next = cpu_to_le32 (qh->qh_dma);
 	// qh->hw_next = cpu_to_le32 (qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_next.qh = NULL;
 	qh->qh_next.qh = NULL;
-	qh_put (qh);			// refcount from reclaim 
+	qh_put (qh);			// refcount from reclaim
 
 
 	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
 	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
 	next = qh->reclaim;
 	next = qh->reclaim;
 	ehci->reclaim = next;
 	ehci->reclaim = next;
-	ehci->reclaim_ready = 0;
 	qh->reclaim = NULL;
 	qh->reclaim = NULL;
 
 
 	qh_completions (ehci, qh, regs);
 	qh_completions (ehci, qh, regs);
@@ -1031,7 +1030,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 			timer_action_done (ehci, TIMER_ASYNC_OFF);
 			timer_action_done (ehci, TIMER_ASYNC_OFF);
 		}
 		}
 		return;
 		return;
-	} 
+	}
 
 
 	qh->qh_state = QH_STATE_UNLINK;
 	qh->qh_state = QH_STATE_UNLINK;
 	ehci->reclaim = qh = qh_get (qh);
 	ehci->reclaim = qh = qh_get (qh);
@@ -1046,17 +1045,16 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 
 	if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
 	if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
 		/* if (unlikely (qh->reclaim != 0))
 		/* if (unlikely (qh->reclaim != 0))
-		 * 	this will recurse, probably not much
+		 *	this will recurse, probably not much
 		 */
 		 */
 		end_unlink_async (ehci, NULL);
 		end_unlink_async (ehci, NULL);
 		return;
 		return;
 	}
 	}
 
 
-	ehci->reclaim_ready = 0;
 	cmd |= CMD_IAAD;
 	cmd |= CMD_IAAD;
 	writel (cmd, &ehci->regs->command);
 	writel (cmd, &ehci->regs->command);
 	(void) readl (&ehci->regs->command);
 	(void) readl (&ehci->regs->command);
-	timer_action (ehci, TIMER_IAA_WATCHDOG);
+	iaa_watchdog_start (ehci);
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/

+ 13 - 13
drivers/usb/host/ehci-sched.c

@@ -1,7 +1,7 @@
 /*
 /*
  * Copyright (c) 2001-2004 by David Brownell
  * Copyright (c) 2001-2004 by David Brownell
  * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
  * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -613,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 static int check_period (
 static int check_period (
-	struct ehci_hcd *ehci, 
+	struct ehci_hcd *ehci,
 	unsigned	frame,
 	unsigned	frame,
 	unsigned	uframe,
 	unsigned	uframe,
 	unsigned	period,
 	unsigned	period,
@@ -629,7 +629,7 @@ static int check_period (
 
 
 	/*
 	/*
 	 * 80% periodic == 100 usec/uframe available
 	 * 80% periodic == 100 usec/uframe available
-	 * convert "usecs we need" to "max already claimed" 
+	 * convert "usecs we need" to "max already claimed"
 	 */
 	 */
 	usecs = 100 - usecs;
 	usecs = 100 - usecs;
 
 
@@ -659,14 +659,14 @@ static int check_period (
 }
 }
 
 
 static int check_intr_schedule (
 static int check_intr_schedule (
-	struct ehci_hcd		*ehci, 
+	struct ehci_hcd		*ehci,
 	unsigned		frame,
 	unsigned		frame,
 	unsigned		uframe,
 	unsigned		uframe,
 	const struct ehci_qh	*qh,
 	const struct ehci_qh	*qh,
 	__le32			*c_maskp
 	__le32			*c_maskp
 )
 )
 {
 {
-    	int		retval = -ENOSPC;
+	int		retval = -ENOSPC;
 	u8		mask = 0;
 	u8		mask = 0;
 
 
 	if (qh->c_usecs && uframe >= 6)		/* FSTN territory? */
 	if (qh->c_usecs && uframe >= 6)		/* FSTN territory? */
@@ -701,7 +701,7 @@ static int check_intr_schedule (
 	/* Make sure this tt's buffer is also available for CSPLITs.
 	/* Make sure this tt's buffer is also available for CSPLITs.
 	 * We pessimize a bit; probably the typical full speed case
 	 * We pessimize a bit; probably the typical full speed case
 	 * doesn't need the second CSPLIT.
 	 * doesn't need the second CSPLIT.
-	 * 
+	 *
 	 * NOTE:  both SPLIT and CSPLIT could be checked in just
 	 * NOTE:  both SPLIT and CSPLIT could be checked in just
 	 * one smart pass...
 	 * one smart pass...
 	 */
 	 */
@@ -728,7 +728,7 @@ static int check_intr_schedule (
  */
  */
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 {
-	int 		status;
+	int		status;
 	unsigned	uframe;
 	unsigned	uframe;
 	__le32		c_mask;
 	__le32		c_mask;
 	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
 	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
@@ -784,7 +784,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 
 
 	/* stuff into the periodic schedule */
 	/* stuff into the periodic schedule */
- 	status = qh_link_periodic (ehci, qh);
+	status = qh_link_periodic (ehci, qh);
 done:
 done:
 	return status;
 	return status;
 }
 }
@@ -1681,7 +1681,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
 		status = -ESHUTDOWN;
 		status = -ESHUTDOWN;
 	else
 	else
 		status = iso_stream_schedule (ehci, urb, stream);
 		status = iso_stream_schedule (ehci, urb, stream);
- 	if (likely (status == 0))
+	if (likely (status == 0))
 		itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
 		itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 
 
@@ -1738,7 +1738,7 @@ sitd_sched_init (
 		if (packet->buf1 != (buf & ~(u64)0x0fff))
 		if (packet->buf1 != (buf & ~(u64)0x0fff))
 			packet->cross = 1;
 			packet->cross = 1;
 
 
-		/* OUT uses multiple start-splits */ 
+		/* OUT uses multiple start-splits */
 		if (stream->bEndpointAddress & USB_DIR_IN)
 		if (stream->bEndpointAddress & USB_DIR_IN)
 			continue;
 			continue;
 		length = (length + 187) / 188;
 		length = (length + 187) / 188;
@@ -1925,7 +1925,7 @@ sitd_link_urb (
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 #define	SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
 #define	SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
-	       			| SITD_STS_XACT | SITD_STS_MMF)
+				| SITD_STS_XACT | SITD_STS_MMF)
 
 
 static unsigned
 static unsigned
 sitd_complete (
 sitd_complete (
@@ -2043,7 +2043,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
 		status = -ESHUTDOWN;
 		status = -ESHUTDOWN;
 	else
 	else
 		status = iso_stream_schedule (ehci, urb, stream);
 		status = iso_stream_schedule (ehci, urb, stream);
- 	if (status == 0)
+	if (status == 0)
 		sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
 		sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 
 
@@ -2226,5 +2226,5 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
 			now_uframe++;
 			now_uframe++;
 			now_uframe %= mod;
 			now_uframe %= mod;
 		}
 		}
-	} 
+	}
 }
 }

+ 33 - 26
drivers/usb/host/ehci.h

@@ -1,6 +1,6 @@
 /*
 /*
  * Copyright (c) 2001-2002 by David Brownell
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * 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
  * 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
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -58,7 +58,6 @@ struct ehci_hcd {			/* one per controller */
 	/* async schedule support */
 	/* async schedule support */
 	struct ehci_qh		*async;
 	struct ehci_qh		*async;
 	struct ehci_qh		*reclaim;
 	struct ehci_qh		*reclaim;
-	unsigned		reclaim_ready : 1;
 	unsigned		scanning : 1;
 	unsigned		scanning : 1;
 
 
 	/* periodic schedule support */
 	/* periodic schedule support */
@@ -81,8 +80,8 @@ struct ehci_hcd {			/* one per controller */
 	struct dma_pool		*itd_pool;	/* itd per iso urb */
 	struct dma_pool		*itd_pool;	/* itd per iso urb */
 	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */
 	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */
 
 
+	struct timer_list	iaa_watchdog;
 	struct timer_list	watchdog;
 	struct timer_list	watchdog;
-	struct notifier_block	reboot_notifier;
 	unsigned long		actions;
 	unsigned long		actions;
 	unsigned		stamp;
 	unsigned		stamp;
 	unsigned long		next_statechange;
 	unsigned long		next_statechange;
@@ -104,7 +103,7 @@ struct ehci_hcd {			/* one per controller */
 #endif
 #endif
 };
 };
 
 
-/* convert between an HCD pointer and the corresponding EHCI_HCD */ 
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
 static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
 static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
 {
 {
 	return (struct ehci_hcd *) (hcd->hcd_priv);
 	return (struct ehci_hcd *) (hcd->hcd_priv);
@@ -115,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
 }
 }
 
 
 
 
+static inline void
+iaa_watchdog_start (struct ehci_hcd *ehci)
+{
+	WARN_ON(timer_pending(&ehci->iaa_watchdog));
+	mod_timer (&ehci->iaa_watchdog,
+			jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
+}
+
+static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
+{
+	del_timer (&ehci->iaa_watchdog);
+}
+
 enum ehci_timer_action {
 enum ehci_timer_action {
 	TIMER_IO_WATCHDOG,
 	TIMER_IO_WATCHDOG,
-	TIMER_IAA_WATCHDOG,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_OFF,
 	TIMER_ASYNC_OFF,
 };
 };
@@ -135,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
 		unsigned long t;
 		unsigned long t;
 
 
 		switch (action) {
 		switch (action) {
-		case TIMER_IAA_WATCHDOG:
-			t = EHCI_IAA_JIFFIES;
-			break;
 		case TIMER_IO_WATCHDOG:
 		case TIMER_IO_WATCHDOG:
 			t = EHCI_IO_JIFFIES;
 			t = EHCI_IO_JIFFIES;
 			break;
 			break;
@@ -154,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
 		// async queue SHRINK often precedes IAA.  while it's ready
 		// async queue SHRINK often precedes IAA.  while it's ready
 		// to go OFF neither can matter, and afterwards the IO
 		// to go OFF neither can matter, and afterwards the IO
 		// watchdog stops unless there's still periodic traffic.
 		// watchdog stops unless there's still periodic traffic.
-		if (action != TIMER_IAA_WATCHDOG
-				&& t > ehci->watchdog.expires
+		if (time_before_eq(t, ehci->watchdog.expires)
 				&& timer_pending (&ehci->watchdog))
 				&& timer_pending (&ehci->watchdog))
 			return;
 			return;
 		mod_timer (&ehci->watchdog, t);
 		mod_timer (&ehci->watchdog, t);
@@ -179,8 +186,8 @@ struct ehci_caps {
 #define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
 #define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
 #define HCS_N_CC(p)		(((p)>>12)&0xf)	/* bits 15:12, #companion HCs */
 #define HCS_N_CC(p)		(((p)>>12)&0xf)	/* bits 15:12, #companion HCs */
 #define HCS_N_PCC(p)		(((p)>>8)&0xf)	/* bits 11:8, ports per CC */
 #define HCS_N_PCC(p)		(((p)>>8)&0xf)	/* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p)	((p)&(1 << 7))	/* true: port routing */ 
-#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */ 
+#define HCS_PORTROUTED(p)	((p)&(1 << 7))	/* true: port routing */
+#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */
 #define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
 #define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
 
 
 	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */
 	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */
@@ -205,7 +212,7 @@ struct ehci_regs {
 #define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */
 #define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */
 #define CMD_IAAD	(1<<6)		/* "doorbell" interrupt async advance */
 #define CMD_IAAD	(1<<6)		/* "doorbell" interrupt async advance */
 #define CMD_ASE		(1<<5)		/* async schedule enable */
 #define CMD_ASE		(1<<5)		/* async schedule enable */
-#define CMD_PSE  	(1<<4)		/* periodic schedule enable */
+#define CMD_PSE		(1<<4)		/* periodic schedule enable */
 /* 3:2 is periodic frame list size */
 /* 3:2 is periodic frame list size */
 #define CMD_RESET	(1<<1)		/* reset HC not bus */
 #define CMD_RESET	(1<<1)		/* reset HC not bus */
 #define CMD_RUN		(1<<0)		/* start/stop HC */
 #define CMD_RUN		(1<<0)		/* start/stop HC */
@@ -231,9 +238,9 @@ struct ehci_regs {
 	/* FRINDEX: offset 0x0C */
 	/* FRINDEX: offset 0x0C */
 	u32		frame_index;	/* current microframe number */
 	u32		frame_index;	/* current microframe number */
 	/* CTRLDSSEGMENT: offset 0x10 */
 	/* CTRLDSSEGMENT: offset 0x10 */
-	u32		segment; 	/* address bits 63:32 if needed */
+	u32		segment;	/* address bits 63:32 if needed */
 	/* PERIODICLISTBASE: offset 0x14 */
 	/* PERIODICLISTBASE: offset 0x14 */
-	u32		frame_list; 	/* points to periodic list */
+	u32		frame_list;	/* points to periodic list */
 	/* ASYNCLISTADDR: offset 0x18 */
 	/* ASYNCLISTADDR: offset 0x18 */
 	u32		async_next;	/* address of next async queue head */
 	u32		async_next;	/* address of next async queue head */
 
 
@@ -302,7 +309,7 @@ struct ehci_dbg_port {
 
 
 /*
 /*
  * EHCI Specification 0.95 Section 3.5
  * EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...) 
+ * QTD: describe data transfer components (buffer, direction, ...)
  * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
  * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
  *
  *
  * These are associated only with "QH" (Queue Head) structures,
  * These are associated only with "QH" (Queue Head) structures,
@@ -312,7 +319,7 @@ struct ehci_qtd {
 	/* first part defined by EHCI spec */
 	/* first part defined by EHCI spec */
 	__le32			hw_next;	  /* see EHCI 3.5.1 */
 	__le32			hw_next;	  /* see EHCI 3.5.1 */
 	__le32			hw_alt_next;      /* see EHCI 3.5.2 */
 	__le32			hw_alt_next;      /* see EHCI 3.5.2 */
-	__le32			hw_token;         /* see EHCI 3.5.3 */       
+	__le32			hw_token;         /* see EHCI 3.5.3 */
 #define	QTD_TOGGLE	(1 << 31)	/* data toggle */
 #define	QTD_TOGGLE	(1 << 31)	/* data toggle */
 #define	QTD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
 #define	QTD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
 #define	QTD_IOC		(1 << 15)	/* interrupt on complete */
 #define	QTD_IOC		(1 << 15)	/* interrupt on complete */
@@ -349,8 +356,8 @@ struct ehci_qtd {
 /* values for that type tag */
 /* values for that type tag */
 #define Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
 #define Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
 #define Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
 #define Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
-#define Q_TYPE_SITD 	__constant_cpu_to_le32 (2 << 1)
-#define Q_TYPE_FSTN 	__constant_cpu_to_le32 (3 << 1)
+#define Q_TYPE_SITD	__constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN	__constant_cpu_to_le32 (3 << 1)
 
 
 /* next async queue entry, or pointer to interrupt/periodic QH */
 /* next async queue entry, or pointer to interrupt/periodic QH */
 #define	QH_NEXT(dma)	(cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
 #define	QH_NEXT(dma)	(cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
@@ -367,7 +374,7 @@ struct ehci_qtd {
  * For entries in the async schedule, the type tag always says "qh".
  * For entries in the async schedule, the type tag always says "qh".
  */
  */
 union ehci_shadow {
 union ehci_shadow {
-	struct ehci_qh 		*qh;		/* Q_TYPE_QH */
+	struct ehci_qh		*qh;		/* Q_TYPE_QH */
 	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
 	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
 	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
 	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
 	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
 	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
@@ -397,7 +404,7 @@ struct ehci_qh {
 #define	QH_HUBPORT	0x3f800000
 #define	QH_HUBPORT	0x3f800000
 #define	QH_MULT		0xc0000000
 #define	QH_MULT		0xc0000000
 	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
 	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
-	
+
 	/* qtd overlay (hardware parts of a struct ehci_qtd) */
 	/* qtd overlay (hardware parts of a struct ehci_qtd) */
 	__le32			hw_qtd_next;
 	__le32			hw_qtd_next;
 	__le32			hw_alt_next;
 	__le32			hw_alt_next;
@@ -472,7 +479,7 @@ struct ehci_iso_stream {
 	struct list_head	td_list;	/* queued itds/sitds */
 	struct list_head	td_list;	/* queued itds/sitds */
 	struct list_head	free_list;	/* list of unused itds/sitds */
 	struct list_head	free_list;	/* list of unused itds/sitds */
 	struct usb_device	*udev;
 	struct usb_device	*udev;
- 	struct usb_host_endpoint *ep;
+	struct usb_host_endpoint *ep;
 
 
 	/* output of (re)scheduling */
 	/* output of (re)scheduling */
 	unsigned long		start;		/* jiffies */
 	unsigned long		start;		/* jiffies */
@@ -492,8 +499,8 @@ struct ehci_iso_stream {
 	unsigned		bandwidth;
 	unsigned		bandwidth;
 
 
 	/* This is used to initialize iTD's hw_bufp fields */
 	/* This is used to initialize iTD's hw_bufp fields */
-	__le32			buf0;		
-	__le32			buf1;		
+	__le32			buf0;
+	__le32			buf1;
 	__le32			buf2;
 	__le32			buf2;
 
 
 	/* this is used to initialize sITD's tt info */
 	/* this is used to initialize sITD's tt info */
@@ -521,7 +528,7 @@ struct ehci_itd {
 
 
 #define ITD_ACTIVE	__constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
 #define ITD_ACTIVE	__constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
 
 
-	__le32			hw_bufp [7];	/* see EHCI 3.3.3 */ 
+	__le32			hw_bufp [7];	/* see EHCI 3.3.3 */
 	__le32			hw_bufp_hi [7];	/* Appendix B */
 	__le32			hw_bufp_hi [7];	/* Appendix B */
 
 
 	/* the rest is HCD-private */
 	/* the rest is HCD-private */
@@ -542,7 +549,7 @@ struct ehci_itd {
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /*
 /*
- * EHCI Specification 0.95 Section 3.4 
+ * EHCI Specification 0.95 Section 3.4
  * siTD, aka split-transaction isochronous Transfer Descriptor
  * siTD, aka split-transaction isochronous Transfer Descriptor
  *       ... describe full speed iso xfers through TT in hubs
  *       ... describe full speed iso xfers through TT in hubs
  * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
  * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)

+ 1 - 1
drivers/usb/host/isp116x-hcd.c

@@ -1207,7 +1207,7 @@ static int isp116x_open_seq(struct inode *inode, struct file *file)
 	return single_open(file, isp116x_show_dbg, inode->i_private);
 	return single_open(file, isp116x_show_dbg, inode->i_private);
 }
 }
 
 
-static struct file_operations isp116x_debug_fops = {
+static const struct file_operations isp116x_debug_fops = {
 	.open = isp116x_open_seq,
 	.open = isp116x_open_seq,
 	.read = seq_read,
 	.read = seq_read,
 	.llseek = seq_lseek,
 	.llseek = seq_lseek,

+ 1 - 1
drivers/usb/host/isp116x.h

@@ -233,7 +233,7 @@ static const int cc_to_error[16] = {
 	/* Bit Stuff  */ -EPROTO,
 	/* Bit Stuff  */ -EPROTO,
 	/* Data Togg  */ -EILSEQ,
 	/* Data Togg  */ -EILSEQ,
 	/* Stall      */ -EPIPE,
 	/* Stall      */ -EPIPE,
-	/* DevNotResp */ -ETIMEDOUT,
+	/* DevNotResp */ -ETIME,
 	/* PIDCheck   */ -EPROTO,
 	/* PIDCheck   */ -EPROTO,
 	/* UnExpPID   */ -EPROTO,
 	/* UnExpPID   */ -EPROTO,
 	/* DataOver   */ -EOVERFLOW,
 	/* DataOver   */ -EOVERFLOW,

+ 5 - 2
drivers/usb/host/ohci-at91.c

@@ -193,7 +193,7 @@ ohci_at91_start (struct usb_hcd *hcd)
 	if ((ret = ohci_init(ohci)) < 0)
 	if ((ret = ohci_init(ohci)) < 0)
 		return ret;
 		return ret;
 
 
-	root->maxchild = board->ports;
+	ohci->num_ports = board->ports;
 
 
 	if ((ret = ohci_run(ohci)) < 0) {
 	if ((ret = ohci_run(ohci)) < 0) {
 		err("can't start %s", hcd->self.bus_name);
 		err("can't start %s", hcd->self.bus_name);
@@ -221,6 +221,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
 	 */
 	 */
 	.start =		ohci_at91_start,
 	.start =		ohci_at91_start,
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -239,7 +240,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
-
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -296,6 +297,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
 	if (!clocked) {
 	if (!clocked) {
 		clk_enable(iclk);
 		clk_enable(iclk);
 		clk_enable(fclk);
 		clk_enable(fclk);
+		clocked = 1;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -310,6 +312,7 @@ MODULE_ALIAS("at91_ohci");
 static struct platform_driver ohci_hcd_at91_driver = {
 static struct platform_driver ohci_hcd_at91_driver = {
 	.probe		= ohci_hcd_at91_drv_probe,
 	.probe		= ohci_hcd_at91_drv_probe,
 	.remove		= ohci_hcd_at91_drv_remove,
 	.remove		= ohci_hcd_at91_drv_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
 	.suspend	= ohci_hcd_at91_drv_suspend,
 	.suspend	= ohci_hcd_at91_drv_suspend,
 	.resume		= ohci_hcd_at91_drv_resume,
 	.resume		= ohci_hcd_at91_drv_resume,
 	.driver		= {
 	.driver		= {

+ 3 - 4
drivers/usb/host/ohci-au1xxx.c

@@ -268,11 +268,8 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
 	 * basic lifecycle operations
 	 * basic lifecycle operations
 	 */
 	 */
 	.start =		ohci_au1xxx_start,
 	.start =		ohci_au1xxx_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_au1xxx_suspend,  -- tbd */
-	/* resume:		ohci_au1xxx_resume,   -- tbd */
-#endif /*CONFIG_PM*/
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -291,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -338,6 +336,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_au1xxx_driver = {
 static struct platform_driver ohci_hcd_au1xxx_driver = {
 	.probe		= ohci_hcd_au1xxx_drv_probe,
 	.probe		= ohci_hcd_au1xxx_drv_probe,
 	.remove		= ohci_hcd_au1xxx_drv_remove,
 	.remove		= ohci_hcd_au1xxx_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_au1xxx_drv_suspend, */
 	/*.suspend	= ohci_hcd_au1xxx_drv_suspend, */
 	/*.resume	= ohci_hcd_au1xxx_drv_resume, */
 	/*.resume	= ohci_hcd_au1xxx_drv_resume, */
 	.driver		= {
 	.driver		= {

+ 12 - 6
drivers/usb/host/ohci-dbg.c

@@ -477,7 +477,7 @@ show_async (struct class_device *class_dev, char *buf)
 	unsigned long		flags;
 	unsigned long		flags;
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 	ohci = hcd_to_ohci(hcd);
 
 
 	/* display control and bulk lists together, for simplicity */
 	/* display control and bulk lists together, for simplicity */
@@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf)
 	seen_count = 0;
 	seen_count = 0;
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 	ohci = hcd_to_ohci(hcd);
 	next = buf;
 	next = buf;
 	size = PAGE_SIZE;
 	size = PAGE_SIZE;
@@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	u32			rdata;
 	u32			rdata;
 
 
 	bus = class_get_devdata(class_dev);
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 	ohci = hcd_to_ohci(hcd);
 	regs = ohci->regs;
 	regs = ohci->regs;
 	next = buf;
 	next = buf;
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
 	size -= temp;
 	size -= temp;
 	next += temp;
 	next += temp;
 
 
+	temp = scnprintf (next, size, "hub poll timer %s\n",
+			ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+	size -= temp;
+	next += temp;
+
 	/* roothub */
 	/* roothub */
 	ohci_dump_roothub (ohci, 1, &next, &size);
 	ohci_dump_roothub (ohci, 1, &next, &size);
 
 
@@ -680,10 +685,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
 static inline void create_debug_files (struct ohci_hcd *ohci)
 static inline void create_debug_files (struct ohci_hcd *ohci)
 {
 {
 	struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev;
 	struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev;
+	int retval;
 
 
-	class_device_create_file(cldev, &class_device_attr_async);
-	class_device_create_file(cldev, &class_device_attr_periodic);
-	class_device_create_file(cldev, &class_device_attr_registers);
+	retval = class_device_create_file(cldev, &class_device_attr_async);
+	retval = class_device_create_file(cldev, &class_device_attr_periodic);
+	retval = class_device_create_file(cldev, &class_device_attr_registers);
 	ohci_dbg (ohci, "created debug files\n");
 	ohci_dbg (ohci, "created debug files\n");
 }
 }
 
 

+ 3 - 0
drivers/usb/host/ohci-ep93xx.c

@@ -128,12 +128,14 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
 	.flags			= HCD_USB11 | HCD_MEMORY,
 	.flags			= HCD_USB11 | HCD_MEMORY,
 	.start			= ohci_ep93xx_start,
 	.start			= ohci_ep93xx_start,
 	.stop			= ohci_stop,
 	.stop			= ohci_stop,
+	.shutdown		= ohci_shutdown,
 	.urb_enqueue		= ohci_urb_enqueue,
 	.urb_enqueue		= ohci_urb_enqueue,
 	.urb_dequeue		= ohci_urb_dequeue,
 	.urb_dequeue		= ohci_urb_dequeue,
 	.endpoint_disable	= ohci_endpoint_disable,
 	.endpoint_disable	= ohci_endpoint_disable,
 	.get_frame_number	= ohci_get_frame,
 	.get_frame_number	= ohci_get_frame,
 	.hub_status_data	= ohci_hub_status_data,
 	.hub_status_data	= ohci_hub_status_data,
 	.hub_control		= ohci_hub_control,
 	.hub_control		= ohci_hub_control,
+	.hub_irq_enable		= ohci_rhsc_enable,
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	.bus_suspend		= ohci_bus_suspend,
 	.bus_suspend		= ohci_bus_suspend,
 	.bus_resume		= ohci_bus_resume,
 	.bus_resume		= ohci_bus_resume,
@@ -202,6 +204,7 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_ep93xx_driver = {
 static struct platform_driver ohci_hcd_ep93xx_driver = {
 	.probe		= ohci_hcd_ep93xx_drv_probe,
 	.probe		= ohci_hcd_ep93xx_drv_probe,
 	.remove		= ohci_hcd_ep93xx_drv_remove,
 	.remove		= ohci_hcd_ep93xx_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	.suspend	= ohci_hcd_ep93xx_drv_suspend,
 	.suspend	= ohci_hcd_ep93xx_drv_suspend,
 	.resume		= ohci_hcd_ep93xx_drv_resume,
 	.resume		= ohci_hcd_ep93xx_drv_resume,

+ 45 - 19
drivers/usb/host/ohci-hcd.c

@@ -88,7 +88,7 @@
 #include <linux/timer.h>
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/usb.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.h> 
 #include <linux/dma-mapping.h> 
 #include <linux/dmapool.h>
 #include <linux/dmapool.h>
 #include <linux/reboot.h>
 #include <linux/reboot.h>
@@ -101,7 +101,7 @@
 
 
 #include "../core/hcd.h"
 #include "../core/hcd.h"
 
 
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
 
@@ -110,9 +110,10 @@
 #undef OHCI_VERBOSE_DEBUG	/* not always helpful */
 #undef OHCI_VERBOSE_DEBUG	/* not always helpful */
 
 
 /* For initializing controller (mask in an HCFS mode too) */
 /* For initializing controller (mask in an HCFS mode too) */
-#define	OHCI_CONTROL_INIT 	OHCI_CTRL_CBSR
+#define	OHCI_CONTROL_INIT	OHCI_CTRL_CBSR
 #define	OHCI_INTR_INIT \
 #define	OHCI_INTR_INIT \
-	(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+		(OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+		| OHCI_INTR_RD | OHCI_INTR_WDH)
 
 
 #ifdef __hppa__
 #ifdef __hppa__
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,12 +129,13 @@
 
 
 static const char	hcd_name [] = "ohci_hcd";
 static const char	hcd_name [] = "ohci_hcd";
 
 
+#define	STATECHANGE_DELAY	msecs_to_jiffies(300)
+
 #include "ohci.h"
 #include "ohci.h"
 
 
 static void ohci_dump (struct ohci_hcd *ohci, int verbose);
 static void ohci_dump (struct ohci_hcd *ohci, int verbose);
 static int ohci_init (struct ohci_hcd *ohci);
 static int ohci_init (struct ohci_hcd *ohci);
 static void ohci_stop (struct usb_hcd *hcd);
 static void ohci_stop (struct usb_hcd *hcd);
-static int ohci_reboot (struct notifier_block *, unsigned long , void *);
 
 
 #include "ohci-hub.c"
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
 #include "ohci-dbg.c"
@@ -416,21 +418,20 @@ static void ohci_usb_reset (struct ohci_hcd *ohci)
 	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 }
 }
 
 
-/* reboot notifier forcibly disables IRQs and DMA, helping kexec and
+/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
  * other cases where the next software may expect clean state from the
  * other cases where the next software may expect clean state from the
  * "firmware".  this is bus-neutral, unlike shutdown() methods.
  * "firmware".  this is bus-neutral, unlike shutdown() methods.
  */
  */
-static int
-ohci_reboot (struct notifier_block *block, unsigned long code, void *null)
+static void
+ohci_shutdown (struct usb_hcd *hcd)
 {
 {
 	struct ohci_hcd *ohci;
 	struct ohci_hcd *ohci;
 
 
-	ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+	ohci = hcd_to_ohci (hcd);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
 	ohci_usb_reset (ohci);
 	ohci_usb_reset (ohci);
 	/* flush the writes */
 	/* flush the writes */
 	(void) ohci_readl (ohci, &ohci->regs->control);
 	(void) ohci_readl (ohci, &ohci->regs->control);
-	return 0;
 }
 }
 
 
 /*-------------------------------------------------------------------------*
 /*-------------------------------------------------------------------------*
@@ -446,7 +447,6 @@ static int ohci_init (struct ohci_hcd *ohci)
 
 
 	disable (ohci);
 	disable (ohci);
 	ohci->regs = hcd->regs;
 	ohci->regs = hcd->regs;
-	ohci->next_statechange = jiffies;
 
 
 	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
 	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
 	 * was never needed for most non-PCI systems ... remove the code?
 	 * was never needed for most non-PCI systems ... remove the code?
@@ -502,7 +502,6 @@ static int ohci_init (struct ohci_hcd *ohci)
 	if ((ret = ohci_mem_init (ohci)) < 0)
 	if ((ret = ohci_mem_init (ohci)) < 0)
 		ohci_stop (hcd);
 		ohci_stop (hcd);
 	else {
 	else {
-		register_reboot_notifier (&ohci->reboot_notifier);
 		create_debug_files (ohci);
 		create_debug_files (ohci);
 	}
 	}
 
 
@@ -637,10 +636,14 @@ static int ohci_run (struct ohci_hcd *ohci)
 		return -EOVERFLOW;
 		return -EOVERFLOW;
 	}
 	}
 
 
- 	/* start controller operations */
+	/* use rhsc irqs after khubd is fully initialized */
+	hcd->poll_rh = 1;
+	hcd->uses_new_polling = 1;
+
+	/* start controller operations */
 	ohci->hc_control &= OHCI_CTRL_RWC;
 	ohci->hc_control &= OHCI_CTRL_RWC;
- 	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- 	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 	hcd->state = HC_STATE_RUNNING;
 	hcd->state = HC_STATE_RUNNING;
 
 
 	/* wake on ConnectStatusChange, matching external hubs */
 	/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +651,7 @@ static int ohci_run (struct ohci_hcd *ohci)
 
 
 	/* Choose the interrupts we care about now, others later on demand */
 	/* Choose the interrupts we care about now, others later on demand */
 	mask = OHCI_INTR_INIT;
 	mask = OHCI_INTR_INIT;
-	ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+	ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
 	ohci_writel (ohci, mask, &ohci->regs->intrenable);
 	ohci_writel (ohci, mask, &ohci->regs->intrenable);
 
 
 	/* handle root hub init quirks ... */
 	/* handle root hub init quirks ... */
@@ -672,6 +675,7 @@ static int ohci_run (struct ohci_hcd *ohci)
 	// flush those writes
 	// flush those writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
 
+	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 	spin_unlock_irq (&ohci->lock);
 	spin_unlock_irq (&ohci->lock);
 
 
 	// POTPGT delay is bits 24-31, in 2 ms units.
 	// POTPGT delay is bits 24-31, in 2 ms units.
@@ -709,7 +713,23 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
 	/* interrupt for some other device? */
 	/* interrupt for some other device? */
 	} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
 	} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
 		return IRQ_NOTMINE;
 		return IRQ_NOTMINE;
-	} 
+	}
+
+	/* NOTE:  vendors didn't always make the same implementation
+	 * choices for RHSC.  Sometimes it triggers on an edge (like
+	 * setting and maybe clearing a port status change bit); and
+	 * it's level-triggered on other silicon, active until khubd
+	 * clears all active port status change bits.  Poll by timer
+	 * til it's fully debounced and the difference won't matter.
+	 */
+	if (ints & OHCI_INTR_RHSC) {
+		ohci_vdbg (ohci, "rhsc\n");
+		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+		hcd->poll_rh = 1;
+		ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+		usb_hcd_poll_rh_status(hcd);
+	}
 
 
 	if (ints & OHCI_INTR_UE) {
 	if (ints & OHCI_INTR_UE) {
 		disable (ohci);
 		disable (ohci);
@@ -775,9 +795,10 @@ static void ohci_stop (struct usb_hcd *hcd)
 
 
 	ohci_usb_reset (ohci);
 	ohci_usb_reset (ohci);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
-	
+	free_irq(hcd->irq, hcd);
+	hcd->irq = -1;
+
 	remove_debug_files (ohci);
 	remove_debug_files (ohci);
-	unregister_reboot_notifier (&ohci->reboot_notifier);
 	ohci_mem_cleanup (ohci);
 	ohci_mem_cleanup (ohci);
 	if (ohci->hcca) {
 	if (ohci->hcca) {
 		dma_free_coherent (hcd->self.controller, 
 		dma_free_coherent (hcd->self.controller, 
@@ -917,6 +938,10 @@ MODULE_LICENSE ("GPL");
 #include "ohci-at91.c"
 #include "ohci-at91.c"
 #endif
 #endif
 
 
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_S3C2410) \
       || defined(CONFIG_ARCH_S3C2410) \
@@ -928,6 +953,7 @@ MODULE_LICENSE ("GPL");
       || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
       || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
       || defined (CONFIG_ARCH_AT91RM9200) \
       || defined (CONFIG_ARCH_AT91RM9200) \
       || defined (CONFIG_ARCH_AT91SAM9261) \
       || defined (CONFIG_ARCH_AT91SAM9261) \
+      || defined (CONFIG_ARCH_PNX4008) \
 	)
 	)
 #error "missing bus glue for ohci-hcd"
 #error "missing bus glue for ohci-hcd"
 #endif
 #endif

+ 46 - 24
drivers/usb/host/ohci-hub.c

@@ -36,6 +36,14 @@
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+
+	ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
+
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 
 
 #define OHCI_SCHED_ENABLES \
 #define OHCI_SCHED_ENABLES \
@@ -123,10 +131,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
 	/* no resumes until devices finish suspending */
 	/* no resumes until devices finish suspending */
 	ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 	ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 
 
+	/* no timer polling */
+	hcd->poll_rh = 0;
+
 done:
 done:
-	/* external suspend vs self autosuspend ... same effect */
-	if (status == 0)
-		usb_hcd_suspend_root_hub(hcd);
 	spin_unlock_irqrestore (&ohci->lock, flags);
 	spin_unlock_irqrestore (&ohci->lock, flags);
 	return status;
 	return status;
 }
 }
@@ -256,8 +264,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
 	/* TRSMRCY */
 	/* TRSMRCY */
 	msleep (10);
 	msleep (10);
 
 
-	/* keep it alive for ~5x suspend + resume costs */
-	ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+	/* keep it alive for more than ~5x suspend + resume costs */
+	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 
 
 	/* maybe turn schedules back on */
 	/* maybe turn schedules back on */
 	enables = 0;
 	enables = 0;
@@ -302,9 +310,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		i, changed = 0, length = 1;
 	int		i, changed = 0, length = 1;
-	int		can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+	int		can_suspend;
 	unsigned long	flags;
 	unsigned long	flags;
 
 
+	can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
 	spin_lock_irqsave (&ohci->lock, flags);
 	spin_lock_irqsave (&ohci->lock, flags);
 
 
 	/* handle autosuspended root:  finish resuming before
 	/* handle autosuspended root:  finish resuming before
@@ -339,6 +348,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 	for (i = 0; i < ohci->num_ports; i++) {
 	for (i = 0; i < ohci->num_ports; i++) {
 		u32	status = roothub_portstatus (ohci, i);
 		u32	status = roothub_portstatus (ohci, i);
 
 
+		/* can't autosuspend with active ports */
+		if ((status & RH_PS_PES) && !(status & RH_PS_PSS))
+			can_suspend = 0;
+
 		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
 		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
 				| RH_PS_OCIC | RH_PS_PRSC)) {
 				| RH_PS_OCIC | RH_PS_PRSC)) {
 			changed = 1;
 			changed = 1;
@@ -348,32 +361,41 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 			    buf [1] |= 1 << (i - 7);
 			    buf [1] |= 1 << (i - 7);
 			continue;
 			continue;
 		}
 		}
+	}
 
 
-		/* can suspend if no ports are enabled; or if all all
-		 * enabled ports are suspended AND remote wakeup is on.
-		 */
-		if (!(status & RH_PS_CCS))
-			continue;
-		if ((status & RH_PS_PSS) && can_suspend)
-			continue;
+	/* after root hub changes, stop polling after debouncing
+	 * for a while and maybe kicking in autosuspend
+	 */
+	if (changed) {
+		ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 		can_suspend = 0;
 		can_suspend = 0;
+	} else if (time_before (jiffies, ohci->next_statechange)) {
+		can_suspend = 0;
+	} else {
+#ifdef	CONFIG_PM
+		can_suspend = can_suspend
+			&& !ohci->ed_rm_list
+			&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+					& ohci->hc_control)
+				== OHCI_USB_OPER;
+#endif
+		if (hcd->uses_new_polling) {
+			hcd->poll_rh = 0;
+			/* use INTR_RHSC iff INTR_RD won't apply */
+			if (!can_suspend)
+				ohci_writel (ohci, OHCI_INTR_RHSC,
+						&ohci->regs->intrenable);
+		}
 	}
 	}
+
 done:
 done:
 	spin_unlock_irqrestore (&ohci->lock, flags);
 	spin_unlock_irqrestore (&ohci->lock, flags);
 
 
-#ifdef CONFIG_PM
-	/* save power by suspending idle root hubs;
+#ifdef	CONFIG_PM
+	/* save power by autosuspending idle root hubs;
 	 * INTR_RD wakes us when there's work
 	 * INTR_RD wakes us when there's work
 	 */
 	 */
-	if (can_suspend
-			&& !changed
-			&& !ohci->ed_rm_list
-			&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
-					& ohci->hc_control)
-				== OHCI_USB_OPER
-			&& time_after (jiffies, ohci->next_statechange)
-			&& usb_trylock_device (hcd->self.root_hub) == 0
-			) {
+	if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) {
 		ohci_vdbg (ohci, "autosuspend\n");
 		ohci_vdbg (ohci, "autosuspend\n");
 		(void) ohci_bus_suspend (hcd);
 		(void) ohci_bus_suspend (hcd);
 		usb_unlock_device (hcd->self.root_hub);
 		usb_unlock_device (hcd->self.root_hub);

+ 3 - 4
drivers/usb/host/ohci-lh7a404.c

@@ -173,11 +173,8 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
 	 * basic lifecycle operations
 	 * basic lifecycle operations
 	 */
 	 */
 	.start =		ohci_lh7a404_start,
 	.start =		ohci_lh7a404_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_lh7a404_suspend,  -- tbd */
-	/* resume:		ohci_lh7a404_resume,   -- tbd */
-#endif /*CONFIG_PM*/
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -196,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -244,6 +242,7 @@ static int ohci_hcd_lh7a404_drv_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_lh7a404_driver = {
 static struct platform_driver ohci_hcd_lh7a404_driver = {
 	.probe		= ohci_hcd_lh7a404_drv_probe,
 	.probe		= ohci_hcd_lh7a404_drv_probe,
 	.remove		= ohci_hcd_lh7a404_drv_remove,
 	.remove		= ohci_hcd_lh7a404_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_lh7a404_drv_suspend, */
 	/*.suspend	= ohci_hcd_lh7a404_drv_suspend, */
 	/*.resume	= ohci_hcd_lh7a404_drv_resume, */
 	/*.resume	= ohci_hcd_lh7a404_drv_resume, */
 	.driver		= {
 	.driver		= {

+ 0 - 1
drivers/usb/host/ohci-mem.c

@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
 	ohci->next_statechange = jiffies;
 	ohci->next_statechange = jiffies;
 	spin_lock_init (&ohci->lock);
 	spin_lock_init (&ohci->lock);
 	INIT_LIST_HEAD (&ohci->pending);
 	INIT_LIST_HEAD (&ohci->pending);
-	ohci->reboot_notifier.notifier_call = ohci_reboot;
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/

+ 77 - 43
drivers/usb/host/ohci-omap.c

@@ -4,7 +4,7 @@
  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
  * (C) Copyright 2000-2005 David Brownell
  * (C) Copyright 2000-2005 David Brownell
  * (C) Copyright 2002 Hewlett-Packard Company
  * (C) Copyright 2002 Hewlett-Packard Company
- * 
+ *
  * OMAP Bus Glue
  * OMAP Bus Glue
  *
  *
  * Modified for OMAP by Tony Lindgren <tony@atomide.com>
  * Modified for OMAP by Tony Lindgren <tony@atomide.com>
@@ -66,15 +66,20 @@ extern int usb_disabled(void);
 extern int ocpi_enable(void);
 extern int ocpi_enable(void);
 
 
 static struct clk *usb_host_ck;
 static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+static int host_enabled;
+static int host_initialized;
 
 
 static void omap_ohci_clock_power(int on)
 static void omap_ohci_clock_power(int on)
 {
 {
 	if (on) {
 	if (on) {
+		clk_enable(usb_dc_ck);
 		clk_enable(usb_host_ck);
 		clk_enable(usb_host_ck);
 		/* guesstimate for T5 == 1x 32K clock + APLL lock time */
 		/* guesstimate for T5 == 1x 32K clock + APLL lock time */
 		udelay(100);
 		udelay(100);
 	} else {
 	} else {
 		clk_disable(usb_host_ck);
 		clk_disable(usb_host_ck);
+		clk_disable(usb_dc_ck);
 	}
 	}
 }
 }
 
 
@@ -87,14 +92,14 @@ static int omap_ohci_transceiver_power(int on)
 	if (on) {
 	if (on) {
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
-				| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
+				| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
 		else if (machine_is_omap_osk())
 		else if (machine_is_omap_osk())
 			tps65010_set_gpio_out_value(GPIO1, LOW);
 			tps65010_set_gpio_out_value(GPIO1, LOW);
 	} else {
 	} else {
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
 			fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
-				& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), 
+				& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
 			       INNOVATOR_FPGA_CAM_USB_CONTROL);
 		else if (machine_is_omap_osk())
 		else if (machine_is_omap_osk())
 			tps65010_set_gpio_out_value(GPIO1, HIGH);
 			tps65010_set_gpio_out_value(GPIO1, HIGH);
@@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_ARCH_OMAP15XX
 /*
 /*
  * OMAP-1510 specific Local Bus clock on/off
  * OMAP-1510 specific Local Bus clock on/off
  */
  */
@@ -121,8 +127,8 @@ static int omap_1510_local_bus_power(int on)
 /*
 /*
  * OMAP-1510 specific Local Bus initialization
  * OMAP-1510 specific Local Bus initialization
  * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
  * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
- *       See also arch/mach-omap/memory.h for __virt_to_dma() and 
- *       __dma_to_virt() which need to match with the physical 
+ *       See also arch/mach-omap/memory.h for __virt_to_dma() and
+ *       __dma_to_virt() which need to match with the physical
  *       Local Bus address below.
  *       Local Bus address below.
  */
  */
 static int omap_1510_local_bus_init(void)
 static int omap_1510_local_bus_init(void)
@@ -130,7 +136,7 @@ static int omap_1510_local_bus_init(void)
 	unsigned int tlb;
 	unsigned int tlb;
 	unsigned long lbaddr, physaddr;
 	unsigned long lbaddr, physaddr;
 
 
-	omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, 
+	omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
 	       OMAP1510_LB_CLOCK_DIV);
 	       OMAP1510_LB_CLOCK_DIV);
 
 
 	/* Configure the Local Bus MMU table */
 	/* Configure the Local Bus MMU table */
@@ -138,7 +144,7 @@ static int omap_1510_local_bus_init(void)
 		lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
 		lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
 		physaddr = tlb * 0x00100000 + PHYS_OFFSET;
 		physaddr = tlb * 0x00100000 + PHYS_OFFSET;
 		omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
 		omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
-		omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, 
+		omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
 		       OMAP1510_LB_MMU_CAM_L);
 		       OMAP1510_LB_MMU_CAM_L);
 		omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
 		omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
 		omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
 		omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
@@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void)
 
 
 	return 0;
 	return 0;
 }
 }
+#else
+#define omap_1510_local_bus_power(x)	{}
+#define omap_1510_local_bus_init()	{}
+#endif
 
 
 #ifdef	CONFIG_USB_OTG
 #ifdef	CONFIG_USB_OTG
 
 
@@ -173,13 +183,14 @@ static void start_hnp(struct ohci_hcd *ohci)
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
+static int ohci_omap_init(struct usb_hcd *hcd)
 {
 {
-	struct omap_usb_config	*config = pdev->dev.platform_data;
+	struct ohci_hcd		*ohci = hcd_to_ohci(hcd);
+	struct omap_usb_config	*config = hcd->self.controller->platform_data;
 	int			need_transceiver = (config->otg != 0);
 	int			need_transceiver = (config->otg != 0);
 	int			ret;
 	int			ret;
 
 
-	dev_dbg(&pdev->dev, "starting USB Controller\n");
+	dev_dbg(hcd->self.controller, "starting USB Controller\n");
 
 
 	if (config->otg) {
 	if (config->otg) {
 		ohci_to_hcd(ohci)->self.otg_port = config->otg;
 		ohci_to_hcd(ohci)->self.otg_port = config->otg;
@@ -200,7 +211,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 		if (ohci->transceiver) {
 		if (ohci->transceiver) {
 			int	status = otg_set_host(ohci->transceiver,
 			int	status = otg_set_host(ohci->transceiver,
 						&ohci_to_hcd(ohci)->self);
 						&ohci_to_hcd(ohci)->self);
-			dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
+			dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n",
 					ohci->transceiver->label, status);
 					ohci->transceiver->label, status);
 			if (status) {
 			if (status) {
 				if (ohci->transceiver)
 				if (ohci->transceiver)
@@ -208,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 				return status;
 				return status;
 			}
 			}
 		} else {
 		} else {
-			dev_err(&pdev->dev, "can't find transceiver\n");
+			dev_err(hcd->self.controller, "can't find transceiver\n");
 			return -ENODEV;
 			return -ENODEV;
 		}
 		}
 	}
 	}
@@ -247,6 +258,10 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 		}
 		}
 		ohci_writel(ohci, rh, &ohci->regs->roothub.a);
 		ohci_writel(ohci, rh, &ohci->regs->roothub.a);
 		distrust_firmware = 0;
 		distrust_firmware = 0;
+	} else if (machine_is_nokia770()) {
+		/* We require a self-powered hub, which should have
+		 * plenty of power. */
+		ohci_to_hcd(ohci)->power_budget = 0;
 	}
 	}
 
 
 	/* FIXME khubd hub requests should manage power switching */
 	/* FIXME khubd hub requests should manage power switching */
@@ -260,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static void omap_stop_hc(struct platform_device *pdev)
+static void ohci_omap_stop(struct usb_hcd *hcd)
 {
 {
-	dev_dbg(&pdev->dev, "stopping USB Controller\n");
+	dev_dbg(hcd->self.controller, "stopping USB Controller\n");
 	omap_ohci_clock_power(0);
 	omap_ohci_clock_power(0);
 }
 }
 
 
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
-
 /**
 /**
  * usb_hcd_omap_probe - initialize OMAP-based HCDs
  * usb_hcd_omap_probe - initialize OMAP-based HCDs
  * Context: !in_interrupt()
  * Context: !in_interrupt()
@@ -283,7 +292,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
  * then invokes the start() method for the HCD associated with it
  * then invokes the start() method for the HCD associated with it
  * through the hotplug entry's driver_data.
  * through the hotplug entry's driver_data.
  */
  */
-int usb_hcd_omap_probe (const struct hc_driver *driver,
+static int usb_hcd_omap_probe (const struct hc_driver *driver,
 			  struct platform_device *pdev)
 			  struct platform_device *pdev)
 {
 {
 	int retval, irq;
 	int retval, irq;
@@ -291,12 +300,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	struct ohci_hcd *ohci;
 	struct ohci_hcd *ohci;
 
 
 	if (pdev->num_resources != 2) {
 	if (pdev->num_resources != 2) {
-		printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", 
+		printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
 		       pdev->num_resources);
 		       pdev->num_resources);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	if (pdev->resource[0].flags != IORESOURCE_MEM 
+	if (pdev->resource[0].flags != IORESOURCE_MEM
 			|| pdev->resource[1].flags != IORESOURCE_IRQ) {
 			|| pdev->resource[1].flags != IORESOURCE_IRQ) {
 		printk(KERN_ERR "hcd probe: invalid resource type\n");
 		printk(KERN_ERR "hcd probe: invalid resource type\n");
 		return -ENODEV;
 		return -ENODEV;
@@ -306,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	if (IS_ERR(usb_host_ck))
 	if (IS_ERR(usb_host_ck))
 		return PTR_ERR(usb_host_ck);
 		return PTR_ERR(usb_host_ck);
 
 
+	if (!cpu_is_omap1510())
+		usb_dc_ck = clk_get(0, "usb_dc_ck");
+	else
+		usb_dc_ck = clk_get(0, "lb_ck");
+
+	if (IS_ERR(usb_dc_ck)) {
+		clk_put(usb_host_ck);
+		return PTR_ERR(usb_dc_ck);
+	}
+
+
 	hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
 	hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
 	if (!hcd) {
 	if (!hcd) {
 		retval = -ENOMEM;
 		retval = -ENOMEM;
@@ -325,9 +345,8 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	ohci = hcd_to_ohci(hcd);
 	ohci = hcd_to_ohci(hcd);
 	ohci_hcd_init(ohci);
 	ohci_hcd_init(ohci);
 
 
-	retval = omap_start_hc(ohci, pdev);
-	if (retval < 0)
-		goto err2;
+	host_initialized = 0;
+	host_enabled = 1;
 
 
 	irq = platform_get_irq(pdev, 0);
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
 	if (irq < 0) {
@@ -335,15 +354,21 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 		goto err2;
 		goto err2;
 	}
 	}
 	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
 	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
-	if (retval == 0)
-		return retval;
+	if (retval)
+		goto err2;
+
+	host_initialized = 1;
+
+	if (!host_enabled)
+		omap_ohci_clock_power(0);
 
 
-	omap_stop_hc(pdev);
+	return 0;
 err2:
 err2:
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 err1:
 err1:
 	usb_put_hcd(hcd);
 	usb_put_hcd(hcd);
 err0:
 err0:
+	clk_put(usb_dc_ck);
 	clk_put(usb_host_ck);
 	clk_put(usb_host_ck);
 	return retval;
 	return retval;
 }
 }
@@ -359,31 +384,41 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
  * Reverses the effect of usb_hcd_omap_probe(), first invoking
  * Reverses the effect of usb_hcd_omap_probe(), first invoking
  * the HCD's stop() method.  It is always called from a thread
  * the HCD's stop() method.  It is always called from a thread
  * context, normally "rmmod", "apmd", or something similar.
  * context, normally "rmmod", "apmd", or something similar.
- *
  */
  */
-void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static inline void
+usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
 {
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+
 	usb_remove_hcd(hcd);
 	usb_remove_hcd(hcd);
+	if (ohci->transceiver) {
+		(void) otg_set_host(ohci->transceiver, 0);
+		put_device(ohci->transceiver->dev);
+	}
 	if (machine_is_omap_osk())
 	if (machine_is_omap_osk())
 		omap_free_gpio(9);
 		omap_free_gpio(9);
-	omap_stop_hc(pdev);
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
 	usb_put_hcd(hcd);
+	clk_put(usb_dc_ck);
 	clk_put(usb_host_ck);
 	clk_put(usb_host_ck);
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static int __devinit
+static int
 ohci_omap_start (struct usb_hcd *hcd)
 ohci_omap_start (struct usb_hcd *hcd)
 {
 {
 	struct omap_usb_config *config;
 	struct omap_usb_config *config;
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		ret;
 	int		ret;
 
 
+	if (!host_enabled)
+		return 0;
 	config = hcd->self.controller->platform_data;
 	config = hcd->self.controller->platform_data;
-	if (config->otg || config->rwc)
+	if (config->otg || config->rwc) {
+		ohci->hc_control = OHCI_CTRL_RWC;
 		writel(OHCI_CTRL_RWC, &ohci->regs->control);
 		writel(OHCI_CTRL_RWC, &ohci->regs->control);
+	}
 
 
 	if ((ret = ohci_run (ohci)) < 0) {
 	if ((ret = ohci_run (ohci)) < 0) {
 		dev_err(hcd->self.controller, "can't start\n");
 		dev_err(hcd->self.controller, "can't start\n");
@@ -409,8 +444,10 @@ static const struct hc_driver ohci_omap_hc_driver = {
 	/*
 	/*
 	 * basic lifecycle operations
 	 * basic lifecycle operations
 	 */
 	 */
+	.reset =		ohci_omap_init,
 	.start =		ohci_omap_start,
 	.start =		ohci_omap_start,
-	.stop =			ohci_stop,
+	.stop =			ohci_omap_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -429,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -446,13 +484,8 @@ static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
 static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
 static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
 {
 {
 	struct usb_hcd		*hcd = platform_get_drvdata(dev);
 	struct usb_hcd		*hcd = platform_get_drvdata(dev);
-	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 
 
 	usb_hcd_omap_remove(hcd, dev);
 	usb_hcd_omap_remove(hcd, dev);
-	if (ohci->transceiver) {
-		(void) otg_set_host(ohci->transceiver, 0);
-		put_device(ohci->transceiver->dev);
-	}
 	platform_set_drvdata(dev, NULL);
 	platform_set_drvdata(dev, NULL);
 
 
 	return 0;
 	return 0;
@@ -472,7 +505,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
 
 
 	omap_ohci_clock_power(0);
 	omap_ohci_clock_power(0);
 	ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
 	ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
-	dev->power.power_state = PMSG_SUSPEND;
+	dev->dev.power.power_state = PMSG_SUSPEND;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -485,8 +518,8 @@ static int ohci_omap_resume(struct platform_device *dev)
 	ohci->next_statechange = jiffies;
 	ohci->next_statechange = jiffies;
 
 
 	omap_ohci_clock_power(1);
 	omap_ohci_clock_power(1);
-	dev->power.power_state = PMSG_ON;
-	usb_hcd_resume_root_hub(dev_get_drvdata(dev));
+	dev->dev.power.power_state = PMSG_ON;
+	usb_hcd_resume_root_hub(platform_get_drvdata(dev));
 	return 0;
 	return 0;
 }
 }
 
 
@@ -500,6 +533,7 @@ static int ohci_omap_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_omap_driver = {
 static struct platform_driver ohci_hcd_omap_driver = {
 	.probe		= ohci_hcd_omap_drv_probe,
 	.probe		= ohci_hcd_omap_drv_probe,
 	.remove		= ohci_hcd_omap_drv_remove,
 	.remove		= ohci_hcd_omap_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.suspend	= ohci_omap_suspend,
 	.suspend	= ohci_omap_suspend,
 	.resume		= ohci_omap_resume,
 	.resume		= ohci_omap_resume,

+ 7 - 1
drivers/usb/host/ohci-pci.c

@@ -176,11 +176,14 @@ static const struct hc_driver ohci_pci_hc_driver = {
 	 */
 	 */
 	.reset =		ohci_pci_reset,
 	.reset =		ohci_pci_reset,
 	.start =		ohci_pci_start,
 	.start =		ohci_pci_start,
+	.stop =			ohci_stop,
+	.shutdown =		ohci_shutdown,
+
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
+	/* these suspend/resume entries are for upstream PCI glue ONLY */
 	.suspend =		ohci_pci_suspend,
 	.suspend =		ohci_pci_suspend,
 	.resume =		ohci_pci_resume,
 	.resume =		ohci_pci_resume,
 #endif
 #endif
-	.stop =			ohci_stop,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -199,6 +202,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -229,6 +233,8 @@ static struct pci_driver ohci_pci_driver = {
 	.suspend =	usb_hcd_pci_suspend,
 	.suspend =	usb_hcd_pci_suspend,
 	.resume =	usb_hcd_pci_resume,
 	.resume =	usb_hcd_pci_resume,
 #endif
 #endif
+
+	.shutdown =	usb_hcd_pci_shutdown,
 };
 };
 
 
  
  

+ 476 - 0
drivers/usb/host/ohci-pnx4008.c

@@ -0,0 +1,476 @@
+/*
+ * drivers/usb/host/ohci-pnx4008.c
+ *
+ * driver for Philips PNX4008 USB Host
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>
+ * 	    Vitaly Wool <vitalywool@gmail.com>
+ *
+ * register initialization is based on code examples provided by Philips
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * NOTE: This driver does not have suspend/resume functionality
+ * This driver is intended for engineering development purposes only
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/platform.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+
+#define USB_CTRL 	IO_ADDRESS(PNX4008_PWRMAN_BASE + 0x64)
+
+/* USB_CTRL bit defines */
+#define USB_SLAVE_HCLK_EN	(1 << 24)
+#define USB_HOST_NEED_CLK_EN	(1 << 21)
+
+#define USB_OTG_CLK_CTRL	IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF4)
+#define USB_OTG_CLK_STAT	IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF8)
+
+/* USB_OTG_CLK_CTRL bit defines */
+#define AHB_M_CLOCK_ON		(1 << 4)
+#define OTG_CLOCK_ON		(1 << 3)
+#define I2C_CLOCK_ON		(1 << 2)
+#define DEV_CLOCK_ON		(1 << 1)
+#define HOST_CLOCK_ON		(1 << 0)
+
+#define USB_OTG_STAT_CONTROL	IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0x110)
+
+/* USB_OTG_STAT_CONTROL bit defines */
+#define TRANSPARENT_I2C_EN	(1 << 7)
+#define HOST_EN			(1 << 0)
+
+/* ISP1301 USB transceiver I2C registers */
+#define	ISP1301_MODE_CONTROL_1		0x04	/* u8 read, set, +1 clear */
+
+#define	MC1_SPEED_REG		(1 << 0)
+#define	MC1_SUSPEND_REG		(1 << 1)
+#define	MC1_DAT_SE0		(1 << 2)
+#define	MC1_TRANSPARENT		(1 << 3)
+#define	MC1_BDIS_ACON_EN	(1 << 4)
+#define	MC1_OE_INT_EN		(1 << 5)
+#define	MC1_UART_EN		(1 << 6)
+#define	MC1_MASK		0x7f
+
+#define	ISP1301_MODE_CONTROL_2		0x12	/* u8 read, set, +1 clear */
+
+#define	MC2_GLOBAL_PWR_DN	(1 << 0)
+#define	MC2_SPD_SUSP_CTRL	(1 << 1)
+#define	MC2_BI_DI		(1 << 2)
+#define	MC2_TRANSP_BDIR0	(1 << 3)
+#define	MC2_TRANSP_BDIR1	(1 << 4)
+#define	MC2_AUDIO_EN		(1 << 5)
+#define	MC2_PSW_EN		(1 << 6)
+#define	MC2_EN2V7		(1 << 7)
+
+#define	ISP1301_OTG_CONTROL_1		0x06	/* u8 read, set, +1 clear */
+#	define	OTG1_DP_PULLUP		(1 << 0)
+#	define	OTG1_DM_PULLUP		(1 << 1)
+#	define	OTG1_DP_PULLDOWN	(1 << 2)
+#	define	OTG1_DM_PULLDOWN	(1 << 3)
+#	define	OTG1_ID_PULLDOWN	(1 << 4)
+#	define	OTG1_VBUS_DRV		(1 << 5)
+#	define	OTG1_VBUS_DISCHRG	(1 << 6)
+#	define	OTG1_VBUS_CHRG		(1 << 7)
+#define	ISP1301_OTG_STATUS		0x10	/* u8 readonly */
+#	define	OTG_B_SESS_END		(1 << 6)
+#	define	OTG_B_SESS_VLD		(1 << 7)
+
+#define ISP1301_I2C_ADDR 0x2C
+
+#define ISP1301_I2C_MODE_CONTROL_1 0x4
+#define ISP1301_I2C_MODE_CONTROL_2 0x12
+#define ISP1301_I2C_OTG_CONTROL_1 0x6
+#define ISP1301_I2C_OTG_CONTROL_2 0x10
+#define ISP1301_I2C_INTERRUPT_SOURCE 0x8
+#define ISP1301_I2C_INTERRUPT_LATCH 0xA
+#define ISP1301_I2C_INTERRUPT_FALLING 0xC
+#define ISP1301_I2C_INTERRUPT_RISING 0xE
+#define ISP1301_I2C_REG_CLEAR_ADDR 1
+
+struct i2c_driver isp1301_driver;
+struct i2c_client *isp1301_i2c_client;
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_clk;
+
+static int isp1301_probe(struct i2c_adapter *adap);
+static int isp1301_detach(struct i2c_client *client);
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg);
+
+static unsigned short normal_i2c[] =
+    { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
+static unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+	.normal_i2c = normal_i2c,
+	.probe = dummy_i2c_addrlist,
+	.ignore = dummy_i2c_addrlist,
+};
+
+struct i2c_driver isp1301_driver = {
+	.id = I2C_DRIVERID_I2CDEV,	/* Fake Id */
+	.class = I2C_CLASS_HWMON,
+	.attach_adapter = isp1301_probe,
+	.detach_client = isp1301_detach,
+	.command = isp1301_command
+};
+
+static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct i2c_client *c;
+
+	c = (struct i2c_client *)kzalloc(sizeof(*c), SLAB_KERNEL);
+
+	if (!c)
+		return -ENOMEM;
+
+	strcpy(c->name, "isp1301");
+	c->flags = 0;
+	c->addr = addr;
+	c->adapter = adap;
+	c->driver = &isp1301_driver;
+
+	isp1301_i2c_client = c;
+
+	return i2c_attach_client(c);
+}
+
+static int isp1301_probe(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, isp1301_attach);
+}
+
+static int isp1301_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(isp1301_i2c_client);
+	return 0;
+}
+
+/* No commands defined */
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+			   void *arg)
+{
+	return 0;
+}
+
+static void i2c_write(u8 buf, u8 subaddr)
+{
+	char tmpbuf[2];
+
+	tmpbuf[0] = subaddr;	/*register number */
+	tmpbuf[1] = buf;	/*register data */
+	i2c_master_send(isp1301_i2c_client, &tmpbuf[0], 2);
+}
+
+static void isp1301_configure(void)
+{
+	/* PNX4008 only supports DAT_SE0 USB mode */
+	/* PNX4008 R2A requires setting the MAX603 to output 3.6V */
+	/* Power up externel charge-pump */
+
+	i2c_write(MC1_DAT_SE0 | MC1_SPEED_REG, ISP1301_I2C_MODE_CONTROL_1);
+	i2c_write(~(MC1_DAT_SE0 | MC1_SPEED_REG),
+		  ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+	i2c_write(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL,
+		  ISP1301_I2C_MODE_CONTROL_2);
+	i2c_write(~(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL),
+		  ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR);
+	i2c_write(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN,
+		  ISP1301_I2C_OTG_CONTROL_1);
+	i2c_write(~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN),
+		  ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+	i2c_write(0xFF,
+		  ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR);
+	i2c_write(0xFF,
+		  ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR);
+	i2c_write(0xFF,
+		  ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR);
+
+}
+
+static inline void isp1301_vbus_on(void)
+{
+	i2c_write(OTG1_VBUS_DRV, ISP1301_I2C_OTG_CONTROL_1);
+}
+
+static inline void isp1301_vbus_off(void)
+{
+	i2c_write(OTG1_VBUS_DRV,
+		  ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+}
+
+static void pnx4008_start_hc(void)
+{
+	unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+	__raw_writel(tmp, USB_OTG_STAT_CONTROL);
+	isp1301_vbus_on();
+}
+
+static void pnx4008_stop_hc(void)
+{
+	unsigned long tmp;
+	isp1301_vbus_off();
+	tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
+	__raw_writel(tmp, USB_OTG_STAT_CONTROL);
+}
+
+static int __devinit ohci_pnx4008_start(struct usb_hcd *hcd)
+{
+	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+	int ret;
+
+	if ((ret = ohci_init(ohci)) < 0)
+		return ret;
+
+	if ((ret = ohci_run(ohci)) < 0) {
+		dev_err(hcd->self.controller, "can't start\n");
+		ohci_stop(hcd);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct hc_driver ohci_pnx4008_hc_driver = {
+	.description = hcd_name,
+	.product_desc =		"pnx4008 OHCI",
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq = ohci_irq,
+	.flags = HCD_USB11 | HCD_MEMORY,
+
+	.hcd_priv_size =	sizeof(struct ohci_hcd),
+	/*
+	 * basic lifecycle operations
+	 */
+	.start = ohci_pnx4008_start,
+	.stop = ohci_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue = ohci_urb_enqueue,
+	.urb_dequeue = ohci_urb_dequeue,
+	.endpoint_disable = ohci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = ohci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data = ohci_hub_status_data,
+	.hub_control = ohci_hub_control,
+
+	.start_port_reset = ohci_start_port_reset,
+};
+
+#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON)
+
+static void pnx4008_set_usb_bits(void)
+{
+	start_int_set_falling_edge(SE_USB_OTG_ATX_INT_N);
+	start_int_ack(SE_USB_OTG_ATX_INT_N);
+	start_int_umask(SE_USB_OTG_ATX_INT_N);
+
+	start_int_set_rising_edge(SE_USB_OTG_TIMER_INT);
+	start_int_ack(SE_USB_OTG_TIMER_INT);
+	start_int_umask(SE_USB_OTG_TIMER_INT);
+
+	start_int_set_rising_edge(SE_USB_I2C_INT);
+	start_int_ack(SE_USB_I2C_INT);
+	start_int_umask(SE_USB_I2C_INT);
+
+	start_int_set_rising_edge(SE_USB_INT);
+	start_int_ack(SE_USB_INT);
+	start_int_umask(SE_USB_INT);
+
+	start_int_set_rising_edge(SE_USB_NEED_CLK_INT);
+	start_int_ack(SE_USB_NEED_CLK_INT);
+	start_int_umask(SE_USB_NEED_CLK_INT);
+
+	start_int_set_rising_edge(SE_USB_AHB_NEED_CLK_INT);
+	start_int_ack(SE_USB_AHB_NEED_CLK_INT);
+	start_int_umask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static void pnx4008_unset_usb_bits(void)
+{
+	start_int_mask(SE_USB_OTG_ATX_INT_N);
+	start_int_mask(SE_USB_OTG_TIMER_INT);
+	start_int_mask(SE_USB_I2C_INT);
+	start_int_mask(SE_USB_INT);
+	start_int_mask(SE_USB_NEED_CLK_INT);
+	start_int_mask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = 0;
+	struct ohci_hcd *ohci;
+	const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
+
+	int ret = 0, irq;
+
+	dev_dbg(&pdev->dev, "%s: " DRIVER_INFO " (pnx4008)\n", hcd_name);
+	if (usb_disabled()) {
+		err("USB is disabled");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (pdev->num_resources != 2
+	    || pdev->resource[0].flags != IORESOURCE_MEM
+	    || pdev->resource[1].flags != IORESOURCE_IRQ) {
+		err("Invalid resource configuration");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* Enable AHB slave USB clock, needed for further USB clock control */
+	__raw_writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
+
+	ret = i2c_add_driver(&isp1301_driver);
+	if (ret < 0) {
+		err("failed to connect I2C to ISP1301 USB Transceiver");
+		goto out;
+	}
+
+	isp1301_configure();
+
+	/* Enable USB PLL */
+	usb_clk = clk_get(&pdev->dev, "ck_pll5");
+	if (IS_ERR(usb_clk)) {
+		err("failed to acquire USB PLL");
+		ret = PTR_ERR(usb_clk);
+		goto out1;
+	}
+
+	ret = clk_enable(usb_clk);
+	if (ret < 0) {
+		err("failed to start USB PLL");
+		goto out2;
+	}
+
+	ret = clk_set_rate(usb_clk, 48000);
+	if (ret < 0) {
+		err("failed to set USB clock rate");
+		goto out3;
+	}
+
+	__raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+	/* Set to enable all needed USB clocks */
+	__raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL);
+
+	while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
+	       USB_CLOCK_MASK) ;
+
+	hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+	if (!hcd) {
+		err("Failed to allocate HC buffer");
+		ret = -ENOMEM;
+		goto out3;
+	}
+
+	/* Set all USB bits in the Start Enable register */
+	pnx4008_set_usb_bits();
+
+	hcd->rsrc_start = pdev->resource[0].start;
+	hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		dev_dbg(&pdev->dev, "request_mem_region failed\n");
+		ret =  -ENOMEM;
+		goto out4;
+	}
+	hcd->regs = (void __iomem *)pdev->resource[0].start;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = -ENXIO;
+		goto out4;
+	}
+
+	hcd->self.hcpriv = (void *)hcd;
+
+	pnx4008_start_hc();
+	platform_set_drvdata(pdev, hcd);
+	ohci = hcd_to_ohci(hcd);
+	ohci_hcd_init(ohci);
+
+	dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+	ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+	if (ret == 0)
+		return ret;
+
+	pnx4008_stop_hc();
+out4:
+	pnx4008_unset_usb_bits();
+	usb_put_hcd(hcd);
+out3:
+	clk_disable(usb_clk);
+out2:
+	clk_put(usb_clk);
+out1:
+	i2c_del_driver(&isp1301_driver);
+out:
+	return ret;
+}
+
+static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+	pnx4008_stop_hc();
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+	pnx4008_unset_usb_bits();
+	clk_disable(usb_clk);
+	clk_put(usb_clk);
+	i2c_del_driver(&isp1301_driver);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver usb_hcd_pnx4008_driver = {
+	.driver = {
+		.name = "usb-ohci",
+	},
+	.probe = usb_hcd_pnx4008_probe,
+	.remove = usb_hcd_pnx4008_remove,
+};
+
+static int __init usb_hcd_pnx4008_init(void)
+{
+	return platform_driver_register(&usb_hcd_pnx4008_driver);
+}
+
+static void __exit usb_hcd_pnx4008_cleanup(void)
+{
+	return platform_driver_unregister(&usb_hcd_pnx4008_driver);
+}
+
+module_init(usb_hcd_pnx4008_init);
+module_exit(usb_hcd_pnx4008_cleanup);

+ 3 - 0
drivers/usb/host/ohci-ppc-soc.c

@@ -148,6 +148,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
 	 */
 	 */
 	.start =		ohci_ppc_soc_start,
 	.start =		ohci_ppc_soc_start,
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -166,6 +167,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -195,6 +197,7 @@ static int ohci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_ppc_soc_driver = {
 static struct platform_driver ohci_hcd_ppc_soc_driver = {
 	.probe		= ohci_hcd_ppc_soc_drv_probe,
 	.probe		= ohci_hcd_ppc_soc_drv_probe,
 	.remove		= ohci_hcd_ppc_soc_drv_remove,
 	.remove		= ohci_hcd_ppc_soc_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	/*.suspend	= ohci_hcd_ppc_soc_drv_suspend,*/
 	/*.suspend	= ohci_hcd_ppc_soc_drv_suspend,*/
 	/*.resume	= ohci_hcd_ppc_soc_drv_resume,*/
 	/*.resume	= ohci_hcd_ppc_soc_drv_resume,*/

+ 3 - 0
drivers/usb/host/ohci-pxa27x.c

@@ -270,6 +270,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
 	 */
 	 */
 	.start =		ohci_pxa27x_start,
 	.start =		ohci_pxa27x_start,
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -288,6 +289,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef  CONFIG_PM
 #ifdef  CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -357,6 +359,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_pxa27x_driver = {
 static struct platform_driver ohci_hcd_pxa27x_driver = {
 	.probe		= ohci_hcd_pxa27x_drv_probe,
 	.probe		= ohci_hcd_pxa27x_drv_probe,
 	.remove		= ohci_hcd_pxa27x_drv_remove,
 	.remove		= ohci_hcd_pxa27x_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	.suspend	= ohci_hcd_pxa27x_drv_suspend, 
 	.suspend	= ohci_hcd_pxa27x_drv_suspend, 
 	.resume		= ohci_hcd_pxa27x_drv_resume,
 	.resume		= ohci_hcd_pxa27x_drv_resume,

+ 4 - 1
drivers/usb/host/ohci-s3c2410.c

@@ -370,7 +370,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
 		goto err_mem;
 		goto err_mem;
 	}
 	}
 
 
-	usb_clk = clk_get(&dev->dev, "upll");
+	usb_clk = clk_get(&dev->dev, "usb-bus-host");
 	if (IS_ERR(usb_clk)) {
 	if (IS_ERR(usb_clk)) {
 		dev_err(&dev->dev, "cannot get usb-host clock\n");
 		dev_err(&dev->dev, "cannot get usb-host clock\n");
 		retval = -ENOENT;
 		retval = -ENOENT;
@@ -447,6 +447,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
 	 */
 	 */
 	.start =		ohci_s3c2410_start,
 	.start =		ohci_s3c2410_start,
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 
 	/*
 	/*
 	 * managing i/o requests and associated device resources
 	 * managing i/o requests and associated device resources
@@ -465,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_s3c2410_hub_status_data,
 	.hub_status_data =	ohci_s3c2410_hub_status_data,
 	.hub_control =		ohci_s3c2410_hub_control,
 	.hub_control =		ohci_s3c2410_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,
@@ -490,6 +492,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
 static struct platform_driver ohci_hcd_s3c2410_driver = {
 static struct platform_driver ohci_hcd_s3c2410_driver = {
 	.probe		= ohci_hcd_s3c2410_drv_probe,
 	.probe		= ohci_hcd_s3c2410_drv_probe,
 	.remove		= ohci_hcd_s3c2410_drv_remove,
 	.remove		= ohci_hcd_s3c2410_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_s3c2410_drv_suspend, */
 	/*.suspend	= ohci_hcd_s3c2410_drv_suspend, */
 	/*.resume	= ohci_hcd_s3c2410_drv_resume, */
 	/*.resume	= ohci_hcd_s3c2410_drv_resume, */
 	.driver		= {
 	.driver		= {

+ 1 - 4
drivers/usb/host/ohci-sa1111.c

@@ -212,10 +212,6 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
 	 * basic lifecycle operations
 	 * basic lifecycle operations
 	 */
 	 */
 	.start =		ohci_sa1111_start,
 	.start =		ohci_sa1111_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_sa1111_suspend,  -- tbd */
-	/* resume:		ohci_sa1111_resume,   -- tbd */
-#endif
 	.stop =			ohci_stop,
 	.stop =			ohci_stop,
 
 
 	/*
 	/*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
 	 */
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
 	.bus_resume =		ohci_bus_resume,

+ 1 - 3
drivers/usb/host/ohci.h

@@ -159,7 +159,7 @@ static const int cc_to_error [16] = {
 	/* Bit Stuff  */               -EPROTO,
 	/* Bit Stuff  */               -EPROTO,
 	/* Data Togg  */               -EILSEQ,
 	/* Data Togg  */               -EILSEQ,
 	/* Stall      */               -EPIPE,
 	/* Stall      */               -EPIPE,
-	/* DevNotResp */               -ETIMEDOUT,
+	/* DevNotResp */               -ETIME,
 	/* PIDCheck   */               -EPROTO,
 	/* PIDCheck   */               -EPROTO,
 	/* UnExpPID   */               -EPROTO,
 	/* UnExpPID   */               -EPROTO,
 	/* DataOver   */               -EOVERFLOW,
 	/* DataOver   */               -EOVERFLOW,
@@ -389,8 +389,6 @@ struct ohci_hcd {
 	unsigned long		next_statechange;	/* suspend/resume */
 	unsigned long		next_statechange;	/* suspend/resume */
 	u32			fminterval;		/* saved register */
 	u32			fminterval;		/* saved register */
 
 
-	struct notifier_block	reboot_notifier;
-
 	unsigned long		flags;		/* for HC bugs */
 	unsigned long		flags;		/* for HC bugs */
 #define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
 #define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
 #define	OHCI_QUIRK_SUPERIO	0x02			/* natsemi */
 #define	OHCI_QUIRK_SUPERIO	0x02			/* natsemi */

+ 2 - 2
drivers/usb/host/sl811-hcd.c

@@ -597,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
 	/* error? retry, until "3 strikes" */
 	/* error? retry, until "3 strikes" */
 	} else if (++ep->error_count >= 3) {
 	} else if (++ep->error_count >= 3) {
 		if (status & SL11H_STATMASK_TMOUT)
 		if (status & SL11H_STATMASK_TMOUT)
-			urbstat = -ETIMEDOUT;
+			urbstat = -ETIME;
 		else if (status & SL11H_STATMASK_OVF)
 		else if (status & SL11H_STATMASK_OVF)
 			urbstat = -EOVERFLOW;
 			urbstat = -EOVERFLOW;
 		else
 		else
@@ -1517,7 +1517,7 @@ static int proc_sl811h_open(struct inode *inode, struct file *file)
 	return single_open(file, proc_sl811h_show, PDE(inode)->data);
 	return single_open(file, proc_sl811h_show, PDE(inode)->data);
 }
 }
 
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_sl811h_open,
 	.open		= proc_sl811h_open,
 	.read		= seq_read,
 	.read		= seq_read,
 	.llseek		= seq_lseek,
 	.llseek		= seq_lseek,

+ 3295 - 0
drivers/usb/host/u132-hcd.c

@@ -0,0 +1,3295 @@
+/*
+* Host Controller Driver for the Elan Digital Systems U132 adapter
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.com
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB host drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.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/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/pci_ids.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include "../core/hcd.h"
+#include "ohci.h"
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+        OHCI_INTR_WDH)
+MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
+MODULE_DESCRIPTION("U132 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+INT_MODULE_PARM(testing, 0);
+/* Some boards misreport power switching/overcurrent*/
+static int distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+        "t setup");
+DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+/*
+* u132_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore u132_module_lock;
+static int u132_exiting = 0;
+static int u132_instances = 0;
+static struct list_head u132_static_list;
+/*
+* end of the global variables protected by u132_module_lock
+*/
+static struct workqueue_struct *workqueue;
+#define MAX_U132_PORTS 7
+#define MAX_U132_ADDRS 128
+#define MAX_U132_UDEVS 4
+#define MAX_U132_ENDPS 100
+#define MAX_U132_RINGS 4
+static const char *cc_to_text[16] = {
+        "No Error ",
+        "CRC Error ",
+        "Bit Stuff ",
+        "Data Togg ",
+        "Stall ",
+        "DevNotResp ",
+        "PIDCheck ",
+        "UnExpPID ",
+        "DataOver ",
+        "DataUnder ",
+        "(for hw) ",
+        "(for hw) ",
+        "BufferOver ",
+        "BuffUnder ",
+        "(for HCD) ",
+        "(for HCD) "
+};
+struct u132_port {
+        struct u132 *u132;
+        int reset;
+        int enable;
+        int power;
+        int Status;
+};
+struct u132_addr {
+        u8 address;
+};
+struct u132_udev {
+        struct kref kref;
+        struct usb_device *usb_device;
+        u8 enumeration;
+        u8 udev_number;
+        u8 usb_addr;
+        u8 portnumber;
+        u8 endp_number_in[16];
+        u8 endp_number_out[16];
+};
+#define ENDP_QUEUE_SHIFT 3
+#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
+#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
+struct u132_urbq {
+        struct list_head urb_more;
+        struct urb *urb;
+};
+struct u132_spin {
+        spinlock_t slock;
+};
+struct u132_endp {
+        struct kref kref;
+        u8 udev_number;
+        u8 endp_number;
+        u8 usb_addr;
+        u8 usb_endp;
+        struct u132 *u132;
+        struct list_head endp_ring;
+        struct u132_ring *ring;
+        unsigned toggle_bits:2;
+        unsigned active:1;
+        unsigned delayed:1;
+        unsigned input:1;
+        unsigned output:1;
+        unsigned pipetype:2;
+        unsigned dequeueing:1;
+        unsigned edset_flush:1;
+        unsigned spare_bits:14;
+        unsigned long jiffies;
+        struct usb_host_endpoint *hep;
+        struct u132_spin queue_lock;
+        u16 queue_size;
+        u16 queue_last;
+        u16 queue_next;
+        struct urb *urb_list[ENDP_QUEUE_SIZE];
+        struct list_head urb_more;
+        struct work_struct scheduler;
+};
+struct u132_ring {
+        unsigned in_use:1;
+        unsigned length:7;
+        u8 number;
+        struct u132 *u132;
+        struct u132_endp *curr_endp;
+        struct work_struct scheduler;
+};
+#define OHCI_QUIRK_AMD756 0x01
+#define OHCI_QUIRK_SUPERIO 0x02
+#define OHCI_QUIRK_INITRESET 0x04
+#define OHCI_BIG_ENDIAN 0x08
+#define OHCI_QUIRK_ZFMICRO 0x10
+struct u132 {
+        struct kref kref;
+        struct list_head u132_list;
+        struct semaphore sw_lock;
+        struct semaphore scheduler_lock;
+        struct u132_platform_data *board;
+        struct platform_device *platform_dev;
+        struct u132_ring ring[MAX_U132_RINGS];
+        int sequence_num;
+        int going;
+        int power;
+        int reset;
+        int num_ports;
+        u32 hc_control;
+        u32 hc_fminterval;
+        u32 hc_roothub_status;
+        u32 hc_roothub_a;
+        u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
+        int flags;
+        unsigned long next_statechange;
+        struct work_struct monitor;
+        int num_endpoints;
+        struct u132_addr addr[MAX_U132_ADDRS];
+        struct u132_udev udev[MAX_U132_UDEVS];
+        struct u132_port port[MAX_U132_PORTS];
+        struct u132_endp *endp[MAX_U132_ENDPS];
+};
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data);
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, u8 addressofs,
+        u8 width, u32 *data);
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, u8 addressofs,
+        u8 width, u32 data);
+/*
+* these can not be inlines because we need the structure offset!!
+* Does anyone have a better way?????
+*/
+#define u132_read_pcimem(u132, member, data) \
+        usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0, data);
+#define u132_write_pcimem(u132, member, data) \
+        usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0, data);
+#define u132_write_pcimem_byte(u132, member, data) \
+        usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+        ohci_regs, member), 0x0e, data);
+static inline struct u132 *udev_to_u132(struct u132_udev *udev)
+{
+        u8 udev_number = udev->udev_number;
+        return container_of(udev, struct u132, udev[udev_number]);
+}
+
+static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
+{
+        return (struct u132 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
+{
+        return container_of((void *)u132, struct usb_hcd, hcd_priv);
+}
+
+static inline void u132_disable(struct u132 *u132)
+{
+        u132_to_hcd(u132)->state = HC_STATE_HALT;
+}
+
+
+#define kref_to_u132(d) container_of(d, struct u132, kref)
+#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
+#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
+#include "../misc/usb_u132.h"
+static const char hcd_name[] = "u132_hcd";
+#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \
+        USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \
+        USB_PORT_STAT_C_RESET) << 16)
+static void u132_hcd_delete(struct kref *kref)
+{
+        struct u132 *u132 = kref_to_u132(kref);
+        struct platform_device *pdev = u132->platform_dev;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        u132->going += 1;
+        down(&u132_module_lock);
+        list_del_init(&u132->u132_list);
+        u132_instances -= 1;
+        up(&u132_module_lock);
+        dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
+                "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
+        usb_put_hcd(hcd);
+}
+
+static inline void u132_u132_put_kref(struct u132 *u132)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static inline void u132_u132_init_kref(struct u132 *u132)
+{
+        kref_init(&u132->kref);
+}
+
+static void u132_udev_delete(struct kref *kref)
+{
+        struct u132_udev *udev = kref_to_u132_udev(kref);
+        udev->udev_number = 0;
+        udev->usb_device = NULL;
+        udev->usb_addr = 0;
+        udev->enumeration = 0;
+}
+
+static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
+{
+        kref_put(&udev->kref, u132_udev_delete);
+}
+
+static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
+{
+        kref_get(&udev->kref);
+}
+
+static inline void u132_udev_init_kref(struct u132 *u132,
+        struct u132_udev *udev)
+{
+        kref_init(&udev->kref);
+}
+
+static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
+        unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &ring->scheduler, delta))
+                        return;
+        } else if (queue_work(workqueue, &ring->scheduler))
+                return;
+        kref_put(&u132->kref, u132_hcd_delete);
+        return;
+}
+
+static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
+        unsigned int delta)
+{
+        kref_get(&u132->kref);
+        u132_ring_requeue_work(u132, ring, delta);
+        return;
+}
+
+static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
+{
+        if (cancel_delayed_work(&ring->scheduler)) {
+                kref_put(&u132->kref, u132_hcd_delete);
+        }
+}
+
+static void u132_endp_delete(struct kref *kref)
+{
+        struct u132_endp *endp = kref_to_u132_endp(kref);
+        struct u132 *u132 = endp->u132;
+        u8 usb_addr = endp->usb_addr;
+        u8 usb_endp = endp->usb_endp;
+        u8 address = u132->addr[usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        u8 endp_number = endp->endp_number;
+        struct usb_host_endpoint *hep = endp->hep;
+        struct u132_ring *ring = endp->ring;
+        struct list_head *head = &endp->endp_ring;
+        ring->length -= 1;
+        if (endp == ring->curr_endp) {
+                if (list_empty(head)) {
+                        ring->curr_endp = NULL;
+                        list_del(head);
+                } else {
+                        struct u132_endp *next_endp = list_entry(head->next,
+                                struct u132_endp, endp_ring);
+                        ring->curr_endp = next_endp;
+                        list_del(head);
+        }} else
+                list_del(head);
+        if (endp->input) {
+                udev->endp_number_in[usb_endp] = 0;
+                u132_udev_put_kref(u132, udev);
+        }
+        if (endp->output) {
+                udev->endp_number_out[usb_endp] = 0;
+                u132_udev_put_kref(u132, udev);
+        }
+        u132->endp[endp_number - 1] = NULL;
+        hep->hcpriv = NULL;
+        kfree(endp);
+        u132_u132_put_kref(u132);
+}
+
+static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
+{
+        kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
+{
+        kref_get(&endp->kref);
+}
+
+static inline void u132_endp_init_kref(struct u132 *u132,
+        struct u132_endp *endp)
+{
+        kref_init(&endp->kref);
+        kref_get(&u132->kref);
+}
+
+static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
+        unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &endp->scheduler, delta))
+                        kref_get(&endp->kref);
+        } else if (queue_work(workqueue, &endp->scheduler))
+                kref_get(&endp->kref);
+        return;
+}
+
+static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
+{
+        if (cancel_delayed_work(&endp->scheduler))
+                kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_monitor_put_kref(struct u132 *u132)
+{
+        kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &u132->monitor, delta)) {
+                        kref_get(&u132->kref);
+                }
+        } else if (queue_work(workqueue, &u132->monitor))
+                kref_get(&u132->kref);
+        return;
+}
+
+static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
+{
+        if (delta > 0) {
+                if (queue_delayed_work(workqueue, &u132->monitor, delta))
+                        return;
+        } else if (queue_work(workqueue, &u132->monitor))
+                return;
+        kref_put(&u132->kref, u132_hcd_delete);
+        return;
+}
+
+static void u132_monitor_cancel_work(struct u132 *u132)
+{
+        if (cancel_delayed_work(&u132->monitor))
+                kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static int read_roothub_info(struct u132 *u132)
+{
+        u32 revision;
+        int retval;
+        retval = u132_read_pcimem(u132, revision, &revision);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+                        "ntrol\n", retval);
+                return retval;
+        } else if ((revision & 0xFF) == 0x10) {
+        } else if ((revision & 0xFF) == 0x11) {
+        } else {
+                dev_err(&u132->platform_dev->dev, "device revision is not valid"
+                        " %08X\n", revision);
+                return -ENODEV;
+        }
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+                        "ntrol\n", retval);
+                return retval;
+        }
+        retval = u132_read_pcimem(u132, roothub.status,
+                &u132->hc_roothub_status);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+                        "g roothub.status\n", retval);
+                return retval;
+        }
+        retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
+        if (retval) {
+                dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+                        "g roothub.a\n", retval);
+                return retval;
+        }
+        {
+                int I = u132->num_ports;
+                int i = 0;
+                while (I-- > 0) {
+                        retval = u132_read_pcimem(u132, roothub.portstatus[i],
+                                &u132->hc_roothub_portstatus[i]);
+                        if (retval) {
+                                dev_err(&u132->platform_dev->dev, "error %d acc"
+                                        "essing device roothub.portstatus[%d]\n"
+                                        , retval, i);
+                                return retval;
+                        } else
+                                i += 1;
+                }
+        }
+        return 0;
+}
+
+static void u132_hcd_monitor_work(void *data)
+{
+        struct u132 *u132 = data;
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                u132_monitor_put_kref(u132);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                u132_monitor_put_kref(u132);
+                return;
+        } else {
+                int retval;
+                down(&u132->sw_lock);
+                retval = read_roothub_info(u132);
+                if (retval) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        u132_disable(u132);
+                        u132->going = 1;
+                        up(&u132->sw_lock);
+                        usb_hc_died(hcd);
+                        ftdi_elan_gone_away(u132->platform_dev);
+                        u132_monitor_put_kref(u132);
+                        return;
+                } else {
+                        u132_monitor_requeue_work(u132, 500);
+                        up(&u132->sw_lock);
+                        return;
+                }
+        }
+}
+
+static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        struct u132_ring *ring;
+        unsigned long irqs;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        urb->error_count = 0;
+        urb->status = status;
+        urb->hcpriv = NULL;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->queue_next += 1;
+        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        } else {
+                struct list_head *next = endp->urb_more.next;
+                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+                        urb_more);
+                list_del(next);
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                        urbq->urb;
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                kfree(urbq);
+        } down(&u132->scheduler_lock);
+        ring = endp->ring;
+        ring->in_use = 0;
+        u132_ring_cancel_work(u132, ring);
+        u132_ring_queue_work(u132, ring, 0);
+        up(&u132->scheduler_lock);
+        u132_endp_put_kref(u132, endp);
+        usb_hcd_giveback_urb(hcd, urb, NULL);
+        return;
+}
+
+static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        u132_endp_put_kref(u132, endp);
+}
+
+static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb, int status)
+{
+        unsigned long irqs;
+        struct usb_hcd *hcd = u132_to_hcd(u132);
+        urb->error_count = 0;
+        urb->status = status;
+        urb->hcpriv = NULL;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->queue_next += 1;
+        if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        } else {
+                struct list_head *next = endp->urb_more.next;
+                struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+                        urb_more);
+                list_del(next);
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                        urbq->urb;
+                endp->active = 0;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                kfree(urbq);
+        } usb_hcd_giveback_urb(hcd, urb, NULL);
+        return;
+}
+
+static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
+                 urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
+                 urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
+                endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
+        struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null))
+{
+        return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
+                endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+
+/*
+* must not LOCK sw_lock
+*
+*/
+static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer + urb->actual_length;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length += len;
+                if ((condition_code == TD_CC_NOERROR) &&
+                        (urb->transfer_buffer_length > urb->actual_length)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        if (urb->actual_length > 0) {
+                                int retval;
+                                up(&u132->scheduler_lock);
+                                retval = edset_single(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_interrupt_recv);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                        } else {
+                                ring->in_use = 0;
+                                endp->active = 0;
+                                endp->jiffies = jiffies +
+                                        msecs_to_jiffies(urb->interval);
+                                u132_ring_cancel_work(u132, ring);
+                                u132_ring_queue_work(u132, ring, 0);
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                        }
+                        return;
+                } else if ((condition_code == TD_DATAUNDERRUN) &&
+                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else {
+                        if (condition_code == TD_CC_NOERROR) {
+                                endp->toggle_bits = toggle_bits;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 1 & toggle_bits);
+                        } else if (condition_code == TD_CC_STALL) {
+                                endp->toggle_bits = 0x2;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 0);
+                        } else {
+                                endp->toggle_bits = 0x2;
+                                usb_settoggle(udev->usb_device, endp->usb_endp,
+                                        0, 0);
+                                dev_err(&u132->platform_dev->dev, "urb=%p givin"
+                                        "g back INTERRUPT %s\n", urb,
+                                        cc_to_text[condition_code]);
+                        }
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                urb->actual_length += len;
+                endp->toggle_bits = toggle_bits;
+                if (urb->transfer_buffer_length > urb->actual_length) {
+                        int retval;
+                        up(&u132->scheduler_lock);
+                        retval = edset_output(u132, ring, endp, urb, address,
+                                endp->toggle_bits, u132_hcd_bulk_output_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer + urb->actual_length;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length += len;
+                if ((condition_code == TD_CC_NOERROR) &&
+                        (urb->transfer_buffer_length > urb->actual_length)) {
+                        int retval;
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, endp->toggle_bits,
+                                u132_hcd_bulk_input_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (condition_code == TD_CC_NOERROR) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else if ((condition_code == TD_DATAUNDERRUN) &&
+                        ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else if (condition_code == TD_DATAUNDERRUN) {
+                        endp->toggle_bits = toggle_bits;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+                                1 & toggle_bits);
+                        dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
+                                ") giving back BULK IN %s\n", urb,
+                                cc_to_text[condition_code]);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb, 0);
+                        return;
+                } else if (condition_code == TD_CC_STALL) {
+                        endp->toggle_bits = 0x2;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else {
+                        endp->toggle_bits = 0x2;
+                        usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+                        dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
+                                "ULK IN code=%d %s\n", urb, condition_code,
+                                cc_to_text[condition_code]);
+                        up(&u132->scheduler_lock);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length = len;
+                if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
+                        TD_DATAUNDERRUN) && ((urb->transfer_flags &
+                        URB_SHORT_NOT_OK) == 0))) {
+                        int retval;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0x3,
+                                u132_hcd_configure_empty_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (condition_code == TD_CC_STALL) {
+                        up(&u132->scheduler_lock);
+                        dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
+                                "NPUT STALL urb %p\n", urb);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
+                                "PUT %s urb %p\n", cc_to_text[condition_code],
+                                urb);
+                        u132_hcd_giveback_urb(u132, endp, urb,
+                                cc_to_error[condition_code]);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                if (usb_pipein(urb->pipe)) {
+                        int retval;
+                        struct u132_ring *ring = endp->ring;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0,
+                                u132_hcd_configure_input_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        int retval;
+                        struct u132_ring *ring = endp->ring;
+                        up(&u132->scheduler_lock);
+                        retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                                ring->number, endp, urb, address,
+                                endp->usb_endp, 0,
+                                u132_hcd_configure_empty_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
+        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        struct u132_udev *udev = &u132->udev[address];
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                u132->addr[0].address = 0;
+                endp->usb_addr = udev->usb_addr;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
+        u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                        ring->number, endp, urb, 0, endp->usb_endp, 0,
+                        u132_hcd_enumeration_empty_recv);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, 0);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                u8 *u = urb->transfer_buffer;
+                u8 *b = buf;
+                int L = len;
+                while (L-- > 0) {
+                        *u++ = *b++;
+                }
+                urb->actual_length = len;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+                        ring->number, endp, urb, address, endp->usb_endp, 0x3,
+                        u132_hcd_initial_empty_sent);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
+        int len, int toggle_bits, int error_count, int condition_code,
+        int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        u8 address = u132->addr[endp->usb_addr].address;
+        down(&u132->scheduler_lock);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                up(&u132->scheduler_lock);
+                u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (endp->dequeueing) {
+                endp->dequeueing = 0;
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+                return;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+                return;
+        } else if (urb->status == -EINPROGRESS) {
+                int retval;
+                struct u132_ring *ring = endp->ring;
+                up(&u132->scheduler_lock);
+                retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+                        ring->number, endp, urb, address, endp->usb_endp, 0,
+                        u132_hcd_initial_input_recv);
+                if (retval == 0) {
+                } else
+                        u132_hcd_giveback_urb(u132, endp, urb, retval);
+                return;
+        } else {
+                dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+                        "s=%d\n", urb, urb->status);
+                up(&u132->scheduler_lock);
+                u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+                return;
+        }
+}
+
+static void u132_hcd_ring_work_scheduler(void *data);
+static void u132_hcd_endp_work_scheduler(void *data);
+/*
+* this work function is only executed from the work queue
+*
+*/
+static void u132_hcd_ring_work_scheduler(void *data)
+{
+        struct u132_ring *ring = data;
+        struct u132 *u132 = ring->u132;
+        down(&u132->scheduler_lock);
+        if (ring->in_use) {
+                up(&u132->scheduler_lock);
+                u132_ring_put_kref(u132, ring);
+                return;
+        } else if (ring->curr_endp) {
+                struct u132_endp *last_endp = ring->curr_endp;
+                struct list_head *scan;
+                struct list_head *head = &last_endp->endp_ring;
+                unsigned long wakeup = 0;
+                list_for_each(scan, head) {
+                        struct u132_endp *endp = list_entry(scan,
+                                struct u132_endp, endp_ring);
+                        if (endp->queue_next == endp->queue_last) {
+                        } else if ((endp->delayed == 0)
+                                || time_after_eq(jiffies, endp->jiffies)) {
+                                ring->curr_endp = endp;
+                                u132_endp_cancel_work(u132, last_endp);
+                                u132_endp_queue_work(u132, last_endp, 0);
+                                up(&u132->scheduler_lock);
+                                u132_ring_put_kref(u132, ring);
+                                return;
+                        } else {
+                                unsigned long delta = endp->jiffies - jiffies;
+                                if (delta > wakeup)
+                                        wakeup = delta;
+                        }
+                }
+                if (last_endp->queue_next == last_endp->queue_last) {
+                } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
+                        last_endp->jiffies)) {
+                        u132_endp_cancel_work(u132, last_endp);
+                        u132_endp_queue_work(u132, last_endp, 0);
+                        up(&u132->scheduler_lock);
+                        u132_ring_put_kref(u132, ring);
+                        return;
+                } else {
+                        unsigned long delta = last_endp->jiffies - jiffies;
+                        if (delta > wakeup)
+                                wakeup = delta;
+                }
+                if (wakeup > 0) {
+                        u132_ring_requeue_work(u132, ring, wakeup);
+                        up(&u132->scheduler_lock);
+                        return;
+                } else {
+                        up(&u132->scheduler_lock);
+                        u132_ring_put_kref(u132, ring);
+                        return;
+                }
+        } else {
+                up(&u132->scheduler_lock);
+                u132_ring_put_kref(u132, ring);
+                return;
+        }
+}
+
+static void u132_hcd_endp_work_scheduler(void *data)
+{
+        struct u132_ring *ring;
+        struct u132_endp *endp = data;
+        struct u132 *u132 = endp->u132;
+        down(&u132->scheduler_lock);
+        ring = endp->ring;
+        if (endp->edset_flush) {
+                endp->edset_flush = 0;
+                if (endp->dequeueing)
+                        usb_ftdi_elan_edset_flush(u132->platform_dev,
+                                ring->number, endp);
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->active) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (ring->in_use) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->queue_next == endp->queue_last) {
+                up(&u132->scheduler_lock);
+                u132_endp_put_kref(u132, endp);
+                return;
+        } else if (endp->pipetype == PIPE_INTERRUPT) {
+                u8 address = u132->addr[endp->usb_addr].address;
+                if (ring->in_use) {
+                        up(&u132->scheduler_lock);
+                        u132_endp_put_kref(u132, endp);
+                        return;
+                } else {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_single(u132, ring, endp, urb, address,
+                                endp->toggle_bits, u132_hcd_interrupt_recv);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else if (endp->pipetype == PIPE_CONTROL) {
+                u8 address = u132->addr[endp->usb_addr].address;
+                if (ring->in_use) {
+                        up(&u132->scheduler_lock);
+                        u132_endp_put_kref(u132, endp);
+                        return;
+                } else if (address == 0) {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, address,
+                                0x2, u132_hcd_initial_setup_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else if (endp->usb_addr == 0) {
+                        int retval;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
+                                u132_hcd_enumeration_address_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                } else {
+                        int retval;
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+                                endp->queue_next];
+                        endp->active = 1;
+                        ring->curr_endp = endp;
+                        ring->in_use = 1;
+                        up(&u132->scheduler_lock);
+                        retval = edset_setup(u132, ring, endp, urb, address,
+                                0x2, u132_hcd_configure_setup_sent);
+                        if (retval == 0) {
+                        } else
+                                u132_hcd_giveback_urb(u132, endp, urb, retval);
+                        return;
+                }
+        } else {
+                if (endp->input) {
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        if (ring->in_use) {
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                                return;
+                        } else {
+                                int retval;
+                                struct urb *urb = endp->urb_list[
+                                        ENDP_QUEUE_MASK & endp->queue_next];
+                                endp->active = 1;
+                                ring->curr_endp = endp;
+                                ring->in_use = 1;
+                                up(&u132->scheduler_lock);
+                                retval = edset_input(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_bulk_input_recv);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                                return;
+                        }
+                } else {        /* output pipe */
+                        u8 address = u132->addr[endp->usb_addr].address;
+                        if (ring->in_use) {
+                                up(&u132->scheduler_lock);
+                                u132_endp_put_kref(u132, endp);
+                                return;
+                        } else {
+                                int retval;
+                                struct urb *urb = endp->urb_list[
+                                        ENDP_QUEUE_MASK & endp->queue_next];
+                                endp->active = 1;
+                                ring->curr_endp = endp;
+                                ring->in_use = 1;
+                                up(&u132->scheduler_lock);
+                                retval = edset_output(u132, ring, endp, urb,
+                                        address, endp->toggle_bits,
+                                        u132_hcd_bulk_output_sent);
+                                if (retval == 0) {
+                                } else
+                                        u132_hcd_giveback_urb(u132, endp, urb,
+                                                retval);
+                                return;
+                        }
+                }
+        }
+}
+
+static void port_power(struct u132 *u132, int pn, int is_on)
+{
+        u132->port[pn].power = is_on;
+}
+
+static void u132_power(struct u132 *u132, int is_on)
+{
+        struct usb_hcd *hcd = u132_to_hcd(u132)
+                ;        /* hub is inactive unless the port is powered */
+        if (is_on) {
+                if (u132->power)
+                        return;
+                u132->power = 1;
+                hcd->self.controller->power.power_state = PMSG_ON;
+        } else {
+                u132->power = 0;
+                hcd->state = HC_STATE_HALT;
+                hcd->self.controller->power.power_state = PMSG_SUSPEND;
+        }
+}
+
+static int u132_periodic_reinit(struct u132 *u132)
+{
+        int retval;
+        u32 fi = u132->hc_fminterval & 0x03fff;
+        u32 fit;
+        u32 fminterval;
+        retval = u132_read_pcimem(u132, fminterval, &fminterval);
+        if (retval)
+                return retval;
+        fit = fminterval & FIT;
+        retval = u132_write_pcimem(u132, fminterval,
+                (fit ^ FIT) | u132->hc_fminterval);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, periodicstart,
+                ((9 *fi) / 10) & 0x3fff);
+        if (retval)
+                return retval;
+        return 0;
+}
+
+static char *hcfs2string(int state)
+{
+        switch (state) {
+        case OHCI_USB_RESET:
+                return "reset";
+        case OHCI_USB_RESUME:
+                return "resume";
+        case OHCI_USB_OPER:
+                return "operational";
+        case OHCI_USB_SUSPEND:
+                return "suspend";
+        }
+        return "?";
+}
+
+static int u132_usb_reset(struct u132 *u132)
+{
+        int retval;
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval)
+                return retval;
+        u132->hc_control &= OHCI_CTRL_RWC;
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        return 0;
+}
+
+static int u132_init(struct u132 *u132)
+{
+        int retval;
+        u32 control;
+        u132_disable(u132);
+        u132->next_statechange =
+                jiffies; /* SMM owns the HC? not for long! */  {
+                u32 control;
+                retval = u132_read_pcimem(u132, control, &control);
+                if (retval)
+                        return retval;
+                if (control & OHCI_CTRL_IR) {
+                        u32 temp = 50;
+                        retval = u132_write_pcimem(u132, intrenable,
+                                OHCI_INTR_OC);
+                        if (retval)
+                                return retval;
+                        retval = u132_write_pcimem_byte(u132, cmdstatus,
+                                OHCI_OCR);
+                        if (retval)
+                                return retval;
+                      check:{
+                                retval = u132_read_pcimem(u132, control,
+                                        &control);
+                                if (retval)
+                                        return retval;
+                        }
+                        if (control & OHCI_CTRL_IR) {
+                                msleep(10);
+                                if (--temp == 0) {
+                                        dev_err(&u132->platform_dev->dev, "USB "
+                                                "HC takeover failed!(BIOS/SMM b"
+                                                "ug) control=%08X\n", control);
+                                        return -EBUSY;
+                                }
+                                goto check;
+                        }
+                        u132_usb_reset(u132);
+                }
+        }
+        retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        if (u132->num_ports == 0) {
+                u32 rh_a = -1;
+                retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+                if (retval)
+                        return retval;
+                u132->num_ports = rh_a & RH_A_NDP;
+                retval = read_roothub_info(u132);
+                if (retval)
+                        return retval;
+        }
+        if (u132->num_ports > MAX_U132_PORTS) {
+                return -EINVAL;
+        }
+        return 0;
+}
+
+
+/* Start an OHCI controller, set the BUS operational
+* resets USB and controller
+* enable interrupts
+*/
+static int u132_run(struct u132 *u132)
+{
+        int retval;
+        u32 control;
+        u32 status;
+        u32 fminterval;
+        u32 periodicstart;
+        u32 cmdstatus;
+        u32 roothub_a;
+        int mask = OHCI_INTR_INIT;
+        int first = u132->hc_fminterval == 0;
+        int sleep_time = 0;
+        int reset_timeout = 30;        /* ... allow extra time */
+        u132_disable(u132);
+        if (first) {
+                u32 temp;
+                retval = u132_read_pcimem(u132, fminterval, &temp);
+                if (retval)
+                        return retval;
+                u132->hc_fminterval = temp & 0x3fff;
+                if (u132->hc_fminterval != FI) {
+                }
+                u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
+        }
+        retval = u132_read_pcimem(u132, control, &u132->hc_control);
+        if (retval)
+                return retval;
+        dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
+                "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
+                u132->hc_control);
+        switch (u132->hc_control & OHCI_CTRL_HCFS) {
+        case OHCI_USB_OPER:
+                sleep_time = 0;
+                break;
+        case OHCI_USB_SUSPEND:
+        case OHCI_USB_RESUME:
+                u132->hc_control &= OHCI_CTRL_RWC;
+                u132->hc_control |= OHCI_USB_RESUME;
+                sleep_time = 10;
+                break;
+        default:
+                u132->hc_control &= OHCI_CTRL_RWC;
+                u132->hc_control |= OHCI_USB_RESET;
+                sleep_time = 50;
+                break;
+        }
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        msleep(sleep_time);
+        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+        if (retval)
+                return retval;
+        if (!(roothub_a & RH_A_NPS)) {
+                int temp;        /* power down each port */
+                for (temp = 0; temp < u132->num_ports; temp++) {
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[temp], RH_PS_LSDA);
+                        if (retval)
+                                return retval;
+                }
+        }
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+      retry:retval = u132_read_pcimem(u132, cmdstatus, &status);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_HCR);
+        if (retval)
+                return retval;
+      extra:{
+                retval = u132_read_pcimem(u132, cmdstatus, &status);
+                if (retval)
+                        return retval;
+                if (0 != (status & OHCI_HCR)) {
+                        if (--reset_timeout == 0) {
+                                dev_err(&u132->platform_dev->dev, "USB HC reset"
+                                        " timed out!\n");
+                                return -ENODEV;
+                        } else {
+                                msleep(5);
+                                goto extra;
+                        }
+                }
+        }
+        if (u132->flags & OHCI_QUIRK_INITRESET) {
+                retval = u132_write_pcimem(u132, control, u132->hc_control);
+                if (retval)
+                        return retval;
+                retval = u132_read_pcimem(u132, control, &control);
+                if (retval)
+                        return retval;
+        }
+        retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, hcca, 0x00000000);
+        if (retval)
+                return retval;
+        retval = u132_periodic_reinit(u132);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, fminterval, &fminterval);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
+        if (retval)
+                return retval;
+        if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+                if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
+                        u132->flags |= OHCI_QUIRK_INITRESET;
+                        goto retry;
+                } else
+                        dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
+                                "\n", fminterval, periodicstart);
+        }                        /* start controller operations */
+        u132->hc_control &= OHCI_CTRL_RWC;
+        u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+        retval = u132_write_pcimem(u132, control, u132->hc_control);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_BLF);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+        retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, intrstatus, mask);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, intrdisable,
+                OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+                OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+                OHCI_INTR_SO);
+        if (retval)
+                return retval;        /* handle root hub init quirks ... */
+        retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+        if (retval)
+                return retval;
+        roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+        if (u132->flags & OHCI_QUIRK_SUPERIO) {
+                roothub_a |= RH_A_NOCP;
+                roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+                if (retval)
+                        return retval;
+        } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+                roothub_a |= RH_A_NPS;
+                retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+                if (retval)
+                        return retval;
+        }
+        retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
+        if (retval)
+                return retval;
+        retval = u132_write_pcimem(u132, roothub.b,
+                (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+        if (retval)
+                return retval;
+        retval = u132_read_pcimem(u132, control, &control);
+        if (retval)
+                return retval;
+        mdelay((roothub_a >> 23) & 0x1fe);
+        u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+        return 0;
+}
+
+static void u132_hcd_stop(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+                        "ed\n", hcd);
+        } else {
+                down(&u132->sw_lock);
+                msleep(100);
+                u132_power(u132, 0);
+                up(&u132->sw_lock);
+        }
+}
+
+static int u132_hcd_start(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else if (hcd->self.controller) {
+                int retval;
+                struct platform_device *pdev =
+                        to_platform_device(hcd->self.controller);
+                u16 vendor = ((struct u132_platform_data *)
+                        (pdev->dev.platform_data))->vendor;
+                u16 device = ((struct u132_platform_data *)
+                        (pdev->dev.platform_data))->device;
+                down(&u132->sw_lock);
+                msleep(10);
+                if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
+                        u132->flags = OHCI_QUIRK_AMD756;
+                } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
+                        dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
+                                "ounds unavailable\n");
+                } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
+                        u132->flags |= OHCI_QUIRK_ZFMICRO;
+                retval = u132_run(u132);
+                if (retval) {
+                        u132_disable(u132);
+                        u132->going = 1;
+                }
+                msleep(100);
+                up(&u132->sw_lock);
+                return retval;
+        } else {
+                dev_err(&u132->platform_dev->dev, "platform_device missing\n");
+                return -ENODEV;
+        }
+}
+
+static int u132_hcd_reset(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval;
+                down(&u132->sw_lock);
+                retval = u132_init(u132);
+                if (retval) {
+                        u132_disable(u132);
+                        u132->going = 1;
+                }
+                up(&u132->sw_lock);
+                return retval;
+        }
+}
+
+static int create_endpoint_and_queue_int(struct u132 *u132,
+        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+        gfp_t mem_flags)
+{
+        struct u132_ring *ring;
+        unsigned long irqs;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        ring = endp->ring = &u132->ring[0];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        endp->pipetype = usb_pipetype(urb->pipe);
+        u132_endp_init_kref(u132, endp);
+        if (usb_pipein(urb->pipe)) {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+                endp->input = 1;
+                endp->output = 0;
+                udev->endp_number_in[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        } else {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+                endp->input = 0;
+                endp->output = 1;
+                udev->endp_number_out[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        }
+        urb->hcpriv = u132;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->delayed = 1;
+        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+        endp->udev_number = address;
+        endp->usb_addr = usb_addr;
+        endp->usb_endp = usb_endp;
+        endp->queue_size = 1;
+        endp->queue_last = 0;
+        endp->queue_next = 0;
+        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
+        return 0;
+}
+
+static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp, u8 address)
+{
+        urb->hcpriv = u132;
+        endp->delayed = 1;
+        endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        } else {
+                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+                        GFP_ATOMIC);
+                if (urbq == NULL) {
+                        endp->queue_size -= 1;
+                        return -ENOMEM;
+                } else {
+                        list_add_tail(&urbq->urb_more, &endp->urb_more);
+                        urbq->urb = urb;
+                }
+        }
+        return 0;
+}
+
+static int create_endpoint_and_queue_bulk(struct u132 *u132,
+        struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+        gfp_t mem_flags)
+{
+        int ring_number;
+        struct u132_ring *ring;
+        unsigned long irqs;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        endp->pipetype = usb_pipetype(urb->pipe);
+        u132_endp_init_kref(u132, endp);
+        if (usb_pipein(urb->pipe)) {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+                ring_number = 3;
+                endp->input = 1;
+                endp->output = 0;
+                udev->endp_number_in[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        } else {
+                endp->toggle_bits = 0x2;
+                usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+                ring_number = 2;
+                endp->input = 0;
+                endp->output = 1;
+                udev->endp_number_out[usb_endp] = endp_number;
+                u132_udev_get_kref(u132, udev);
+        }
+        ring = endp->ring = &u132->ring[ring_number - 1];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        urb->hcpriv = u132;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        endp->udev_number = address;
+        endp->usb_addr = usb_addr;
+        endp->usb_endp = usb_endp;
+        endp->queue_size = 1;
+        endp->queue_last = 0;
+        endp->queue_next = 0;
+        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+        u132_endp_queue_work(u132, endp, 0);
+        return 0;
+}
+
+static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+         struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp, u8 address)
+{
+        urb->hcpriv = u132;
+        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+        } else {
+                struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+                        GFP_ATOMIC);
+                if (urbq == NULL) {
+                        endp->queue_size -= 1;
+                        return -ENOMEM;
+                } else {
+                        list_add_tail(&urbq->urb_more, &endp->urb_more);
+                        urbq->urb = urb;
+                }
+        }
+        return 0;
+}
+
+static int create_endpoint_and_queue_control(struct u132 *u132,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
+        gfp_t mem_flags)
+{
+        struct u132_ring *ring;
+        u8 endp_number = ++u132->num_endpoints;
+        struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+                kmalloc(sizeof(struct u132_endp), mem_flags);
+        if (!endp) {
+                return -ENOMEM;
+        }
+        INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+        spin_lock_init(&endp->queue_lock.slock);
+        INIT_LIST_HEAD(&endp->urb_more);
+        ring = endp->ring = &u132->ring[0];
+        if (ring->curr_endp) {
+                list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+        } else {
+                INIT_LIST_HEAD(&endp->endp_ring);
+                ring->curr_endp = endp;
+        }
+        ring->length += 1;
+        endp->dequeueing = 0;
+        endp->edset_flush = 0;
+        endp->active = 0;
+        endp->delayed = 0;
+        endp->endp_number = endp_number;
+        endp->u132 = u132;
+        endp->hep = hep;
+        u132_endp_init_kref(u132, endp);
+        u132_endp_get_kref(u132, endp);
+        if (usb_addr == 0) {
+                unsigned long irqs;
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                endp->udev_number = address;
+                endp->usb_addr = usb_addr;
+                endp->usb_endp = usb_endp;
+                endp->input = 1;
+                endp->output = 1;
+                endp->pipetype = usb_pipetype(urb->pipe);
+                u132_udev_init_kref(u132, udev);
+                u132_udev_get_kref(u132, udev);
+                udev->endp_number_in[usb_endp] = endp_number;
+                udev->endp_number_out[usb_endp] = endp_number;
+                urb->hcpriv = u132;
+                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+                endp->queue_size = 1;
+                endp->queue_last = 0;
+                endp->queue_next = 0;
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                u132_endp_queue_work(u132, endp, 0);
+                return 0;
+        } else {                /*(usb_addr > 0) */
+                unsigned long irqs;
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                endp->udev_number = address;
+                endp->usb_addr = usb_addr;
+                endp->usb_endp = usb_endp;
+                endp->input = 1;
+                endp->output = 1;
+                endp->pipetype = usb_pipetype(urb->pipe);
+                u132_udev_get_kref(u132, udev);
+                udev->enumeration = 2;
+                udev->endp_number_in[usb_endp] = endp_number;
+                udev->endp_number_out[usb_endp] = endp_number;
+                urb->hcpriv = u132;
+                spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+                endp->queue_size = 1;
+                endp->queue_last = 0;
+                endp->queue_next = 0;
+                endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                u132_endp_queue_work(u132, endp, 0);
+                return 0;
+        }
+}
+
+static int queue_control_on_old_endpoint(struct u132 *u132,
+        struct usb_host_endpoint *hep, struct urb *urb,
+        struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+        u8 usb_endp)
+{
+        if (usb_addr == 0) {
+                if (usb_pipein(urb->pipe)) {
+                        urb->hcpriv = u132;
+                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                                endp->urb_list[ENDP_QUEUE_MASK &
+                                        endp->queue_last++] = urb;
+                        } else {
+                                struct u132_urbq *urbq =
+                                        kmalloc(sizeof(struct u132_urbq),
+                                        GFP_ATOMIC);
+                                if (urbq == NULL) {
+                                        endp->queue_size -= 1;
+                                        return -ENOMEM;
+                                } else {
+                                        list_add_tail(&urbq->urb_more,
+                                                &endp->urb_more);
+                                        urbq->urb = urb;
+                                }
+                        }
+                        return 0;
+                } else {        /* usb_pipeout(urb->pipe) */
+                        struct u132_addr *addr = &u132->addr[usb_dev->devnum];
+                        int I = MAX_U132_UDEVS;
+                        int i = 0;
+                        while (--I > 0) {
+                                struct u132_udev *udev = &u132->udev[++i];
+                                if (udev->usb_device) {
+                                        continue;
+                                } else {
+                                        udev->enumeration = 1;
+                                        u132->addr[0].address = i;
+                                        endp->udev_number = i;
+                                        udev->udev_number = i;
+                                        udev->usb_addr = usb_dev->devnum;
+                                        u132_udev_init_kref(u132, udev);
+                                        udev->endp_number_in[usb_endp] =
+                                                endp->endp_number;
+                                        u132_udev_get_kref(u132, udev);
+                                        udev->endp_number_out[usb_endp] =
+                                                endp->endp_number;
+                                        udev->usb_device = usb_dev;
+                                        ((u8 *) (urb->setup_packet))[2] =
+                                                addr->address = i;
+                                        u132_udev_get_kref(u132, udev);
+                                        break;
+                                }
+                        }
+                        if (I == 0) {
+                                dev_err(&u132->platform_dev->dev, "run out of d"
+                                        "evice space\n");
+                                return -EINVAL;
+                        }
+                        urb->hcpriv = u132;
+                        if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                                endp->urb_list[ENDP_QUEUE_MASK &
+                                        endp->queue_last++] = urb;
+                        } else {
+                                struct u132_urbq *urbq =
+                                        kmalloc(sizeof(struct u132_urbq),
+                                        GFP_ATOMIC);
+                                if (urbq == NULL) {
+                                        endp->queue_size -= 1;
+                                        return -ENOMEM;
+                                } else {
+                                        list_add_tail(&urbq->urb_more,
+                                                &endp->urb_more);
+                                        urbq->urb = urb;
+                                }
+                        }
+                        return 0;
+                }
+        } else {                /*(usb_addr > 0) */
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                urb->hcpriv = u132;
+                if (udev->enumeration == 2) {
+                } else
+                        udev->enumeration = 2;
+                if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+                        endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+                                urb;
+                } else {
+                        struct u132_urbq *urbq =
+                                kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
+                        if (urbq == NULL) {
+                                endp->queue_size -= 1;
+                                return -ENOMEM;
+                        } else {
+                                list_add_tail(&urbq->urb_more, &endp->urb_more);
+                                urbq->urb = urb;
+                        }
+                }
+                return 0;
+        }
+}
+
+static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
+        struct urb *urb, gfp_t mem_flags)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (irqs_disabled()) {
+                if (__GFP_WAIT & mem_flags) {
+                        printk(KERN_ERR "invalid context for function that migh"
+                                "t sleep\n");
+                        return -EINVAL;
+                }
+        }
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed urb="
+                        "%p status=%d\n", urb, urb->status);
+                return -ESHUTDOWN;
+        } else {
+                u8 usb_addr = usb_pipedevice(urb->pipe);
+                u8 usb_endp = usb_pipeendpoint(urb->pipe);
+                struct usb_device *usb_dev = urb->dev;
+                if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+                        u8 address = u132->addr[usb_addr].address;
+                        struct u132_udev *udev = &u132->udev[address];
+                        struct u132_endp *endp = hep->hcpriv;
+                        urb->actual_length = 0;
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_int_on_old_endpoint(u132, udev,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp, address);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp,
+                                                msecs_to_jiffies(urb->interval))
+                                                ;
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else {        /*(endp == NULL) */
+                                return create_endpoint_and_queue_int(u132, udev,
+                                         hep, urb, usb_dev, usb_addr, usb_endp,
+                                        address, mem_flags);
+                        }
+                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                        dev_err(&u132->platform_dev->dev, "the hardware does no"
+                                "t support PIPE_ISOCHRONOUS\n");
+                        return -EINVAL;
+                } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+                        u8 address = u132->addr[usb_addr].address;
+                        struct u132_udev *udev = &u132->udev[address];
+                        struct u132_endp *endp = hep->hcpriv;
+                        urb->actual_length = 0;
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_bulk_on_old_endpoint(u132, udev,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp, address);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp, 0);
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else
+                                return create_endpoint_and_queue_bulk(u132,
+                                        udev, hep, urb, usb_dev, usb_addr,
+                                        usb_endp, address, mem_flags);
+                } else {
+                        struct u132_endp *endp = hep->hcpriv;
+                        u16 urb_size = 8;
+                        u8 *b = urb->setup_packet;
+                        int i = 0;
+                        char data[30 *3 + 4];
+                        char *d = data;
+                        int m = (sizeof(data) - 1) / 3;
+                        int l = 0;
+                        data[0] = 0;
+                        while (urb_size-- > 0) {
+                                if (i > m) {
+                                } else if (i++ < m) {
+                                        int w = sprintf(d, " %02X", *b++);
+                                        d += w;
+                                        l += w;
+                                } else
+                                        d += sprintf(d, " ..");
+                        }
+                        if (endp) {
+                                unsigned long irqs;
+                                int retval;
+                                spin_lock_irqsave(&endp->queue_lock.slock,
+                                        irqs);
+                                retval = queue_control_on_old_endpoint(u132,
+                                        hep, urb, usb_dev, endp, usb_addr,
+                                        usb_endp);
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                if (retval) {
+                                        return retval;
+                                } else {
+                                        u132_endp_queue_work(u132, endp, 0);
+                                        return 0;
+                                }
+                        } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+                                return -EINVAL;
+                        } else
+                                return create_endpoint_and_queue_control(u132,
+                                        hep, urb, usb_dev, usb_addr, usb_endp,
+                                        mem_flags);
+                }
+        }
+}
+
+static int dequeue_from_overflow_chain(struct u132 *u132,
+        struct u132_endp *endp, struct urb *urb)
+{
+        struct list_head *scan;
+        struct list_head *head = &endp->urb_more;
+        list_for_each(scan, head) {
+                struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
+                        urb_more);
+                if (urbq->urb == urb) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        list_del(scan);
+                        endp->queue_size -= 1;
+                        urb->error_count = 0;
+                        urb->hcpriv = NULL;
+                        usb_hcd_giveback_urb(hcd, urb, NULL);
+                        return 0;
+                } else
+                        continue;
+        }
+        dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
+                "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
+                "\n", urb, endp->endp_number, endp, endp->ring->number,
+                endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+                endp->usb_endp, endp->usb_addr, endp->queue_size,
+                endp->queue_next, endp->queue_last);
+        return -EINVAL;
+}
+
+static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
+        struct urb *urb)
+{
+        unsigned long irqs;
+        spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+        if (endp->queue_size == 0) {
+                dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
+                        "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
+                        endp->endp_number, endp, endp->ring->number,
+                        endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+                        endp->usb_endp, endp->usb_addr);
+                spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                return -EINVAL;
+        }
+        if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
+                if (endp->active) {
+                        endp->dequeueing = 1;
+                        endp->edset_flush = 1;
+                        u132_endp_queue_work(u132, endp, 0);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        urb->hcpriv = NULL;
+                        return 0;
+                } else {
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        u132_hcd_abandon_urb(u132, endp, urb, urb->status);
+                        return 0;
+                }
+        } else {
+                u16 queue_list = 0;
+                u16 queue_size = endp->queue_size;
+                u16 queue_scan = endp->queue_next;
+                struct urb **urb_slot = NULL;
+                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+                        if (urb == endp->urb_list[ENDP_QUEUE_MASK &
+                                ++queue_scan]) {
+                                urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+                                        queue_scan];
+                                break;
+                        } else
+                                continue;
+                }
+                while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+                        *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
+                                ++queue_scan];
+                        urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+                                queue_scan];
+                }
+                if (urb_slot) {
+                        struct usb_hcd *hcd = u132_to_hcd(u132);
+                        endp->queue_size -= 1;
+                        if (list_empty(&endp->urb_more)) {
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                        } else {
+                                struct list_head *next = endp->urb_more.next;
+                                struct u132_urbq *urbq = list_entry(next,
+                                        struct u132_urbq, urb_more);
+                                list_del(next);
+                                *urb_slot = urbq->urb;
+                                spin_unlock_irqrestore(&endp->queue_lock.slock,
+                                        irqs);
+                                kfree(urbq);
+                        } urb->error_count = 0;
+                        urb->hcpriv = NULL;
+                        usb_hcd_giveback_urb(hcd, urb, NULL);
+                        return 0;
+                } else if (list_empty(&endp->urb_more)) {
+                        dev_err(&u132->platform_dev->dev, "urb=%p not found in "
+                                "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
+                                "=%d size=%d next=%04X last=%04X\n", urb,
+                                endp->endp_number, endp, endp->ring->number,
+                                endp->input ? 'I' : ' ',
+                                endp->output ? 'O' : ' ', endp->usb_endp,
+                                endp->usb_addr, endp->queue_size,
+                                endp->queue_next, endp->queue_last);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        return -EINVAL;
+                } else {
+                        int retval = dequeue_from_overflow_chain(u132, endp,
+                                urb);
+                        spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+                        return retval;
+                }
+        }
+}
+
+static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 2) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else {
+                u8 usb_addr = usb_pipedevice(urb->pipe);
+                u8 usb_endp = usb_pipeendpoint(urb->pipe);
+                u8 address = u132->addr[usb_addr].address;
+                struct u132_udev *udev = &u132->udev[address];
+                if (usb_pipein(urb->pipe)) {
+                        u8 endp_number = udev->endp_number_in[usb_endp];
+                        struct u132_endp *endp = u132->endp[endp_number - 1];
+                        return u132_endp_urb_dequeue(u132, endp, urb);
+                } else {
+                        u8 endp_number = udev->endp_number_out[usb_endp];
+                        struct u132_endp *endp = u132->endp[endp_number - 1];
+                        return u132_endp_urb_dequeue(u132, endp, urb);
+                }
+        }
+}
+
+static void u132_endpoint_disable(struct usb_hcd *hcd,
+        struct usb_host_endpoint *hep)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 2) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else {
+                struct u132_endp *endp = hep->hcpriv;
+                if (endp)
+                        u132_endp_put_kref(u132, endp);
+        }
+}
+
+static int u132_get_frame(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int frame = 0;
+                dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
+                msleep(100);
+                return frame;
+        }
+}
+
+static int u132_roothub_descriptor(struct u132 *u132,
+        struct usb_hub_descriptor *desc)
+{
+        int retval;
+        u16 temp;
+        u32 rh_a = -1;
+        u32 rh_b = -1;
+        retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+        if (retval)
+                return retval;
+        desc->bDescriptorType = 0x29;
+        desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
+        desc->bHubContrCurrent = 0;
+        desc->bNbrPorts = u132->num_ports;
+        temp = 1 + (u132->num_ports / 8);
+        desc->bDescLength = 7 + 2 *temp;
+        temp = 0;
+        if (rh_a & RH_A_NPS)
+                temp |= 0x0002;
+        if (rh_a & RH_A_PSM)
+                temp |= 0x0001;
+        if (rh_a & RH_A_NOCP) {
+                temp |= 0x0010;
+        } else if (rh_a & RH_A_OCPM)
+                temp |= 0x0008;
+        desc->wHubCharacteristics = cpu_to_le16(temp);
+        retval = u132_read_pcimem(u132, roothub.b, &rh_b);
+        if (retval)
+                return retval;
+        memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
+        desc->bitmap[0] = rh_b & RH_B_DR;
+        if (u132->num_ports > 7) {
+                desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
+                desc->bitmap[2] = 0xff;
+        } else
+                desc->bitmap[1] = 0xff;
+        return 0;
+}
+
+static int u132_roothub_status(struct u132 *u132, __le32 *desc)
+{
+        u32 rh_status = -1;
+        int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
+        *desc = cpu_to_le32(rh_status);
+        return ret_status;
+}
+
+static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int port = wIndex - 1;
+                u32 rh_portstatus = -1;
+                int ret_portstatus = u132_read_pcimem(u132,
+                        roothub.portstatus[port], &rh_portstatus);
+                *desc = cpu_to_le32(rh_portstatus);
+                if (*(u16 *) (desc + 2)) {
+                        dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
+                                "ge = %08X\n", port, *desc);
+                }
+                return ret_portstatus;
+        }
+}
+
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+#define PORT_RESET_MSEC 10
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+static int u132_roothub_portreset(struct u132 *u132, int port_index)
+{
+        int retval;
+        u32 fmnumber;
+        u16 now;
+        u16 reset_done;
+        retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+        if (retval)
+                return retval;
+        now = fmnumber;
+        reset_done = now + PORT_RESET_MSEC;
+        do {
+                u32 portstat;
+                do {
+                        retval = u132_read_pcimem(u132,
+                                roothub.portstatus[port_index], &portstat);
+                        if (retval)
+                                return retval;
+                        if (RH_PS_PRS & portstat) {
+                                continue;
+                        } else
+                                break;
+                } while (tick_before(now, reset_done));
+                if (RH_PS_PRS & portstat)
+                        return -ENODEV;
+                if (RH_PS_CCS & portstat) {
+                        if (RH_PS_PRSC & portstat) {
+                                retval = u132_write_pcimem(u132,
+                                        roothub.portstatus[port_index],
+                                        RH_PS_PRSC);
+                                if (retval)
+                                        return retval;
+                        }
+                } else
+                        break;        /* start the next reset,
+                                sleep till it's probably done */
+                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+                         RH_PS_PRS);
+                if (retval)
+                        return retval;
+                msleep(PORT_RESET_HW_MSEC);
+                retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+                if (retval)
+                        return retval;
+                now = fmnumber;
+        } while (tick_before(now, reset_done));
+        return 0;
+}
+
+static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
+        u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int retval;
+                int port_index = wIndex - 1;
+                struct u132_port *port = &u132->port[port_index];
+                port->Status &= ~(1 << wValue);
+                switch (wValue) {
+                case USB_PORT_FEAT_SUSPEND:
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[port_index], RH_PS_PSS);
+                        if (retval)
+                                return retval;
+                        return 0;
+                case USB_PORT_FEAT_POWER:
+                        retval = u132_write_pcimem(u132,
+                                roothub.portstatus[port_index], RH_PS_PPS);
+                        if (retval)
+                                return retval;
+                        return 0;
+                case USB_PORT_FEAT_RESET:
+                        retval = u132_roothub_portreset(u132, port_index);
+                        if (retval)
+                                return retval;
+                        return 0;
+                default:
+                        return -EPIPE;
+                }
+        }
+}
+
+static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
+        u16 wIndex)
+{
+        if (wIndex == 0 || wIndex > u132->num_ports) {
+                return -EINVAL;
+        } else {
+                int port_index = wIndex - 1;
+                u32 temp;
+                int retval;
+                struct u132_port *port = &u132->port[port_index];
+                port->Status &= ~(1 << wValue);
+                switch (wValue) {
+                case USB_PORT_FEAT_ENABLE:
+                        temp = RH_PS_CCS;
+                        break;
+                case USB_PORT_FEAT_C_ENABLE:
+                        temp = RH_PS_PESC;
+                        break;
+                case USB_PORT_FEAT_SUSPEND:
+                        temp = RH_PS_POCI;
+                        if ((u132->hc_control & OHCI_CTRL_HCFS)
+                                != OHCI_USB_OPER) {
+                                dev_err(&u132->platform_dev->dev, "TODO resume_"
+                                        "root_hub\n");
+                        }
+                        break;
+                case USB_PORT_FEAT_C_SUSPEND:
+                        temp = RH_PS_PSSC;
+                        break;
+                case USB_PORT_FEAT_POWER:
+                        temp = RH_PS_LSDA;
+                        break;
+                case USB_PORT_FEAT_C_CONNECTION:
+                        temp = RH_PS_CSC;
+                        break;
+                case USB_PORT_FEAT_C_OVER_CURRENT:
+                        temp = RH_PS_OCIC;
+                        break;
+                case USB_PORT_FEAT_C_RESET:
+                        temp = RH_PS_PRSC;
+                        break;
+                default:
+                        return -EPIPE;
+                }
+                retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+                         temp);
+                if (retval)
+                        return retval;
+                return 0;
+        }
+}
+
+
+/* the virtual root hub timer IRQ checks for hub status*/
+static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
+                        "ed %d\n", hcd, u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+                        "ed\n", hcd);
+                dump_stack();
+                return -ESHUTDOWN;
+        } else {
+                int i, changed = 0, length = 1;
+                if (u132->flags & OHCI_QUIRK_AMD756) {
+                        if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
+                                dev_err(&u132->platform_dev->dev, "bogus NDP, r"
+                                        "ereads as NDP=%d\n",
+                                        u132->hc_roothub_a & RH_A_NDP);
+                                goto done;
+                        }
+                }
+                if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC)) {
+                        buf[0] = changed = 1;
+                } else
+                        buf[0] = 0;
+                if (u132->num_ports > 7) {
+                        buf[1] = 0;
+                        length++;
+                }
+                for (i = 0; i < u132->num_ports; i++) {
+                        if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
+                                RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+                                RH_PS_PRSC)) {
+                                changed = 1;
+                                if (i < 7) {
+                                        buf[0] |= 1 << (i + 1);
+                                } else
+                                        buf[1] |= 1 << (i - 7);
+                                continue;
+                        }
+                        if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS)) {
+                                continue;
+                        }
+                        if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS)) {
+                                continue;
+                        }
+                }
+              done:return changed ? length : 0;
+        }
+}
+
+static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+        u16 wIndex, char *buf, u16 wLength)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                down(&u132->sw_lock);
+                switch (typeReq) {
+                case ClearHubFeature:
+                        switch (wValue) {
+                        case C_HUB_OVER_CURRENT:
+                        case C_HUB_LOCAL_POWER:
+                                break;
+                        default:
+                                goto stall;
+                        }
+                        break;
+                case SetHubFeature:
+                        switch (wValue) {
+                        case C_HUB_OVER_CURRENT:
+                        case C_HUB_LOCAL_POWER:
+                                break;
+                        default:
+                                goto stall;
+                        }
+                        break;
+                case ClearPortFeature:{
+                                retval = u132_roothub_clearportfeature(u132,
+                                        wValue, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetHubDescriptor:{
+                                retval = u132_roothub_descriptor(u132,
+                                        (struct usb_hub_descriptor *)buf);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetHubStatus:{
+                                retval = u132_roothub_status(u132,
+                                        (__le32 *) buf);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case GetPortStatus:{
+                                retval = u132_roothub_portstatus(u132,
+                                        (__le32 *) buf, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                case SetPortFeature:{
+                                retval = u132_roothub_setportfeature(u132,
+                                        wValue, wIndex);
+                                if (retval)
+                                        goto error;
+                                break;
+                        }
+                default:
+                        goto stall;
+                      error:u132_disable(u132);
+                        u132->going = 1;
+                        break;
+                      stall:retval = -EPIPE;
+                        break;
+                }
+                up(&u132->sw_lock);
+                return retval;
+        }
+}
+
+static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static void u132_hub_irq_enable(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+        } else if (u132->going > 0)
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+}
+
+
+#ifdef CONFIG_PM
+static int u132_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_hcd_resume(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_bus_suspend(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+static int u132_bus_resume(struct usb_hcd *hcd)
+{
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else
+                return 0;
+}
+
+#else
+#define u132_hcd_suspend NULL
+#define u132_hcd_resume NULL
+#define u132_bus_suspend NULL
+#define u132_bus_resume NULL
+#endif
+static struct hc_driver u132_hc_driver = {
+        .description = hcd_name,
+        .hcd_priv_size = sizeof(struct u132),
+        .irq = NULL,
+        .flags = HCD_USB11 | HCD_MEMORY,
+        .reset = u132_hcd_reset,
+        .start = u132_hcd_start,
+        .suspend = u132_hcd_suspend,
+        .resume = u132_hcd_resume,
+        .stop = u132_hcd_stop,
+        .urb_enqueue = u132_urb_enqueue,
+        .urb_dequeue = u132_urb_dequeue,
+        .endpoint_disable = u132_endpoint_disable,
+        .get_frame_number = u132_get_frame,
+        .hub_status_data = u132_hub_status_data,
+        .hub_control = u132_hub_control,
+        .bus_suspend = u132_bus_suspend,
+        .bus_resume = u132_bus_resume,
+        .start_port_reset = u132_start_port_reset,
+        .hub_irq_enable = u132_hub_irq_enable,
+};
+
+/*
+* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
+* is held for writing, thus this module must not call usb_remove_hcd()
+* synchronously - but instead should immediately stop activity to the
+* device and ansynchronously call usb_remove_hcd()
+*/
+static int __devexit u132_remove(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        if (hcd) {
+                struct u132 *u132 = hcd_to_u132(hcd);
+                dump_stack();
+                if (u132->going++ > 1) {
+                        return -ENODEV;
+                } else {
+                        int rings = MAX_U132_RINGS;
+                        int endps = MAX_U132_ENDPS;
+                        msleep(100);
+                        down(&u132->sw_lock);
+                        u132_monitor_cancel_work(u132);
+                        while (rings-- > 0) {
+                                struct u132_ring *ring = &u132->ring[rings];
+                                u132_ring_cancel_work(u132, ring);
+                        } while (endps-- > 0) {
+                                struct u132_endp *endp = u132->endp[endps];
+                                if (endp)
+                                        u132_endp_cancel_work(u132, endp);
+                        }
+                        u132->going += 1;
+                        printk(KERN_INFO "removing device u132.%d\n",
+                                u132->sequence_num);
+                        up(&u132->sw_lock);
+                        usb_remove_hcd(hcd);
+                        u132_u132_put_kref(u132);
+                        return 0;
+                }
+        } else
+                return 0;
+}
+
+static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
+{
+        int rings = MAX_U132_RINGS;
+        int ports = MAX_U132_PORTS;
+        int addrs = MAX_U132_ADDRS;
+        int udevs = MAX_U132_UDEVS;
+        int endps = MAX_U132_ENDPS;
+        u132->board = pdev->dev.platform_data;
+        u132->platform_dev = pdev;
+        u132->power = 0;
+        u132->reset = 0;
+        init_MUTEX(&u132->sw_lock);
+        init_MUTEX(&u132->scheduler_lock);
+        while (rings-- > 0) {
+                struct u132_ring *ring = &u132->ring[rings];
+                ring->u132 = u132;
+                ring->number = rings + 1;
+                ring->length = 0;
+                ring->curr_endp = NULL;
+                INIT_WORK(&ring->scheduler, u132_hcd_ring_work_scheduler,
+                        (void *)ring);
+        } down(&u132->sw_lock);
+        INIT_WORK(&u132->monitor, u132_hcd_monitor_work, (void *)u132);
+        while (ports-- > 0) {
+                struct u132_port *port = &u132->port[ports];
+                port->u132 = u132;
+                port->reset = 0;
+                port->enable = 0;
+                port->power = 0;
+                port->Status = 0;
+        } while (addrs-- > 0) {
+                struct u132_addr *addr = &u132->addr[addrs];
+                addr->address = 0;
+        } while (udevs-- > 0) {
+                struct u132_udev *udev = &u132->udev[udevs];
+                int i = ARRAY_SIZE(udev->endp_number_in);
+                int o = ARRAY_SIZE(udev->endp_number_out);
+                udev->usb_device = NULL;
+                udev->udev_number = 0;
+                udev->usb_addr = 0;
+                udev->portnumber = 0;
+                while (i-- > 0) {
+                        udev->endp_number_in[i] = 0;
+                }
+                while (o-- > 0) {
+                        udev->endp_number_out[o] = 0;
+                }
+        }
+        while (endps-- > 0) {
+                u132->endp[endps] = NULL;
+        }
+        up(&u132->sw_lock);
+        return;
+}
+
+static int __devinit u132_probe(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd;
+        msleep(100);
+        if (u132_exiting > 0) {
+                return -ENODEV;
+        }                        /* refuse to confuse usbcore */
+        if (pdev->dev.dma_mask) {
+                return -EINVAL;
+        }
+        hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
+        if (!hcd) {
+                printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
+                        );
+                ftdi_elan_gone_away(pdev);
+                return -ENOMEM;
+        } else {
+                int retval = 0;
+                struct u132 *u132 = hcd_to_u132(hcd);
+                hcd->rsrc_start = 0;
+                down(&u132_module_lock);
+                list_add_tail(&u132->u132_list, &u132_static_list);
+                u132->sequence_num = ++u132_instances;
+                up(&u132_module_lock);
+                u132_u132_init_kref(u132);
+                u132_initialise(u132, pdev);
+                hcd->product_desc = "ELAN U132 Host Controller";
+                retval = usb_add_hcd(hcd, 0, 0);
+                if (retval != 0) {
+                        dev_err(&u132->platform_dev->dev, "init error %d\n",
+                                retval);
+                        u132_u132_put_kref(u132);
+                        return retval;
+                } else {
+                        u132_monitor_queue_work(u132, 100);
+                        return 0;
+                }
+        }
+}
+
+
+#ifdef CONFIG_PM
+/* for this device there's no useful distinction between the controller
+* and its root hub, except that the root hub only gets direct PM calls
+* when CONFIG_USB_SUSPEND is enabled.
+*/
+static int u132_suspend(struct platform_device *pdev, pm_message_t state)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                if (state.event == PM_EVENT_FREEZE) {
+                        retval = u132_bus_suspend(hcd);
+                } else if (state.event == PM_EVENT_SUSPEND) {
+                        int ports = MAX_U132_PORTS;
+                        while (ports-- > 0) {
+                                port_power(u132, ports, 0);
+                        }
+                }
+                if (retval == 0)
+                        pdev->dev.power.power_state = state;
+                return retval;
+        }
+}
+
+static int u132_resume(struct platform_device *pdev)
+{
+        struct usb_hcd *hcd = platform_get_drvdata(pdev);
+        struct u132 *u132 = hcd_to_u132(hcd);
+        if (u132->going > 1) {
+                dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+                        , u132->going);
+                return -ENODEV;
+        } else if (u132->going > 0) {
+                dev_err(&u132->platform_dev->dev, "device is being removed\n");
+                return -ESHUTDOWN;
+        } else {
+                int retval = 0;
+                if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+                        int ports = MAX_U132_PORTS;
+                        while (ports-- > 0) {
+                                port_power(u132, ports, 1);
+                        }
+                        retval = 0;
+                } else {
+                        pdev->dev.power.power_state = PMSG_ON;
+                        retval = u132_bus_resume(hcd);
+                }
+                return retval;
+        }
+}
+
+#else
+#define u132_suspend NULL
+#define u132_resume NULL
+#endif
+/*
+* this driver is loaded explicitely by ftdi_u132
+*
+* the platform_driver struct is static because it is per type of module
+*/
+static struct platform_driver u132_platform_driver = {
+        .probe = u132_probe,
+        .remove = __devexit_p(u132_remove),
+        .suspend = u132_suspend,
+        .resume = u132_resume,
+        .driver = {
+                   .name = (char *)hcd_name,
+                   .owner = THIS_MODULE,
+                   },
+};
+static int __init u132_hcd_init(void)
+{
+        int retval;
+        INIT_LIST_HEAD(&u132_static_list);
+        u132_instances = 0;
+        u132_exiting = 0;
+        init_MUTEX(&u132_module_lock);
+        if (usb_disabled())
+                return -ENODEV;
+        printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
+                __DATE__);
+        workqueue = create_singlethread_workqueue("u132");
+        retval = platform_driver_register(&u132_platform_driver);
+        return retval;
+}
+
+
+module_init(u132_hcd_init);
+static void __exit u132_hcd_exit(void)
+{
+        struct u132 *u132;
+        struct u132 *temp;
+        down(&u132_module_lock);
+        u132_exiting += 1;
+        up(&u132_module_lock);
+        list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
+                platform_device_unregister(u132->platform_dev);
+        } platform_driver_unregister(&u132_platform_driver);
+        printk(KERN_INFO "u132-hcd driver deregistered\n");
+        wait_event(u132_hcd_wait, u132_instances == 0);
+        flush_workqueue(workqueue);
+        destroy_workqueue(workqueue);
+}
+
+
+module_exit(u132_hcd_exit);
+MODULE_LICENSE("GPL");

+ 2 - 2
drivers/usb/host/uhci-debug.c

@@ -16,7 +16,7 @@
 
 
 #include "uhci-hcd.h"
 #include "uhci-hcd.h"
 
 
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#define uhci_debug_operations (* (const struct file_operations *) NULL)
 static struct dentry *uhci_debugfs_root;
 static struct dentry *uhci_debugfs_root;
 
 
 #ifdef DEBUG
 #ifdef DEBUG
@@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
 }
 }
 
 
 #undef uhci_debug_operations
 #undef uhci_debug_operations
-static struct file_operations uhci_debug_operations = {
+static const struct file_operations uhci_debug_operations = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.open =		uhci_debug_open,
 	.open =		uhci_debug_open,
 	.llseek =	uhci_debug_lseek,
 	.llseek =	uhci_debug_lseek,

+ 9 - 3
drivers/usb/host/uhci-hub.c

@@ -84,6 +84,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
 		unsigned long port_addr)
 		unsigned long port_addr)
 {
 {
 	int status;
 	int status;
+	int i;
 
 
 	if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
 	if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
 		CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
 		CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
@@ -92,9 +93,14 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
 
 
 		/* The controller won't actually turn off the RD bit until
 		/* The controller won't actually turn off the RD bit until
 		 * it has had a chance to send a low-speed EOP sequence,
 		 * it has had a chance to send a low-speed EOP sequence,
-		 * which takes 3 bit times (= 2 microseconds).  We'll delay
-		 * slightly longer for good luck. */
-		udelay(4);
+		 * which is supposed to take 3 bit times (= 2 microseconds).
+		 * Experiments show that some controllers take longer, so
+		 * we'll poll for completion. */
+		for (i = 0; i < 10; ++i) {
+			if (!(inw(port_addr) & USBPORTSC_RD))
+				break;
+			udelay(1);
+		}
 	}
 	}
 	clear_bit(port, &uhci->resuming_ports);
 	clear_bit(port, &uhci->resuming_ports);
 }
 }

+ 2 - 2
drivers/usb/image/mdc800.c

@@ -424,7 +424,7 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res)
  ***************************************************************************/
  ***************************************************************************/
 
 
 static struct usb_driver mdc800_usb_driver;
 static struct usb_driver mdc800_usb_driver;
-static struct file_operations mdc800_device_ops;
+static const struct file_operations mdc800_device_ops;
 static struct usb_class_driver mdc800_class = {
 static struct usb_class_driver mdc800_class = {
 	.name =		"mdc800%d",
 	.name =		"mdc800%d",
 	.fops =		&mdc800_device_ops,
 	.fops =		&mdc800_device_ops,
@@ -941,7 +941,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
 ****************************************************************************/
 ****************************************************************************/
 
 
 /* File Operations of this drivers */
 /* File Operations of this drivers */
-static struct file_operations mdc800_device_ops =
+static const struct file_operations mdc800_device_ops =
 {
 {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.read =		mdc800_device_read,
 	.read =		mdc800_device_read,

+ 26 - 4
drivers/usb/input/Kconfig

@@ -205,10 +205,12 @@ config USB_TOUCHSCREEN
 	depends on USB && INPUT
 	depends on USB && INPUT
 	---help---
 	---help---
 	  USB Touchscreen driver for:
 	  USB Touchscreen driver for:
-	  - eGalax Touchkit USB
+	  - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
 	  - PanJit TouchSet USB
 	  - PanJit TouchSet USB
-	  - 3M MicroTouch USB
+	  - 3M MicroTouch USB (EX II series)
 	  - ITM
 	  - ITM
+	  - some other eTurboTouch
+	  - Gunze AHL61
 
 
 	  Have a look at <http://linux.chapter7.ch/touchkit/> for
 	  Have a look at <http://linux.chapter7.ch/touchkit/> for
 	  a usage description and the required user-space stuff.
 	  a usage description and the required user-space stuff.
@@ -218,7 +220,7 @@ config USB_TOUCHSCREEN
 
 
 config USB_TOUCHSCREEN_EGALAX
 config USB_TOUCHSCREEN_EGALAX
 	default y
 	default y
-	bool "eGalax device support" if EMBEDDED
+	bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
 	depends on USB_TOUCHSCREEN
 	depends on USB_TOUCHSCREEN
 
 
 config USB_TOUCHSCREEN_PANJIT
 config USB_TOUCHSCREEN_PANJIT
@@ -228,7 +230,7 @@ config USB_TOUCHSCREEN_PANJIT
 
 
 config USB_TOUCHSCREEN_3M
 config USB_TOUCHSCREEN_3M
 	default y
 	default y
-	bool "3M/Microtouch device support" if EMBEDDED
+	bool "3M/Microtouch EX II series device support" if EMBEDDED
 	depends on USB_TOUCHSCREEN
 	depends on USB_TOUCHSCREEN
 
 
 config USB_TOUCHSCREEN_ITM
 config USB_TOUCHSCREEN_ITM
@@ -236,6 +238,16 @@ config USB_TOUCHSCREEN_ITM
 	bool "ITM device support" if EMBEDDED
 	bool "ITM device support" if EMBEDDED
 	depends on USB_TOUCHSCREEN
 	depends on USB_TOUCHSCREEN
 
 
+config USB_TOUCHSCREEN_ETURBO
+	default y
+	bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+	depends on USB_TOUCHSCREEN
+
+config USB_TOUCHSCREEN_GUNZE
+	default y
+	bool "Gunze AHL61 device support" if EMBEDDED
+	depends on USB_TOUCHSCREEN
+
 config USB_YEALINK
 config USB_YEALINK
 	tristate "Yealink usb-p1k voip phone"
 	tristate "Yealink usb-p1k voip phone"
 	depends on USB && INPUT && EXPERIMENTAL
 	depends on USB && INPUT && EXPERIMENTAL
@@ -326,3 +338,13 @@ config USB_APPLETOUCH
 
 
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called appletouch.
 	  module will be called appletouch.
+
+config USB_TRANCEVIBRATOR
+	tristate "PlayStation 2 Trance Vibrator driver support"
+	depends on USB
+	help
+	  Say Y here if you want to connect a PlayStation 2 Trance Vibrator
+	  device to your computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called trancevibrator.

+ 2 - 0
drivers/usb/input/Makefile

@@ -3,6 +3,7 @@
 #
 #
 
 
 # Multipart objects.
 # Multipart objects.
+wacom-objs	:= wacom_sys.o wacom_wac.o
 usbhid-objs	:= hid-core.o
 usbhid-objs	:= hid-core.o
 
 
 # Optional parts of multipart objects.
 # Optional parts of multipart objects.
@@ -44,6 +45,7 @@ obj-$(CONFIG_USB_ACECAD)	+= acecad.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
 obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
 obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 
 
 ifeq ($(CONFIG_USB_DEBUG),y)
 ifeq ($(CONFIG_USB_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 EXTRA_CFLAGS += -DDEBUG

+ 1 - 4
drivers/usb/input/acecad.c

@@ -141,10 +141,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
 
 
 	endpoint = &interface->endpoint[0].desc;
 	endpoint = &interface->endpoint[0].desc;
 
 
-	if (!(endpoint->bEndpointAddress & 0x80))
-		return -ENODEV;
-
-	if ((endpoint->bmAttributes & 3) != 3)
+	if (!usb_endpoint_is_int_in(endpoint))
 		return -ENODEV;
 		return -ENODEV;
 
 
 	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
 	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

+ 1 - 4
drivers/usb/input/appletouch.c

@@ -436,10 +436,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
 	iface_desc = iface->cur_altsetting;
 	iface_desc = iface->cur_altsetting;
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
 		endpoint = &iface_desc->endpoint[i].desc;
 		endpoint = &iface_desc->endpoint[i].desc;
-		if (!int_in_endpointAddr &&
-		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
-		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-					== USB_ENDPOINT_XFER_INT)) {
+		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
 			/* we found an interrupt in endpoint */
 			/* we found an interrupt in endpoint */
 			int_in_endpointAddr = endpoint->bEndpointAddress;
 			int_in_endpointAddr = endpoint->bEndpointAddress;
 			break;
 			break;

+ 2 - 6
drivers/usb/input/ati_remote.c

@@ -732,12 +732,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
 	endpoint_in = &iface_host->endpoint[0].desc;
 	endpoint_in = &iface_host->endpoint[0].desc;
 	endpoint_out = &iface_host->endpoint[1].desc;
 	endpoint_out = &iface_host->endpoint[1].desc;
 
 
-	if (!(endpoint_in->bEndpointAddress & USB_DIR_IN)) {
-		err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
-		return -ENODEV;
-	}
-	if ((endpoint_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
-		err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+	if (!usb_endpoint_is_int_in(endpoint_in)) {
+		err("%s: Unexpected endpoint_in\n", __FUNCTION__);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 	if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
 	if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {

+ 21 - 3
drivers/usb/input/hid-core.c

@@ -1023,7 +1023,8 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
 			return;
 			return;
 		case -EILSEQ:		/* protocol error or unplug */
 		case -EILSEQ:		/* protocol error or unplug */
 		case -EPROTO:		/* protocol error or unplug */
 		case -EPROTO:		/* protocol error or unplug */
-		case -ETIMEDOUT:	/* NAK */
+		case -ETIME:		/* protocol error or unplug */
+		case -ETIMEDOUT:	/* Should never happen, but... */
 			clear_bit(HID_IN_RUNNING, &hid->iofl);
 			clear_bit(HID_IN_RUNNING, &hid->iofl);
 			hid_io_error(hid);
 			hid_io_error(hid);
 			return;
 			return;
@@ -1535,13 +1536,17 @@ void hid_init_reports(struct hid_device *hid)
 #define USB_VENDOR_ID_GLAB		0x06c2
 #define USB_VENDOR_ID_GLAB		0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
 #define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
 #define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
-#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
 #define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
 #define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT	0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT	0x0051
 #define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
 #define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL	0x0058
 
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
 #define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
 #define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT	0x8201
 #define USB_DEVICE_ID_DUAL_USB_JOYPAD   0x8866
 #define USB_DEVICE_ID_DUAL_USB_JOYPAD   0x8866
 
 
 #define USB_VENDOR_ID_WISEGROUP_LTD	0x6677
 #define USB_VENDOR_ID_WISEGROUP_LTD	0x6677
@@ -1591,6 +1596,10 @@ void hid_init_reports(struct hid_device *hid)
 
 
 #define USB_VENDOR_ID_YEALINK		0x6993
 #define USB_VENDOR_ID_YEALINK		0x6993
 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
+
+#define USB_VENDOR_ID_ALCOR		0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
+
 /*
 /*
  * Alphabetically sorted blacklist by quirk type.
  * Alphabetically sorted blacklist by quirk type.
  */
  */
@@ -1608,6 +1617,7 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
@@ -1620,9 +1630,12 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
@@ -1690,7 +1703,11 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
@@ -1701,6 +1718,7 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
 
 
 	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },

+ 1 - 1
drivers/usb/input/hiddev.c

@@ -722,7 +722,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 	return -EINVAL;
 	return -EINVAL;
 }
 }
 
 
-static struct file_operations hiddev_fops = {
+static const struct file_operations hiddev_fops = {
 	.owner =	THIS_MODULE,
 	.owner =	THIS_MODULE,
 	.read =		hiddev_read,
 	.read =		hiddev_read,
 	.write =	hiddev_write,
 	.write =	hiddev_write,

+ 1 - 1
drivers/usb/input/itmtouch.c

@@ -87,7 +87,7 @@ static void itmtouch_irq(struct urb *urb, struct pt_regs *regs)
 	case 0:
 	case 0:
 		/* success */
 		/* success */
 		break;
 		break;
-	case -ETIMEDOUT:
+	case -ETIME:
 		/* this urb is timing out */
 		/* this urb is timing out */
 		dbg("%s - urb timed out - was the device unplugged?",
 		dbg("%s - urb timed out - was the device unplugged?",
 		    __FUNCTION__);
 		    __FUNCTION__);

+ 1 - 2
drivers/usb/input/keyspan_remote.c

@@ -420,8 +420,7 @@ static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_i
 	for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
 	for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
 		endpoint = &iface->endpoint[i].desc;
 		endpoint = &iface->endpoint[i].desc;
 
 
-		if ((endpoint->bEndpointAddress & USB_DIR_IN) &&
-		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+		if (usb_endpoint_is_int_in(endpoint)) {
 			/* we found our interrupt in endpoint */
 			/* we found our interrupt in endpoint */
 			return endpoint;
 			return endpoint;
 		}
 		}

+ 1 - 1
drivers/usb/input/mtouchusb.c

@@ -107,7 +107,7 @@ static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
 	case 0:
 	case 0:
 		/* success */
 		/* success */
 		break;
 		break;
-	case -ETIMEDOUT:
+	case -ETIME:
 		/* this urb is timing out */
 		/* this urb is timing out */
 		dbg("%s - urb timed out - was the device unplugged?",
 		dbg("%s - urb timed out - was the device unplugged?",
 		    __FUNCTION__);
 		    __FUNCTION__);

+ 1 - 3
drivers/usb/input/powermate.c

@@ -313,9 +313,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
 
 
 	interface = intf->cur_altsetting;
 	interface = intf->cur_altsetting;
 	endpoint = &interface->endpoint[0].desc;
 	endpoint = &interface->endpoint[0].desc;
-	if (!(endpoint->bEndpointAddress & 0x80))
-		return -EIO;
-	if ((endpoint->bmAttributes & 3) != 3)
+	if (!usb_endpoint_is_int_in(endpoint))
 		return -EIO;
 		return -EIO;
 
 
 	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

Some files were not shown because too many files changed in this diff