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,
     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
-    peripheral devices as leaves (and slaves).
+    peripherals as leaves (and slaves).
     Modern PCs support several such trees of USB devices, usually
     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
     connect a USB 1.1 device directly to the machine's "root hub".
     </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>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
     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).
     </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>Linux can run inside USB devices as well as on
     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,
-    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>
 
     </chapter>
@@ -103,17 +96,14 @@
 <chapter id="host">
     <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.
     </para>
 
@@ -122,21 +112,21 @@
      
     <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.
 	</para></listitem>
 
 	<listitem><para>The device description model includes one or more
 	"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>
 
-	<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
 	standardized by USB "Class" specifications, or may be specific to
 	a vendor or device.</para>
@@ -162,7 +152,7 @@
 	</para></listitem>
 
 	<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,
 	using request structures called "URBs" (USB Request Blocks).
 	</para></listitem>
@@ -463,14 +453,25 @@
 	    file in your Linux kernel sources.
 	    </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>
-
 	</sect1>
 
 	<sect1>

+ 3 - 0
Documentation/devices.txt

@@ -2543,6 +2543,9 @@ Your cooperation is appreciated.
 		 64 = /dev/usb/rio500	Diamond Rio 500
 		 65 = /dev/usb/usblcd	USBLCD Interface (info@usblcd.de)
 		 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
 		    ...
 		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
 			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
 			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,
 			reset this status with usb_clear_halt().
@@ -163,6 +163,3 @@ usb_get_*/usb_set_*():
 usb_control_msg():
 usb_bulk_msg():
 -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
   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
 

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

@@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = {
 /*
  * 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 = {
 	/* 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);
 
 	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_set_udc_info(&udc_info);

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

@@ -26,7 +26,7 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 
 #include <asm/io.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,
     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_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,
     struct ub_request *urq, 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;
 	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);
-		ub_end_rq(rq, 0);
+		ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
 		return 0;
 	}
 
@@ -693,7 +699,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
 
 drop:
 	ub_put_cmd(lun, cmd);
-	ub_end_rq(rq, 0);
+	ub_end_rq(rq, DID_ERROR << 16);
 	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_request *urq = cmd->back;
 	struct request *rq;
-	int uptodate;
+	unsigned int scsi_status;
 
 	rq = urq->rq;
 
 	if (cmd->error == 0) {
-		uptodate = 1;
-
 		if (blk_pc_request(rq)) {
 			if (cmd->act_len >= rq->data_len)
 				rq->data_len = 0;
 			else
 				rq->data_len -= cmd->act_len;
 		}
+		scsi_status = 0;
 	} else {
-		uptodate = 0;
-
 		if (blk_pc_request(rq)) {
 			/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
 			memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
 			rq->sense_len = UB_SENSE_SIZE;
 			if (sc->top_sense[0] != 0)
-				rq->errors = SAM_STAT_CHECK_CONDITION;
+				scsi_status = SAM_STAT_CHECK_CONDITION;
 			else
-				rq->errors = DID_ERROR << 16;
+				scsi_status = DID_ERROR << 16;
 		} else {
 			if (cmd->error == -EIO) {
 				if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
 					return;
 			}
+			scsi_status = SAM_STAT_CHECK_CONDITION;
 		}
 	}
 
 	urq->rq = NULL;
 
 	ub_put_cmd(lun, cmd);
-	ub_end_rq(rq, uptodate);
+	ub_end_rq(rq, scsi_status);
 	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_last(rq, uptodate);
 }

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

@@ -30,7 +30,7 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/i2c.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";
 	case -EILSEQ:
 		return "CRC mismatch, timeout, or unknown USB error";
-	case -ETIMEDOUT:
+	case -ETIME:
 		return "timed out";
 	case -EPIPE:
 		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"},
 	{-EFBIG, "Too much ISO frames requested"},
 	{-ENOSR, "Buffer error (overrun)"},
-	{-EPIPE, "Specified endpoint is stalled (device not responding)"},
+	{-EPIPE, "Specified endpoint is stalled"},
 	{-EOVERFLOW, "Babble (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"},
 	{-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) {
 		case 0:         /* success */
-		case -ETIMEDOUT:    /* NAK */
 			break;
 		case -ECONNRESET:   /* kill */
 		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 -ENOENT:
 		case -ESHUTDOWN:
-		case -ETIMEDOUT:
+		case -ETIME:
 			/* this urb is dead, cleanup */
 			dprintk("%s:urb shutting down with status: %d\n",
 					__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[] = {
 	{ -ENOSR,	"Buffer error (overrun)" },
 	{ -EPIPE,	"Stalled (device not responding)" },
-	{ -EOVERFLOW,	"Babble (bad cable?)" },
+	{ -EOVERFLOW,	"Babble (device sends too much data)" },
 	{ -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 - 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 -EPROTO:		errmsg = "Bit-stuff error (bad cable?)"; 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);
 		/* 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" },
 	{ -ENOSR,     "Buffer error (overrun)" },
 	{ -EPIPE,     "Specified endpoint is stalled (device not responding)"},
-	{ -EOVERFLOW, "Babble (bad cable?)" },
+	{ -EOVERFLOW, "Babble (too much data)" },
 	{ -EPROTO,    "Bit-stuff error (bad cable?)" },
 	{ -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 }
 };
 
-
-
 /****************************************************************************
  * 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 */
 			done = 1;
 			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 */
 			urb->status = 0;
 			netif_wake_queue(self->netdev);
@@ -712,10 +710,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
 			 * Jean II */
 			done = 1;
 			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 */
 			if(skb != NULL) {
 				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++;	
 			/* Also precursor to a hot-unplug on UHCI. */
 			/* Fallthrough... */
-		case -ECONNRESET:		/* -104 */
+		case -ECONNRESET:
 			/* Random error, if I remember correctly */
 			/* uhci_cleanup_unlink() is going to kill the Rx
 			 * URB just after we return. No problem, at this
 			 * point the URB will be idle ;-) - Jean II */
-		case -ESHUTDOWN:		/* -108 */
+		case -ESHUTDOWN:
 			/* That's usually a hot-unplug. Submit will fail... */
-		case -ETIMEDOUT:		/* -110 */
+		case -ETIME:
 			/* Usually precursor to a hot-unplug on OHCI. */
 		default:
 			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) {
 		case -EILSEQ:
 		case -ENODEV:
-		case -ETIMEDOUT:
+		case -ETIME:
 		case -ENOENT:
 		case -EPIPE:
 		case -EOVERFLOW:
@@ -201,7 +201,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
 	switch(urb->status) {
 		case -EILSEQ:
 		case -ENODEV:
-		case -ETIMEDOUT:
+		case -ETIME:
 		case -ENOENT:
 		case -EPIPE:
 		case -EOVERFLOW:

+ 1 - 0
drivers/usb/Kconfig

@@ -25,6 +25,7 @@ config USB_ARCH_HAS_OHCI
 	default y if PXA27x
 	default y if ARCH_EP93XX
 	default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
+	default y if ARCH_PNX4008
 	# PPC:
 	default y if STB03xxx
 	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_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
+obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_ETRAX_USB_HOST)	+= 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/
 
+obj-$(CONFIG_USB_ACECAD)	+= input/
 obj-$(CONFIG_USB_AIPTEK)	+= input/
 obj-$(CONFIG_USB_ATI_REMOTE)	+= input/
 obj-$(CONFIG_USB_HID)		+= input/
@@ -31,8 +33,8 @@ obj-$(CONFIG_USB_KBTAB)		+= input/
 obj-$(CONFIG_USB_MOUSE)		+= input/
 obj-$(CONFIG_USB_MTOUCH)	+= input/
 obj-$(CONFIG_USB_POWERMATE)	+= input/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
 obj-$(CONFIG_USB_WACOM)		+= input/
-obj-$(CONFIG_USB_ACECAD)	+= input/
 obj-$(CONFIG_USB_XPAD)		+= input/
 
 obj-$(CONFIG_USB_CATC)		+= net/
@@ -47,22 +49,24 @@ obj-$(CONFIG_USB_MICROTEK)	+= image/
 
 obj-$(CONFIG_USB_SERIAL)	+= serial/
 
+obj-$(CONFIG_USB_ADUTUX)	+= misc/
+obj-$(CONFIG_USB_APPLEDISPLAY)	+= misc/
 obj-$(CONFIG_USB_AUERSWALD)	+= misc/
 obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
 obj-$(CONFIG_USB_CYTHERM)	+= misc/
 obj-$(CONFIG_USB_EMI26)		+= misc/
 obj-$(CONFIG_USB_EMI62)		+= misc/
+obj-$(CONFIG_USB_FTDI_ELAN)	+= misc/
 obj-$(CONFIG_USB_IDMOUSE)	+= misc/
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_LD)		+= misc/
 obj-$(CONFIG_USB_LED)		+= misc/
 obj-$(CONFIG_USB_LEGOTOWER)	+= misc/
+obj-$(CONFIG_USB_PHIDGETSERVO)	+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
+obj-$(CONFIG_USB_SISUSBVGA)	+= misc/
 obj-$(CONFIG_USB_TEST)		+= 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_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;
 }
 
-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,
@@ -1708,37 +1714,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
 		return ret;
 	}
 
-	create_fs_entries(sc, intf);
+	ret = create_fs_entries(intf);
+	if (ret) {
+		uea_stop(sc);
+		kfree(sc);
+		return ret;
+	}
 	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)
 {
 	struct uea_softc *sc = usbatm->driver_data;
 
-	destroy_fs_entries(sc, intf);
+	destroy_fs_entries(intf);
 	uea_stop(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;
 }
 
-static struct file_operations usblp_fops = {
+static const struct file_operations usblp_fops = {
 	.owner =	THIS_MODULE,
 	.read =		usblp_read,
 	.write =	usblp_write,
@@ -927,7 +927,9 @@ static int usblp_probe(struct usb_interface *intf,
 
 	/* Retrieve and store the device ID string. */
 	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
 	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++) {
 			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)
 					epwrite = epd;
 
-			} else {
+			if (usb_endpoint_is_bulk_in(epd))
 				if (!epread)
 					epread = epd;
-			}
 		}
 
 		/* 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 \
 			config.o file.o buffer.o sysfs.o endpoint.o \
-			devio.o notify.o
+			devio.o notify.o generic.o
 
 ifeq ($(CONFIG_PCI),y)
 	usbcore-objs	+= hcd-pci.o

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

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

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

@@ -475,7 +475,9 @@ int usb_get_configuration(struct usb_device *dev)
 		if (result < 0) {
 			dev_err(ddev, "unable to read config index %d "
 			    "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) {
 			dev_err(ddev, "config index %d descriptor too short "
 			    "(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 */
 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;
 
 	lock_kernel();
@@ -603,7 +603,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
 			unlock_kernel();
 			return POLLIN;
 		}
-		
+
 		/* we may have dropped BKL - need to check for having lost the race */
 		if (file->private_data) {
 			kfree(st);
@@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
 	return ret;
 }
 
-struct file_operations usbfs_devices_fops = {
+const struct file_operations usbfs_devices_fops = {
 	.llseek =	usb_device_lseek,
 	.read =		usb_device_read,
 	.poll =		usb_device_poll,

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

@@ -59,6 +59,9 @@
 #define USB_DEVICE_MAX			USB_MAXBUS * 128
 static struct class *usb_device_class;
 
+/* Mutual exclusion for removal, open, and release */
+DEFINE_MUTEX(usbfs_mutex);
+
 struct async {
 	struct list_head asynclist;
 	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
 
-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)
@@ -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)
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	ssize_t ret = 0;
 	unsigned len;
@@ -127,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
 
 	pos = *ppos;
 	usb_lock_device(dev);
-	if (!connected(dev)) {
+	if (!connected(ps)) {
 		ret = -ENODEV;
 		goto err;
 	} 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)
 {
-        struct async *as = (struct async *)urb->context;
+        struct async *as = urb->context;
         struct dev_state *ps = as->ps;
 	struct siginfo sinfo;
 
@@ -541,25 +545,25 @@ static int usbdev_open(struct inode *inode, struct file *file)
 	struct dev_state *ps;
 	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;
 	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
-		goto out_nolock;
+		goto out;
 
-	lock_kernel();
 	ret = -ENOENT;
 	/* check if we are called from a real node or usbfs */
 	if (imajor(inode) == USB_DEVICE_MAJOR)
 		dev = usbdev_lookup_minor(iminor(inode));
 	if (!dev)
 		dev = inode->i_private;
-	if (!dev) {
-		kfree(ps);
+	if (!dev)
 		goto out;
-	}
+	ret = usb_autoresume_device(dev, 1);
+	if (ret)
+		goto out;
+
 	usb_get_dev(dev);
 	ret = 0;
 	ps->dev = dev;
@@ -579,30 +583,36 @@ static int usbdev_open(struct inode *inode, struct file *file)
 	list_add_tail(&ps->list, &dev->filelist);
 	file->private_data = ps;
  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)
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	unsigned int ifnum;
 
 	usb_lock_device(dev);
+
+	/* Protect against simultaneous open */
+	mutex_lock(&usbfs_mutex);
 	list_del_init(&ps->list);
+	mutex_unlock(&usbfs_mutex);
+
 	for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
 			ifnum++) {
 		if (test_bit(ifnum, &ps->ifclaimed))
 			releaseintf(ps, ifnum);
 	}
 	destroy_all_async(ps);
+	usb_autosuspend_device(dev, 1);
 	usb_unlock_device(dev);
 	usb_put_dev(dev);
-	ps->dev = NULL;
 	kfree(ps);
-        return 0;
+	return 0;
 }
 
 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);
 		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 */
 	case USBDEVFS_CONNECT:
 		usb_unlock_device(ps->dev);
-		bus_rescan_devices(intf->dev.bus);
+		retval = bus_rescan_devices(intf->dev.bus);
 		usb_lock_device(ps->dev);
 		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)
 {
-	struct dev_state *ps = (struct dev_state *)file->private_data;
+	struct dev_state *ps = file->private_data;
 	struct usb_device *dev = ps->dev;
 	void __user *p = (void __user *)arg;
 	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))
 		return -EPERM;
 	usb_lock_device(dev);
-	if (!connected(dev)) {
+	if (!connected(ps)) {
 		usb_unlock_device(dev);
 		return -ENODEV;
 	}
@@ -1556,18 +1566,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 /* No kernel lock - fine */
 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);
 	if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
 		mask |= POLLOUT | POLLWRNORM;
-	if (!connected(ps->dev))
+	if (!connected(ps))
 		mask |= POLLERR | POLLHUP;
 	return mask;
 }
 
-struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbfs_device_file_operations = {
 	.llseek =	usbdev_lseek,
 	.read =		usbdev_read,
 	.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
  * 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;
 };
 
-
-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
 
 /*
@@ -80,6 +49,7 @@ static ssize_t store_new_id(struct device_driver *driver,
 	u32 idVendor = 0;
 	u32 idProduct = 0;
 	int fields = 0;
+	int retval = 0;
 
 	fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
 	if (fields < 2)
@@ -99,10 +69,12 @@ static ssize_t store_new_id(struct device_driver *driver,
 	spin_unlock(&usb_drv->dynids.lock);
 
 	if (get_driver(driver)) {
-		driver_attach(driver);
+		retval = driver_attach(driver);
 		put_driver(driver);
 	}
 
+	if (retval)
+		return retval;
 	return count;
 }
 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;
 
 	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);
 exit:
 	return error;
@@ -127,7 +99,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
 		return;
 
 	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);
 }
 
@@ -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)
 {
-	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;
 	int error = -ENODEV;
 
 	dev_dbg(dev, "%s\n", __FUNCTION__);
 
-	if (!driver->probe)
+	if (is_usb_device(dev))		/* Sanity check */
 		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);
 	if (!id)
@@ -196,48 +204,165 @@ static int usb_probe_interface(struct device *dev)
 	if (id) {
 		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
 		 * state whatsoever.  We use it to record when it's bound to
 		 * a driver that may start I/0:  it's not frozen/quiesced.
 		 */
 		mark_active(intf);
 		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);
 		if (error) {
 			mark_quiesced(intf);
+			intf->needs_remote_wakeup = 0;
 			intf->condition = USB_INTERFACE_UNBOUND;
 		} else
 			intf->condition = USB_INTERFACE_BOUND;
+
+		usb_autosuspend_device(udev, 1);
 	}
 
 	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)
 {
+	struct usb_driver *driver = to_usb_driver(dev->driver);
 	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;
 
+	/* Autoresume for set_interface call below */
+	udev = interface_to_usbdev(intf);
+	error = usb_autoresume_device(udev, 1);
+
 	/* release all urbs for this interface */
 	usb_disable_interface(interface_to_usbdev(intf), intf);
 
-	if (driver && driver->disconnect)
-		driver->disconnect(intf);
+	driver->disconnect(intf);
 
 	/* reset other interface state */
 	usb_set_interface(interface_to_usbdev(intf),
 			intf->altsetting[0].desc.bInterfaceNumber,
 			0);
 	usb_set_intfdata(intf, NULL);
+
 	intf->condition = USB_INTERFACE_UNBOUND;
 	mark_quiesced(intf);
+	intf->needs_remote_wakeup = 0;
+
+	if (!error)
+		usb_autosuspend_device(udev, 1);
 
 	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 */
 static int usb_match_one_id(struct usb_interface *interface,
 			    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);
 
 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_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;
 }
 
+#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.
  *
- * 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.
  *
  * 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())
 		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);
 	INIT_LIST_HEAD(&new_driver->dynids.list);
 
-	retval = driver_register(&new_driver->driver);
+	retval = driver_register(&new_driver->drvwrap.driver);
 
 	if (!retval) {
-		pr_info("%s: registered new driver %s\n",
+		pr_info("%s: registered new interface driver %s\n",
 			usbcore_name, new_driver->name);
 		usbfs_update_special();
 		usb_create_newid_file(new_driver);
 	} 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);
 	}
 
@@ -448,8 +763,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
 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
  *
  * 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)
 {
-	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_free_dynids(driver);
-	driver_unregister(&driver->driver);
+	driver_unregister(&driver->drvwrap.driver);
 
 	usbfs_update_special();
 }
 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);
 }
 
-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];
 	struct ep_device *ep_dev;
@@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent,
 	retval = device_register(&ep_dev->dev);
 	if (retval)
 		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;
 
 	/* create the symlink to the old-style "ep_XX" directory */
 	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:
-	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:
 	kfree(ep_dev);
-	return;
+	destroy_endpoint_class();
+	return retval;
 }
 
 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;
 }
 
-static struct file_operations usb_fops = {
+static const struct file_operations usb_fops = {
 	.owner =	THIS_MODULE,
 	.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 */
 
+/**
+ * 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 <asm/irq.h>
 #include <asm/byteorder.h>
+#include <linux/platform_device.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)
 {
-	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 */
 		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);
 		if (urb == hcd->status_urb) {
 			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);
 		if (urb)
 			usb_hcd_giveback_urb (hcd, urb, NULL);
-		local_irq_enable ();
+		local_irq_restore (flags);
 	}
 
 	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;
 
 int usb_host_init(void)
@@ -730,39 +695,12 @@ static void usb_bus_init (struct usb_bus *bus)
 	bus->devnum_next = 1;
 
 	bus->root_hub = NULL;
-	bus->hcpriv = NULL;
 	bus->busnum = -1;
 	bus->bandwidth_allocated = 0;
 	bus->bandwidth_int_reqs  = 0;
 	bus->bandwidth_isoc_reqs = 0;
 
 	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
  * 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;
-	struct usb_hcd		*hcd = urb->dev->bus->hcpriv;
+	struct usb_hcd		*hcd = bus_to_hcd(urb->dev->bus);
 	struct usb_host_endpoint *ep;
 	unsigned long		flags;
 
@@ -1186,7 +1124,7 @@ doit:
 	/* lower level hcd code should use *_dma exclusively,
 	 * 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)
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			urb->setup_dma = dma_map_single (
@@ -1221,9 +1159,10 @@ done:
 /*-------------------------------------------------------------------------*/
 
 /* 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))
 		return -ESHUTDOWN;
 	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()
  * 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_hcd			*hcd = NULL;
@@ -1296,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
 	spin_lock (&hcd_data_lock);
 
 	sys = &urb->dev->dev;
-	hcd = urb->dev->bus->hcpriv;
+	hcd = bus_to_hcd(urb->dev->bus);
 	if (hcd == NULL) {
 		retval = -ENODEV;
 		goto done;
@@ -1354,41 +1293,33 @@ done:
 /*-------------------------------------------------------------------------*/
 
 /* 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.
  *
  * example:  a qh stored in ep->hcpriv, holding state related to endpoint
  * 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 urb		*urb;
 
-	hcd = udev->bus->hcpriv;
+	hcd = bus_to_hcd(udev->bus);
 
 	WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
 			udev->state != USB_STATE_NOTATTACHED);
 
 	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 */
 rescan:
 	spin_lock (&hcd_data_lock);
 	list_for_each_entry (urb, &ep->urb_list, urb_list) {
 		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)
 			continue;
 		usb_get_urb (urb);
@@ -1430,6 +1361,30 @@ rescan:
 	might_sleep ();
 	if (hcd->driver->endpoint_disable)
 		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;
 }
 
-/*
- * 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 
  * @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
  * @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);
 	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)
 			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
 			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
  * @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;
 	}
 	dev_set_drvdata(dev, hcd);
+	kref_init(&hcd->kref);
 
 	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.bus_name = bus_name;
+	hcd->self.uses_dma = (dev->dma_mask != NULL);
 
 	init_timer(&hcd->rh_timer);
 	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);
 
+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)
 {
-	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);
 
@@ -1915,6 +1819,16 @@ void usb_remove_hcd(struct usb_hcd *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)

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

@@ -55,12 +55,13 @@
 
 /*-------------------------------------------------------------------------*/
 
-struct usb_hcd {	/* usb_bus.hcpriv points to this */
+struct usb_hcd {
 
 	/*
 	 * housekeeping
 	 */
 	struct usb_bus		self;		/* hcd is-a bus */
+	struct kref		kref;		/* reference counter */
 
 	const char		*product_desc;	/* product/vendor string */
 	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		poll_rh:1;	/* poll for rh status? */
 	unsigned		poll_pending:1;	/* status has changed? */
+	unsigned		wireless:1;	/* Wireless USB HCD */
 
 	int			irq;		/* irq allocated */
 	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;
 }
 
-
-// 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 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;
 
@@ -192,6 +174,9 @@ struct hc_driver {
 	/* cleanly make HCD stop writing memory and doing I/O */
 	void	(*stop) (struct usb_hcd *hcd);
 
+	/* shutdown HCD */
+	void	(*shutdown) (struct usb_hcd *hcd);
+
 	/* return current frame number */
 	int	(*get_frame_number) (struct usb_hcd *hcd);
 
@@ -218,15 +203,25 @@ struct hc_driver {
 		/* 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,
 		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 int usb_add_hcd(struct usb_hcd *hcd,
 		unsigned int irqnum, unsigned long irqflags);
 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
 struct pci_dev;
 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);
 #endif /* CONFIG_PM */
 
+extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
+
 #endif /* CONFIG_PCI */
 
 /* 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,
 		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 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 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))
 
 #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_root_hub_lost_power (struct usb_device *rhdev);
 extern int hcd_bus_suspend (struct usb_bus *bus);
 extern int hcd_bus_resume (struct usb_bus *bus);
 #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)
 {
 	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 */
 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 i;
 	unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
 			goto resubmit;
 		hub->error = urb->status;
 		/* FALL THROUGH */
-	
+
 	/* let khubd handle things */
 	case 0:			/* we got data:  port status changed */
 		bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
 	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 */
 	hub->quiescing = 1;
 	hub->activating = 0;
 	hub->resume_root_hub = 0;
-}
 
-static void hub_quiesce(struct usb_hub *hub)
-{
 	/* (blocking) stop khubd and related activity */
-	__hub_quiesce(hub);
 	usb_kill_urb(hub->urb);
 	if (hub->has_indicators)
 		cancel_delayed_work(&hub->leds);
@@ -868,13 +864,8 @@ descriptor_error:
 
 	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;
 
 	/* We found a hub */
@@ -1022,26 +1013,29 @@ void usb_set_device_state(struct usb_device *udev,
 	if (udev->state == USB_STATE_NOTATTACHED)
 		;	/* do nothing */
 	else if (new_state != USB_STATE_NOTATTACHED) {
-		udev->state = new_state;
 
 		/* root hub wakeup capabilities are managed out-of-band
 		 * and may involve silicon errata ... ignore them here.
 		 */
 		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,
 					(udev->actconfig->desc.bmAttributes
 					 & USB_CONFIG_ATT_WAKEUP));
-			else if (new_state != USB_STATE_SUSPENDED)
+			else
 				device_init_wakeup(&udev->dev, 0);
 		}
+		udev->state = new_state;
 	} else
 		recursively_mark_NOTATTACHED(udev);
 	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
@@ -1059,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
 	unsigned long flags;
 
 	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);
 	hub = hdev_to_hub(rhdev);
 	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);
 
-#endif
+#endif	/* CONFIG_PM */
 
 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
 	 * so that the hardware is now fully quiesced.
 	 */
+	dev_dbg (&udev->dev, "unregistering device\n");
 	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.
 	 */
-	dev_dbg (&udev->dev, "unregistering device\n");
 	release_address(udev);
-	usb_remove_sysfs_dev_files(udev);
 
 	/* Avoid races with recursively_mark_NOTATTACHED() */
 	spin_lock_irq(&device_state_lock);
 	*pdev = NULL;
 	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
@@ -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 err;
-	int c;
 
 	err = usb_get_configuration(udev);
 	if (err < 0) {
@@ -1371,8 +1254,7 @@ int usb_new_device(struct usb_device *udev)
 					USB_DT_OTG, (void **) &desc) == 0) {
 			if (desc->bmAttributes & USB_OTG_HNP) {
 				unsigned		port1 = udev->portnum;
-				struct usb_device	*root = udev->parent;
-				
+
 				dev_info(&udev->dev,
 					"Dual-Role OTG device on %sHNP port\n",
 					(port1 == bus->otg_port)
@@ -1407,9 +1289,9 @@ int usb_new_device(struct usb_device *udev)
 		 * (Includes HNP test device.)
 		 */
 		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);
-			err = __usb_suspend_device(udev, udev->bus->otg_port);
+			err = __usb_port_suspend(udev, udev->bus->otg_port);
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
@@ -1418,34 +1300,15 @@ int usb_new_device(struct usb_device *udev)
 	}
 #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);
 	if (err) {
 		dev_err(&udev->dev, "can't device_add, error %d\n", err);
 		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;
 
@@ -1472,6 +1335,18 @@ static int hub_port_status(struct usb_hub *hub, int port1,
 	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 SET_ADDRESS_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 (!(portstatus & USB_PORT_STAT_RESET) &&
 		    (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;
 			else if (portstatus & USB_PORT_STAT_LOW_SPEED)
 				udev->speed = USB_SPEED_LOW;
@@ -1607,6 +1484,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
  	kick_khubd(hub);
 }
 
+#ifdef	CONFIG_PM
 
 #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
 	 * 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),
 				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
 				USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1659,7 +1537,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
 				USB_CTRL_SET_TIMEOUT);
 	} else {
 		/* 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);
 		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
  * (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;
 
@@ -1692,49 +1571,29 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
 	if (port1 < 0)
 		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)
 		status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
 				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;
 }
 
-#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
  * Context: must be able to sleep; device not locked; pm locks held
  *
  * Suspends a USB device that isn't in active use, conserving power.
  * Devices may wake out of a suspend, if anything important happens,
  * 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.
  *
  * 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.
  */
-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
  * 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;
 	u16	devstatus;
@@ -1783,7 +1634,6 @@ static int finish_device_resume(struct usb_device *udev)
 	usb_set_device_state(udev, udev->actconfig
 			? USB_STATE_CONFIGURED
 			: USB_STATE_ADDRESS);
-	udev->dev.power.power_state = PMSG_ON;
 
  	/* 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,
@@ -1798,9 +1648,6 @@ static int finish_device_resume(struct usb_device *udev)
 			"gone after usb resume? status %d\n",
 			status);
 	else if (udev->actconfig) {
-		unsigned	i;
-		int		(*resume)(struct device *);
-
 		le16_to_cpus(&devstatus);
 		if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
 				&& udev->parent) {
@@ -1811,24 +1658,9 @@ static int finish_device_resume(struct usb_device *udev)
 					USB_DEVICE_REMOTE_WAKEUP, 0,
 					NULL, 0,
 					USB_CTRL_SET_TIMEOUT);
-			if (status) {
+			if (status)
 				dev_dbg(&udev->dev, "disable remote "
 					"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;
 
@@ -1839,8 +1671,6 @@ static int finish_device_resume(struct usb_device *udev)
 	return status;
 }
 
-#ifdef	CONFIG_USB_SUSPEND
-
 static int
 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);
 
+	set_bit(port1, hub->busy_bits);
+
 	/* see 7.1.7.7; affects power usage, but not budgeting */
 	status = clear_port_feature(hub->hdev,
 			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 */
 		if (udev)
-			dev_dbg(&udev->dev, "RESUME\n");
+			dev_dbg(&udev->dev, "usb %sresume\n",
+					udev->auto_pm ? "auto-" : "");
 		msleep(25);
 
 #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 */
 			msleep(10);
 			if (udev)
-				status = finish_device_resume(udev);
+				status = finish_port_resume(udev);
 		}
 	}
 	if (status < 0)
 		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;
 }
 
-#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
  * 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.
  */
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
 {
 	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) {
-#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;
 }
 
@@ -1952,23 +1775,60 @@ static int remote_wakeup(struct usb_device *udev)
 {
 	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);
+	mutex_lock_nested(&udev->pm_mutex, udev->level);
 	if (udev->state == USB_STATE_SUSPENDED) {
-		dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
 		/* TRSMRCY = 10 msec */
 		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);
-#endif
 	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)
 {
 	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;
 
 		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
-				|| udev->state != USB_STATE_SUSPENDED
+				udev->state != USB_STATE_SUSPENDED
+#else
+				udev->dev.power.power_state.event
+					== PM_EVENT_ON
 #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;
 		}
 	}
@@ -2035,66 +1899,22 @@ static int hub_resume(struct usb_interface *intf)
 		}
 	}
 
+	/* tell khubd to look for changes on this 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;
 }
 
-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)
 {
 	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;
 	unsigned		delay = HUB_SHORT_RESET_TIME;
 	enum usb_device_speed	oldspeed = udev->speed;
+	char 			*speed, *type;
 
 	/* root hub ports have a slightly longer reset period
 	 * (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 ...
 	 * 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) {
+	case USB_SPEED_VARIABLE:	/* fixed at 512 */
+		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
+		break;
 	case USB_SPEED_HIGH:		/* fixed at 64 */
 		udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
 		break;
@@ -2265,17 +2091,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 		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,
-			"%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  */
 	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
 			 * early status stage.  Also, retry on all errors;
 			 * 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) {
 				buf->bMaxPacketSize0 = 0;
@@ -2326,7 +2158,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 					buf, GET_DESCRIPTOR_BUFSIZE,
 					(i ? USB_CTRL_GET_TIMEOUT : 1000));
 				switch (buf->bMaxPacketSize0) {
-				case 8: case 16: case 32: case 64:
+				case 8: case 16: case 32: case 64: case 255:
 					if (buf->bDescriptorType ==
 							USB_DT_DEVICE) {
 						r = 0;
@@ -2400,7 +2232,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 	if (retval)
 		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 (udev->speed != USB_SPEED_FULL ||
 				!(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);
 		udev->speed = USB_SPEED_UNKNOWN;
  		udev->bus_mA = hub->mA_per_port;
+		udev->level = hdev->level + 1;
 
 		/* set the address */
 		choose_address(udev);
@@ -2736,17 +2570,6 @@ static void hub_events(void)
 		usb_get_intf(intf);
 		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
 		 * disconnected while waiting for the lock to succeed. */
 		if (locktree(hdev) < 0) {
@@ -2763,6 +2586,13 @@ static void hub_events(void)
 			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 (hub->quiescing)
 			goto loop;
@@ -2900,7 +2730,7 @@ static void hub_events(void)
 
 		/* If this is a root hub, tell the HCD it's okay to
 		 * re-enable port-change interrupts now. */
-		if (!hdev->parent)
+		if (!hdev->parent && !hub->busy_bits[0])
 			usb_enable_root_hub_irq(hdev->bus);
 
 loop:
@@ -3075,6 +2905,9 @@ int usb_reset_device(struct usb_device *udev)
 			break;
 	}
 	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)
 		goto re_enumerate;
  
@@ -3128,6 +2961,7 @@ re_enumerate:
 	hub_port_logical_disconnect(parent_hub, port1);
 	return -ENODEV;
 }
+EXPORT_SYMBOL(usb_reset_device);
 
 /**
  * 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;
 	}
 
+	/* Prevent autosuspend during the reset */
+	usb_autoresume_device(udev, 1);
+
 	if (iface && iface->condition != USB_INTERFACE_BINDING)
 		iface = NULL;
 
@@ -3204,5 +3041,7 @@ int usb_reset_composite_device(struct usb_device *udev,
 		}
 	}
 
+	usb_autosuspend_device(udev, 1);
 	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		change_bits[1];	/* ports with logical connect
 							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 */
 #error event_bits[] is too short!
 #endif

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

@@ -44,7 +44,7 @@
 #include "hcd.h"
 
 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 int usbfs_mount_count;	/* = 0 */
 static int ignore_mount = 0;
@@ -407,7 +407,7 @@ static int default_open (struct inode *inode, struct file *file)
 	return 0;
 }
 
-static struct file_operations default_file_operations = {
+static const struct file_operations default_file_operations = {
 	.read =		default_read_file,
 	.write =	default_write_file,
 	.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,
 				      struct dentry *parent, void *data,
-				      struct file_operations *fops,
+				      const struct file_operations *fops,
 				      uid_t uid, gid_t gid)
 {
 	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); 	
 	urb->context = &done;
 	urb->actual_length = 0;
 	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;
-		/* 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)
 		*actual_length = urb->actual_length;
+
 	usb_free_urb(urb);
 	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)
 {
-	struct usb_sg_request	*io = (struct usb_sg_request *) urb->context;
+	struct usb_sg_request	*io = urb->context;
 
 	spin_lock (&io->lock);
 
@@ -999,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
 		ep = dev->ep_in[epnum];
 		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)
 		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,
 	 * so that if we run out then nothing will have changed. */
 	n = nintf = 0;
@@ -1418,6 +1400,11 @@ free_interfaces:
 					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.
 	 * getting rid of old interfaces means unbinding their drivers.
 	 */
@@ -1437,6 +1424,7 @@ free_interfaces:
 	dev->actconfig = cp;
 	if (!cp) {
 		usb_set_device_state(dev, USB_STATE_ADDRESS);
+		usb_autosuspend_device(dev, 1);
 		goto free_interfaces;
 	}
 	usb_set_device_state(dev, USB_STATE_CONFIGURED);
@@ -1505,8 +1493,68 @@ free_interfaces:
 		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;
 }
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 
 // synchronous request completion model
 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)
 {
+	/* Protect against simultaneous usbfs open */
+	mutex_lock(&usbfs_mutex);
 	blocking_notifier_call_chain(&usb_notifier_list,
 			USB_DEVICE_REMOVE, udev);
+	mutex_unlock(&usbfs_mutex);
 }
 
 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,
 		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;
 
 	if (sscanf (buf, "%u", &config) != 1 || config > 255)
@@ -186,6 +186,7 @@ usb_descriptor_attr (bMaxPacketSize0, "%d\n")
 
 static struct attribute *dev_attrs[] = {
 	/* current configuration's attributes */
+	&dev_attr_configuration.attr,
 	&dev_attr_bNumInterfaces.attr,
 	&dev_attr_bConfigurationValue.attr,
 	&dev_attr_bmAttributes.attr,
@@ -209,20 +210,40 @@ static struct attribute_group dev_attr_grp = {
 	.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;
+	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)
@@ -238,7 +259,6 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
 		device_remove_file(dev, &dev_attr_product);
 	if (udev->serial)
 		device_remove_file(dev, &dev_attr_serial);
-	device_remove_file (dev, &dev_attr_configuration);
 }
 
 /* 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]);
 }
 
-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_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)
 		alt->string = usb_cache_string(udev, alt->desc.iInterface);
 	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);
+	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)

+ 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;
 
-	urb = (struct urb *)kmalloc(sizeof(struct urb) + 
+	urb = kmalloc(sizeof(struct urb) +
 		iso_packets * sizeof(struct usb_iso_packet_descriptor),
 		mem_flags);
 	if (!urb) {
@@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 {
 	int			pipe, temp, max;
 	struct usb_device	*dev;
-	struct usb_operations	*op;
 	int			is_out;
 
 	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
 			|| dev->state == USB_STATE_SUSPENDED)
 		return -EHOSTUNREACH;
-	if (!(op = dev->bus->op) || !op->submit_urb)
-		return -ENODEV;
 
 	urb->status = -EINPROGRESS;
 	urb->actual_length = 0;
@@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
 		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)
 		return -EINVAL;
-	if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+	if (!(urb->dev && urb->dev->bus))
 		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)
 {
 	might_sleep();
-	if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+	if (!(urb && urb->dev && urb->dev->bus))
 		return;
 	spin_lock_irq(&urb->lock);
 	++urb->reject;
 	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);
 
 	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
  * 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;
 	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
  * 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;
 
@@ -112,87 +113,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
 	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 {
 	int minor;
 	struct usb_interface *interface;
@@ -204,7 +124,7 @@ static int __find_interface(struct device * dev, void * data)
 	struct usb_interface *intf;
 
 	/* can't look at usb devices, only interfaces */
-	if (dev->driver == &usb_generic_driver)
+	if (is_usb_device(dev))
 		return 0;
 
 	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 find_interface_arg argb;
+	int retval;
 
 	argb.minor = minor;
 	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;
 }
 
-#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.
  * @dev: device that's been disconnected
@@ -361,14 +170,33 @@ static void usb_release_dev(struct device *dev)
 
 	udev = to_usb_device(dev);
 
+#ifdef	CONFIG_PM
+	cancel_delayed_work(&udev->autosuspend);
+	flush_scheduled_work();
+#endif
 	usb_destroy_configuration(udev);
-	usb_bus_put(udev->bus);
+	usb_put_hcd(bus_to_hcd(udev->bus));
 	kfree(udev->product);
 	kfree(udev->manufacturer);
 	kfree(udev->serial);
 	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)
  * @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)
 		return NULL;
 
-	bus = usb_bus_get(bus);
-	if (!bus) {
+	if (!usb_get_hcd(bus_to_hcd(bus))) {
 		kfree(dev);
 		return NULL;
 	}
@@ -399,11 +226,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 	device_initialize(&dev->dev);
 	dev->dev.bus = &usb_bus_type;
 	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->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);
 	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
 	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;
 	INIT_LIST_HEAD(&dev->filelist);
 
+#ifdef	CONFIG_PM
+	mutex_init(&dev->pm_mutex);
+	INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
 	return dev;
 }
 
@@ -549,7 +381,7 @@ void usb_put_intf(struct usb_interface *intf)
  * case the driver already owns the device lock.)
  */
 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;
 
@@ -672,7 +504,139 @@ exit:
  */
 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
 )
 {
-	if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+	if (!dev || !dev->bus)
 		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
 )
 {
-	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().
  */
-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 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
  * 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 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().
  */
-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 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);
 }
 
-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 */
 __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();
 	if (retval)
 		goto hub_init_failed;
-	retval = driver_register(&usb_generic_driver);
+	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
 	if (!retval)
 		goto out;
 
@@ -1171,7 +1027,7 @@ static void __exit usb_exit(void)
 	if (nousb)
 		return;
 
-	driver_unregister(&usb_generic_driver);
+	usb_deregister_device_driver(&usb_generic_driver);
 	usb_major_cleanup();
 	usbfs_cleanup();
 	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_driver_claim_interface);
-EXPORT_SYMBOL(usb_driver_release_interface);
 EXPORT_SYMBOL(usb_find_interface);
 EXPORT_SYMBOL(usb_ifnum_to_if);
 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_find_device);
 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_free);
 

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

@@ -1,10 +1,10 @@
 /* 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_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_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);
 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 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 int  usb_hub_init(void);
@@ -30,28 +29,74 @@ extern void usb_major_cleanup(void);
 extern int usb_host_init(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 */
 
 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)
 {
-	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;
 
 /* usbfs stuff */
+extern struct mutex usbfs_mutex;
 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 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
 	   talking to it.  Peripheral controllers are often discrete silicon,
 	   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
 	   motherboards.
 
@@ -404,6 +404,20 @@ config USB_G_SERIAL
 	  which includes instructions and a "driver info file" needed to
 	  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
 # 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_ether-objs			:= ether.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
 g_file_storage-objs		:= file_storage.o usbstring.o config.o \
 					epautoconf.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_USB_ETH)		+= g_ether.o
 obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.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);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_udc_open,
 	.read		= seq_read,
 	.llseek		= seq_lseek,

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

@@ -889,11 +889,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
 static void
 dummy_gadget_release (struct device *dev)
 {
-#if 0		/* usb_bus_put isn't EXPORTed! */
 	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)
@@ -915,9 +913,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
 	if (rc < 0)
 		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);
 	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
 #endif
 
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
 #define DEV_CONFIG_CDC
 #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)
 {
 	struct eth_dev          *dev = netdev_priv(net);
-	u32                     length;
+	int                     length;
 	struct usb_request      *resp = dev->stat_req;
 
 	/* in case RNDIS calls this after disconnect */
@@ -2230,6 +2230,9 @@ eth_bind (struct usb_gadget *gadget)
 	if (gadget_is_pxa (gadget)) {
 		/* pxa doesn't support altsettings */
 		cdc = 0;
+	} else if (gadget_is_musbhdrc(gadget)) {
+		/* reduce tx dma overhead by avoiding special cases */
+		zlp = 0;
 	} else if (gadget_is_sh(gadget)) {
 		/* sh doesn't support multiple interfaces or configs */
 		cdc = 0;
@@ -2564,7 +2567,7 @@ static struct usb_gadget_driver eth_driver = {
 
 	.function	= (char *) driver_desc,
 	.bind		= eth_bind,
-	.unbind		= __exit_p(eth_unbind),
+	.unbind		= eth_unbind,
 
 	.setup		= eth_setup,
 	.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 <asm/uaccess.h>
 #include <linux/slab.h>
+#include <linux/poll.h>
 
 #include <linux/device.h>
 #include <linux/moduleparam.h>
@@ -222,7 +223,6 @@ static void put_ep (struct ep_data *data)
 	/* needs no more cleanup */
 	BUG_ON (!list_empty (&data->epfiles));
 	BUG_ON (waitqueue_active (&data->wait));
-	BUG_ON (down_trylock (&data->lock) != 0);
 	kfree (data);
 }
 
@@ -477,6 +477,10 @@ static int
 ep_release (struct inode *inode, struct file *fd)
 {
 	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 */
 	if (data->state != STATE_EP_UNBOUND) {
@@ -485,6 +489,7 @@ ep_release (struct inode *inode, struct file *fd)
 		data->hs_desc.bDescriptorType = 0;
 		usb_ep_disable(data->ep);
 	}
+	up (&data->lock);
 	put_ep (data);
 	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 */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
 	.owner =	THIS_MODULE,
 	.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 usb_ep		*ep;
 	u32			tag;
-	int			value;
+	int			value, length = len;
 
 	if ((value = down_interruptible (&data->lock)) < 0)
 		return value;
@@ -792,7 +797,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 			goto fail0;
 		}
 	}
-	value = len;
 
 	spin_lock_irq (&data->dev->lock);
 	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->state = STATE_EP_DEFER_ENABLE;
 	}
-	if (value == 0)
+	if (value == 0) {
 		fd->f_op = &ep_io_operations;
+		value = length;
+	}
 gone:
 	spin_unlock_irq (&data->dev->lock);
 	if (value < 0) {
@@ -867,7 +873,7 @@ ep_open (struct inode *inode, struct file *fd)
 }
 
 /* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 
@@ -1009,7 +1015,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 			else {
 				len = min (len, (size_t)dev->req->actual);
 // 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;
 				clean_req (dev->gadget->ep0, dev->req);
 				/* NOTE userspace can't yet choose to stall */
@@ -1229,6 +1235,35 @@ dev_release (struct inode *inode, struct file *fd)
 	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,
 		unsigned code, unsigned long value)
 {
@@ -1241,14 +1276,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
 }
 
 /* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 
 	.read =		ep0_read,
 	.write =	ep0_write,
 	.fasync =	ep0_fasync,
-	// .poll =	ep0_poll,
+	.poll =		ep0_poll,
 	.ioctl =	dev_ioctl,
 	.release =	dev_release,
 };
@@ -1696,16 +1731,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
 {
 	struct dev_data		*dev = get_gadget_data (gadget);
 
+	spin_lock (&dev->lock);
 	if (dev->state == STATE_UNCONNECTED) {
 		DBG (dev, "already unconnected\n");
-		return;
+		goto exit;
 	}
 	dev->state = STATE_UNCONNECTED;
 
 	INFO (dev, "disconnected\n");
-	spin_lock (&dev->lock);
 	next_event (dev, GADGETFS_DISCONNECT);
 	ep0_readable (dev);
+exit:
 	spin_unlock (&dev->lock);
 }
 
@@ -1922,7 +1958,7 @@ dev_open (struct inode *inode, struct file *fd)
 	return value;
 }
 
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
 

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

@@ -2,7 +2,7 @@
  * Driver for the PLX NET2280 USB device controller.
  * Specs and errata are available from <http://www.plxtech.com>.
  *
- * PLX Technology Inc. (formerly NetChip Technology) supported the 
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
  * development of this driver.
  *
  *
@@ -26,7 +26,8 @@
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003-2005 PLX Technology, Inc.
  *
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ *	with 2282 chip
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
 static const char driver_desc [] = DRIVER_DESC;
 
 static const char ep0name [] = "ep0";
-static const char *ep_name [] = {
+static const char *const ep_name [] = {
 	ep0name,
 	"ep-a", "ep-b", "ep-c", "ep-d",
 	"ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	if (!ep->is_in)
 		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
 	else if (dev->pdev->device != 0x2280) {
-		/* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
+		/* Added for 2282, Don't use nak packets on an in endpoint,
+		 * this was ignored on 2280
+		 */
 		writel ((1 << CLEAR_NAK_OUT_PACKETS)
 			| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
 	}
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
 	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)
 {
@@ -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 *
 net2280_alloc_buffer (
 	struct usb_ep		*_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
 		return NULL;
 	*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,
 				bytes, dma, gfp_flags);
-	} else
+	else
 		retval = kmalloc(bytes, gfp_flags);
-#endif
 	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
 net2280_free_buffer (
 	struct usb_ep *_ep,
-	void *buf,
+	void *address,
 	dma_addr_t dma,
 	unsigned bytes
 ) {
 	/* free memory into the right allocator */
-#ifndef	USE_KMALLOC
 	if (dma != DMA_ADDR_INVALID) {
 		struct net2280_ep	*ep;
+		struct free_record	*buf = address;
+		unsigned long		flags;
 
 		ep = container_of(_ep, struct net2280_ep, ep);
 		if (!_ep)
 			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
-#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)
 		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);
 
 	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 */
 	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),
 			&ep->regs->ep_stat);
 
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
 	(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,
 	.disable	= net2280_disable,
 
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
 	}
 
 	/* Indexed Registers */
-		// none yet 
+		// none yet
 
 	/* Statistics */
 	t = scnprintf (next, size, "\nirqs:  ");
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
 				({ char *val;
 				 switch (d->bmAttributes & 0x03) {
 				 case USB_ENDPOINT_XFER_BULK:
-				 	val = "bulk"; break;
+					val = "bulk"; break;
 				 case USB_ENDPOINT_XFER_INT:
-				 	val = "intr"; break;
+					val = "intr"; break;
 				 default:
-				 	val = "iso"; break;
+					val = "iso"; break;
 				 }; val; }),
 				le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
 				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
  * @gadget: access to the net2280 device that will be updated
  * @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- * 	1 for two 2kB buffers (ep-a and ep-b only);
- * 	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ *	1 for two 2kB buffers (ep-a and ep-b only);
+ *	2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
  *
  * returns zero on success, else negative errno.  when this succeeds,
  * the contents of gadget->ep_list may have changed.
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
 				req->td->dmacount = 0;
 				t = readl (&ep->regs->ep_avail);
 				dma_done (ep, req, count,
-					(ep->out_overflow || t) ? -EOVERFLOW : 0);
+					(ep->out_overflow || t)
+						? -EOVERFLOW : 0);
 			}
 
 			/* 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);
 		u.raw [0] = readl (&dev->usb->setup0123);
 		u.raw [1] = readl (&dev->usb->setup4567);
-		
+
 		cpu_to_le32s (&u.raw [0]);
 		cpu_to_le32s (&u.raw [1]);
 
@@ -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.
 	 * 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).
 	 */
 	if (stat & tmp) {
 		writel (tmp, &dev->regs->irqstat1);
-		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && 
-				((readl (&dev->usb->usbstat) & mask) == 0))
-				|| ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) 
+		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+					&& ((readl (&dev->usb->usbstat) & mask)
+							== 0))
+				|| ((readl (&dev->usb->usbctl)
+					& (1 << VBUS_PIN)) == 0)
 			    ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
 			DEBUG (dev, "disconnect %s\n",
 					dev->driver->driver.name);
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 
 	/* now all the pci goodies ... */
 	if (pci_enable_device (pdev) < 0) {
-   	        retval = -ENODEV;
+	        retval = -ENODEV;
 		goto done;
 	}
 	dev->enabled = 1;
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 	dev->region = 1;
 
+	/* FIXME provide firmware download interface to put
+	 * 8051 code into the chip, e.g. to turn on PCI PM.
+	 */
+
 	base = ioremap_nocache (resource, len);
 	if (base == NULL) {
 		DEBUG (dev, "can't map memory\n");
@@ -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,
 	.device =	0x2280,
 	.subvendor =	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,
 	.device =	0x2282,
 	.subvendor =	PCI_ANY_ID,

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

@@ -40,7 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.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);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_udc_open,
 	.read		= seq_read,
 	.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 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)
 {
         bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
 
 #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)
 {
 	struct pxa2xx_udc *dev = &memory;
-	int retval, out_dma = 1;
+	int retval, out_dma = 1, vbus_irq;
 	u32 chiprev;
 
 	/* 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 */
 	dev->dev = &pdev->dev;
 	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);
 	dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ lubbock_fail0:
 		HEX_DISPLAY(dev->stats.irqs);
 		LUB_DISC_BLNK_LED &= 0xff;
 #endif
-	}
+	} else
 #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();
 
 	return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
 		free_irq(LUBBOCK_USB_IRQ, dev);
 	}
 #endif
+	if (dev->mach->gpio_vbus)
+		free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
 	platform_set_drvdata(pdev, NULL);
 	the_controller = NULL;
 	return 0;

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

@@ -177,27 +177,19 @@ struct pxa2xx_udc {
 
 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));
 			list_del(&req_entry->re_entry);
 			req->length = len;
+			spin_unlock_irqrestore(&dev->dev_lock, flags);
 			if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
 				printk(KERN_ERR
 				"gs_send: cannot queue read request, ret=%d\n",
 					ret);
+				spin_lock_irqsave(&dev->dev_lock, flags);
 				break;
 			}
+			spin_lock_irqsave(&dev->dev_lock, flags);
 		} else {
 			break;
 		}

+ 29 - 0
drivers/usb/host/Kconfig

@@ -83,6 +83,7 @@ config USB_OHCI_HCD
 	tristate "OHCI HCD support"
 	depends on USB && USB_ARCH_HAS_OHCI
 	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+	select I2C if ARCH_PNX4008
 	---help---
 	  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
@@ -141,6 +142,34 @@ config USB_UHCI_HCD
 	  To compile this driver as a module, choose M here: the
 	  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
 	tristate "SL811HS HCD support"
 	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_SL811_HCD)	+= sl811-hcd.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

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

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

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

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -65,7 +65,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
 		for (i = 0; i < HCS_N_PORTS (params); i++) {
 			// FIXME MIPS won't readb() ...
 			byte = readb (&ehci->caps->portroute[(i>>1)]);
-			sprintf(tmp, "%d ", 
+			sprintf(tmp, "%d ",
 				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
 			strcat(buf, tmp);
 		}
@@ -141,12 +141,12 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 }
 
 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",
 		label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
 	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[1]),
 		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[7]));
 	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[1]),
 		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__))
-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",
 		label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
 	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_uframe),
 		le32_to_cpu(sitd->hw_results),
@@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf)
 	*buf = 0;
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	size = PAGE_SIZE;
@@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf)
 	seen_count = 0;
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	size = PAGE_SIZE;
@@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	static char		label [] = "";
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ehci = hcd_to_ehci (hcd);
 	next = buf;
 	size = PAGE_SIZE;
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	}
 
 	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;
 		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)
 {
 	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)

+ 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,
 #endif
 	.stop = ehci_stop,
+	.shutdown = ehci_shutdown,
 
 	/*
 	 * managing i/o requests and associated device resources
@@ -329,6 +330,7 @@ MODULE_ALIAS("fsl-ehci");
 static struct platform_driver ehci_fsl_driver = {
 	.probe = ehci_fsl_drv_probe,
 	.remove = ehci_fsl_drv_remove,
+	.shutdown = usb_hcd_platform_shutdown,
 	.driver = {
 		   .name = "fsl-ehci",
 		   },

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

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2000-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -70,7 +70,7 @@
  * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
  *	only scheduling is different, no arbitrary limitations.
  * 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-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *	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_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_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #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);
 
 #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;
 	unsigned long		flags;
+	u32			status;
 
 	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) {
-		u32		status = readl (&ehci->regs->status);
-
+		status = readl (&ehci->regs->status);
 		if (status & STS_IAA) {
 			ehci_vdbg (ehci, "lost IAA\n");
 			COUNT (ehci->stats.lost_iaa);
 			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))
- 		start_unlink_async (ehci, ehci->async);
+		start_unlink_async (ehci, ehci->async);
 
 	/* ehci could run by timer, without IRQs ... */
 	ehci_work (ehci, NULL);
@@ -292,21 +304,20 @@ static void ehci_watchdog (unsigned long param)
 	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
  * 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);
 
 	/* make BIOS/etc use companion controller during reboot */
 	writel (0, &ehci->regs->configured_flag);
-	return 0;
 }
 
 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)
 {
 	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
 	 * it reports urb completions.  this flag guards against bogus
@@ -370,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
 
 	/* no more interrupts ... */
 	del_timer_sync (&ehci->watchdog);
+	del_timer_sync (&ehci->iaa_watchdog);
 
 	spin_lock_irq(&ehci->lock);
 	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 */
 	writel (0, &ehci->regs->configured_flag);
-	unregister_reboot_notifier (&ehci->reboot_notifier);
 
 	remove_debug_files (ehci);
 
@@ -417,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
 	ehci->watchdog.function = ehci_watchdog;
 	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.
 	 * 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 ... */
 	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;
 	else					// N microframes cached
 		ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
 	ehci->reclaim = NULL;
-	ehci->reclaim_ready = 0;
 	ehci->next_uframe = -1;
 
 	/*
@@ -483,9 +495,6 @@ static int ehci_init(struct usb_hcd *hcd)
 	}
 	ehci->command = temp;
 
-	ehci->reboot_notifier.notifier_call = ehci_reboot;
-	register_reboot_notifier(&ehci->reboot_notifier);
-
 	return 0;
 }
 
@@ -499,7 +508,6 @@ static int ehci_run (struct usb_hcd *hcd)
 
 	/* EHCI spec section 4.1 */
 	if ((retval = ehci_reset(ehci)) != 0) {
-		unregister_reboot_notifier(&ehci->reboot_notifier);
 		ehci_mem_cleanup(ehci);
 		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] */
 	if (status & STS_IAA) {
 		COUNT (ehci->stats.reclaim);
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, regs);
 		bh = 1;
 	}
 
@@ -715,10 +723,14 @@ static int ehci_urb_enqueue (
 
 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;
 
 		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;
 		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);
 }
 
@@ -755,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 		qh = (struct ehci_qh *) urb->hcpriv;
 		if (!qh)
 			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;
 
 	case PIPE_INTERRUPT:
@@ -847,6 +867,7 @@ rescan:
 		unlink_async (ehci, qh);
 		/* FALL THROUGH */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
+	case QH_STATE_UNLINK_WAIT:
 idle_timeout:
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		schedule_timeout_uninterruptible(1);

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

@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2001-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 	}
 	ehci->command = readl (&ehci->regs->command);
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, NULL);
 	ehci_work(ehci, NULL);
 
 	/* 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 */
 	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.
- 		 */
- 		intr_enable = 1;
+		 */
+		intr_enable = 1;
 		writel (0, &ehci->regs->segment);
 		writel (ehci->periodic_dma, &ehci->regs->frame_list);
 		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;
 		retval++;
 	}
-	
+
 	/* no hub change reports (bit 0) for now (power, ...) */
 
 	/* 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 (
 	struct usb_hcd	*hcd,

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

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -25,7 +25,7 @@
  *	- data used only by the HCD ... kmalloc is fine
  *	- async and periodic schedules, shared by HC and HCD ... these
  *	  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.
  * 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.
  * 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;
 
 	/* 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,
 			sizeof (struct ehci_qtd),
 			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 */
-	ehci->qh_pool = dma_pool_create ("ehci_qh", 
+	ehci->qh_pool = dma_pool_create ("ehci_qh",
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_qh),
 			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 */
-	ehci->itd_pool = dma_pool_create ("ehci_itd", 
+	ehci->itd_pool = dma_pool_create ("ehci_itd",
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_itd),
 			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 */
-	ehci->sitd_pool = dma_pool_create ("ehci_sitd", 
+	ehci->sitd_pool = dma_pool_create ("ehci_sitd",
 			ehci_to_hcd(ehci)->self.controller,
 			sizeof (struct ehci_sitd),
 			32 /* byte alignment (for hw parts) */,

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

@@ -303,7 +303,7 @@ restart:
 	/* emptying the schedule aborts any urbs */
 	spin_lock_irq(&ehci->lock);
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async (ehci, NULL);
 	ehci_work(ehci, NULL);
 	spin_unlock_irq(&ehci->lock);
 
@@ -338,6 +338,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
 	.resume =		ehci_pci_resume,
 #endif
 	.stop =			ehci_stop,
+	.shutdown =		ehci_shutdown,
 
 	/*
 	 * managing i/o requests and associated device resources
@@ -384,4 +385,5 @@ static struct pci_driver ehci_pci_driver = {
 	.suspend =	usb_hcd_pci_suspend,
 	.resume =	usb_hcd_pci_resume,
 #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
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -31,7 +31,7 @@
  * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
  * interrupts) needs careful scheduling.  Performance improvements can be
  * an ongoing challenge.  That's in "ehci-sched.c".
- * 
+ *
  * 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
  * (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
 					|| QTD_CERR(token) == 0)
 				&& (!ehci_is_TDI(ehci)
-                	                || urb->dev->tt->hub !=
+			                || urb->dev->tt->hub !=
 					   ehci_to_hcd(ehci)->self.root_hub)) {
 #ifdef DEBUG
 			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))
 				continue;
-			
+
 			/* issue status after short control reads */
 			if (unlikely (do_status != 0)
 					&& QTD_PID (token) == 0 /* OUT */) {
@@ -388,7 +388,7 @@ halt:
 				wmb ();
 			}
 		}
- 
+
 		/* remove it from the queue */
 		spin_lock (&urb->lock);
 		qtd_copy_status (ehci, urb, qtd->length, token);
@@ -518,7 +518,7 @@ qh_urb_transaction (
 		/* for zero length DATA stages, STATUS is always IN */
 		if (len == 0)
 			token |= (1 /* "in" */ << 8);
-	} 
+	}
 
 	/*
 	 * data transfer stage:  buffer setup
@@ -759,7 +759,7 @@ qh_make (
 		}
 		break;
 	default:
- 		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
 done:
 		qh_put (qh);
 		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		*next;
 
-	timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+	iaa_watchdog_done (ehci);
 
 	// qh->hw_next = cpu_to_le32 (qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
 	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) */
 	next = qh->reclaim;
 	ehci->reclaim = next;
-	ehci->reclaim_ready = 0;
 	qh->reclaim = NULL;
 
 	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);
 		}
 		return;
-	} 
+	}
 
 	qh->qh_state = QH_STATE_UNLINK;
 	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 (qh->reclaim != 0))
-		 * 	this will recurse, probably not much
+		 *	this will recurse, probably not much
 		 */
 		end_unlink_async (ehci, NULL);
 		return;
 	}
 
-	ehci->reclaim_ready = 0;
 	cmd |= CMD_IAAD;
 	writel (cmd, &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) 2003 Michal Sojka, for high-speed iso transfers
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -613,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 /*-------------------------------------------------------------------------*/
 
 static int check_period (
-	struct ehci_hcd *ehci, 
+	struct ehci_hcd *ehci,
 	unsigned	frame,
 	unsigned	uframe,
 	unsigned	period,
@@ -629,7 +629,7 @@ static int check_period (
 
 	/*
 	 * 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;
 
@@ -659,14 +659,14 @@ static int check_period (
 }
 
 static int check_intr_schedule (
-	struct ehci_hcd		*ehci, 
+	struct ehci_hcd		*ehci,
 	unsigned		frame,
 	unsigned		uframe,
 	const struct ehci_qh	*qh,
 	__le32			*c_maskp
 )
 {
-    	int		retval = -ENOSPC;
+	int		retval = -ENOSPC;
 	u8		mask = 0;
 
 	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.
 	 * We pessimize a bit; probably the typical full speed case
 	 * doesn't need the second CSPLIT.
-	 * 
+	 *
 	 * NOTE:  both SPLIT and CSPLIT could be checked in just
 	 * one smart pass...
 	 */
@@ -728,7 +728,7 @@ done:
  */
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	int 		status;
+	int		status;
 	unsigned	uframe;
 	__le32		c_mask;
 	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);
 
 	/* stuff into the periodic schedule */
- 	status = qh_link_periodic (ehci, qh);
+	status = qh_link_periodic (ehci, qh);
 done:
 	return status;
 }
@@ -1681,7 +1681,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
 		status = -ESHUTDOWN;
 	else
 		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);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 
@@ -1738,7 +1738,7 @@ sitd_sched_init (
 		if (packet->buf1 != (buf & ~(u64)0x0fff))
 			packet->cross = 1;
 
-		/* OUT uses multiple start-splits */ 
+		/* OUT uses multiple start-splits */
 		if (stream->bEndpointAddress & USB_DIR_IN)
 			continue;
 		length = (length + 187) / 188;
@@ -1925,7 +1925,7 @@ sitd_link_urb (
 /*-------------------------------------------------------------------------*/
 
 #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
 sitd_complete (
@@ -2043,7 +2043,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
 		status = -ESHUTDOWN;
 	else
 		status = iso_stream_schedule (ehci, urb, stream);
- 	if (status == 0)
+	if (status == 0)
 		sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
 	spin_unlock_irqrestore (&ehci->lock, flags);
 
@@ -2226,5 +2226,5 @@ restart:
 			now_uframe++;
 			now_uframe %= mod;
 		}
-	} 
+	}
 }

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

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2001-2002 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -58,7 +58,6 @@ struct ehci_hcd {			/* one per controller */
 	/* async schedule support */
 	struct ehci_qh		*async;
 	struct ehci_qh		*reclaim;
-	unsigned		reclaim_ready : 1;
 	unsigned		scanning : 1;
 
 	/* 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		*sitd_pool;	/* sitd per split iso urb */
 
+	struct timer_list	iaa_watchdog;
 	struct timer_list	watchdog;
-	struct notifier_block	reboot_notifier;
 	unsigned long		actions;
 	unsigned		stamp;
 	unsigned long		next_statechange;
@@ -104,7 +103,7 @@ struct ehci_hcd {			/* one per controller */
 #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)
 {
 	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 {
 	TIMER_IO_WATCHDOG,
-	TIMER_IAA_WATCHDOG,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_OFF,
 };
@@ -135,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
 		unsigned long t;
 
 		switch (action) {
-		case TIMER_IAA_WATCHDOG:
-			t = EHCI_IAA_JIFFIES;
-			break;
 		case TIMER_IO_WATCHDOG:
 			t = EHCI_IO_JIFFIES;
 			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
 		// to go OFF neither can matter, and afterwards the IO
 		// 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))
 			return;
 		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_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_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 */
 
 	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_IAAD	(1<<6)		/* "doorbell" interrupt async advance */
 #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 */
 #define CMD_RESET	(1<<1)		/* reset HC not bus */
 #define CMD_RUN		(1<<0)		/* start/stop HC */
@@ -231,9 +238,9 @@ struct ehci_regs {
 	/* FRINDEX: offset 0x0C */
 	u32		frame_index;	/* current microframe number */
 	/* CTRLDSSEGMENT: offset 0x10 */
-	u32		segment; 	/* address bits 63:32 if needed */
+	u32		segment;	/* address bits 63:32 if needed */
 	/* PERIODICLISTBASE: offset 0x14 */
-	u32		frame_list; 	/* points to periodic list */
+	u32		frame_list;	/* points to periodic list */
 	/* ASYNCLISTADDR: offset 0x18 */
 	u32		async_next;	/* address of next async queue head */
 
@@ -302,7 +309,7 @@ struct ehci_dbg_port {
 
 /*
  * 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".
  *
  * These are associated only with "QH" (Queue Head) structures,
@@ -312,7 +319,7 @@ struct ehci_qtd {
 	/* first part defined by EHCI spec */
 	__le32			hw_next;	  /* see EHCI 3.5.1 */
 	__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_LENGTH(tok)	(((tok)>>16) & 0x7fff)
 #define	QTD_IOC		(1 << 15)	/* interrupt on complete */
@@ -349,8 +356,8 @@ struct ehci_qtd {
 /* values for that type tag */
 #define Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 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 */
 #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".
  */
 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_sitd	*sitd;		/* Q_TYPE_SITD */
 	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
@@ -397,7 +404,7 @@ struct ehci_qh {
 #define	QH_HUBPORT	0x3f800000
 #define	QH_MULT		0xc0000000
 	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
-	
+
 	/* qtd overlay (hardware parts of a struct ehci_qtd) */
 	__le32			hw_qtd_next;
 	__le32			hw_alt_next;
@@ -472,7 +479,7 @@ struct ehci_iso_stream {
 	struct list_head	td_list;	/* queued itds/sitds */
 	struct list_head	free_list;	/* list of unused itds/sitds */
 	struct usb_device	*udev;
- 	struct usb_host_endpoint *ep;
+	struct usb_host_endpoint *ep;
 
 	/* output of (re)scheduling */
 	unsigned long		start;		/* jiffies */
@@ -492,8 +499,8 @@ struct ehci_iso_stream {
 	unsigned		bandwidth;
 
 	/* This is used to initialize iTD's hw_bufp fields */
-	__le32			buf0;		
-	__le32			buf1;		
+	__le32			buf0;
+	__le32			buf1;
 	__le32			buf2;
 
 	/* 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)
 
-	__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 */
 
 	/* 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
  *       ... describe full speed iso xfers through TT in hubs
  * 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);
 }
 
-static struct file_operations isp116x_debug_fops = {
+static const struct file_operations isp116x_debug_fops = {
 	.open = isp116x_open_seq,
 	.read = seq_read,
 	.llseek = seq_lseek,

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

@@ -233,7 +233,7 @@ static const int cc_to_error[16] = {
 	/* Bit Stuff  */ -EPROTO,
 	/* Data Togg  */ -EILSEQ,
 	/* Stall      */ -EPIPE,
-	/* DevNotResp */ -ETIMEDOUT,
+	/* DevNotResp */ -ETIME,
 	/* PIDCheck   */ -EPROTO,
 	/* UnExpPID   */ -EPROTO,
 	/* 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)
 		return ret;
 
-	root->maxchild = board->ports;
+	ohci->num_ports = board->ports;
 
 	if ((ret = ohci_run(ohci)) < 0) {
 		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,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
-
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
@@ -296,6 +297,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
 	if (!clocked) {
 		clk_enable(iclk);
 		clk_enable(fclk);
+		clocked = 1;
 	}
 
 	return 0;
@@ -310,6 +312,7 @@ MODULE_ALIAS("at91_ohci");
 static struct platform_driver ohci_hcd_at91_driver = {
 	.probe		= ohci_hcd_at91_drv_probe,
 	.remove		= ohci_hcd_at91_drv_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
 	.suspend	= ohci_hcd_at91_drv_suspend,
 	.resume		= ohci_hcd_at91_drv_resume,
 	.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
 	 */
 	.start =		ohci_au1xxx_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_au1xxx_suspend,  -- tbd */
-	/* resume:		ohci_au1xxx_resume,   -- tbd */
-#endif /*CONFIG_PM*/
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_au1xxx_drv_probe,
 	.remove		= ohci_hcd_au1xxx_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_au1xxx_drv_suspend, */
 	/*.resume	= ohci_hcd_au1xxx_drv_resume, */
 	.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;
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 
 	/* display control and bulk lists together, for simplicity */
@@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf)
 	seen_count = 0;
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 	next = buf;
 	size = PAGE_SIZE;
@@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf)
 	u32			rdata;
 
 	bus = class_get_devdata(class_dev);
-	hcd = bus->hcpriv;
+	hcd = bus_to_hcd(bus);
 	ohci = hcd_to_ohci(hcd);
 	regs = ohci->regs;
 	next = buf;
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
 	size -= 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 */
 	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)
 {
 	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");
 }
 

+ 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,
 	.start			= ohci_ep93xx_start,
 	.stop			= ohci_stop,
+	.shutdown		= ohci_shutdown,
 	.urb_enqueue		= ohci_urb_enqueue,
 	.urb_dequeue		= ohci_urb_dequeue,
 	.endpoint_disable	= ohci_endpoint_disable,
 	.get_frame_number	= ohci_get_frame,
 	.hub_status_data	= ohci_hub_status_data,
 	.hub_control		= ohci_hub_control,
+	.hub_irq_enable		= ohci_rhsc_enable,
 #ifdef CONFIG_PM
 	.bus_suspend		= ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_ep93xx_drv_probe,
 	.remove		= ohci_hcd_ep93xx_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
 	.suspend	= ohci_hcd_ep93xx_drv_suspend,
 	.resume		= ohci_hcd_ep93xx_drv_resume,

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

@@ -88,7 +88,7 @@
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
 #include <linux/dma-mapping.h> 
 #include <linux/dmapool.h>
 #include <linux/reboot.h>
@@ -101,7 +101,7 @@
 
 #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_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -110,9 +110,10 @@
 #undef OHCI_VERBOSE_DEBUG	/* not always helpful */
 
 /* 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 \
-	(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__
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,12 +129,13 @@
 
 static const char	hcd_name [] = "ohci_hcd";
 
+#define	STATECHANGE_DELAY	msecs_to_jiffies(300)
+
 #include "ohci.h"
 
 static void ohci_dump (struct ohci_hcd *ohci, int verbose);
 static int ohci_init (struct ohci_hcd *ohci);
 static void ohci_stop (struct usb_hcd *hcd);
-static int ohci_reboot (struct notifier_block *, unsigned long , void *);
 
 #include "ohci-hub.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);
 }
 
-/* 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
  * "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;
 
-	ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+	ohci = hcd_to_ohci (hcd);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
 	ohci_usb_reset (ohci);
 	/* flush the writes */
 	(void) ohci_readl (ohci, &ohci->regs->control);
-	return 0;
 }
 
 /*-------------------------------------------------------------------------*
@@ -446,7 +447,6 @@ static int ohci_init (struct ohci_hcd *ohci)
 
 	disable (ohci);
 	ohci->regs = hcd->regs;
-	ohci->next_statechange = jiffies;
 
 	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
 	 * 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)
 		ohci_stop (hcd);
 	else {
-		register_reboot_notifier (&ohci->reboot_notifier);
 		create_debug_files (ohci);
 	}
 
@@ -637,10 +636,14 @@ retry:
 		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_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;
 
 	/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +651,7 @@ retry:
 
 	/* Choose the interrupts we care about now, others later on demand */
 	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);
 
 	/* handle root hub init quirks ... */
@@ -672,6 +675,7 @@ retry:
 	// flush those writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
+	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 	spin_unlock_irq (&ohci->lock);
 
 	// 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? */
 	} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
 		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) {
 		disable (ohci);
@@ -775,9 +795,10 @@ static void ohci_stop (struct usb_hcd *hcd)
 
 	ohci_usb_reset (ohci);
 	ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
-	
+	free_irq(hcd->irq, hcd);
+	hcd->irq = -1;
+
 	remove_debug_files (ohci);
-	unregister_reboot_notifier (&ohci->reboot_notifier);
 	ohci_mem_cleanup (ohci);
 	if (ohci->hcca) {
 		dma_free_coherent (hcd->self.controller, 
@@ -917,6 +938,10 @@ MODULE_LICENSE ("GPL");
 #include "ohci-at91.c"
 #endif
 
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_S3C2410) \
@@ -928,6 +953,7 @@ MODULE_LICENSE ("GPL");
       || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
       || defined (CONFIG_ARCH_AT91RM9200) \
       || defined (CONFIG_ARCH_AT91SAM9261) \
+      || defined (CONFIG_ARCH_PNX4008) \
 	)
 #error "missing bus glue for ohci-hcd"
 #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
 
 #define OHCI_SCHED_ENABLES \
@@ -123,10 +131,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
 	/* no resumes until devices finish suspending */
 	ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 
+	/* no timer polling */
+	hcd->poll_rh = 0;
+
 done:
-	/* external suspend vs self autosuspend ... same effect */
-	if (status == 0)
-		usb_hcd_suspend_root_hub(hcd);
 	spin_unlock_irqrestore (&ohci->lock, flags);
 	return status;
 }
@@ -256,8 +264,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
 	/* TRSMRCY */
 	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 */
 	enables = 0;
@@ -302,9 +310,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		i, changed = 0, length = 1;
-	int		can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+	int		can_suspend;
 	unsigned long	flags;
 
+	can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
 	spin_lock_irqsave (&ohci->lock, flags);
 
 	/* 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++) {
 		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
 				| RH_PS_OCIC | RH_PS_PRSC)) {
 			changed = 1;
@@ -348,32 +361,41 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 			    buf [1] |= 1 << (i - 7);
 			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;
+	} 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:
 	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
 	 */
-	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");
 		(void) ohci_bus_suspend (hcd);
 		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
 	 */
 	.start =		ohci_lh7a404_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_lh7a404_suspend,  -- tbd */
-	/* resume:		ohci_lh7a404_resume,   -- tbd */
-#endif /*CONFIG_PM*/
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_lh7a404_drv_probe,
 	.remove		= ohci_hcd_lh7a404_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_lh7a404_drv_suspend, */
 	/*.resume	= ohci_hcd_lh7a404_drv_resume, */
 	.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;
 	spin_lock_init (&ohci->lock);
 	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 2000-2005 David Brownell
  * (C) Copyright 2002 Hewlett-Packard Company
- * 
+ *
  * OMAP Bus Glue
  *
  * Modified for OMAP by Tony Lindgren <tony@atomide.com>
@@ -66,15 +66,20 @@ extern int usb_disabled(void);
 extern int ocpi_enable(void);
 
 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)
 {
 	if (on) {
+		clk_enable(usb_dc_ck);
 		clk_enable(usb_host_ck);
 		/* guesstimate for T5 == 1x 32K clock + APLL lock time */
 		udelay(100);
 	} else {
 		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 (machine_is_omap_innovator() && cpu_is_omap1510())
 			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);
 		else if (machine_is_omap_osk())
 			tps65010_set_gpio_out_value(GPIO1, LOW);
 	} else {
 		if (machine_is_omap_innovator() && cpu_is_omap1510())
 			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);
 		else if (machine_is_omap_osk())
 			tps65010_set_gpio_out_value(GPIO1, HIGH);
@@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on)
 	return 0;
 }
 
+#ifdef CONFIG_ARCH_OMAP15XX
 /*
  * 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
  * 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.
  */
 static int omap_1510_local_bus_init(void)
@@ -130,7 +136,7 @@ static int omap_1510_local_bus_init(void)
 	unsigned int tlb;
 	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);
 
 	/* Configure the Local Bus MMU table */
@@ -138,7 +144,7 @@ static int omap_1510_local_bus_init(void)
 		lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
 		physaddr = tlb * 0x00100000 + PHYS_OFFSET;
 		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);
 		omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
 		omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
@@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void)
 
 	return 0;
 }
+#else
+#define omap_1510_local_bus_power(x)	{}
+#define omap_1510_local_bus_init()	{}
+#endif
 
 #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			ret;
 
-	dev_dbg(&pdev->dev, "starting USB Controller\n");
+	dev_dbg(hcd->self.controller, "starting USB Controller\n");
 
 	if (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) {
 			int	status = otg_set_host(ohci->transceiver,
 						&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);
 			if (status) {
 				if (ohci->transceiver)
@@ -208,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 				return status;
 			}
 		} else {
-			dev_err(&pdev->dev, "can't find transceiver\n");
+			dev_err(hcd->self.controller, "can't find transceiver\n");
 			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);
 		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 */
@@ -260,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
 	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);
 }
 
 
 /*-------------------------------------------------------------------------*/
 
-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
  * 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
  * 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)
 {
 	int retval, irq;
@@ -291,12 +300,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	struct ohci_hcd *ohci;
 
 	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);
 		return -ENODEV;
 	}
 
-	if (pdev->resource[0].flags != IORESOURCE_MEM 
+	if (pdev->resource[0].flags != IORESOURCE_MEM
 			|| pdev->resource[1].flags != IORESOURCE_IRQ) {
 		printk(KERN_ERR "hcd probe: invalid resource type\n");
 		return -ENODEV;
@@ -306,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	if (IS_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);
 	if (!hcd) {
 		retval = -ENOMEM;
@@ -325,9 +345,8 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 	ohci = hcd_to_ohci(hcd);
 	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);
 	if (irq < 0) {
@@ -335,15 +354,21 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
 		goto err2;
 	}
 	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:
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 err1:
 	usb_put_hcd(hcd);
 err0:
+	clk_put(usb_dc_ck);
 	clk_put(usb_host_ck);
 	return retval;
 }
@@ -359,31 +384,41 @@ err0:
  * Reverses the effect of usb_hcd_omap_probe(), first invoking
  * the HCD's stop() method.  It is always called from a thread
  * 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);
+	if (ohci->transceiver) {
+		(void) otg_set_host(ohci->transceiver, 0);
+		put_device(ohci->transceiver->dev);
+	}
 	if (machine_is_omap_osk())
 		omap_free_gpio(9);
-	omap_stop_hc(pdev);
 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
+	clk_put(usb_dc_ck);
 	clk_put(usb_host_ck);
 }
 
 /*-------------------------------------------------------------------------*/
 
-static int __devinit
+static int
 ohci_omap_start (struct usb_hcd *hcd)
 {
 	struct omap_usb_config *config;
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		ret;
 
+	if (!host_enabled)
+		return 0;
 	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);
+	}
 
 	if ((ret = ohci_run (ohci)) < 0) {
 		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
 	 */
+	.reset =		ohci_omap_init,
 	.start =		ohci_omap_start,
-	.stop =			ohci_stop,
+	.stop =			ohci_omap_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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)
 {
 	struct usb_hcd		*hcd = platform_get_drvdata(dev);
-	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 
 	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);
 
 	return 0;
@@ -472,7 +505,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
 
 	omap_ohci_clock_power(0);
 	ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
-	dev->power.power_state = PMSG_SUSPEND;
+	dev->dev.power.power_state = PMSG_SUSPEND;
 	return 0;
 }
 
@@ -485,8 +518,8 @@ static int ohci_omap_resume(struct platform_device *dev)
 	ohci->next_statechange = jiffies;
 
 	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;
 }
 
@@ -500,6 +533,7 @@ static int ohci_omap_resume(struct platform_device *dev)
 static struct platform_driver ohci_hcd_omap_driver = {
 	.probe		= ohci_hcd_omap_drv_probe,
 	.remove		= ohci_hcd_omap_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef	CONFIG_PM
 	.suspend	= ohci_omap_suspend,
 	.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,
 	.start =		ohci_pci_start,
+	.stop =			ohci_stop,
+	.shutdown =		ohci_shutdown,
+
 #ifdef	CONFIG_PM
+	/* these suspend/resume entries are for upstream PCI glue ONLY */
 	.suspend =		ohci_pci_suspend,
 	.resume =		ohci_pci_resume,
 #endif
-	.stop =			ohci_stop,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
@@ -229,6 +233,8 @@ static struct pci_driver ohci_pci_driver = {
 	.suspend =	usb_hcd_pci_suspend,
 	.resume =	usb_hcd_pci_resume,
 #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,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_ppc_soc_drv_probe,
 	.remove		= ohci_hcd_ppc_soc_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef	CONFIG_PM
 	/*.suspend	= ohci_hcd_ppc_soc_drv_suspend,*/
 	/*.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,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef  CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_pxa27x_drv_probe,
 	.remove		= ohci_hcd_pxa27x_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 #ifdef CONFIG_PM
 	.suspend	= ohci_hcd_pxa27x_drv_suspend, 
 	.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;
 	}
 
-	usb_clk = clk_get(&dev->dev, "upll");
+	usb_clk = clk_get(&dev->dev, "usb-bus-host");
 	if (IS_ERR(usb_clk)) {
 		dev_err(&dev->dev, "cannot get usb-host clock\n");
 		retval = -ENOENT;
@@ -447,6 +447,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
 	 */
 	.start =		ohci_s3c2410_start,
 	.stop =			ohci_stop,
+	.shutdown = 		ohci_shutdown,
 
 	/*
 	 * 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_control =		ohci_s3c2410_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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 = {
 	.probe		= ohci_hcd_s3c2410_drv_probe,
 	.remove		= ohci_hcd_s3c2410_drv_remove,
+	.shutdown 	= usb_hcd_platform_shutdown,
 	/*.suspend	= ohci_hcd_s3c2410_drv_suspend, */
 	/*.resume	= ohci_hcd_s3c2410_drv_resume, */
 	.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
 	 */
 	.start =		ohci_sa1111_start,
-#ifdef	CONFIG_PM
-	/* suspend:		ohci_sa1111_suspend,  -- tbd */
-	/* resume:		ohci_sa1111_resume,   -- tbd */
-#endif
 	.stop =			ohci_stop,
 
 	/*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.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,
 	/* Data Togg  */               -EILSEQ,
 	/* Stall      */               -EPIPE,
-	/* DevNotResp */               -ETIMEDOUT,
+	/* DevNotResp */               -ETIME,
 	/* PIDCheck   */               -EPROTO,
 	/* UnExpPID   */               -EPROTO,
 	/* DataOver   */               -EOVERFLOW,
@@ -389,8 +389,6 @@ struct ohci_hcd {
 	unsigned long		next_statechange;	/* suspend/resume */
 	u32			fminterval;		/* saved register */
 
-	struct notifier_block	reboot_notifier;
-
 	unsigned long		flags;		/* for HC bugs */
 #define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
 #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" */
 	} else if (++ep->error_count >= 3) {
 		if (status & SL11H_STATMASK_TMOUT)
-			urbstat = -ETIMEDOUT;
+			urbstat = -ETIME;
 		else if (status & SL11H_STATMASK_OVF)
 			urbstat = -EOVERFLOW;
 		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);
 }
 
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
 	.open		= proc_sl811h_open,
 	.read		= seq_read,
 	.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"
 
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#define uhci_debug_operations (* (const struct file_operations *) NULL)
 static struct dentry *uhci_debugfs_root;
 
 #ifdef DEBUG
@@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
 }
 
 #undef uhci_debug_operations
-static struct file_operations uhci_debug_operations = {
+static const struct file_operations uhci_debug_operations = {
 	.owner =	THIS_MODULE,
 	.open =		uhci_debug_open,
 	.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)
 {
 	int status;
+	int i;
 
 	if (inw(port_addr) & (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
 		 * 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);
 }

+ 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 file_operations mdc800_device_ops;
+static const struct file_operations mdc800_device_ops;
 static struct usb_class_driver mdc800_class = {
 	.name =		"mdc800%d",
 	.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 */
-static struct file_operations mdc800_device_ops =
+static const struct file_operations mdc800_device_ops =
 {
 	.owner =	THIS_MODULE,
 	.read =		mdc800_device_read,

+ 26 - 4
drivers/usb/input/Kconfig

@@ -205,10 +205,12 @@ config USB_TOUCHSCREEN
 	depends on USB && INPUT
 	---help---
 	  USB Touchscreen driver for:
-	  - eGalax Touchkit USB
+	  - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
 	  - PanJit TouchSet USB
-	  - 3M MicroTouch USB
+	  - 3M MicroTouch USB (EX II series)
 	  - ITM
+	  - some other eTurboTouch
+	  - Gunze AHL61
 
 	  Have a look at <http://linux.chapter7.ch/touchkit/> for
 	  a usage description and the required user-space stuff.
@@ -218,7 +220,7 @@ config USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_EGALAX
 	default y
-	bool "eGalax device support" if EMBEDDED
+	bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
 	depends on USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_PANJIT
@@ -228,7 +230,7 @@ config USB_TOUCHSCREEN_PANJIT
 
 config USB_TOUCHSCREEN_3M
 	default y
-	bool "3M/Microtouch device support" if EMBEDDED
+	bool "3M/Microtouch EX II series device support" if EMBEDDED
 	depends on USB_TOUCHSCREEN
 
 config USB_TOUCHSCREEN_ITM
@@ -236,6 +238,16 @@ config USB_TOUCHSCREEN_ITM
 	bool "ITM device support" if EMBEDDED
 	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
 	tristate "Yealink usb-p1k voip phone"
 	depends on USB && INPUT && EXPERIMENTAL
@@ -326,3 +338,13 @@ config USB_APPLETOUCH
 
 	  To compile this driver as a module, choose M here: the
 	  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.
+wacom-objs	:= wacom_sys.o wacom_wac.o
 usbhid-objs	:= hid-core.o
 
 # Optional parts of multipart objects.
@@ -44,6 +45,7 @@ obj-$(CONFIG_USB_ACECAD)	+= acecad.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
 obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
 
 ifeq ($(CONFIG_USB_DEBUG),y)
 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;
 
-	if (!(endpoint->bEndpointAddress & 0x80))
-		return -ENODEV;
-
-	if ((endpoint->bmAttributes & 3) != 3)
+	if (!usb_endpoint_is_int_in(endpoint))
 		return -ENODEV;
 
 	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;
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
 		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 */
 			int_in_endpointAddr = endpoint->bEndpointAddress;
 			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_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;
 	}
 	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;
 		case -EILSEQ:		/* 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);
 			hid_io_error(hid);
 			return;
@@ -1535,13 +1536,17 @@ void hid_init_reports(struct hid_device *hid)
 #define USB_VENDOR_ID_GLAB		0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
 #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_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_PHIDGET_MOTORCONTROL	0x0058
 
 #define USB_VENDOR_ID_WISEGROUP		0x0925
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
 #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_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_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.
  */
@@ -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_23, 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_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, 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_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_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_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_PHIDGET_MOTORCONTROL, 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_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_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 + 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 + 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 + 300, 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_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_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_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },

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

@@ -722,7 +722,7 @@ inval:
 	return -EINVAL;
 }
 
-static struct file_operations hiddev_fops = {
+static const struct file_operations hiddev_fops = {
 	.owner =	THIS_MODULE,
 	.read =		hiddev_read,
 	.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:
 		/* success */
 		break;
-	case -ETIMEDOUT:
+	case -ETIME:
 		/* this urb is timing out */
 		dbg("%s - urb timed out - was the device unplugged?",
 		    __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) {
 		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 */
 			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:
 		/* success */
 		break;
-	case -ETIMEDOUT:
+	case -ETIME:
 		/* this urb is timing out */
 		dbg("%s - urb timed out - was the device unplugged?",
 		    __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;
 	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;
 
 	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),

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